feat(drizzle): add uuidv7 support (#16113)
## Goal Let projects use **UUID v7** (time-ordered UUIDs) for collection IDs via `idType: 'uuidv7'` on Postgres-related adapters (and SQLite wiring), while keeping the same storage shape as v4 UUIDs (native `uuid` on Postgres, text on SQLite). ### What? - Adds a new adapter option `idType: 'uuidv7'` alongside existing `'serial' | 'uuid'` (Postgres) and `'number' | 'uuid'` (SQLite). - Generates IDs in application code with `uuid` package `v7()` (not database-native), so older Postgres versions are still supported. - Reuses the same Drizzle column modeling as UUID v4 where appropriate (`defaultV7` on the raw column, `$defaultFn` / generated schema code). - Adds integration tests behind `PAYLOAD_DATABASE=postgres-uuidv7` and a test adapter preset `postgres-uuidv7`. ### Why? UUID v7 is sortable by creation time and tends to be friendlier for B-tree indexes than random UUID v4, without changing the wire format or Postgres column type. ### How? - Bump `uuid` in `@payloadcms/drizzle` to a version that exports `v7`. - Extend TypeScript `idType` unions and `UUIDRawColumn` with `defaultV7`. - In schema builders (`setColumnID`, `buildDrizzleTable`, `columnToCodeConverter`), set app-side defaults for v7; treat `uuidv7` like `uuid` for relationships and query sanitization via a small `isUUIDType` helper and `getCollectionIdType` mapping to `'text'`. Fixes #11449 --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1213991890379567 --------- Co-authored-by: Sasha Rakhmatulin <sasha@ritsuko.dev>
This commit is contained in:
co-authored by
Sasha Rakhmatulin
parent
79f4cc7fae
commit
ac01e82bcd
@@ -3,7 +3,7 @@ description: Starts the required database for tests
|
||||
|
||||
inputs:
|
||||
database:
|
||||
description: 'Database type: mongodb, mongodb-atlas, cosmosdb, documentdb, firestore, postgres, postgres-custom-schema, postgres-uuid, postgres-read-replica, postgres-read-replicas, vercel-postgres-read-replica, sqlite, sqlite-uuid, supabase, d1'
|
||||
description: 'Database type: mongodb, mongodb-atlas, cosmosdb, documentdb, firestore, postgres, postgres-custom-schema, postgres-uuid, postgres-uuidv7, postgres-read-replica, postgres-read-replicas, vercel-postgres-read-replica, sqlite, sqlite-uuid, sqlite-uuidv7, supabase, d1'
|
||||
required: true
|
||||
|
||||
outputs:
|
||||
|
||||
@@ -10,10 +10,12 @@ createIntConfig({
|
||||
'postgres',
|
||||
'postgres-custom-schema',
|
||||
'postgres-uuid',
|
||||
'postgres-uuidv7',
|
||||
'postgres-read-replica',
|
||||
'supabase',
|
||||
'sqlite',
|
||||
'sqlite-uuid',
|
||||
'sqlite-uuidv7',
|
||||
],
|
||||
shards: 3,
|
||||
})
|
||||
|
||||
@@ -134,9 +134,11 @@ Set `PAYLOAD_DATABASE` in your `.env` file to choose the database adapter:
|
||||
- `postgres` - PostgreSQL with pgvector and PostGIS
|
||||
- `postgres-custom-schema` - PostgreSQL with custom schema
|
||||
- `postgres-uuid` - PostgreSQL with UUID primary keys
|
||||
- `postgres-uuidv7` - PostgreSQL with UUID V7 primary keys
|
||||
- `postgres-read-replica` - PostgreSQL with read replica
|
||||
- `sqlite` - SQLite
|
||||
- `sqlite-uuid` - SQLite with UUID primary keys
|
||||
- `sqlite-uuidv7` - SQLite with UUID V7 primary keys
|
||||
- `supabase` - Supabase (PostgreSQL)
|
||||
- `d1` - D1 (SQLite)
|
||||
|
||||
|
||||
@@ -77,13 +77,12 @@
|
||||
"drizzle-orm": "0.44.7",
|
||||
"prompts": "2.4.2",
|
||||
"to-snake-case": "1.0.0",
|
||||
"uuid": "9.0.0"
|
||||
"uuid": "11.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@types/pg": "8.10.2",
|
||||
"@types/to-snake-case": "1.0.0",
|
||||
"@types/uuid": "10.0.0",
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -64,7 +64,7 @@ const filename = fileURLToPath(import.meta.url)
|
||||
|
||||
export function sqliteD1Adapter(args: Args): DatabaseAdapterObj<SQLiteD1Adapter> {
|
||||
const sqliteIDType = args.idType || 'number'
|
||||
const payloadIDType = sqliteIDType === 'uuid' ? 'text' : 'number'
|
||||
const payloadIDType = sqliteIDType === 'uuid' || sqliteIDType === 'uuidv7' ? 'text' : 'number'
|
||||
const allowIDOnCreate = args.allowIDOnCreate ?? false
|
||||
|
||||
function adapter({ payload }: { payload: Payload }) {
|
||||
|
||||
@@ -55,13 +55,12 @@
|
||||
"mongoose": "8.15.1",
|
||||
"mongoose-paginate-v2": "1.9.4",
|
||||
"prompts": "2.4.2",
|
||||
"uuid": "10.0.0"
|
||||
"uuid": "11.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@types/mongoose-aggregate-paginate-v2": "1.0.12",
|
||||
"@types/prompts": "^2.4.5",
|
||||
"@types/uuid": "10.0.0",
|
||||
"mongodb": "6.16.0",
|
||||
"mongodb-memory-server": "10.1.4",
|
||||
"payload": "workspace:*"
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
"pg": "8.16.3",
|
||||
"prompts": "2.4.2",
|
||||
"to-snake-case": "1.0.0",
|
||||
"uuid": "10.0.0"
|
||||
"uuid": "11.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hyrious/esbuild-plugin-commonjs": "0.2.6",
|
||||
|
||||
@@ -53,7 +53,7 @@ export type Args = {
|
||||
extensions?: string[]
|
||||
/** Generated schema from payload generate:db-schema file path */
|
||||
generateSchemaOutputFile?: string
|
||||
idType?: 'serial' | 'uuid'
|
||||
idType?: 'serial' | 'uuid' | 'uuidv7'
|
||||
localesSuffix?: string
|
||||
logger?: DrizzleConfig['logger']
|
||||
migrationDir?: string
|
||||
|
||||
@@ -79,13 +79,12 @@
|
||||
"drizzle-orm": "0.44.7",
|
||||
"prompts": "2.4.2",
|
||||
"to-snake-case": "1.0.0",
|
||||
"uuid": "9.0.0"
|
||||
"uuid": "11.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@types/pg": "8.10.2",
|
||||
"@types/to-snake-case": "1.0.0",
|
||||
"@types/uuid": "10.0.0",
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -66,7 +66,7 @@ const filename = fileURLToPath(import.meta.url)
|
||||
|
||||
export function sqliteAdapter(args: Args): DatabaseAdapterObj<SQLiteAdapter> {
|
||||
const sqliteIDType = args.idType || 'number'
|
||||
const payloadIDType = sqliteIDType === 'uuid' ? 'text' : 'number'
|
||||
const payloadIDType = sqliteIDType === 'uuid' || sqliteIDType === 'uuidv7' ? 'text' : 'number'
|
||||
const allowIDOnCreate = args.allowIDOnCreate ?? false
|
||||
|
||||
function adapter({ payload }: { payload: Payload }) {
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
"pg": "8.16.3",
|
||||
"prompts": "2.4.2",
|
||||
"to-snake-case": "1.0.0",
|
||||
"uuid": "10.0.0"
|
||||
"uuid": "11.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hyrious/esbuild-plugin-commonjs": "0.2.6",
|
||||
|
||||
@@ -53,7 +53,7 @@ export type Args = {
|
||||
forceUseVercelPostgres?: boolean
|
||||
/** Generated schema from payload generate:db-schema file path */
|
||||
generateSchemaOutputFile?: string
|
||||
idType?: 'serial' | 'uuid'
|
||||
idType?: 'serial' | 'uuid' | 'uuidv7'
|
||||
localesSuffix?: string
|
||||
logger?: DrizzleConfig['logger']
|
||||
migrationDir?: string
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
"drizzle-orm": "0.44.7",
|
||||
"prompts": "2.4.2",
|
||||
"to-snake-case": "1.0.0",
|
||||
"uuid": "9.0.0"
|
||||
"uuid": "11.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@libsql/client": "0.14.0",
|
||||
|
||||
@@ -68,6 +68,11 @@ export const columnToCodeConverter: ColumnToCodeConverter = ({
|
||||
code = `${code}.defaultRandom()`
|
||||
}
|
||||
|
||||
if (column.type === 'uuid' && column.defaultV7) {
|
||||
addImport('uuid', 'v7 as uuidv7')
|
||||
code = `${code}.$defaultFn(() => uuidv7())`
|
||||
}
|
||||
|
||||
if (column.notNull) {
|
||||
code = `${code}.notNull()`
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
varchar,
|
||||
vector,
|
||||
} from 'drizzle-orm/pg-core'
|
||||
import { v7 as uuidv7 } from 'uuid'
|
||||
|
||||
import type { RawColumn, RawTable } from '../../types.js'
|
||||
import type { BasePostgresAdapter } from '../types.js'
|
||||
@@ -109,6 +110,10 @@ export const buildDrizzleTable = ({
|
||||
builder = builder.defaultRandom()
|
||||
}
|
||||
|
||||
if (column.defaultV7) {
|
||||
builder = builder.$defaultFn(() => uuidv7())
|
||||
}
|
||||
|
||||
columns[key] = builder
|
||||
break
|
||||
}
|
||||
|
||||
@@ -34,6 +34,17 @@ export const setColumnID: SetColumnID = ({ adapter, columns, fields }) => {
|
||||
return 'uuid'
|
||||
}
|
||||
|
||||
if (adapter.idType === 'uuidv7') {
|
||||
columns.id = {
|
||||
name: 'id',
|
||||
type: 'uuid',
|
||||
defaultV7: true,
|
||||
primaryKey: true,
|
||||
}
|
||||
|
||||
return 'uuid'
|
||||
}
|
||||
|
||||
columns.id = {
|
||||
name: 'id',
|
||||
type: 'serial',
|
||||
|
||||
@@ -147,7 +147,7 @@ export type BasePostgresAdapter = {
|
||||
* Used for returning properly formed errors from unique fields
|
||||
*/
|
||||
fieldConstraints: Record<string, Record<string, string>>
|
||||
idType: 'serial' | 'uuid'
|
||||
idType: 'serial' | 'uuid' | 'uuidv7'
|
||||
initializing: Promise<void>
|
||||
insert: Insert
|
||||
localesSuffix?: string
|
||||
|
||||
@@ -19,6 +19,7 @@ import type { DrizzleAdapter, GenericColumn } from '../types.js'
|
||||
import type { BuildQueryJoinAliases } from './buildQuery.js'
|
||||
|
||||
import { isPolymorphicRelationship } from '../utilities/isPolymorphicRelationship.js'
|
||||
import { isUUIDType } from '../utilities/isUUIDType.js'
|
||||
import { jsonBuildObject } from '../utilities/json.js'
|
||||
import { DistinctSymbol } from '../utilities/rawConstraint.js'
|
||||
import { resolveBlockTableName } from '../utilities/validateExistingBlockIsIdentical.js'
|
||||
@@ -111,7 +112,7 @@ export const getTableColumnFromPath = ({
|
||||
constraints,
|
||||
field: {
|
||||
name: 'id',
|
||||
type: adapter.idType === 'uuid' ? 'text' : 'number',
|
||||
type: isUUIDType(adapter.idType) ? 'text' : 'number',
|
||||
} as NumberField | TextField,
|
||||
table: adapter.tables[newTableName],
|
||||
}
|
||||
@@ -414,7 +415,7 @@ export const getTableColumnFromPath = ({
|
||||
constraints,
|
||||
field: {
|
||||
name: 'id',
|
||||
type: adapter.idType === 'uuid' ? 'text' : 'number',
|
||||
type: isUUIDType(adapter.idType) ? 'text' : 'number',
|
||||
} as NumberField | TextField,
|
||||
table: aliasRelationshipTable,
|
||||
}
|
||||
@@ -531,7 +532,7 @@ export const getTableColumnFromPath = ({
|
||||
constraints,
|
||||
field: {
|
||||
name: 'id',
|
||||
type: adapter.idType === 'uuid' ? 'text' : 'number',
|
||||
type: isUUIDType(adapter.idType) ? 'text' : 'number',
|
||||
} as NumberField | TextField,
|
||||
table: newAliasTable,
|
||||
}
|
||||
@@ -706,8 +707,9 @@ export const getTableColumnFromPath = ({
|
||||
|
||||
const columns: TableColumn['columns'] = field.relationTo
|
||||
.map((relationTo) => {
|
||||
let idType: 'number' | 'text' | 'uuid' =
|
||||
adapter.idType === 'uuid' ? 'uuid' : 'number'
|
||||
let idType: 'number' | 'text' | 'uuid' = isUUIDType(adapter.idType)
|
||||
? 'uuid'
|
||||
: 'number'
|
||||
|
||||
const { customIDType } = adapter.payload.collections[relationTo]
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import type { DrizzleAdapter } from '../types.js'
|
||||
|
||||
import { getCollectionIdType } from '../utilities/getCollectionIdType.js'
|
||||
import { isPolymorphicRelationship } from '../utilities/isPolymorphicRelationship.js'
|
||||
import { isUUIDType } from '../utilities/isUUIDType.js'
|
||||
import { isRawConstraint } from '../utilities/rawConstraint.js'
|
||||
|
||||
type SanitizeQueryValueArgs = {
|
||||
@@ -59,7 +60,7 @@ export const sanitizeQueryValue = ({
|
||||
) {
|
||||
const allPossibleIDTypes: (number | string)[] = []
|
||||
formattedValue.forEach((val) => {
|
||||
if (adapter.idType !== 'uuid' && typeof val === 'string') {
|
||||
if (!isUUIDType(adapter.idType) && typeof val === 'string') {
|
||||
allPossibleIDTypes.push(val, parseInt(val))
|
||||
} else if (typeof val === 'string') {
|
||||
allPossibleIDTypes.push(val)
|
||||
|
||||
@@ -18,6 +18,7 @@ import type {
|
||||
import { createTableName } from '../createTableName.js'
|
||||
import { buildForeignKeyName } from '../utilities/buildForeignKeyName.js'
|
||||
import { buildIndexName } from '../utilities/buildIndexName.js'
|
||||
import { isUUIDType } from '../utilities/isUUIDType.js'
|
||||
import { traverseFields } from './traverseFields.js'
|
||||
|
||||
type Args = {
|
||||
@@ -619,8 +620,9 @@ export const buildTable = ({
|
||||
config: relationshipConfig,
|
||||
throwValidationError: true,
|
||||
})
|
||||
let colType: 'integer' | 'numeric' | 'uuid' | 'varchar' =
|
||||
adapter.idType === 'uuid' ? 'uuid' : 'integer'
|
||||
let colType: 'integer' | 'numeric' | 'uuid' | 'varchar' = isUUIDType(adapter.idType)
|
||||
? 'uuid'
|
||||
: 'integer'
|
||||
const relatedCollectionCustomIDType =
|
||||
adapter.payload.collections[relationshipConfig.slug]?.customIDType
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import { createTableName } from '../createTableName.js'
|
||||
import { buildIndexName } from '../utilities/buildIndexName.js'
|
||||
import { getArrayRelationName } from '../utilities/getArrayRelationName.js'
|
||||
import { hasLocalesTable } from '../utilities/hasLocalesTable.js'
|
||||
import { isUUIDType } from '../utilities/isUUIDType.js'
|
||||
import {
|
||||
InternalBlockTableNameIndex,
|
||||
setInternalBlockIndex,
|
||||
@@ -945,7 +946,7 @@ export const traverseFields = ({
|
||||
const tableName = adapter.tableNameMap.get(toSnakeCase(field.relationTo))
|
||||
|
||||
// get the id type of the related collection
|
||||
let colType: IDType = adapter.idType === 'uuid' ? 'uuid' : 'integer'
|
||||
let colType: IDType = isUUIDType(adapter.idType) ? 'uuid' : 'integer'
|
||||
const relatedCollectionCustomID = relationshipConfig.fields.find(
|
||||
(field) => fieldAffectsData(field) && field.name === 'id',
|
||||
)
|
||||
|
||||
@@ -69,6 +69,11 @@ export const columnToCodeConverter: ColumnToCodeConverter = ({
|
||||
defaultStatement = `$defaultFn(() => randomUUID())`
|
||||
}
|
||||
|
||||
if (column.defaultV7) {
|
||||
addImport('uuid', 'v7 as uuidv7')
|
||||
defaultStatement = `$defaultFn(() => uuidv7())`
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
text,
|
||||
uniqueIndex,
|
||||
} from 'drizzle-orm/sqlite-core'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { v4 as uuidv4, v7 as uuidv7 } from 'uuid'
|
||||
|
||||
import type { BuildDrizzleTable, RawColumn } from '../../types.js'
|
||||
|
||||
@@ -72,6 +72,10 @@ export const buildDrizzleTable: BuildDrizzleTable = ({ adapter, locales, rawTabl
|
||||
builder = builder.$defaultFn(() => uuidv4())
|
||||
}
|
||||
|
||||
if (column.defaultV7) {
|
||||
builder = builder.$defaultFn(() => uuidv7())
|
||||
}
|
||||
|
||||
columns[key] = builder
|
||||
break
|
||||
}
|
||||
|
||||
@@ -34,6 +34,17 @@ export const setColumnID: SetColumnID = ({ adapter, columns, fields }) => {
|
||||
return 'uuid'
|
||||
}
|
||||
|
||||
if (adapter.idType === 'uuidv7') {
|
||||
columns.id = {
|
||||
name: 'id',
|
||||
type: 'uuid',
|
||||
defaultV7: true,
|
||||
primaryKey: true,
|
||||
}
|
||||
|
||||
return 'uuid'
|
||||
}
|
||||
|
||||
columns.id = {
|
||||
name: 'id',
|
||||
type: 'integer',
|
||||
|
||||
@@ -60,7 +60,7 @@ export type BaseSQLiteArgs = {
|
||||
blocksAsJSON?: boolean
|
||||
/** Generated schema from payload generate:db-schema file path */
|
||||
generateSchemaOutputFile?: string
|
||||
idType?: 'number' | 'uuid'
|
||||
idType?: 'number' | 'uuid' | 'uuidv7'
|
||||
localesSuffix?: string
|
||||
logger?: DrizzleConfig['logger']
|
||||
migrationDir?: string
|
||||
|
||||
@@ -248,6 +248,8 @@ export type TimestampRawColumn = {
|
||||
*/
|
||||
export type UUIDRawColumn = {
|
||||
defaultRandom?: boolean
|
||||
/** App-side UUID v7 default (Postgres & SQLite); mutually exclusive with defaultRandom in practice */
|
||||
defaultV7?: boolean
|
||||
type: 'uuid'
|
||||
} & BaseRawColumn
|
||||
|
||||
@@ -404,7 +406,7 @@ export interface DrizzleAdapter extends BaseDatabaseAdapter {
|
||||
fieldConstraints: Record<string, Record<string, string>>
|
||||
|
||||
foreignKeys: Set<string>
|
||||
idType: 'serial' | 'uuid'
|
||||
idType: 'serial' | 'uuid' | 'uuidv7'
|
||||
indexes: Set<string>
|
||||
initializing: Promise<void>
|
||||
insert: Insert
|
||||
|
||||
@@ -7,6 +7,7 @@ const typeMap: Record<string, 'number' | 'text'> = {
|
||||
serial: 'number',
|
||||
text: 'text',
|
||||
uuid: 'text',
|
||||
uuidv7: 'text',
|
||||
}
|
||||
|
||||
export const getCollectionIdType = ({
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export const isUUIDType = (idType: string): boolean => idType === 'uuid' || idType === 'uuidv7'
|
||||
@@ -123,7 +123,7 @@
|
||||
"path-to-regexp": "6.3.0",
|
||||
"qs-esm": "8.0.1",
|
||||
"sass": "1.77.4",
|
||||
"uuid": "10.0.0"
|
||||
"uuid": "11.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.27.2",
|
||||
@@ -136,7 +136,6 @@
|
||||
"@types/busboy": "1.5.4",
|
||||
"@types/react": "19.2.9",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@types/uuid": "10.0.0",
|
||||
"babel-plugin-react-compiler": "19.1.0-rc.3",
|
||||
"esbuild": "0.27.1",
|
||||
"esbuild-sass-plugin": "3.3.1",
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
"ts-essentials": "10.0.3",
|
||||
"tsx": "4.21.0",
|
||||
"undici": "7.24.4",
|
||||
"uuid": "10.0.0",
|
||||
"uuid": "11.1.0",
|
||||
"ws": "^8.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -140,7 +140,6 @@
|
||||
"@types/nodemailer": "7.0.2",
|
||||
"@types/pluralize": "0.0.33",
|
||||
"@types/range-parser": "1.2.7",
|
||||
"@types/uuid": "10.0.0",
|
||||
"@types/ws": "^8.5.10",
|
||||
"copyfiles": "2.4.1",
|
||||
"cross-env": "7.0.3",
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
"@payloadcms/ui": "workspace:*",
|
||||
"lodash.get": "^4.4.2",
|
||||
"stripe": "^10.2.0",
|
||||
"uuid": "10.0.0"
|
||||
"uuid": "11.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
@@ -74,7 +74,6 @@
|
||||
"@types/lodash.get": "^4.4.7",
|
||||
"@types/react": "19.2.9",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@types/uuid": "10.0.0",
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -380,7 +380,6 @@
|
||||
"@lexical/utils": "0.41.0",
|
||||
"@payloadcms/translations": "workspace:*",
|
||||
"@payloadcms/ui": "workspace:*",
|
||||
"@types/uuid": "10.0.0",
|
||||
"acorn": "8.16.0",
|
||||
"bson-objectid": "2.0.4",
|
||||
"csstype": "3.1.3",
|
||||
@@ -394,7 +393,7 @@
|
||||
"qs-esm": "8.0.1",
|
||||
"react-error-boundary": "4.1.2",
|
||||
"ts-essentials": "10.0.3",
|
||||
"uuid": "10.0.0"
|
||||
"uuid": "11.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.27.2",
|
||||
|
||||
@@ -159,7 +159,7 @@
|
||||
"sonner": "^1.7.2",
|
||||
"ts-essentials": "10.0.3",
|
||||
"use-context-selector": "2.0.0",
|
||||
"uuid": "10.0.0"
|
||||
"uuid": "11.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.27.2",
|
||||
@@ -171,7 +171,6 @@
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@types/react": "19.2.9",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@types/uuid": "10.0.0",
|
||||
"babel-plugin-react-compiler": "19.1.0-rc.3",
|
||||
"esbuild": "0.27.1",
|
||||
"esbuild-sass-plugin": "3.3.1",
|
||||
|
||||
+57
-278
@@ -67,10 +67,9 @@ export interface Config {
|
||||
};
|
||||
blocks: {};
|
||||
collections: {
|
||||
relation: Relation;
|
||||
'posts-versioned': PostsVersioned;
|
||||
'posts-batches': PostsBatch;
|
||||
posts: Post;
|
||||
categories: Category;
|
||||
articles: Article;
|
||||
'payload-kv': PayloadKv;
|
||||
users: User;
|
||||
'payload-locked-documents': PayloadLockedDocument;
|
||||
@@ -79,10 +78,9 @@ export interface Config {
|
||||
};
|
||||
collectionsJoins: {};
|
||||
collectionsSelect: {
|
||||
relation: RelationSelect<false> | RelationSelect<true>;
|
||||
'posts-versioned': PostsVersionedSelect<false> | PostsVersionedSelect<true>;
|
||||
'posts-batches': PostsBatchesSelect<false> | PostsBatchesSelect<true>;
|
||||
posts: PostsSelect<false> | PostsSelect<true>;
|
||||
categories: CategoriesSelect<false> | CategoriesSelect<true>;
|
||||
articles: ArticlesSelect<false> | ArticlesSelect<true>;
|
||||
'payload-kv': PayloadKvSelect<false> | PayloadKvSelect<true>;
|
||||
users: UsersSelect<false> | UsersSelect<true>;
|
||||
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
||||
@@ -90,18 +88,12 @@ export interface Config {
|
||||
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
||||
};
|
||||
db: {
|
||||
defaultIDType: number;
|
||||
defaultIDType: string;
|
||||
};
|
||||
fallbackLocale: ('false' | 'none' | 'null') | false | null | ('en' | 'fr') | ('en' | 'fr')[];
|
||||
globals: {
|
||||
'global-versioned': GlobalVersioned;
|
||||
global: Global;
|
||||
};
|
||||
globalsSelect: {
|
||||
'global-versioned': GlobalVersionedSelect<false> | GlobalVersionedSelect<true>;
|
||||
global: GlobalSelect<false> | GlobalSelect<true>;
|
||||
};
|
||||
locale: 'en' | 'fr';
|
||||
fallbackLocale: null;
|
||||
globals: {};
|
||||
globalsSelect: {};
|
||||
locale: null;
|
||||
widgets: {
|
||||
collections: CollectionsWidget;
|
||||
};
|
||||
@@ -131,93 +123,32 @@ export interface UserAuthOperations {
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "relation".
|
||||
* via the `definition` "posts".
|
||||
*/
|
||||
export interface Relation {
|
||||
id: number;
|
||||
export interface Post {
|
||||
id: string;
|
||||
title?: string | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "categories".
|
||||
*/
|
||||
export interface Category {
|
||||
id: string;
|
||||
name?: string | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts-versioned".
|
||||
* via the `definition` "articles".
|
||||
*/
|
||||
export interface PostsVersioned {
|
||||
id: number;
|
||||
export interface Article {
|
||||
id: string;
|
||||
title?: string | null;
|
||||
content?:
|
||||
| {
|
||||
text?: string | null;
|
||||
relation?: (number | null) | Relation;
|
||||
manyRelations?: (number | Relation)[] | null;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'textBlock';
|
||||
}[]
|
||||
| null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
_status?: ('draft' | 'published') | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts-batches".
|
||||
*/
|
||||
export interface PostsBatch {
|
||||
id: number;
|
||||
title?: string | null;
|
||||
content?:
|
||||
| {
|
||||
text?: string | null;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'textBlock';
|
||||
}[]
|
||||
| null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts".
|
||||
*/
|
||||
export interface Post {
|
||||
id: number;
|
||||
title?: string | null;
|
||||
content?:
|
||||
| (
|
||||
| {
|
||||
text?: string | null;
|
||||
select?: ('option1' | 'option2') | null;
|
||||
relation?: (number | null) | Relation;
|
||||
manyRelations?: (number | Relation)[] | null;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'textBlock';
|
||||
}
|
||||
| {
|
||||
text?: string | null;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'block_second';
|
||||
}
|
||||
| {
|
||||
text?: string | null;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'block_third';
|
||||
}
|
||||
)[]
|
||||
| null;
|
||||
localizedContent?:
|
||||
| {
|
||||
text?: string | null;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'textBlockLocalized';
|
||||
}[]
|
||||
| null;
|
||||
category?: (string | null) | Category;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
@@ -226,7 +157,7 @@ export interface Post {
|
||||
* via the `definition` "payload-kv".
|
||||
*/
|
||||
export interface PayloadKv {
|
||||
id: number;
|
||||
id: string;
|
||||
key: string;
|
||||
data:
|
||||
| {
|
||||
@@ -243,7 +174,7 @@ export interface PayloadKv {
|
||||
* via the `definition` "users".
|
||||
*/
|
||||
export interface User {
|
||||
id: number;
|
||||
id: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
email: string;
|
||||
@@ -268,32 +199,28 @@ export interface User {
|
||||
* via the `definition` "payload-locked-documents".
|
||||
*/
|
||||
export interface PayloadLockedDocument {
|
||||
id: number;
|
||||
id: string;
|
||||
document?:
|
||||
| ({
|
||||
relationTo: 'relation';
|
||||
value: number | Relation;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'posts-versioned';
|
||||
value: number | PostsVersioned;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'posts-batches';
|
||||
value: number | PostsBatch;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'categories';
|
||||
value: string | Category;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'articles';
|
||||
value: string | Article;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'users';
|
||||
value: number | User;
|
||||
value: string | User;
|
||||
} | null);
|
||||
globalSlug?: string | null;
|
||||
user: {
|
||||
relationTo: 'users';
|
||||
value: number | User;
|
||||
value: string | User;
|
||||
};
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
@@ -303,10 +230,10 @@ export interface PayloadLockedDocument {
|
||||
* via the `definition` "payload-preferences".
|
||||
*/
|
||||
export interface PayloadPreference {
|
||||
id: number;
|
||||
id: string;
|
||||
user: {
|
||||
relationTo: 'users';
|
||||
value: number | User;
|
||||
value: string | User;
|
||||
};
|
||||
key?: string | null;
|
||||
value?:
|
||||
@@ -326,7 +253,7 @@ export interface PayloadPreference {
|
||||
* via the `definition` "payload-migrations".
|
||||
*/
|
||||
export interface PayloadMigration {
|
||||
id: number;
|
||||
id: string;
|
||||
name?: string | null;
|
||||
batch?: number | null;
|
||||
updatedAt: string;
|
||||
@@ -334,101 +261,29 @@ export interface PayloadMigration {
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "relation_select".
|
||||
* via the `definition` "posts_select".
|
||||
*/
|
||||
export interface RelationSelect<T extends boolean = true> {
|
||||
export interface PostsSelect<T extends boolean = true> {
|
||||
title?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "categories_select".
|
||||
*/
|
||||
export interface CategoriesSelect<T extends boolean = true> {
|
||||
name?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts-versioned_select".
|
||||
* via the `definition` "articles_select".
|
||||
*/
|
||||
export interface PostsVersionedSelect<T extends boolean = true> {
|
||||
export interface ArticlesSelect<T extends boolean = true> {
|
||||
title?: T;
|
||||
content?:
|
||||
| T
|
||||
| {
|
||||
textBlock?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
relation?: T;
|
||||
manyRelations?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
_status?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts-batches_select".
|
||||
*/
|
||||
export interface PostsBatchesSelect<T extends boolean = true> {
|
||||
title?: T;
|
||||
content?:
|
||||
| T
|
||||
| {
|
||||
textBlock?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts_select".
|
||||
*/
|
||||
export interface PostsSelect<T extends boolean = true> {
|
||||
title?: T;
|
||||
content?:
|
||||
| T
|
||||
| {
|
||||
textBlock?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
select?: T;
|
||||
relation?: T;
|
||||
manyRelations?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
block_second?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
block_third?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
localizedContent?:
|
||||
| T
|
||||
| {
|
||||
textBlockLocalized?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
category?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
@@ -494,82 +349,6 @@ export interface PayloadMigrationsSelect<T extends boolean = true> {
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "global-versioned".
|
||||
*/
|
||||
export interface GlobalVersioned {
|
||||
id: number;
|
||||
content?:
|
||||
| {
|
||||
text?: string | null;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'textBlock';
|
||||
}[]
|
||||
| null;
|
||||
_status?: ('draft' | 'published') | null;
|
||||
updatedAt?: string | null;
|
||||
createdAt?: string | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "global".
|
||||
*/
|
||||
export interface Global {
|
||||
id: number;
|
||||
content?:
|
||||
| {
|
||||
text?: string | null;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'textBlock';
|
||||
}[]
|
||||
| null;
|
||||
updatedAt?: string | null;
|
||||
createdAt?: string | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "global-versioned_select".
|
||||
*/
|
||||
export interface GlobalVersionedSelect<T extends boolean = true> {
|
||||
content?:
|
||||
| T
|
||||
| {
|
||||
textBlock?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
_status?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
globalType?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "global_select".
|
||||
*/
|
||||
export interface GlobalSelect<T extends boolean = true> {
|
||||
content?:
|
||||
| T
|
||||
| {
|
||||
textBlock?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
globalType?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "collections_widget".
|
||||
|
||||
Generated
+33
-62
@@ -322,8 +322,8 @@ importers:
|
||||
specifier: 1.0.0
|
||||
version: 1.0.0
|
||||
uuid:
|
||||
specifier: 9.0.0
|
||||
version: 9.0.0
|
||||
specifier: 11.1.0
|
||||
version: 11.1.0
|
||||
devDependencies:
|
||||
'@payloadcms/eslint-config':
|
||||
specifier: workspace:*
|
||||
@@ -334,9 +334,6 @@ importers:
|
||||
'@types/to-snake-case':
|
||||
specifier: 1.0.0
|
||||
version: 1.0.0
|
||||
'@types/uuid':
|
||||
specifier: 10.0.0
|
||||
version: 10.0.0
|
||||
payload:
|
||||
specifier: workspace:*
|
||||
version: link:../payload
|
||||
@@ -353,8 +350,8 @@ importers:
|
||||
specifier: 2.4.2
|
||||
version: 2.4.2
|
||||
uuid:
|
||||
specifier: 10.0.0
|
||||
version: 10.0.0
|
||||
specifier: 11.1.0
|
||||
version: 11.1.0
|
||||
devDependencies:
|
||||
'@payloadcms/eslint-config':
|
||||
specifier: workspace:*
|
||||
@@ -365,9 +362,6 @@ importers:
|
||||
'@types/prompts':
|
||||
specifier: ^2.4.5
|
||||
version: 2.4.9
|
||||
'@types/uuid':
|
||||
specifier: 10.0.0
|
||||
version: 10.0.0
|
||||
mongodb:
|
||||
specifier: 6.16.0
|
||||
version: 6.16.0(@aws-sdk/credential-providers@3.1026.0)(socks@2.8.7)
|
||||
@@ -405,8 +399,8 @@ importers:
|
||||
specifier: 1.0.0
|
||||
version: 1.0.0
|
||||
uuid:
|
||||
specifier: 10.0.0
|
||||
version: 10.0.0
|
||||
specifier: 11.1.0
|
||||
version: 11.1.0
|
||||
devDependencies:
|
||||
'@hyrious/esbuild-plugin-commonjs':
|
||||
specifier: 0.2.6
|
||||
@@ -448,8 +442,8 @@ importers:
|
||||
specifier: 1.0.0
|
||||
version: 1.0.0
|
||||
uuid:
|
||||
specifier: 9.0.0
|
||||
version: 9.0.0
|
||||
specifier: 11.1.0
|
||||
version: 11.1.0
|
||||
devDependencies:
|
||||
'@payloadcms/eslint-config':
|
||||
specifier: workspace:*
|
||||
@@ -460,9 +454,6 @@ importers:
|
||||
'@types/to-snake-case':
|
||||
specifier: 1.0.0
|
||||
version: 1.0.0
|
||||
'@types/uuid':
|
||||
specifier: 10.0.0
|
||||
version: 10.0.0
|
||||
payload:
|
||||
specifier: workspace:*
|
||||
version: link:../payload
|
||||
@@ -494,8 +485,8 @@ importers:
|
||||
specifier: 1.0.0
|
||||
version: 1.0.0
|
||||
uuid:
|
||||
specifier: 10.0.0
|
||||
version: 10.0.0
|
||||
specifier: 11.1.0
|
||||
version: 11.1.0
|
||||
devDependencies:
|
||||
'@hyrious/esbuild-plugin-commonjs':
|
||||
specifier: 0.2.6
|
||||
@@ -534,8 +525,8 @@ importers:
|
||||
specifier: 1.0.0
|
||||
version: 1.0.0
|
||||
uuid:
|
||||
specifier: 9.0.0
|
||||
version: 9.0.0
|
||||
specifier: 11.1.0
|
||||
version: 11.1.0
|
||||
devDependencies:
|
||||
'@libsql/client':
|
||||
specifier: 0.14.0
|
||||
@@ -819,8 +810,8 @@ importers:
|
||||
specifier: 1.77.4
|
||||
version: 1.77.4
|
||||
uuid:
|
||||
specifier: 10.0.0
|
||||
version: 10.0.0
|
||||
specifier: 11.1.0
|
||||
version: 11.1.0
|
||||
devDependencies:
|
||||
'@babel/cli':
|
||||
specifier: 7.27.2
|
||||
@@ -852,9 +843,6 @@ importers:
|
||||
'@types/react-dom':
|
||||
specifier: 19.2.3
|
||||
version: 19.2.3(@types/react@19.2.9)
|
||||
'@types/uuid':
|
||||
specifier: 10.0.0
|
||||
version: 10.0.0
|
||||
babel-plugin-react-compiler:
|
||||
specifier: 19.1.0-rc.3
|
||||
version: 19.1.0-rc.3
|
||||
@@ -964,8 +952,8 @@ importers:
|
||||
specifier: 7.24.4
|
||||
version: 7.24.4
|
||||
uuid:
|
||||
specifier: 10.0.0
|
||||
version: 10.0.0
|
||||
specifier: 11.1.0
|
||||
version: 11.1.0
|
||||
ws:
|
||||
specifier: ^8.16.0
|
||||
version: 8.20.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)
|
||||
@@ -994,9 +982,6 @@ importers:
|
||||
'@types/range-parser':
|
||||
specifier: 1.2.7
|
||||
version: 1.2.7
|
||||
'@types/uuid':
|
||||
specifier: 10.0.0
|
||||
version: 10.0.0
|
||||
'@types/ws':
|
||||
specifier: ^8.5.10
|
||||
version: 8.18.1
|
||||
@@ -1359,8 +1344,8 @@ importers:
|
||||
specifier: ^10.2.0
|
||||
version: 10.17.0
|
||||
uuid:
|
||||
specifier: 10.0.0
|
||||
version: 10.0.0
|
||||
specifier: 11.1.0
|
||||
version: 11.1.0
|
||||
devDependencies:
|
||||
'@payloadcms/eslint-config':
|
||||
specifier: workspace:*
|
||||
@@ -1377,9 +1362,6 @@ importers:
|
||||
'@types/react-dom':
|
||||
specifier: 19.2.3
|
||||
version: 19.2.3(@types/react@19.2.9)
|
||||
'@types/uuid':
|
||||
specifier: 10.0.0
|
||||
version: 10.0.0
|
||||
payload:
|
||||
specifier: workspace:*
|
||||
version: link:../payload
|
||||
@@ -1434,9 +1416,6 @@ importers:
|
||||
'@payloadcms/ui':
|
||||
specifier: workspace:*
|
||||
version: link:../ui
|
||||
'@types/uuid':
|
||||
specifier: 10.0.0
|
||||
version: 10.0.0
|
||||
acorn:
|
||||
specifier: 8.16.0
|
||||
version: 8.16.0
|
||||
@@ -1483,8 +1462,8 @@ importers:
|
||||
specifier: 10.0.3
|
||||
version: 10.0.3(typescript@5.7.3)
|
||||
uuid:
|
||||
specifier: 10.0.0
|
||||
version: 10.0.0
|
||||
specifier: 11.1.0
|
||||
version: 11.1.0
|
||||
devDependencies:
|
||||
'@babel/cli':
|
||||
specifier: 7.27.2
|
||||
@@ -1805,8 +1784,8 @@ importers:
|
||||
specifier: 2.0.0
|
||||
version: 2.0.0(react@19.2.4)(scheduler@0.25.0)
|
||||
uuid:
|
||||
specifier: 10.0.0
|
||||
version: 10.0.0
|
||||
specifier: 11.1.0
|
||||
version: 11.1.0
|
||||
devDependencies:
|
||||
'@babel/cli':
|
||||
specifier: 7.27.2
|
||||
@@ -1835,9 +1814,6 @@ importers:
|
||||
'@types/react-dom':
|
||||
specifier: 19.2.3
|
||||
version: 19.2.3(@types/react@19.2.9)
|
||||
'@types/uuid':
|
||||
specifier: 10.0.0
|
||||
version: 10.0.0
|
||||
babel-plugin-react-compiler:
|
||||
specifier: 19.1.0-rc.3
|
||||
version: 19.1.0-rc.3
|
||||
@@ -2581,8 +2557,8 @@ importers:
|
||||
specifier: 5.7.3
|
||||
version: 5.7.3
|
||||
uuid:
|
||||
specifier: 10.0.0
|
||||
version: 10.0.0
|
||||
specifier: 11.1.0
|
||||
version: 11.1.0
|
||||
vitest:
|
||||
specifier: 4.1.2
|
||||
version: 4.1.2(@opentelemetry/api@1.9.1)(@types/node@22.19.9)(@vitest/ui@4.1.2)(happy-dom@20.8.9(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@28.0.0(@noble/hashes@1.8.0))(vite@7.3.2(@types/node@22.19.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass-embedded@1.99.0)(sass@1.77.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
@@ -8080,9 +8056,6 @@ packages:
|
||||
'@types/use-sync-external-store@0.0.6':
|
||||
resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==}
|
||||
|
||||
'@types/uuid@10.0.0':
|
||||
resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==}
|
||||
|
||||
'@types/webidl-conversions@7.0.3':
|
||||
resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==}
|
||||
|
||||
@@ -14287,12 +14260,12 @@ packages:
|
||||
resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==}
|
||||
hasBin: true
|
||||
|
||||
uuid@8.3.2:
|
||||
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
||||
uuid@11.1.0:
|
||||
resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==}
|
||||
hasBin: true
|
||||
|
||||
uuid@9.0.0:
|
||||
resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==}
|
||||
uuid@8.3.2:
|
||||
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
||||
hasBin: true
|
||||
|
||||
uuid@9.0.1:
|
||||
@@ -20232,7 +20205,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@sentry/bundler-plugin-core': 2.22.7
|
||||
unplugin: 1.0.1
|
||||
uuid: 9.0.0
|
||||
uuid: 9.0.1
|
||||
webpack: 5.105.4(@swc/core@1.15.3)(esbuild@0.25.12)
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
@@ -20242,7 +20215,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@sentry/bundler-plugin-core': 3.6.1
|
||||
unplugin: 1.0.1
|
||||
uuid: 9.0.0
|
||||
uuid: 9.0.1
|
||||
webpack: 5.105.4(@swc/core@1.15.3)(esbuild@0.25.12)
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
@@ -21413,8 +21386,6 @@ snapshots:
|
||||
|
||||
'@types/use-sync-external-store@0.0.6': {}
|
||||
|
||||
'@types/uuid@10.0.0': {}
|
||||
|
||||
'@types/webidl-conversions@7.0.3': {}
|
||||
|
||||
'@types/whatwg-mimetype@3.0.2': {}
|
||||
@@ -28303,7 +28274,7 @@ snapshots:
|
||||
https-proxy-agent: 5.0.1
|
||||
node-fetch: 2.7.0
|
||||
stream-events: 1.0.5
|
||||
uuid: 9.0.0
|
||||
uuid: 9.0.1
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
@@ -28761,9 +28732,9 @@ snapshots:
|
||||
|
||||
uuid@10.0.0: {}
|
||||
|
||||
uuid@8.3.2: {}
|
||||
uuid@11.1.0: {}
|
||||
|
||||
uuid@9.0.0: {}
|
||||
uuid@8.3.2: {}
|
||||
|
||||
uuid@9.0.1: {}
|
||||
|
||||
|
||||
@@ -96,6 +96,9 @@ export interface Config {
|
||||
menu: MenuSelect<false> | MenuSelect<true>;
|
||||
};
|
||||
locale: null;
|
||||
widgets: {
|
||||
collections: CollectionsWidget;
|
||||
};
|
||||
user: User;
|
||||
jobs: {
|
||||
tasks: unknown;
|
||||
@@ -435,6 +438,16 @@ export interface MenuSelect<T extends boolean = true> {
|
||||
createdAt?: T;
|
||||
globalType?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "collections_widget".
|
||||
*/
|
||||
export interface CollectionsWidget {
|
||||
data?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
width: 'full';
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "auth".
|
||||
|
||||
@@ -24,7 +24,9 @@ describe('Custom GraphQL', () => {
|
||||
})
|
||||
|
||||
if (
|
||||
!['cosmosdb', 'firestore', 'sqlite', 'sqlite-uuid'].includes(process.env.PAYLOAD_DATABASE || '')
|
||||
!['cosmosdb', 'firestore', 'sqlite', 'sqlite-uuid', 'sqlite-uuidv7'].includes(
|
||||
process.env.PAYLOAD_DATABASE || '',
|
||||
)
|
||||
) {
|
||||
describe('Isolated Transaction ID', () => {
|
||||
it('should isolate transaction IDs between queries in the same request', async () => {
|
||||
@@ -61,7 +63,7 @@ describe('Custom GraphQL', () => {
|
||||
})
|
||||
})
|
||||
} else {
|
||||
it('should not run isolated transaction ID tests for sqlite/firestore/cosmosdb', () => {
|
||||
it('should not run isolated transaction ID tests for sqlite (incl. uuid variants)/firestore/cosmosdb', () => {
|
||||
expect(true).toBe(true)
|
||||
})
|
||||
}
|
||||
|
||||
+159
-5
@@ -29,7 +29,7 @@ import {
|
||||
} from 'payload'
|
||||
import { assert } from 'ts-essentials'
|
||||
import { fileURLToPath } from 'url'
|
||||
import { afterAll, beforeAll, beforeEach, expect } from 'vitest'
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, expect } from 'vitest'
|
||||
|
||||
import type { NextRESTClient } from '../__helpers/shared/NextRESTClient.js'
|
||||
import type { Global2, Post } from './payload-types.js'
|
||||
@@ -41,10 +41,13 @@ import { removeFiles } from '../__helpers/shared/removeFiles.js'
|
||||
import { devUser } from '../credentials.js'
|
||||
import { seed } from './seed.js'
|
||||
import {
|
||||
customIDsSlug,
|
||||
defaultValuesSlug,
|
||||
errorOnUnnamedFieldsSlug,
|
||||
fieldsPersistanceSlug,
|
||||
postsSlug,
|
||||
relationASlug,
|
||||
relationBSlug,
|
||||
} from './shared.js'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
@@ -231,6 +234,155 @@ describe('database', () => {
|
||||
const resFind = await payload.findByID({ id: res.id, collection: 'posts', depth: 0 })
|
||||
expect(resFind.categoriesCustomID[0]).toBe(9999)
|
||||
})
|
||||
|
||||
const describeUuidV7Adapter =
|
||||
process.env.PAYLOAD_DATABASE === 'postgres-uuidv7' ||
|
||||
process.env.PAYLOAD_DATABASE === 'sqlite-uuidv7'
|
||||
? describe
|
||||
: describe.skip
|
||||
|
||||
describeUuidV7Adapter('uuidv7', () => {
|
||||
const createdRows: { collection: string; id: number | string }[] = []
|
||||
|
||||
const track = (collection: string, id: number | string) => {
|
||||
createdRows.push({ collection, id })
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
for (const { collection, id } of [...createdRows].reverse()) {
|
||||
try {
|
||||
await payload.delete({
|
||||
collection: collection as
|
||||
| typeof customIDsSlug
|
||||
| typeof postsSlug
|
||||
| typeof relationASlug
|
||||
| typeof relationBSlug,
|
||||
id,
|
||||
})
|
||||
} catch {
|
||||
// ignore: concurrent cleanup or FK already removed
|
||||
}
|
||||
}
|
||||
|
||||
createdRows.length = 0
|
||||
})
|
||||
|
||||
it('should generate valid UUID with version 7', async () => {
|
||||
const doc = await payload.create({
|
||||
collection: postsSlug,
|
||||
data: { title: 'uuidv7 test' },
|
||||
})
|
||||
|
||||
track(postsSlug, doc.id)
|
||||
|
||||
expect(doc.id).toMatch(
|
||||
/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
|
||||
)
|
||||
|
||||
expect(String(doc.id).charAt(14)).toBe('7')
|
||||
})
|
||||
|
||||
it('should generate chronologically ordered IDs', async () => {
|
||||
const doc1 = await payload.create({
|
||||
collection: postsSlug,
|
||||
data: { title: 'uuidv7 first' },
|
||||
})
|
||||
const doc2 = await payload.create({
|
||||
collection: postsSlug,
|
||||
data: { title: 'uuidv7 second' },
|
||||
})
|
||||
|
||||
track(postsSlug, doc1.id)
|
||||
track(postsSlug, doc2.id)
|
||||
|
||||
expect(doc2.id > doc1.id).toBe(true)
|
||||
})
|
||||
|
||||
it('should findByID with uuidv7', async () => {
|
||||
const created = await payload.create({
|
||||
collection: postsSlug,
|
||||
data: { title: 'uuidv7 findable' },
|
||||
})
|
||||
|
||||
track(postsSlug, created.id)
|
||||
|
||||
const found = await payload.findByID({
|
||||
collection: postsSlug,
|
||||
id: created.id,
|
||||
})
|
||||
|
||||
expect(found.id).toBe(created.id)
|
||||
expect(found.title).toBe('uuidv7 findable')
|
||||
})
|
||||
|
||||
it('should query with where clause on uuidv7 id', async () => {
|
||||
const created = await payload.create({
|
||||
collection: postsSlug,
|
||||
data: { title: 'uuidv7 queryable' },
|
||||
})
|
||||
|
||||
track(postsSlug, created.id)
|
||||
|
||||
const result = await payload.find({
|
||||
collection: postsSlug,
|
||||
where: { id: { equals: created.id } },
|
||||
})
|
||||
|
||||
expect(result.docs).toHaveLength(1)
|
||||
expect(result.docs[0]!.id).toBe(created.id)
|
||||
})
|
||||
|
||||
it('should handle relationships with uuidv7 IDs', async () => {
|
||||
const relA = await payload.create({
|
||||
collection: relationASlug,
|
||||
data: { title: 'uuidv7 rel A' },
|
||||
})
|
||||
const relB = await payload.create({
|
||||
collection: relationBSlug,
|
||||
data: {
|
||||
title: 'uuidv7 rel B',
|
||||
relationship: relA.id,
|
||||
},
|
||||
})
|
||||
|
||||
track(relationBSlug, relB.id)
|
||||
track(relationASlug, relA.id)
|
||||
|
||||
const found = await payload.findByID({
|
||||
collection: relationBSlug,
|
||||
id: relB.id,
|
||||
depth: 1,
|
||||
})
|
||||
|
||||
expect(found.relationship).toBeDefined()
|
||||
})
|
||||
|
||||
it('should work with versions and uuidv7 adapter', async () => {
|
||||
const doc = await payload.create({
|
||||
collection: customIDsSlug,
|
||||
data: { title: 'v7 versioned' },
|
||||
})
|
||||
|
||||
track(customIDsSlug, doc.id)
|
||||
|
||||
await payload.update({
|
||||
collection: customIDsSlug,
|
||||
id: doc.id,
|
||||
data: { title: 'v7 versioned updated' },
|
||||
})
|
||||
|
||||
const versions = await payload.findVersions({
|
||||
collection: customIDsSlug,
|
||||
where: { parent: { equals: doc.id } },
|
||||
})
|
||||
|
||||
expect(versions.totalDocs).toBeGreaterThanOrEqual(1)
|
||||
})
|
||||
|
||||
it('defaultIDType should be text for uuidv7', () => {
|
||||
expect(payload.db.defaultIDType).toBe('text')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('timestamps', () => {
|
||||
@@ -751,7 +903,7 @@ describe('database', () => {
|
||||
let id: any = null
|
||||
if (payload.db.name === 'mongoose') {
|
||||
id = new mongoose.Types.ObjectId().toHexString()
|
||||
} else if (payload.db.idType === 'uuid') {
|
||||
} else if (payload.db.idType === 'uuid' || payload.db.idType === 'uuidv7') {
|
||||
id = randomUUID()
|
||||
} else {
|
||||
id = 9999
|
||||
@@ -766,7 +918,7 @@ describe('database', () => {
|
||||
let id: any = null
|
||||
if (payload.db.name === 'mongoose') {
|
||||
id = new mongoose.Types.ObjectId().toHexString()
|
||||
} else if (payload.db.idType === 'uuid') {
|
||||
} else if (payload.db.idType === 'uuid' || payload.db.idType === 'uuidv7') {
|
||||
id = randomUUID()
|
||||
} else {
|
||||
id = 99999
|
||||
@@ -788,7 +940,7 @@ describe('database', () => {
|
||||
let id: any = null
|
||||
if (payload.db.name === 'mongoose') {
|
||||
id = new mongoose.Types.ObjectId().toHexString()
|
||||
} else if (payload.db.idType === 'uuid') {
|
||||
} else if (payload.db.idType === 'uuid' || payload.db.idType === 'uuidv7') {
|
||||
id = randomUUID()
|
||||
} else {
|
||||
id = 999999
|
||||
@@ -2021,7 +2173,9 @@ describe('database', () => {
|
||||
describe('local api', () => {
|
||||
// sqlite cannot handle concurrent write transactions
|
||||
if (
|
||||
!['cosmosdb', 'firestore', 'sqlite', 'sqlite-uuid'].includes(process.env.PAYLOAD_DATABASE)
|
||||
!['cosmosdb', 'firestore', 'sqlite', 'sqlite-uuid', 'sqlite-uuidv7'].includes(
|
||||
process.env.PAYLOAD_DATABASE || '',
|
||||
)
|
||||
) {
|
||||
it('should commit multiple operations in isolation', async () => {
|
||||
const req = {
|
||||
|
||||
+111
-90
@@ -138,7 +138,7 @@ export interface Config {
|
||||
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
||||
};
|
||||
db: {
|
||||
defaultIDType: number;
|
||||
defaultIDType: string;
|
||||
};
|
||||
fallbackLocale: ('false' | 'none' | 'null') | false | null | ('en' | 'es' | 'uk') | ('en' | 'es' | 'uk')[];
|
||||
globals: {
|
||||
@@ -188,7 +188,7 @@ export interface UserAuthOperations {
|
||||
* via the `definition` "noTimeStamps".
|
||||
*/
|
||||
export interface NoTimeStamp {
|
||||
id: number;
|
||||
id: string;
|
||||
title?: string | null;
|
||||
}
|
||||
/**
|
||||
@@ -196,12 +196,12 @@ export interface NoTimeStamp {
|
||||
* via the `definition` "categories".
|
||||
*/
|
||||
export interface Category {
|
||||
id: number;
|
||||
id: string;
|
||||
title?: string | null;
|
||||
simple?: (number | null) | Simple;
|
||||
simple?: (string | null) | Simple;
|
||||
hideout?: {
|
||||
camera1?: {
|
||||
time1Image?: (number | null) | Post;
|
||||
time1Image?: (string | null) | Post;
|
||||
};
|
||||
};
|
||||
updatedAt: string;
|
||||
@@ -213,7 +213,7 @@ export interface Category {
|
||||
* via the `definition` "simple".
|
||||
*/
|
||||
export interface Simple {
|
||||
id: number;
|
||||
id: string;
|
||||
text?: string | null;
|
||||
number?: number | null;
|
||||
updatedAt: string;
|
||||
@@ -224,9 +224,9 @@ export interface Simple {
|
||||
* via the `definition` "posts".
|
||||
*/
|
||||
export interface Post {
|
||||
id: number;
|
||||
id: string;
|
||||
title: string;
|
||||
category?: (number | null) | Category;
|
||||
category?: (string | null) | Category;
|
||||
categoryID?:
|
||||
| {
|
||||
[k: string]: unknown;
|
||||
@@ -238,16 +238,16 @@ export interface Post {
|
||||
| null;
|
||||
categoryTitle?: string | null;
|
||||
categorySimpleText?: string | null;
|
||||
categories?: (number | Category)[] | null;
|
||||
categories?: (string | Category)[] | null;
|
||||
categoriesCustomID?: (number | CategoriesCustomId)[] | null;
|
||||
categoryPoly?: {
|
||||
relationTo: 'categories';
|
||||
value: number | Category;
|
||||
value: string | Category;
|
||||
} | null;
|
||||
categoryPolyMany?:
|
||||
| {
|
||||
relationTo: 'categories';
|
||||
value: number | Category;
|
||||
value: string | Category;
|
||||
}[]
|
||||
| null;
|
||||
categoryCustomID?: (number | null) | CategoriesCustomId;
|
||||
@@ -255,11 +255,11 @@ export interface Post {
|
||||
| (
|
||||
| {
|
||||
relationTo: 'categories';
|
||||
value: number | Category;
|
||||
value: string | Category;
|
||||
}
|
||||
| {
|
||||
relationTo: 'simple';
|
||||
value: number | Simple;
|
||||
value: string | Simple;
|
||||
}
|
||||
)[]
|
||||
| null;
|
||||
@@ -267,11 +267,11 @@ export interface Post {
|
||||
| (
|
||||
| {
|
||||
relationTo: 'categories';
|
||||
value: number | Category;
|
||||
value: string | Category;
|
||||
}
|
||||
| {
|
||||
relationTo: 'simple';
|
||||
value: number | Simple;
|
||||
value: string | Simple;
|
||||
}
|
||||
)[]
|
||||
| null;
|
||||
@@ -301,11 +301,11 @@ export interface Post {
|
||||
| (
|
||||
| {
|
||||
relationTo: 'categories';
|
||||
value: number | Category;
|
||||
value: string | Category;
|
||||
}
|
||||
| {
|
||||
relationTo: 'simple';
|
||||
value: number | Simple;
|
||||
value: string | Simple;
|
||||
}
|
||||
)[]
|
||||
| null;
|
||||
@@ -367,7 +367,7 @@ export interface CategoriesCustomId {
|
||||
* via the `definition` "simple-localized".
|
||||
*/
|
||||
export interface SimpleLocalized {
|
||||
id: number;
|
||||
id: string;
|
||||
text?: string | null;
|
||||
number?: number | null;
|
||||
updatedAt: string;
|
||||
@@ -378,7 +378,7 @@ export interface SimpleLocalized {
|
||||
* via the `definition` "error-on-unnamed-fields".
|
||||
*/
|
||||
export interface ErrorOnUnnamedField {
|
||||
id: number;
|
||||
id: string;
|
||||
groupWithinUnnamedTab: {
|
||||
text: string;
|
||||
};
|
||||
@@ -390,7 +390,7 @@ export interface ErrorOnUnnamedField {
|
||||
* via the `definition` "default-values".
|
||||
*/
|
||||
export interface DefaultValue {
|
||||
id: number;
|
||||
id: string;
|
||||
title?: string | null;
|
||||
defaultValue?: string | null;
|
||||
array?:
|
||||
@@ -417,7 +417,7 @@ export interface DefaultValue {
|
||||
* via the `definition` "relation-a".
|
||||
*/
|
||||
export interface RelationA {
|
||||
id: number;
|
||||
id: string;
|
||||
title?: string | null;
|
||||
richText?: {
|
||||
root: {
|
||||
@@ -442,9 +442,9 @@ export interface RelationA {
|
||||
* via the `definition` "relation-b".
|
||||
*/
|
||||
export interface RelationB {
|
||||
id: number;
|
||||
id: string;
|
||||
title?: string | null;
|
||||
relationship?: (number | null) | RelationA;
|
||||
relationship?: (string | null) | RelationA;
|
||||
richText?: {
|
||||
root: {
|
||||
type: string;
|
||||
@@ -468,14 +468,14 @@ export interface RelationB {
|
||||
* via the `definition` "pg-migrations".
|
||||
*/
|
||||
export interface PgMigration {
|
||||
id: number;
|
||||
relation1?: (number | null) | RelationA;
|
||||
id: string;
|
||||
relation1?: (string | null) | RelationA;
|
||||
myArray?:
|
||||
| {
|
||||
relation2?: (number | null) | RelationB;
|
||||
relation2?: (string | null) | RelationB;
|
||||
mySubArray?:
|
||||
| {
|
||||
relation3?: (number | null) | RelationB;
|
||||
relation3?: (string | null) | RelationB;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
@@ -483,12 +483,12 @@ export interface PgMigration {
|
||||
}[]
|
||||
| null;
|
||||
myGroup?: {
|
||||
relation4?: (number | null) | RelationB;
|
||||
relation4?: (string | null) | RelationB;
|
||||
};
|
||||
myBlocks?:
|
||||
| {
|
||||
relation5?: (number | null) | RelationA;
|
||||
relation6?: (number | null) | RelationB;
|
||||
relation5?: (string | null) | RelationA;
|
||||
relation6?: (string | null) | RelationB;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'myBlock';
|
||||
@@ -502,10 +502,10 @@ export interface PgMigration {
|
||||
* via the `definition` "custom-schema".
|
||||
*/
|
||||
export interface CustomSchema {
|
||||
id: number;
|
||||
id: string;
|
||||
text?: string | null;
|
||||
localizedText?: string | null;
|
||||
relationship?: (number | RelationA)[] | null;
|
||||
relationship?: (string | RelationA)[] | null;
|
||||
select?: ('a' | 'b' | 'c')[] | null;
|
||||
radio?: ('a' | 'b' | 'c') | null;
|
||||
array?:
|
||||
@@ -533,7 +533,7 @@ export interface CustomSchema {
|
||||
* via the `definition` "places".
|
||||
*/
|
||||
export interface Place {
|
||||
id: number;
|
||||
id: string;
|
||||
country?: string | null;
|
||||
city?: string | null;
|
||||
updatedAt: string;
|
||||
@@ -544,7 +544,7 @@ export interface Place {
|
||||
* via the `definition` "virtual-relations".
|
||||
*/
|
||||
export interface VirtualRelation {
|
||||
id: number;
|
||||
id: string;
|
||||
postTitle?: string | null;
|
||||
postsTitles?: string[] | null;
|
||||
postCategoriesTitles?: string[] | null;
|
||||
@@ -570,8 +570,8 @@ export interface VirtualRelation {
|
||||
| boolean
|
||||
| null;
|
||||
postLocalized?: string | null;
|
||||
post?: (number | null) | Post;
|
||||
posts?: (number | Post)[] | null;
|
||||
post?: (string | null) | Post;
|
||||
posts?: (string | Post)[] | null;
|
||||
customID?: (string | null) | CustomId;
|
||||
customIDValue?: string | null;
|
||||
updatedAt: string;
|
||||
@@ -594,7 +594,7 @@ export interface CustomId {
|
||||
* via the `definition` "fields-persistance".
|
||||
*/
|
||||
export interface FieldsPersistance {
|
||||
id: number;
|
||||
id: string;
|
||||
text?: string | null;
|
||||
textHooked?: string | null;
|
||||
array?:
|
||||
@@ -605,6 +605,15 @@ export interface FieldsPersistance {
|
||||
textWithinRow?: string | null;
|
||||
textWithinCollapsible?: string | null;
|
||||
textWithinTabs?: string | null;
|
||||
blockWithVirtual?:
|
||||
| {
|
||||
text?: string | null;
|
||||
virtualField?: string | null;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'blockWithVirtual';
|
||||
}[]
|
||||
| null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
@@ -613,7 +622,7 @@ export interface FieldsPersistance {
|
||||
* via the `definition` "fake-custom-ids".
|
||||
*/
|
||||
export interface FakeCustomId {
|
||||
id: number;
|
||||
id: string;
|
||||
title?: string | null;
|
||||
group?: {
|
||||
id?: string | null;
|
||||
@@ -629,11 +638,11 @@ export interface FakeCustomId {
|
||||
* via the `definition` "relationships-migration".
|
||||
*/
|
||||
export interface RelationshipsMigration {
|
||||
id: number;
|
||||
relationship?: (number | null) | DefaultValue;
|
||||
id: string;
|
||||
relationship?: (string | null) | DefaultValue;
|
||||
relationship_2?: {
|
||||
relationTo: 'default-values';
|
||||
value: number | DefaultValue;
|
||||
value: string | DefaultValue;
|
||||
} | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
@@ -643,7 +652,7 @@ export interface RelationshipsMigration {
|
||||
* via the `definition` "compound-indexes".
|
||||
*/
|
||||
export interface CompoundIndex {
|
||||
id: number;
|
||||
id: string;
|
||||
one?: string | null;
|
||||
two?: string | null;
|
||||
three?: string | null;
|
||||
@@ -658,7 +667,7 @@ export interface CompoundIndex {
|
||||
* via the `definition` "aliases".
|
||||
*/
|
||||
export interface Alias {
|
||||
id: number;
|
||||
id: string;
|
||||
thisIsALongFieldNameThatCanCauseAPostgresErrorEvenThoughWeSetAShorterDBName?:
|
||||
| {
|
||||
nestedArray?:
|
||||
@@ -678,7 +687,7 @@ export interface Alias {
|
||||
* via the `definition` "blocks-docs".
|
||||
*/
|
||||
export interface BlocksDoc {
|
||||
id: number;
|
||||
id: string;
|
||||
testBlocksLocalized?:
|
||||
| {
|
||||
text?: string | null;
|
||||
@@ -703,7 +712,7 @@ export interface BlocksDoc {
|
||||
* via the `definition` "unique-fields".
|
||||
*/
|
||||
export interface UniqueField {
|
||||
id: number;
|
||||
id: string;
|
||||
slugField?: string | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
@@ -713,7 +722,7 @@ export interface UniqueField {
|
||||
* via the `definition` "select-has-many".
|
||||
*/
|
||||
export interface SelectHasMany {
|
||||
id: number;
|
||||
id: string;
|
||||
roles?: ('user' | 'admin' | 'editor')[] | null;
|
||||
food?: ('apple' | 'bananabread' | 'banana')[] | null;
|
||||
updatedAt: string;
|
||||
@@ -724,7 +733,7 @@ export interface SelectHasMany {
|
||||
* via the `definition` "virtual-linked-tenants".
|
||||
*/
|
||||
export interface VirtualLinkedTenant {
|
||||
id: number;
|
||||
id: string;
|
||||
slug: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
@@ -734,9 +743,9 @@ export interface VirtualLinkedTenant {
|
||||
* via the `definition` "virtual-linked-roles".
|
||||
*/
|
||||
export interface VirtualLinkedRole {
|
||||
id: number;
|
||||
project: number | VirtualLinkedProject;
|
||||
tenant: number | VirtualLinkedTenant;
|
||||
id: string;
|
||||
project: string | VirtualLinkedProject;
|
||||
tenant: string | VirtualLinkedTenant;
|
||||
tenantSlug?: string | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
@@ -746,9 +755,9 @@ export interface VirtualLinkedRole {
|
||||
* via the `definition` "virtual-linked-projects".
|
||||
*/
|
||||
export interface VirtualLinkedProject {
|
||||
id: number;
|
||||
id: string;
|
||||
roles?: {
|
||||
docs?: (number | VirtualLinkedRole)[];
|
||||
docs?: (string | VirtualLinkedRole)[];
|
||||
hasNextPage?: boolean;
|
||||
totalDocs?: number;
|
||||
};
|
||||
@@ -760,7 +769,7 @@ export interface VirtualLinkedProject {
|
||||
* via the `definition` "payload-kv".
|
||||
*/
|
||||
export interface PayloadKv {
|
||||
id: number;
|
||||
id: string;
|
||||
key: string;
|
||||
data:
|
||||
| {
|
||||
@@ -777,7 +786,7 @@ export interface PayloadKv {
|
||||
* via the `definition` "users".
|
||||
*/
|
||||
export interface User {
|
||||
id: number;
|
||||
id: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
email: string;
|
||||
@@ -802,23 +811,23 @@ export interface User {
|
||||
* via the `definition` "payload-locked-documents".
|
||||
*/
|
||||
export interface PayloadLockedDocument {
|
||||
id: number;
|
||||
id: string;
|
||||
document?:
|
||||
| ({
|
||||
relationTo: 'noTimeStamps';
|
||||
value: number | NoTimeStamp;
|
||||
value: string | NoTimeStamp;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'categories';
|
||||
value: number | Category;
|
||||
value: string | Category;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'simple';
|
||||
value: number | Simple;
|
||||
value: string | Simple;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'simple-localized';
|
||||
value: number | SimpleLocalized;
|
||||
value: string | SimpleLocalized;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'categories-custom-id';
|
||||
@@ -826,43 +835,43 @@ export interface PayloadLockedDocument {
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'error-on-unnamed-fields';
|
||||
value: number | ErrorOnUnnamedField;
|
||||
value: string | ErrorOnUnnamedField;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'default-values';
|
||||
value: number | DefaultValue;
|
||||
value: string | DefaultValue;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'relation-a';
|
||||
value: number | RelationA;
|
||||
value: string | RelationA;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'relation-b';
|
||||
value: number | RelationB;
|
||||
value: string | RelationB;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'pg-migrations';
|
||||
value: number | PgMigration;
|
||||
value: string | PgMigration;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'custom-schema';
|
||||
value: number | CustomSchema;
|
||||
value: string | CustomSchema;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'places';
|
||||
value: number | Place;
|
||||
value: string | Place;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'virtual-relations';
|
||||
value: number | VirtualRelation;
|
||||
value: string | VirtualRelation;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'fields-persistance';
|
||||
value: number | FieldsPersistance;
|
||||
value: string | FieldsPersistance;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'custom-ids';
|
||||
@@ -870,52 +879,52 @@ export interface PayloadLockedDocument {
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'fake-custom-ids';
|
||||
value: number | FakeCustomId;
|
||||
value: string | FakeCustomId;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'relationships-migration';
|
||||
value: number | RelationshipsMigration;
|
||||
value: string | RelationshipsMigration;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'compound-indexes';
|
||||
value: number | CompoundIndex;
|
||||
value: string | CompoundIndex;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'aliases';
|
||||
value: number | Alias;
|
||||
value: string | Alias;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'blocks-docs';
|
||||
value: number | BlocksDoc;
|
||||
value: string | BlocksDoc;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'unique-fields';
|
||||
value: number | UniqueField;
|
||||
value: string | UniqueField;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'select-has-many';
|
||||
value: number | SelectHasMany;
|
||||
value: string | SelectHasMany;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'virtual-linked-tenants';
|
||||
value: number | VirtualLinkedTenant;
|
||||
value: string | VirtualLinkedTenant;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'virtual-linked-roles';
|
||||
value: number | VirtualLinkedRole;
|
||||
value: string | VirtualLinkedRole;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'virtual-linked-projects';
|
||||
value: number | VirtualLinkedProject;
|
||||
value: string | VirtualLinkedProject;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'users';
|
||||
value: number | User;
|
||||
value: string | User;
|
||||
} | null);
|
||||
globalSlug?: string | null;
|
||||
user: {
|
||||
relationTo: 'users';
|
||||
value: number | User;
|
||||
value: string | User;
|
||||
};
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
@@ -925,10 +934,10 @@ export interface PayloadLockedDocument {
|
||||
* via the `definition` "payload-preferences".
|
||||
*/
|
||||
export interface PayloadPreference {
|
||||
id: number;
|
||||
id: string;
|
||||
user: {
|
||||
relationTo: 'users';
|
||||
value: number | User;
|
||||
value: string | User;
|
||||
};
|
||||
key?: string | null;
|
||||
value?:
|
||||
@@ -948,7 +957,7 @@ export interface PayloadPreference {
|
||||
* via the `definition` "payload-migrations".
|
||||
*/
|
||||
export interface PayloadMigration {
|
||||
id: number;
|
||||
id: string;
|
||||
name?: string | null;
|
||||
batch?: number | null;
|
||||
updatedAt: string;
|
||||
@@ -1291,6 +1300,18 @@ export interface FieldsPersistanceSelect<T extends boolean = true> {
|
||||
textWithinRow?: T;
|
||||
textWithinCollapsible?: T;
|
||||
textWithinTabs?: T;
|
||||
blockWithVirtual?:
|
||||
| T
|
||||
| {
|
||||
blockWithVirtual?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
virtualField?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
@@ -1514,7 +1535,7 @@ export interface PayloadMigrationsSelect<T extends boolean = true> {
|
||||
* via the `definition` "header".
|
||||
*/
|
||||
export interface Header {
|
||||
id: number;
|
||||
id: string;
|
||||
itemsLvl1?:
|
||||
| {
|
||||
label?: string | null;
|
||||
@@ -1547,7 +1568,7 @@ export interface Header {
|
||||
* via the `definition` "global".
|
||||
*/
|
||||
export interface Global {
|
||||
id: number;
|
||||
id: string;
|
||||
text?: string | null;
|
||||
updatedAt?: string | null;
|
||||
createdAt?: string | null;
|
||||
@@ -1557,7 +1578,7 @@ export interface Global {
|
||||
* via the `definition` "global-2".
|
||||
*/
|
||||
export interface Global2 {
|
||||
id: number;
|
||||
id: string;
|
||||
text?: string | null;
|
||||
updatedAt?: string | null;
|
||||
createdAt?: string | null;
|
||||
@@ -1567,7 +1588,7 @@ export interface Global2 {
|
||||
* via the `definition` "global-3".
|
||||
*/
|
||||
export interface Global3 {
|
||||
id: number;
|
||||
id: string;
|
||||
text?: string | null;
|
||||
updatedAt?: string | null;
|
||||
createdAt?: string | null;
|
||||
@@ -1577,9 +1598,9 @@ export interface Global3 {
|
||||
* via the `definition` "virtual-relation-global".
|
||||
*/
|
||||
export interface VirtualRelationGlobal {
|
||||
id: number;
|
||||
id: string;
|
||||
postTitle?: string | null;
|
||||
post?: (number | null) | Post;
|
||||
post?: (string | null) | Post;
|
||||
updatedAt?: string | null;
|
||||
createdAt?: string | null;
|
||||
}
|
||||
|
||||
@@ -86,6 +86,15 @@ export const allDatabaseAdapters = {
|
||||
connectionString: process.env.POSTGRES_URL || process.env.DATABASE_URL || '${defaultPostgresUrl}',
|
||||
},
|
||||
})`,
|
||||
'postgres-uuidv7': `
|
||||
import { postgresAdapter } from '@payloadcms/db-postgres'
|
||||
|
||||
export const databaseAdapter = postgresAdapter({
|
||||
idType: 'uuidv7',
|
||||
pool: {
|
||||
connectionString: process.env.POSTGRES_URL || process.env.DATABASE_URL || '${defaultPostgresUrl}',
|
||||
},
|
||||
})`,
|
||||
'postgres-read-replica': `
|
||||
import { postgresAdapter } from '@payloadcms/db-postgres'
|
||||
|
||||
@@ -146,6 +155,15 @@ export const databaseAdapter = contentAPIAdapter({
|
||||
url: process.env.SQLITE_URL || process.env.DATABASE_URL || 'file:./payload.db',
|
||||
}
|
||||
})`,
|
||||
'sqlite-uuidv7': `
|
||||
import { sqliteAdapter } from '@payloadcms/db-sqlite'
|
||||
|
||||
export const databaseAdapter = sqliteAdapter({
|
||||
idType: 'uuidv7',
|
||||
client: {
|
||||
url: process.env.SQLITE_URL || process.env.DATABASE_URL || 'file:./payload.db',
|
||||
}
|
||||
})`,
|
||||
supabase: `
|
||||
import { postgresAdapter } from '@payloadcms/db-postgres'
|
||||
|
||||
|
||||
+1
-1
@@ -113,7 +113,7 @@
|
||||
"tempy": "^1.0.1",
|
||||
"ts-essentials": "10.0.3",
|
||||
"typescript": "5.7.3",
|
||||
"uuid": "10.0.0",
|
||||
"uuid": "11.1.0",
|
||||
"vitest": "4.1.2",
|
||||
"wrangler": "~4.61.1",
|
||||
"zod": "^3.25.5"
|
||||
|
||||
@@ -1812,7 +1812,8 @@ describe('Relationships', () => {
|
||||
polymorphic: {
|
||||
equals: {
|
||||
relationTo: 'movies',
|
||||
value: payload.db.idType === 'uuid' ? randomUUID() : 99,
|
||||
value:
|
||||
payload.db.idType === 'uuid' || payload.db.idType === 'uuidv7' ? randomUUID() : 99,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import type { PostgresAdapter } from '@payloadcms/db-postgres'
|
||||
import type { DatabaseAdapterObj } from 'payload'
|
||||
|
||||
import path from 'path'
|
||||
import { buildConfig } from 'payload'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
const dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
const { databaseAdapter } = (await import(path.resolve(dirname, '../databaseAdapter.js'))) as {
|
||||
databaseAdapter: DatabaseAdapterObj<PostgresAdapter>
|
||||
}
|
||||
|
||||
export default await buildConfig({
|
||||
db: databaseAdapter,
|
||||
secret: 'uuid-v7-test-secret',
|
||||
admin: {
|
||||
importMap: {
|
||||
baseDir: path.resolve(dirname),
|
||||
},
|
||||
},
|
||||
collections: [
|
||||
{
|
||||
slug: 'posts',
|
||||
fields: [{ name: 'title', type: 'text' }],
|
||||
},
|
||||
{
|
||||
slug: 'categories',
|
||||
fields: [{ name: 'name', type: 'text' }],
|
||||
},
|
||||
{
|
||||
slug: 'articles',
|
||||
fields: [
|
||||
{ name: 'title', type: 'text' },
|
||||
{
|
||||
name: 'category',
|
||||
type: 'relationship',
|
||||
relationTo: 'categories',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
@@ -0,0 +1,142 @@
|
||||
import type { Payload } from 'payload'
|
||||
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
import { validate as uuidValidate } from 'uuid'
|
||||
import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest'
|
||||
|
||||
import { initPayloadInt } from '../__helpers/shared/initPayloadInt.js'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
process.env.PAYLOAD_CONFIG_PATH = path.join(dirname, 'config.ts')
|
||||
|
||||
type UuidV7Collection = 'articles' | 'categories' | 'posts'
|
||||
|
||||
const describeUuidV7 = process.env.PAYLOAD_DATABASE === 'postgres-uuidv7' ? describe : describe.skip
|
||||
|
||||
describeUuidV7('UUID v7 idType (postgres)', () => {
|
||||
let payload: Payload
|
||||
|
||||
/** Created in order; deleted in reverse (articles → categories → posts). */
|
||||
const createdStack: { collection: UuidV7Collection; id: string }[] = []
|
||||
|
||||
const track = (collection: UuidV7Collection, id: string) => {
|
||||
createdStack.push({ collection, id })
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
;({ payload } = await initPayloadInt(dirname))
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
if (!payload) {
|
||||
return
|
||||
}
|
||||
|
||||
for (const { collection, id } of [...createdStack].reverse()) {
|
||||
try {
|
||||
await payload.delete({ collection, id })
|
||||
} catch {
|
||||
// ignore: already deleted or FK race
|
||||
}
|
||||
}
|
||||
|
||||
createdStack.length = 0
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
if (payload) {
|
||||
await payload.destroy()
|
||||
}
|
||||
})
|
||||
|
||||
it('should expose uuidv7 adapter idType', () => {
|
||||
expect(payload.db.idType).toBe('uuidv7')
|
||||
})
|
||||
|
||||
it('should create a document with a valid UUID v7 default id', async () => {
|
||||
const doc = await payload.create({
|
||||
collection: 'posts',
|
||||
data: { title: 'uuid v7 post' },
|
||||
})
|
||||
|
||||
track('posts', String(doc.id))
|
||||
|
||||
expect(typeof doc.id).toBe('string')
|
||||
expect(uuidValidate(doc.id)).toBe(true)
|
||||
expect(doc.id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i)
|
||||
expect(doc.id.charAt(14)).toBe('7')
|
||||
})
|
||||
|
||||
it('should order ids lexicographically for consecutive creates', async () => {
|
||||
const first = await payload.create({
|
||||
collection: 'posts',
|
||||
data: { title: 'first' },
|
||||
})
|
||||
const second = await payload.create({
|
||||
collection: 'posts',
|
||||
data: { title: 'second' },
|
||||
})
|
||||
|
||||
track('posts', String(first.id))
|
||||
track('posts', String(second.id))
|
||||
|
||||
expect(second.id > first.id).toBe(true)
|
||||
})
|
||||
|
||||
it('should findByID with generated id', async () => {
|
||||
const created = await payload.create({
|
||||
collection: 'posts',
|
||||
data: { title: 'find me' },
|
||||
})
|
||||
|
||||
track('posts', String(created.id))
|
||||
|
||||
const found = await payload.findByID({
|
||||
collection: 'posts',
|
||||
id: created.id,
|
||||
})
|
||||
|
||||
expect(found.id).toBe(created.id)
|
||||
expect(found.title).toBe('find me')
|
||||
})
|
||||
|
||||
it('should resolve relationship to category', async () => {
|
||||
const category = await payload.create({
|
||||
collection: 'categories',
|
||||
data: { name: 'Cat A' },
|
||||
})
|
||||
const article = await payload.create({
|
||||
collection: 'articles',
|
||||
data: {
|
||||
title: 'Article 1',
|
||||
category: category.id,
|
||||
},
|
||||
depth: 1,
|
||||
})
|
||||
|
||||
track('articles', String(article.id))
|
||||
track('categories', String(category.id))
|
||||
|
||||
expect(article.category).toMatchObject({ id: category.id })
|
||||
})
|
||||
|
||||
it('should query by id equals', async () => {
|
||||
const created = await payload.create({
|
||||
collection: 'posts',
|
||||
data: { title: 'query by id' },
|
||||
})
|
||||
|
||||
track('posts', String(created.id))
|
||||
|
||||
const res = await payload.find({
|
||||
collection: 'posts',
|
||||
where: { id: { equals: created.id } },
|
||||
})
|
||||
|
||||
expect(res.docs).toHaveLength(1)
|
||||
expect(res.docs[0].id).toBe(created.id)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user