@opensaas/stack-core 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -25,12 +25,12 @@ export declare class DatabaseError extends Error {
25
25
  export declare function executeResolveInput<TOutput = Record<string, unknown>, TCreateInput = Record<string, unknown>, TUpdateInput = Record<string, unknown>>(hooks: Hooks<TOutput, TCreateInput, TUpdateInput> | undefined, args: {
26
26
  operation: 'create';
27
27
  resolvedData: TCreateInput;
28
- item?: undefined;
28
+ item: undefined;
29
29
  context: AccessContext;
30
30
  } | {
31
31
  operation: 'update';
32
32
  resolvedData: TUpdateInput;
33
- item?: TOutput;
33
+ item: TOutput;
34
34
  context: AccessContext;
35
35
  }): Promise<TCreateInput | TUpdateInput>;
36
36
  /**
@@ -38,29 +38,41 @@ export declare function executeResolveInput<TOutput = Record<string, unknown>, T
38
38
  * Allows custom validation logic
39
39
  */
40
40
  export declare function executeValidateInput<TOutput = Record<string, unknown>, TCreateInput = Record<string, unknown>, TUpdateInput = Record<string, unknown>>(hooks: Hooks<TOutput, TCreateInput, TUpdateInput> | undefined, args: {
41
- operation: 'create' | 'update';
42
- resolvedData: TCreateInput | TUpdateInput;
43
- item?: TOutput;
41
+ operation: 'create';
42
+ resolvedData: TCreateInput;
43
+ item: undefined;
44
+ context: AccessContext;
45
+ } | {
46
+ operation: 'update';
47
+ resolvedData: TUpdateInput;
48
+ item: TOutput;
44
49
  context: AccessContext;
45
50
  }): Promise<void>;
46
51
  /**
47
52
  * Execute beforeOperation hook
48
53
  * Runs before database operation (cannot modify data)
49
54
  */
50
- export declare function executeBeforeOperation<TOutput = Record<string, unknown>, TCreateInput = Record<string, unknown>, TUpdateInput = Record<string, unknown>>(hooks: Hooks<TOutput, TCreateInput, TUpdateInput> | undefined, args: {
51
- operation: 'create' | 'update' | 'delete';
52
- resolvedData?: TCreateInput | TUpdateInput;
53
- item?: TOutput;
55
+ export declare function executeBeforeOperation<TOutput = Record<string, unknown>>(hooks: Hooks<TOutput> | undefined, args: {
56
+ operation: 'create';
57
+ context: AccessContext;
58
+ } | {
59
+ operation: 'update' | 'delete';
60
+ item: TOutput;
54
61
  context: AccessContext;
55
62
  }): Promise<void>;
56
63
  /**
57
64
  * Execute afterOperation hook
58
65
  * Runs after database operation
59
66
  */
60
- export declare function executeAfterOperation<TOutput = Record<string, unknown>, TCreateInput = Record<string, unknown>, TUpdateInput = Record<string, unknown>>(hooks: Hooks<TOutput, TCreateInput, TUpdateInput> | undefined, args: {
61
- operation: 'create' | 'update' | 'delete';
62
- resolvedData?: TCreateInput | TUpdateInput;
67
+ export declare function executeAfterOperation<TOutput = Record<string, unknown>>(hooks: Hooks<TOutput> | undefined, args: {
68
+ operation: 'create';
69
+ item: TOutput;
70
+ originalItem: undefined;
71
+ context: AccessContext;
72
+ } | {
73
+ operation: 'update' | 'delete';
63
74
  item: TOutput;
75
+ originalItem: TOutput;
64
76
  context: AccessContext;
65
77
  }): Promise<void>;
66
78
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAGrD;;GAEG;AACH,qBAAa,eAAgB,SAAQ,KAAK;IACjC,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;gBAE9B,MAAM,EAAE,MAAM,EAAE,EAAE,WAAW,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM;CAMvE;AAED;;;GAGG;AACH,qBAAa,aAAc,SAAQ,KAAK;IAC/B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC,IAAI,CAAC,EAAE,MAAM,CAAA;gBAER,OAAO,EAAE,MAAM,EAAE,WAAW,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EAAE,IAAI,CAAC,EAAE,MAAM;CAMrF;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAEtC,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,YAAY,EAAE,YAAY,CAAC,GAAG,SAAS,EAC7D,IAAI,EACA;IACE,SAAS,EAAE,QAAQ,CAAA;IACnB,YAAY,EAAE,YAAY,CAAA;IAC1B,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,OAAO,EAAE,aAAa,CAAA;CACvB,GACD;IACE,SAAS,EAAE,QAAQ,CAAA;IACnB,YAAY,EAAE,YAAY,CAAA;IAC1B,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,aAAa,CAAA;CACvB,GACJ,OAAO,CAAC,YAAY,GAAG,YAAY,CAAC,CAStC;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAEtC,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,YAAY,EAAE,YAAY,CAAC,GAAG,SAAS,EAC7D,IAAI,EAAE;IACJ,SAAS,EAAE,QAAQ,GAAG,QAAQ,CAAA;IAC9B,YAAY,EAAE,YAAY,GAAG,YAAY,CAAA;IACzC,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,aAAa,CAAA;CACvB,GACA,OAAO,CAAC,IAAI,CAAC,CAmBf;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAEtC,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,YAAY,EAAE,YAAY,CAAC,GAAG,SAAS,EAC7D,IAAI,EAAE;IACJ,SAAS,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;IACzC,YAAY,CAAC,EAAE,YAAY,GAAG,YAAY,CAAA;IAC1C,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,aAAa,CAAA;CACvB,GACA,OAAO,CAAC,IAAI,CAAC,CAMf;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAEtC,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,YAAY,EAAE,YAAY,CAAC,GAAG,SAAS,EAC7D,IAAI,EAAE;IACJ,SAAS,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;IACzC,YAAY,CAAC,EAAE,YAAY,GAAG,YAAY,CAAA;IAC1C,IAAI,EAAE,OAAO,CAAA;IACb,OAAO,EAAE,aAAa,CAAA;CACvB,GACA,OAAO,CAAC,IAAI,CAAC,CAMf;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACzC,SAAS,GAAE,QAAQ,GAAG,QAAmB,GACxC;IAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAW3D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"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"}
@@ -34,8 +34,6 @@ export async function executeResolveInput(hooks, args) {
34
34
  if (!hooks?.resolveInput) {
35
35
  return args.resolvedData;
36
36
  }
37
- // Type assertion is safe because we've constrained the args type
38
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
39
37
  const result = await hooks.resolveInput(args);
40
38
  return result;
41
39
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opensaas/stack-core",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "Core stack for OpenSaas - schema definition, access control, and runtime utilities",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -43,7 +43,9 @@ export type FieldHooks<
43
43
  *
44
44
  * @example
45
45
  * ```typescript
46
- * resolveInput: async ({ inputValue, operation }) => {
46
+ * resolveInput: async ({ inputValue, operation, item }) => {
47
+ * // For create operations, item is undefined
48
+ * // For update operations, item is the existing record
47
49
  * if (typeof inputValue === 'string' && !isHashedPassword(inputValue)) {
48
50
  * return await hashPassword(inputValue)
49
51
  * }
@@ -51,14 +53,25 @@ export type FieldHooks<
51
53
  * }
52
54
  * ```
53
55
  */
54
- resolveInput?: (args: {
55
- operation: 'create' | 'update'
56
- inputValue: GetFieldValueType<TTypeInfo['fields'], TFieldKey> | undefined
57
- item?: TTypeInfo['item']
58
- listKey: string
59
- fieldName: TFieldKey
60
- context: import('../access/types.js').AccessContext
61
- }) =>
56
+ resolveInput?: (
57
+ args:
58
+ | {
59
+ operation: 'create'
60
+ inputValue: GetFieldValueType<TTypeInfo['fields'], TFieldKey> | undefined
61
+ item: undefined
62
+ listKey: string
63
+ fieldName: TFieldKey
64
+ context: import('../access/types.js').AccessContext
65
+ }
66
+ | {
67
+ operation: 'update'
68
+ inputValue: GetFieldValueType<TTypeInfo['fields'], TFieldKey> | undefined
69
+ item: TTypeInfo['item']
70
+ listKey: string
71
+ fieldName: TFieldKey
72
+ context: import('../access/types.js').AccessContext
73
+ },
74
+ ) =>
62
75
  | Promise<GetFieldValueType<TTypeInfo['fields'], TFieldKey> | undefined>
63
76
  | GetFieldValueType<TTypeInfo['fields'], TFieldKey>
64
77
  | undefined
@@ -71,19 +84,34 @@ export type FieldHooks<
71
84
  * @example
72
85
  * ```typescript
73
86
  * beforeOperation: async ({ resolvedValue, operation, item }) => {
74
- * console.log(`About to ${operation} field with value:`, resolvedValue)
87
+ * // For create operations, item is undefined
88
+ * // For update/delete operations, item is the existing record
89
+ * if (operation === 'update' && item) {
90
+ * console.log(`Updating field from ${item.fieldName} to ${resolvedValue}`)
91
+ * }
75
92
  * await sendAuditLog({ operation, item })
76
93
  * }
77
94
  * ```
78
95
  */
79
- beforeOperation?: (args: {
80
- operation: 'create' | 'update' | 'delete'
81
- resolvedValue: GetFieldValueType<TTypeInfo['fields'], TFieldKey> | undefined
82
- item?: TTypeInfo['item']
83
- listKey: string
84
- fieldName: TFieldKey
85
- context: import('../access/types.js').AccessContext
86
- }) => Promise<void> | void
96
+ beforeOperation?: (
97
+ args:
98
+ | {
99
+ operation: 'create'
100
+ resolvedValue: GetFieldValueType<TTypeInfo['fields'], TFieldKey> | undefined
101
+ item: undefined
102
+ listKey: string
103
+ fieldName: TFieldKey
104
+ context: import('../access/types.js').AccessContext
105
+ }
106
+ | {
107
+ operation: 'update' | 'delete'
108
+ resolvedValue: GetFieldValueType<TTypeInfo['fields'], TFieldKey> | undefined
109
+ item: TTypeInfo['item']
110
+ listKey: string
111
+ fieldName: TFieldKey
112
+ context: import('../access/types.js').AccessContext
113
+ },
114
+ ) => Promise<void> | void
87
115
 
88
116
  /**
89
117
  * Perform side effects after database operation
@@ -92,20 +120,38 @@ export type FieldHooks<
92
120
  *
93
121
  * @example
94
122
  * ```typescript
95
- * afterOperation: async ({ operation, value, item }) => {
123
+ * afterOperation: async ({ operation, value, item, originalItem }) => {
124
+ * // For query/create operations, originalItem is undefined
125
+ * // For update/delete operations, originalItem is the item before the operation
126
+ * if (operation === 'update' && originalItem) {
127
+ * console.log('Changed from:', originalItem[fieldName], 'to:', value)
128
+ * }
96
129
  * await invalidateCache({ listKey, itemId: item.id })
97
130
  * await sendWebhook({ operation, item })
98
131
  * }
99
132
  * ```
100
133
  */
101
- afterOperation?: (args: {
102
- operation: 'create' | 'update' | 'delete' | 'query'
103
- value: GetFieldValueType<TTypeInfo['fields'], TFieldKey> | undefined
104
- item: TTypeInfo['item']
105
- listKey: string
106
- fieldName: TFieldKey
107
- context: import('../access/types.js').AccessContext
108
- }) => Promise<void> | void
134
+ afterOperation?: (
135
+ args:
136
+ | {
137
+ operation: 'query' | 'create'
138
+ value: GetFieldValueType<TTypeInfo['fields'], TFieldKey> | undefined
139
+ item: TTypeInfo['item']
140
+ originalItem: undefined
141
+ listKey: string
142
+ fieldName: TFieldKey
143
+ context: import('../access/types.js').AccessContext
144
+ }
145
+ | {
146
+ operation: 'update' | 'delete'
147
+ value: GetFieldValueType<TTypeInfo['fields'], TFieldKey> | undefined
148
+ item: TTypeInfo['item']
149
+ originalItem: TTypeInfo['item']
150
+ listKey: string
151
+ fieldName: TFieldKey
152
+ context: import('../access/types.js').AccessContext
153
+ },
154
+ ) => Promise<void> | void
109
155
 
110
156
  /**
111
157
  * Transform field value after database read
@@ -175,6 +221,23 @@ export type BaseFieldConfig<TTypeInfo extends TypeInfo> = {
175
221
  * Transforms field values and types in query results using Prisma's native extension system
176
222
  */
177
223
  resultExtension?: ResultExtensionConfig
224
+ /**
225
+ * Database configuration
226
+ */
227
+ db?: {
228
+ /**
229
+ * Custom database column name
230
+ * Adds a @map attribute in Prisma schema
231
+ * @example
232
+ * ```typescript
233
+ * fields: {
234
+ * firstName: text({ db: { map: 'first_name' } })
235
+ * }
236
+ * // Generates: firstName String @map("first_name")
237
+ * ```
238
+ */
239
+ map?: string
240
+ }
178
241
  ui?: {
179
242
  /**
180
243
  * Custom React component to render this field
@@ -275,6 +338,23 @@ export type IntegerField<TTypeInfo extends TypeInfo = TypeInfo> = BaseFieldConfi
275
338
  }
276
339
  }
277
340
 
341
+ export type DecimalField<TTypeInfo extends TypeInfo = TypeInfo> = BaseFieldConfig<TTypeInfo> & {
342
+ type: 'decimal'
343
+ defaultValue?: string
344
+ precision?: number
345
+ scale?: number
346
+ db?: {
347
+ map?: string
348
+ isNullable?: boolean
349
+ }
350
+ validation?: {
351
+ isRequired?: boolean
352
+ min?: string
353
+ max?: string
354
+ }
355
+ isIndexed?: boolean | 'unique'
356
+ }
357
+
278
358
  export type CheckboxField<TTypeInfo extends TypeInfo = TypeInfo> = BaseFieldConfig<TTypeInfo> & {
279
359
  type: 'checkbox'
280
360
  }
@@ -305,8 +385,45 @@ export type SelectField<TTypeInfo extends TypeInfo = TypeInfo> = BaseFieldConfig
305
385
  export type RelationshipField<TTypeInfo extends TypeInfo = TypeInfo> =
306
386
  BaseFieldConfig<TTypeInfo> & {
307
387
  type: 'relationship'
308
- ref: string // Format: 'ListName.fieldName'
388
+ ref: string // Format: 'ListName.fieldName' or 'ListName'
309
389
  many?: boolean
390
+ db?: {
391
+ /**
392
+ * Controls foreign key placement and column name for bidirectional relationships
393
+ * Can be a boolean or an object with a map property
394
+ * Only valid on single (non-many) relationships
395
+ * Cannot be true on both sides of a one-to-one relationship
396
+ *
397
+ * When a boolean, defaults the foreign key column name to the field name
398
+ * When an object with map, uses the provided column name
399
+ *
400
+ * @example
401
+ * ```typescript
402
+ * // One-to-one: User has one Account (default foreign key name)
403
+ * User: list({
404
+ * fields: {
405
+ * account: relationship({ ref: 'Account.user', db: { foreignKey: true } })
406
+ * // Generates: accountId String? @unique
407
+ * }
408
+ * })
409
+ *
410
+ * // One-to-one: User has one Account (custom foreign key name)
411
+ * User: list({
412
+ * fields: {
413
+ * account: relationship({ ref: 'Account.user', db: { foreignKey: { map: 'account_id' } } })
414
+ * // Generates: accountId String? @unique @map("account_id")
415
+ * }
416
+ * })
417
+ *
418
+ * Account: list({
419
+ * fields: {
420
+ * user: relationship({ ref: 'User.account' }) // No foreign key on this side
421
+ * }
422
+ * })
423
+ * ```
424
+ */
425
+ foreignKey?: boolean | { map?: string }
426
+ }
310
427
  ui?: {
311
428
  displayMode?: 'select' | 'cards'
312
429
  }
@@ -544,19 +661,69 @@ export type ResolveInputHookArgs<
544
661
  }
545
662
 
546
663
  /**
547
- * Hook arguments for other hooks (validateInput, beforeOperation, afterOperation)
548
- * These hooks receive the same structure regardless of operation
664
+ * Hook arguments for validateInput hook
665
+ * Uses discriminated union to provide proper types based on operation
666
+ * - create: resolvedData is CreateInput, item is undefined
667
+ * - update: resolvedData is UpdateInput, item is the existing record
549
668
  */
550
- export type HookArgs<
669
+ export type ValidateInputHookArgs<
551
670
  TOutput = Record<string, unknown>,
552
671
  TCreateInput = Record<string, unknown>,
553
672
  TUpdateInput = Record<string, unknown>,
554
- > = {
555
- operation: 'create' | 'update' | 'delete'
556
- resolvedData?: TCreateInput | TUpdateInput
557
- item?: TOutput
558
- context: import('../access/types.js').AccessContext
559
- }
673
+ > =
674
+ | {
675
+ operation: 'create'
676
+ resolvedData: TCreateInput
677
+ item: undefined
678
+ context: import('../access/types.js').AccessContext
679
+ addValidationError: (msg: string) => void
680
+ }
681
+ | {
682
+ operation: 'update'
683
+ resolvedData: TUpdateInput
684
+ item: TOutput
685
+ context: import('../access/types.js').AccessContext
686
+ addValidationError: (msg: string) => void
687
+ }
688
+
689
+ /**
690
+ * Hook arguments for beforeOperation hook
691
+ * Uses discriminated union to provide proper types based on operation
692
+ * - create: no resolvedData, no item
693
+ * - update: no resolvedData, has item
694
+ * - delete: no resolvedData, has item
695
+ */
696
+ export type BeforeOperationHookArgs<TOutput = Record<string, unknown>> =
697
+ | {
698
+ operation: 'create'
699
+ context: import('../access/types.js').AccessContext
700
+ }
701
+ | {
702
+ operation: 'update' | 'delete'
703
+ item: TOutput
704
+ context: import('../access/types.js').AccessContext
705
+ }
706
+
707
+ /**
708
+ * Hook arguments for afterOperation hook
709
+ * Uses discriminated union to provide proper types based on operation
710
+ * - create: has item, no originalItem
711
+ * - update: has item, has originalItem
712
+ * - delete: has item, has originalItem
713
+ */
714
+ export type AfterOperationHookArgs<TOutput = Record<string, unknown>> =
715
+ | {
716
+ operation: 'create'
717
+ item: TOutput
718
+ originalItem: undefined
719
+ context: import('../access/types.js').AccessContext
720
+ }
721
+ | {
722
+ operation: 'update' | 'delete'
723
+ item: TOutput
724
+ originalItem: TOutput
725
+ context: import('../access/types.js').AccessContext
726
+ }
560
727
 
561
728
  export type Hooks<
562
729
  TOutput = Record<string, unknown>,
@@ -567,13 +734,10 @@ export type Hooks<
567
734
  args: ResolveInputHookArgs<TOutput, TCreateInput, TUpdateInput>,
568
735
  ) => Promise<TCreateInput | TUpdateInput>
569
736
  validateInput?: (
570
- args: HookArgs<TOutput, TCreateInput, TUpdateInput> & {
571
- operation: 'create' | 'update'
572
- addValidationError: (msg: string) => void
573
- },
737
+ args: ValidateInputHookArgs<TOutput, TCreateInput, TUpdateInput>,
574
738
  ) => Promise<void>
575
- beforeOperation?: (args: HookArgs<TOutput, TCreateInput, TUpdateInput>) => Promise<void>
576
- afterOperation?: (args: HookArgs<TOutput, TCreateInput, TUpdateInput>) => Promise<void>
739
+ beforeOperation?: (args: BeforeOperationHookArgs<TOutput>) => Promise<void>
740
+ afterOperation?: (args: AfterOperationHookArgs<TOutput>) => Promise<void>
577
741
  }
578
742
 
579
743
  // Generic `any` default allows ListConfig to work with any list item type
@@ -107,6 +107,8 @@ async function executeFieldAfterOperationHooks(
107
107
  operation: 'create' | 'update' | 'delete' | 'query',
108
108
  context: AccessContext,
109
109
  listKey: string,
110
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
111
+ originalItem?: any,
110
112
  ): Promise<void> {
111
113
  for (const [fieldName, fieldConfig] of Object.entries(fields)) {
112
114
  // Skip if no hooks defined
@@ -122,6 +124,7 @@ async function executeFieldAfterOperationHooks(
122
124
  fieldName,
123
125
  listKey,
124
126
  item,
127
+ originalItem,
125
128
  context,
126
129
  })
127
130
  }
@@ -483,6 +486,7 @@ function createFindUnique<TPrisma extends PrismaClientLike>(
483
486
  'query',
484
487
  context,
485
488
  listName,
489
+ undefined, // originalItem is undefined for query operations
486
490
  )
487
491
 
488
492
  return filtered
@@ -579,6 +583,7 @@ function createFindMany<TPrisma extends PrismaClientLike>(
579
583
  'query',
580
584
  context,
581
585
  listName,
586
+ undefined, // originalItem is undefined for query operations
582
587
  ),
583
588
  ),
584
589
  )
