fix(db-postgres, db-sqlite): enum schema (#7453)

- updates drizzle-kit and drizzle-orm
- fix enum creation to fully support custom schemas
- sqlite by default will not use transactions
This commit is contained in:
Dan Ribbens
2024-07-31 16:42:00 -04:00
committed by GitHub
parent 1ec78a16f0
commit 075819964d
23 changed files with 185 additions and 278 deletions

View File

@@ -196,6 +196,7 @@ jobs:
- postgres-custom-schema - postgres-custom-schema
- postgres-uuid - postgres-uuid
- supabase - supabase
- sqlite
env: env:
POSTGRES_USER: postgres POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres POSTGRES_PASSWORD: postgres

View File

@@ -119,7 +119,7 @@
"create-payload-app": "workspace:*", "create-payload-app": "workspace:*",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"dotenv": "16.4.5", "dotenv": "16.4.5",
"drizzle-orm": "0.29.4", "drizzle-orm": "0.32.1",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"execa": "5.1.1", "execa": "5.1.1",
"form-data": "3.0.1", "form-data": "3.0.1",

View File

@@ -47,8 +47,8 @@
"dependencies": { "dependencies": {
"@payloadcms/drizzle": "workspace:*", "@payloadcms/drizzle": "workspace:*",
"console-table-printer": "2.11.2", "console-table-printer": "2.11.2",
"drizzle-kit": "0.20.14-1f2c838", "drizzle-kit": "0.23.1-7816536",
"drizzle-orm": "0.29.4", "drizzle-orm": "0.32.1",
"pg": "8.11.3", "pg": "8.11.3",
"prompts": "2.4.2", "prompts": "2.4.2",
"to-snake-case": "1.0.0", "to-snake-case": "1.0.0",

View File

@@ -53,6 +53,7 @@ export const connect: Connect = async function connect(
const { hotReload } = options const { hotReload } = options
this.schema = { this.schema = {
pgSchema: this.pgSchema,
...this.tables, ...this.tables,
...this.relations, ...this.relations,
...this.enums, ...this.enums,

View File

@@ -49,6 +49,12 @@ export const createMigration: CreateMigration = async function createMigration(
let drizzleJsonBefore = defaultDrizzleSnapshot let drizzleJsonBefore = defaultDrizzleSnapshot
if (this.schemaName) {
drizzleJsonBefore.schemas = {
[this.schemaName]: this.schemaName,
}
}
if (!upSQL) { if (!upSQL) {
// Get latest migration snapshot // Get latest migration snapshot
const latestSnapshot = fs const latestSnapshot = fs

View File

@@ -7,10 +7,11 @@ export const defaultDrizzleSnapshot: DrizzleSnapshotJSON = {
schemas: {}, schemas: {},
tables: {}, tables: {},
}, },
dialect: 'pg', dialect: 'postgresql',
enums: {}, enums: {},
prevId: '00000000-0000-0000-0000-00000000000', prevId: '00000000-0000-0000-0000-00000000000',
schemas: {}, schemas: {},
sequences: {},
tables: {}, tables: {},
version: '5', version: '7',
} }

View File

@@ -32,6 +32,7 @@ import {
updateOne, updateOne,
updateVersion, updateVersion,
} from '@payloadcms/drizzle' } from '@payloadcms/drizzle'
import { type PgSchema, pgEnum, pgSchema, pgTable } from 'drizzle-orm/pg-core'
import { createDatabaseAdapter } from 'payload' import { createDatabaseAdapter } from 'payload'
import type { Args, PostgresAdapter } from './types.js' import type { Args, PostgresAdapter } from './types.js'
@@ -62,12 +63,19 @@ export function postgresAdapter(args: Args): DatabaseAdapterObj<PostgresAdapter>
const migrationDir = findMigrationDir(args.migrationDir) const migrationDir = findMigrationDir(args.migrationDir)
let resolveInitializing let resolveInitializing
let rejectInitializing let rejectInitializing
let adapterSchema: PostgresAdapter['pgSchema']
const initializing = new Promise<void>((res, rej) => { const initializing = new Promise<void>((res, rej) => {
resolveInitializing = res resolveInitializing = res
rejectInitializing = rej rejectInitializing = rej
}) })
if (args.schemaName) {
adapterSchema = pgSchema(args.schemaName)
} else {
adapterSchema = { enum: pgEnum, table: pgTable }
}
return createDatabaseAdapter<PostgresAdapter>({ return createDatabaseAdapter<PostgresAdapter>({
name: 'postgres', name: 'postgres',
defaultDrizzleSnapshot, defaultDrizzleSnapshot,
@@ -83,7 +91,7 @@ export function postgresAdapter(args: Args): DatabaseAdapterObj<PostgresAdapter>
localesSuffix: args.localesSuffix || '_locales', localesSuffix: args.localesSuffix || '_locales',
logger: args.logger, logger: args.logger,
operators: operatorMap, operators: operatorMap,
pgSchema: undefined, pgSchema: adapterSchema,
pool: undefined, pool: undefined,
poolOptions: args.pool, poolOptions: args.pool,
push: args.push, push: args.push,

View File

@@ -1,7 +1,6 @@
import type { Init, SanitizedCollectionConfig } from 'payload' import type { Init, SanitizedCollectionConfig } from 'payload'
import { createTableName } from '@payloadcms/drizzle' import { createTableName } from '@payloadcms/drizzle'
import { pgEnum, pgSchema, pgTable } from 'drizzle-orm/pg-core'
import { buildVersionCollectionFields, buildVersionGlobalFields } from 'payload' import { buildVersionCollectionFields, buildVersionGlobalFields } from 'payload'
import toSnakeCase from 'to-snake-case' import toSnakeCase from 'to-snake-case'
@@ -10,13 +9,8 @@ import type { PostgresAdapter } from './types.js'
import { buildTable } from './schema/build.js' import { buildTable } from './schema/build.js'
export const init: Init = function init(this: PostgresAdapter) { export const init: Init = function init(this: PostgresAdapter) {
if (this.schemaName) {
this.pgSchema = pgSchema(this.schemaName)
} else {
this.pgSchema = { table: pgTable }
}
if (this.payload.config.localization) { if (this.payload.config.localization) {
this.enums.enum__locales = pgEnum( this.enums.enum__locales = this.pgSchema.enum(
'_locales', '_locales',
this.payload.config.localization.locales.map(({ code }) => code) as [string, ...string[]], this.payload.config.localization.locales.map(({ code }) => code) as [string, ...string[]],
) )

View File

@@ -1,4 +1,3 @@
/* eslint-disable no-param-reassign */
import { index, uniqueIndex } from 'drizzle-orm/pg-core' import { index, uniqueIndex } from 'drizzle-orm/pg-core'
import type { GenericColumn } from '../types.js' import type { GenericColumn } from '../types.js'

View File

@@ -18,7 +18,6 @@ import {
integer, integer,
jsonb, jsonb,
numeric, numeric,
pgEnum,
text, text,
timestamp, timestamp,
varchar, varchar,
@@ -237,7 +236,7 @@ export const traverseFields = ({
throwValidationError, throwValidationError,
}) })
adapter.enums[enumName] = pgEnum( adapter.enums[enumName] = adapter.pgSchema.enum(
enumName, enumName,
field.options.map((option) => { field.options.map((option) => {
if (optionIsObject(option)) { if (optionIsObject(option)) {

View File

@@ -21,6 +21,7 @@ import type {
PgSchema, PgSchema,
PgTableWithColumns, PgTableWithColumns,
PgTransactionConfig, PgTransactionConfig,
pgEnum,
} from 'drizzle-orm/pg-core' } from 'drizzle-orm/pg-core'
import type { PgTableFn } from 'drizzle-orm/pg-core/table' import type { PgTableFn } from 'drizzle-orm/pg-core/table'
import type { Payload, PayloadRequest } from 'payload' import type { Payload, PayloadRequest } from 'payload'
@@ -106,6 +107,13 @@ type PostgresDrizzleAdapter = Omit<
| 'relations' | 'relations'
> >
type Schema =
| {
enum: typeof pgEnum
table: PgTableFn
}
| PgSchema
export type PostgresAdapter = { export type PostgresAdapter = {
countDistinct: CountDistinct countDistinct: CountDistinct
defaultDrizzleSnapshot: DrizzleSnapshotJSON defaultDrizzleSnapshot: DrizzleSnapshotJSON
@@ -125,7 +133,7 @@ export type PostgresAdapter = {
localesSuffix?: string localesSuffix?: string
logger: DrizzleConfig['logger'] logger: DrizzleConfig['logger']
operators: Operators operators: Operators
pgSchema?: { table: PgTableFn } | PgSchema pgSchema?: Schema
pool: Pool pool: Pool
poolOptions: Args['pool'] poolOptions: Args['pool']
push: boolean push: boolean
@@ -133,7 +141,7 @@ export type PostgresAdapter = {
relations: Record<string, GenericRelation> relations: Record<string, GenericRelation>
relationshipsSuffix?: string relationshipsSuffix?: string
resolveInitializing: () => void resolveInitializing: () => void
schema: Record<string, GenericEnum | GenericRelation | GenericTable> schema: DrizzleConfig
schemaName?: Args['schemaName'] schemaName?: Args['schemaName']
sessions: { sessions: {
[id: string]: { [id: string]: {

View File

@@ -46,8 +46,8 @@
"@libsql/client": "^0.6.2", "@libsql/client": "^0.6.2",
"@payloadcms/drizzle": "workspace:*", "@payloadcms/drizzle": "workspace:*",
"console-table-printer": "2.11.2", "console-table-printer": "2.11.2",
"drizzle-kit": "0.20.14-1f2c838", "drizzle-kit": "0.23.1-7816536",
"drizzle-orm": "0.29.4", "drizzle-orm": "0.32.1",
"prompts": "2.4.2", "prompts": "2.4.2",
"to-snake-case": "1.0.0", "to-snake-case": "1.0.0",
"uuid": "9.0.0" "uuid": "9.0.0"

View File

@@ -10,5 +10,5 @@ export const defaultDrizzleSnapshot: DrizzleSQLiteSnapshotJSON = {
enums: {}, enums: {},
prevId: '00000000-0000-0000-0000-00000000000', prevId: '00000000-0000-0000-0000-00000000000',
tables: {}, tables: {},
version: '5', version: '6',
} }

View File

@@ -105,7 +105,7 @@ export function sqliteAdapter(args: Args): DatabaseAdapterObj<SQLiteAdapter> {
versionsSuffix: args.versionsSuffix || '_v', versionsSuffix: args.versionsSuffix || '_v',
// DatabaseAdapter // DatabaseAdapter
beginTransaction: args.transactionOptions === false ? undefined : beginTransaction, beginTransaction: args.transactionOptions ? beginTransaction : undefined,
commitTransaction, commitTransaction,
connect, connect,
convertPathToJSONTraversal, convertPathToJSONTraversal,

View File

@@ -1,9 +1,8 @@
/* eslint-disable no-param-reassign */ import type { Relation } from 'drizzle-orm'
import type { ColumnDataType, Relation } from 'drizzle-orm'
import type { import type {
AnySQLiteColumn,
ForeignKeyBuilder, ForeignKeyBuilder,
IndexBuilder, IndexBuilder,
SQLiteColumn,
SQLiteColumnBuilder, SQLiteColumnBuilder,
SQLiteTableWithColumns, SQLiteTableWithColumns,
UniqueConstraintBuilder, UniqueConstraintBuilder,
@@ -32,18 +31,7 @@ import { traverseFields } from './traverseFields.js'
export type BaseExtraConfig = Record< export type BaseExtraConfig = Record<
string, string,
(cols: { (cols: {
[x: string]: SQLiteColumn<{ [x: string]: AnySQLiteColumn
baseColumn: never
columnType: string
data: unknown
dataType: ColumnDataType
driverParam: unknown
enumValues: string[]
hasDefault: false
name: string
notNull: false
tableName: string
}>
}) => ForeignKeyBuilder | IndexBuilder | UniqueConstraintBuilder }) => ForeignKeyBuilder | IndexBuilder | UniqueConstraintBuilder
> >

View File

@@ -1,7 +1,6 @@
/* eslint-disable no-param-reassign */ import type { AnySQLiteColumn} from 'drizzle-orm/sqlite-core';
import { index, uniqueIndex } from 'drizzle-orm/sqlite-core'
import type { GenericColumn } from '../types.js' import { index, uniqueIndex } from 'drizzle-orm/sqlite-core'
type CreateIndexArgs = { type CreateIndexArgs = {
columnName: string columnName: string
@@ -11,7 +10,7 @@ type CreateIndexArgs = {
} }
export const createIndex = ({ name, columnName, tableName, unique }: CreateIndexArgs) => { export const createIndex = ({ name, columnName, tableName, unique }: CreateIndexArgs) => {
return (table: { [x: string]: GenericColumn }) => { return (table: { [x: string]: AnySQLiteColumn }) => {
let columns let columns
if (Array.isArray(name)) { if (Array.isArray(name)) {
columns = name columns = name

View File

@@ -1,10 +1,10 @@
import type { Client, Config, ResultSet } from '@libsql/client' import type { Client, Config, ResultSet } from '@libsql/client'
import type { Operators } from '@payloadcms/drizzle' import type { Operators } from '@payloadcms/drizzle'
import type { BuildQueryJoinAliases, DrizzleAdapter } from '@payloadcms/drizzle/types' import type { BuildQueryJoinAliases, DrizzleAdapter } from '@payloadcms/drizzle/types'
import type { ColumnDataType, DrizzleConfig, Relation, Relations, SQL } from 'drizzle-orm' import type { DrizzleConfig, Relation, Relations, SQL } from 'drizzle-orm'
import type { LibSQLDatabase } from 'drizzle-orm/libsql' import type { LibSQLDatabase } from 'drizzle-orm/libsql'
import type { import type {
SQLiteColumn, AnySQLiteColumn,
SQLiteInsertOnConflictDoUpdateConfig, SQLiteInsertOnConflictDoUpdateConfig,
SQLiteTableWithColumns, SQLiteTableWithColumns,
SQLiteTransactionConfig, SQLiteTransactionConfig,
@@ -25,24 +25,8 @@ export type Args = {
versionsSuffix?: string versionsSuffix?: string
} }
export type GenericColumn = SQLiteColumn<
{
baseColumn: never
columnType: string
data: unknown
dataType: ColumnDataType
driverParam: unknown
enumValues: string[]
hasDefault: false
name: string
notNull: false
tableName: string
},
object
>
export type GenericColumns = { export type GenericColumns = {
[x: string]: GenericColumn [x: string]: AnySQLiteColumn
} }
export type GenericTable = SQLiteTableWithColumns<{ export type GenericTable = SQLiteTableWithColumns<{

View File

@@ -39,7 +39,7 @@
}, },
"dependencies": { "dependencies": {
"console-table-printer": "2.11.2", "console-table-printer": "2.11.2",
"drizzle-orm": "0.29.4", "drizzle-orm": "0.32.1",
"prompts": "2.4.2", "prompts": "2.4.2",
"to-snake-case": "1.0.0", "to-snake-case": "1.0.0",
"uuid": "9.0.0" "uuid": "9.0.0"

View File

@@ -120,6 +120,7 @@ export type RequireDrizzleKit = () => {
pushSchema: ( pushSchema: (
schema: Record<string, unknown>, schema: Record<string, unknown>,
drizzle: DrizzleAdapter['drizzle'], drizzle: DrizzleAdapter['drizzle'],
filterSchema?: string[],
) => Promise<{ apply; hasDataLoss; warnings }> ) => Promise<{ apply; hasDataLoss; warnings }>
} }

View File

@@ -12,7 +12,11 @@ export const pushDevSchema = async (adapter: DrizzleAdapter) => {
const { pushSchema } = adapter.requireDrizzleKit() const { pushSchema } = adapter.requireDrizzleKit()
// This will prompt if clarifications are needed for Drizzle to push new schema // This will prompt if clarifications are needed for Drizzle to push new schema
const { apply, hasDataLoss, warnings } = await pushSchema(adapter.schema, adapter.drizzle) const { apply, hasDataLoss, warnings } = await pushSchema(
adapter.schema,
adapter.drizzle,
adapter.schemaName ? [adapter.schemaName] : undefined,
)
if (warnings.length) { if (warnings.length) {
let message = `Warnings detected during schema push: \n\n${warnings.join('\n')}\n\n` let message = `Warnings detected during schema push: \n\n${warnings.join('\n')}\n\n`

168
pnpm-lock.yaml generated
View File

@@ -10,7 +10,7 @@ overrides:
copyfiles: 2.4.1 copyfiles: 2.4.1
cross-env: 7.0.3 cross-env: 7.0.3
dotenv: 16.4.5 dotenv: 16.4.5
drizzle-orm: 0.29.4 drizzle-orm: 0.32.1
graphql: ^16.8.1 graphql: ^16.8.1
mongodb-memory-server: ^9.0 mongodb-memory-server: ^9.0
react: ^19.0.0-rc-6230622a1a-20240610 react: ^19.0.0-rc-6230622a1a-20240610
@@ -102,8 +102,8 @@ importers:
specifier: 16.4.5 specifier: 16.4.5
version: 16.4.5 version: 16.4.5
drizzle-orm: drizzle-orm:
specifier: 0.29.4 specifier: 0.32.1
version: 0.29.4(@libsql/client@0.6.2)(@types/pg@8.10.2)(pg@8.11.3)(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.0) version: 0.32.1(@libsql/client@0.6.2)(@types/pg@8.10.2)(pg@8.11.3)(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.0)
escape-html: escape-html:
specifier: ^1.0.3 specifier: ^1.0.3
version: 1.0.3 version: 1.0.3
@@ -302,11 +302,11 @@ importers:
specifier: 2.11.2 specifier: 2.11.2
version: 2.11.2 version: 2.11.2
drizzle-kit: drizzle-kit:
specifier: 0.20.14-1f2c838 specifier: 0.23.1-7816536
version: 0.20.14-1f2c838 version: 0.23.1-7816536
drizzle-orm: drizzle-orm:
specifier: 0.29.4 specifier: 0.32.1
version: 0.29.4(@libsql/client@0.6.2)(@types/pg@8.10.2)(pg@8.11.3)(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.0) version: 0.32.1(@libsql/client@0.6.2)(@types/pg@8.10.2)(pg@8.11.3)(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.0)
pg: pg:
specifier: 8.11.3 specifier: 8.11.3
version: 8.11.3 version: 8.11.3
@@ -351,11 +351,11 @@ importers:
specifier: 2.11.2 specifier: 2.11.2
version: 2.11.2 version: 2.11.2
drizzle-kit: drizzle-kit:
specifier: 0.20.14-1f2c838 specifier: 0.23.1-7816536
version: 0.20.14-1f2c838 version: 0.23.1-7816536
drizzle-orm: drizzle-orm:
specifier: 0.29.4 specifier: 0.32.1
version: 0.29.4(@libsql/client@0.6.2)(@types/pg@8.10.2)(pg@8.11.3)(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.0) version: 0.32.1(@libsql/client@0.6.2)(@types/pg@8.10.2)(pg@8.11.3)(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.0)
prompts: prompts:
specifier: 2.4.2 specifier: 2.4.2
version: 2.4.2 version: 2.4.2
@@ -385,8 +385,8 @@ importers:
specifier: 2.11.2 specifier: 2.11.2
version: 2.11.2 version: 2.11.2
drizzle-orm: drizzle-orm:
specifier: 0.29.4 specifier: 0.32.1
version: 0.29.4(@libsql/client@0.6.2)(@types/pg@8.10.2)(pg@8.11.3)(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.0) version: 0.32.1(@libsql/client@0.6.2)(@types/pg@8.10.2)(pg@8.11.3)(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.0)
prompts: prompts:
specifier: 2.4.2 specifier: 2.4.2
version: 2.4.2 version: 2.4.2
@@ -3986,10 +3986,8 @@ packages:
tslib: 2.6.3 tslib: 2.6.3
dev: false dev: false
/@drizzle-team/studio@0.0.39: /@drizzle-team/brocli@0.8.2:
resolution: {integrity: sha512-c5Hkm7MmQC2n5qAsKShjQrHoqlfGslB8+qWzsGGZ+2dHMRTNG60UuzalF0h0rvBax5uzPXuGkYLGaQ+TUX3yMw==} resolution: {integrity: sha512-zTrFENsqGvOkBOuHDC1pXCkDXNd2UhP4lI3gYGhQ1R1SPeAAfqzPsV1dcpMy4uNU6kB5VpU5NGhvwxVNETR02A==}
dependencies:
superjson: 2.2.1
dev: false dev: false
/@effect/schema@0.68.12(effect@3.4.5): /@effect/schema@0.68.12(effect@3.4.5):
@@ -8274,11 +8272,6 @@ packages:
resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
engines: {node: '>=10'} engines: {node: '>=10'}
/camelcase@7.0.1:
resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==}
engines: {node: '>=14.16'}
dev: false
/caniuse-lite@1.0.30001641: /caniuse-lite@1.0.30001641:
resolution: {integrity: sha512-Phv5thgl67bHYo1TtMY/MurjkHhV4EDaCosezRXgZ8jzA/Ub+wjxAvbGvjoFENStinwi5kCyOYV3mi5tOGykwA==} resolution: {integrity: sha512-Phv5thgl67bHYo1TtMY/MurjkHhV4EDaCosezRXgZ8jzA/Ub+wjxAvbGvjoFENStinwi5kCyOYV3mi5tOGykwA==}
@@ -8300,6 +8293,7 @@ packages:
/chalk@5.3.0: /chalk@5.3.0:
resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==}
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
dev: true
/changelogen@0.5.5: /changelogen@0.5.5:
resolution: {integrity: sha512-IzgToIJ/R9NhVKmL+PW33ozYkv53bXvufDNUSH3GTKXq1iCHGgkbgbtqEWbo8tnWNnt7nPDpjL8PwSG2iS8RVw==} resolution: {integrity: sha512-IzgToIJ/R9NhVKmL+PW33ozYkv53bXvufDNUSH3GTKXq1iCHGgkbgbtqEWbo8tnWNnt7nPDpjL8PwSG2iS8RVw==}
@@ -8513,11 +8507,6 @@ packages:
engines: {node: '>= 12'} engines: {node: '>= 12'}
dev: true dev: true
/commander@9.5.0:
resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==}
engines: {node: ^12.20.0 || >=14}
dev: false
/comment-json@4.2.4: /comment-json@4.2.4:
resolution: {integrity: sha512-E5AjpSW+O+N5T2GsOQMHLLsJvrYw6G/AFt9GvU6NguEAfzKShh7hRiLtVo6S9KbRpFMGqE5ojo0/hE+sdteWvQ==} resolution: {integrity: sha512-E5AjpSW+O+N5T2GsOQMHLLsJvrYw6G/AFt9GvU6NguEAfzKShh7hRiLtVo6S9KbRpFMGqE5ojo0/hE+sdteWvQ==}
engines: {node: '>= 6'} engines: {node: '>= 6'}
@@ -8605,13 +8594,6 @@ packages:
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
dev: false dev: false
/copy-anything@3.0.5:
resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==}
engines: {node: '>=12.13'}
dependencies:
is-what: 4.1.16
dev: false
/copyfiles@2.4.1: /copyfiles@2.4.1:
resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==} resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==}
hasBin: true hasBin: true
@@ -9016,12 +8998,6 @@ packages:
engines: {node: '>=0.3.1'} engines: {node: '>=0.3.1'}
dev: false dev: false
/difflib@0.2.4:
resolution: {integrity: sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w==}
dependencies:
heap: 0.2.7
dev: false
/dir-glob@3.0.1: /dir-glob@3.0.1:
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
engines: {node: '>=8'} engines: {node: '>=8'}
@@ -9087,49 +9063,37 @@ packages:
engines: {node: '>=12'} engines: {node: '>=12'}
dev: true dev: true
/dreamopt@0.8.0: /drizzle-kit@0.23.1-7816536:
resolution: {integrity: sha512-vyJTp8+mC+G+5dfgsY+r3ckxlz+QMX40VjPQsZc5gxVAxLmi64TBoVkP54A/pRAXMXsbu2GMMBrZPxNv23waMg==} resolution: {integrity: sha512-mc7Mo8Kuze3VJSccOjuOSibCRgMvyshp0r6ZVnGVAo+hgLgfIjOkyVIPDadzNFwGCH01j7gmjI1CkO8uvORuJg==}
engines: {node: '>=0.4.0'}
dependencies:
wordwrap: 1.0.0
dev: false
/drizzle-kit@0.20.14-1f2c838:
resolution: {integrity: sha512-Gun36T6RcfwrZAZ+Gk2ZeWJtvUhO002H/SvkY9BNcsACvhsd4OxkkxzrznuZ3Kz5NEdg12k7yvZqNoimPp68aw==}
hasBin: true hasBin: true
dependencies: dependencies:
'@drizzle-team/studio': 0.0.39 '@drizzle-team/brocli': 0.8.2
'@esbuild-kit/esm-loader': 2.6.5 '@esbuild-kit/esm-loader': 2.6.5
camelcase: 7.0.1
chalk: 5.3.0
commander: 9.5.0
env-paths: 3.0.0
esbuild: 0.19.12 esbuild: 0.19.12
esbuild-register: 3.5.0(esbuild@0.19.12) esbuild-register: 3.5.0(esbuild@0.19.12)
glob: 8.1.0
hanji: 0.0.5
json-diff: 0.9.0
minimatch: 7.4.6
semver: 7.6.2
zod: 3.23.8
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dev: false dev: false
/drizzle-orm@0.29.4(@libsql/client@0.6.2)(@types/pg@8.10.2)(pg@8.11.3)(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.0): /drizzle-orm@0.32.1(@libsql/client@0.6.2)(@types/pg@8.10.2)(pg@8.11.3)(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.0):
resolution: {integrity: sha512-ZnSM8TAxFhzH7p1s3+w3pRE/eKaOeNkH9SKitm717pubDVVcV2I0BCDBPGKV+pe02+wMfw37ntlTcCyo2rA3IA==} resolution: {integrity: sha512-Wq1J+lL8PzwR5K3a1FfoWsbs8powjr3pGA4+5+2ueN1VTLDNFYEolUyUWFtqy8DVRvYbL2n7sXZkgVmK9dQkng==}
peerDependencies: peerDependencies:
'@aws-sdk/client-rds-data': '>=3' '@aws-sdk/client-rds-data': '>=3'
'@cloudflare/workers-types': '>=3' '@cloudflare/workers-types': '>=3'
'@electric-sql/pglite': '>=0.1.1'
'@libsql/client': '*' '@libsql/client': '*'
'@neondatabase/serverless': '>=0.1' '@neondatabase/serverless': '>=0.1'
'@op-engineering/op-sqlite': '>=2'
'@opentelemetry/api': ^1.4.1 '@opentelemetry/api': ^1.4.1
'@planetscale/database': '>=1' '@planetscale/database': '>=1'
'@prisma/client': '*'
'@tidbcloud/serverless': '*'
'@types/better-sqlite3': '*' '@types/better-sqlite3': '*'
'@types/pg': '*' '@types/pg': '*'
'@types/react': npm:types-react@19.0.0-rc.0 '@types/react': npm:types-react@19.0.0-rc.0
'@types/sql.js': '*' '@types/sql.js': '*'
'@vercel/postgres': '*' '@vercel/postgres': '>=0.8.0'
'@xata.io/client': '*'
better-sqlite3: '>=7' better-sqlite3: '>=7'
bun-types: '*' bun-types: '*'
expo-sqlite: '>=13.2.0' expo-sqlite: '>=13.2.0'
@@ -9138,6 +9102,7 @@ packages:
mysql2: '>=2' mysql2: '>=2'
pg: '>=8' pg: '>=8'
postgres: '>=3' postgres: '>=3'
prisma: '*'
react: ^19.0.0-rc-6230622a1a-20240610 react: ^19.0.0-rc-6230622a1a-20240610
sql.js: '>=1' sql.js: '>=1'
sqlite3: '>=5' sqlite3: '>=5'
@@ -9146,14 +9111,22 @@ packages:
optional: true optional: true
'@cloudflare/workers-types': '@cloudflare/workers-types':
optional: true optional: true
'@electric-sql/pglite':
optional: true
'@libsql/client': '@libsql/client':
optional: true optional: true
'@neondatabase/serverless': '@neondatabase/serverless':
optional: true optional: true
'@op-engineering/op-sqlite':
optional: true
'@opentelemetry/api': '@opentelemetry/api':
optional: true optional: true
'@planetscale/database': '@planetscale/database':
optional: true optional: true
'@prisma/client':
optional: true
'@tidbcloud/serverless':
optional: true
'@types/better-sqlite3': '@types/better-sqlite3':
optional: true optional: true
'@types/pg': '@types/pg':
@@ -9164,6 +9137,8 @@ packages:
optional: true optional: true
'@vercel/postgres': '@vercel/postgres':
optional: true optional: true
'@xata.io/client':
optional: true
better-sqlite3: better-sqlite3:
optional: true optional: true
bun-types: bun-types:
@@ -9180,6 +9155,8 @@ packages:
optional: true optional: true
postgres: postgres:
optional: true optional: true
prisma:
optional: true
react: react:
optional: true optional: true
sql.js: sql.js:
@@ -9270,11 +9247,6 @@ packages:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'} engines: {node: '>=0.12'}
/env-paths@3.0.0:
resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dev: false
/error-ex@1.3.2: /error-ex@1.3.2:
resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
dependencies: dependencies:
@@ -10746,18 +10718,6 @@ packages:
once: 1.4.0 once: 1.4.0
path-is-absolute: 1.0.1 path-is-absolute: 1.0.1
/glob@8.1.0:
resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
engines: {node: '>=12'}
deprecated: Glob versions prior to v9 are no longer supported
dependencies:
fs.realpath: 1.0.0
inflight: 1.0.6
inherits: 2.0.4
minimatch: 5.1.6
once: 1.4.0
dev: false
/global-modules@1.0.0: /global-modules@1.0.0:
resolution: {integrity: sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==} resolution: {integrity: sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@@ -10917,13 +10877,6 @@ packages:
duplexer: 0.1.2 duplexer: 0.1.2
dev: true dev: true
/hanji@0.0.5:
resolution: {integrity: sha512-Abxw1Lq+TnYiL4BueXqMau222fPSPMFtya8HdpWsz/xVAhifXou71mPh/kY2+08RgFcVccjG3uZHs6K5HAe3zw==}
dependencies:
lodash.throttle: 4.1.1
sisteransi: 1.0.5
dev: false
/has-bigints@1.0.2: /has-bigints@1.0.2:
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
dev: false dev: false
@@ -10969,10 +10922,6 @@ packages:
dependencies: dependencies:
function-bind: 1.1.2 function-bind: 1.1.2
/heap@0.2.7:
resolution: {integrity: sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==}
dev: false
/help-me@5.0.0: /help-me@5.0.0:
resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==}
dev: false dev: false
@@ -11520,11 +11469,6 @@ packages:
get-intrinsic: 1.2.4 get-intrinsic: 1.2.4
dev: false dev: false
/is-what@4.1.16:
resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==}
engines: {node: '>=12.13'}
dev: false
/is-whitespace@0.3.0: /is-whitespace@0.3.0:
resolution: {integrity: sha512-RydPhl4S6JwAyj0JJjshWJEFG6hNye3pZFBRZaTUfZFwGHxzppNaNOVgQuS/E/SlhrApuMXrpnK1EEIXfdo3Dg==} resolution: {integrity: sha512-RydPhl4S6JwAyj0JJjshWJEFG6hNye3pZFBRZaTUfZFwGHxzppNaNOVgQuS/E/SlhrApuMXrpnK1EEIXfdo3Dg==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@@ -12191,15 +12135,6 @@ packages:
/json-buffer@3.0.1: /json-buffer@3.0.1:
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
/json-diff@0.9.0:
resolution: {integrity: sha512-cVnggDrVkAAA3OvFfHpFEhOnmcsUpleEKq4d4O8sQWWSH40MBrWstKigVB1kGrgLWzuom+7rRdaCsnBD6VyObQ==}
hasBin: true
dependencies:
cli-color: 2.0.4
difflib: 0.2.4
dreamopt: 0.8.0
dev: false
/json-parse-even-better-errors@2.3.1: /json-parse-even-better-errors@2.3.1:
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
@@ -12463,10 +12398,6 @@ packages:
/lodash.merge@4.6.2: /lodash.merge@4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
/lodash.throttle@4.1.1:
resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==}
dev: false
/lodash@4.17.21: /lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
@@ -12662,13 +12593,7 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
dependencies: dependencies:
brace-expansion: 2.0.1 brace-expansion: 2.0.1
dev: true
/minimatch@7.4.6:
resolution: {integrity: sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==}
engines: {node: '>=10'}
dependencies:
brace-expansion: 2.0.1
dev: false
/minimatch@9.0.1: /minimatch@9.0.1:
resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==} resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==}
@@ -15297,13 +15222,6 @@ packages:
resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==}
dev: false dev: false
/superjson@2.2.1:
resolution: {integrity: sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==}
engines: {node: '>=16'}
dependencies:
copy-anything: 3.0.5
dev: false
/supports-color@5.5.0: /supports-color@5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
engines: {node: '>=4'} engines: {node: '>=4'}
@@ -16212,10 +16130,6 @@ packages:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
/wordwrap@1.0.0:
resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==}
dev: false
/wrap-ansi@7.0.0: /wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'} engines: {node: '>=10'}

View File

@@ -361,103 +361,103 @@ describe('database', () => {
expect(firstResult.id).toStrictEqual(first.id) expect(firstResult.id).toStrictEqual(first.id)
expect(secondResult.id).toStrictEqual(second.id) expect(secondResult.id).toStrictEqual(second.id)
}) })
}
it('should commit multiple operations async', async () => { it('should commit multiple operations async', async () => {
const req = { const req = {
payload, payload,
user, user,
} as unknown as PayloadRequest } as unknown as PayloadRequest
let first let first
let second let second
const firstReq = payload const firstReq = payload
.create({ .create({
collection, collection,
data: { data: {
title, title,
}, },
req, req,
}) })
.then((res) => { .then((res) => {
first = res first = res
}) })
const secondReq = payload const secondReq = payload
.create({ .create({
collection, collection,
data: { data: {
title, title,
}, },
req, req,
}) })
.then((res) => { .then((res) => {
second = res second = res
}) })
await Promise.all([firstReq, secondReq]) await Promise.all([firstReq, secondReq])
await commitTransaction(req) await commitTransaction(req)
expect(req.transactionID).toBeUndefined() expect(req.transactionID).toBeUndefined()
const firstResult = await payload.findByID({ const firstResult = await payload.findByID({
id: first.id,
collection,
req,
})
const secondResult = await payload.findByID({
id: second.id,
collection,
req,
})
expect(firstResult.id).toStrictEqual(first.id)
expect(secondResult.id).toStrictEqual(second.id)
})
it('should rollback operations on failure', async () => {
const req = {
payload,
user,
} as unknown as PayloadRequest
await initTransaction(req)
const first = await payload.create({
collection,
data: {
title,
},
req,
})
try {
await payload.create({
collection,
data: {
throwAfterChange: true,
title,
},
req,
})
} catch (error: unknown) {
// catch error and carry on
}
expect(req.transactionID).toBeFalsy()
// this should not do anything but is needed to be certain about the next assertion
await commitTransaction(req)
await expect(() =>
payload.findByID({
id: first.id, id: first.id,
collection, collection,
req, req,
}), })
).rejects.toThrow('Not Found') const secondResult = await payload.findByID({
}) id: second.id,
collection,
req,
})
expect(firstResult.id).toStrictEqual(first.id)
expect(secondResult.id).toStrictEqual(second.id)
})
it('should rollback operations on failure', async () => {
const req = {
payload,
user,
} as unknown as PayloadRequest
await initTransaction(req)
const first = await payload.create({
collection,
data: {
title,
},
req,
})
try {
await payload.create({
collection,
data: {
throwAfterChange: true,
title,
},
req,
})
} catch (error: unknown) {
// catch error and carry on
}
expect(req.transactionID).toBeFalsy()
// this should not do anything but is needed to be certain about the next assertion
await commitTransaction(req)
await expect(() =>
payload.findByID({
id: first.id,
collection,
req,
}),
).rejects.toThrow('Not Found')
})
}
}) })
}) })

View File

@@ -11,8 +11,8 @@ const Hooks: CollectionConfig = {
}, },
hooks: { hooks: {
beforeOperation: [ beforeOperation: [
({ req, operation }) => { ({ operation, req }) => {
if (!req.transactionID && ['create', 'delete', 'update'].includes(operation)) { if (typeof req.payload.db.beginTransaction === 'function' && !req.transactionID && ['create', 'delete', 'update'].includes(operation)) {
throw new Error('transactionID is missing in beforeOperation hook') throw new Error('transactionID is missing in beforeOperation hook')
} }
}, },