@@ -616,6 +621,7 @@ function createCreate<TPrisma extends PrismaClientLike>(
616
621
  let resolvedData = await executeResolveInput(listConfig.hooks, {
617
622
  operation: 'create',
618
623
  resolvedData: args.data,
624
+ item: undefined,
619
625
  context,
620
626
  })
621
627
 
@@ -632,6 +638,7 @@ function createCreate<TPrisma extends PrismaClientLike>(
632
638
  await executeValidateInput(listConfig.hooks, {
633
639
  operation: 'create',
634
640
  resolvedData,
641
+ item: undefined,
635
642
  context,
636
643
  })
637
644
 
@@ -677,6 +684,7 @@ function createCreate<TPrisma extends PrismaClientLike>(
677
684
  await executeAfterOperation(listConfig.hooks, {
678
685
  operation: 'create',
679
686
  item,
687
+ originalItem: undefined,
680
688
  context,
681
689
  })
682
690
 
@@ -688,6 +696,7 @@ function createCreate<TPrisma extends PrismaClientLike>(
688
696
  'create',
689
697
  context,
690
698
  listName,
699
+ undefined, // originalItem is undefined for create operations
691
700
  )
692
701
 
693
702
  // 11. Filter readable fields and apply resolveOutput hooks (including nested relationships)
@@ -832,6 +841,7 @@ function createUpdate<TPrisma extends PrismaClientLike>(
832
841
  await executeAfterOperation(listConfig.hooks, {
833
842
  operation: 'update',
834
843
  item: updated,
844
+ originalItem: item, // item is the original item before the update
835
845
  context,
836
846
  })
837
847
 
@@ -843,6 +853,7 @@ function createUpdate<TPrisma extends PrismaClientLike>(
843
853
  'update',
844
854
  context,
845
855
  listName,
856
+ item, // item is the original item before the update
846
857
  )
847
858
 
848
859
  // 12. Filter readable fields and apply resolveOutput hooks (including nested relationships)
@@ -930,6 +941,7 @@ function createDelete<TPrisma extends PrismaClientLike>(
930
941
  await executeAfterOperation(listConfig.hooks, {
931
942
  operation: 'delete',
932
943
  item: deleted,
944
+ originalItem: item, // item is the original item before deletion
933
945
  context,
934
946
  })
935
947
 
@@ -941,6 +953,7 @@ function createDelete<TPrisma extends PrismaClientLike>(
941
953
  'delete',
942
954
  context,
943
955
  listName,
956
+ item, // item is the original item before deletion
944
957
  )
945
958
 
946
959
  return deleted
@@ -87,6 +87,7 @@ async function processNestedCreate(
87
87
  let resolvedData = await executeResolveInput(relatedListConfig.hooks, {
88
88
  operation: 'create',
89
89
  resolvedData: item,
90
+ item: undefined,
90
91
  context,
91
92
  })
92
93
 
@@ -113,6 +114,7 @@ async function processNestedCreate(
113
114
  await executeValidateInput(relatedListConfig.hooks, {
114
115
  operation: 'create',
115
116
  resolvedData,
117
+ item: undefined,
116
118
  context,
117
119
  })
118
120