@declaro/data 2.0.0-beta.12 → 2.0.0-beta.121

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.
Files changed (130) hide show
  1. package/{LICENSE → LICENSE.md} +1 -1
  2. package/README.md +0 -0
  3. package/dist/browser/index.js +26 -0
  4. package/dist/browser/index.js.map +93 -0
  5. package/dist/node/index.cjs +13226 -0
  6. package/dist/node/index.cjs.map +93 -0
  7. package/dist/node/index.js +13205 -0
  8. package/dist/node/index.js.map +93 -0
  9. package/dist/ts/application/model-controller.d.ts +50 -0
  10. package/dist/ts/application/model-controller.d.ts.map +1 -0
  11. package/dist/ts/application/model-controller.test.d.ts +2 -0
  12. package/dist/ts/application/model-controller.test.d.ts.map +1 -0
  13. package/dist/ts/application/read-only-model-controller.d.ts +20 -0
  14. package/dist/ts/application/read-only-model-controller.d.ts.map +1 -0
  15. package/dist/ts/application/read-only-model-controller.test.d.ts +2 -0
  16. package/dist/ts/application/read-only-model-controller.test.d.ts.map +1 -0
  17. package/dist/ts/domain/events/domain-event.d.ts +41 -0
  18. package/dist/ts/domain/events/domain-event.d.ts.map +1 -0
  19. package/dist/ts/domain/events/domain-event.test.d.ts +2 -0
  20. package/dist/ts/domain/events/domain-event.test.d.ts.map +1 -0
  21. package/dist/ts/domain/events/event-types.d.ts +34 -0
  22. package/dist/ts/domain/events/event-types.d.ts.map +1 -0
  23. package/dist/ts/domain/events/mutation-event.d.ts +6 -0
  24. package/dist/ts/domain/events/mutation-event.d.ts.map +1 -0
  25. package/dist/ts/domain/events/mutation-event.test.d.ts +2 -0
  26. package/dist/ts/domain/events/mutation-event.test.d.ts.map +1 -0
  27. package/dist/ts/domain/events/query-event.d.ts +6 -0
  28. package/dist/ts/domain/events/query-event.d.ts.map +1 -0
  29. package/dist/ts/domain/events/query-event.test.d.ts +2 -0
  30. package/dist/ts/domain/events/query-event.test.d.ts.map +1 -0
  31. package/dist/ts/domain/events/request-event.d.ts +11 -0
  32. package/dist/ts/domain/events/request-event.d.ts.map +1 -0
  33. package/dist/ts/domain/events/request-event.test.d.ts +2 -0
  34. package/dist/ts/domain/events/request-event.test.d.ts.map +1 -0
  35. package/dist/ts/domain/interfaces/repository.d.ts +110 -0
  36. package/dist/ts/domain/interfaces/repository.d.ts.map +1 -0
  37. package/dist/ts/domain/models/pagination.d.ts +28 -0
  38. package/dist/ts/domain/models/pagination.d.ts.map +1 -0
  39. package/dist/ts/domain/services/base-model-service.d.ts +23 -0
  40. package/dist/ts/domain/services/base-model-service.d.ts.map +1 -0
  41. package/dist/ts/domain/services/model-service-args.d.ts +9 -0
  42. package/dist/ts/domain/services/model-service-args.d.ts.map +1 -0
  43. package/dist/ts/domain/services/model-service.d.ts +72 -0
  44. package/dist/ts/domain/services/model-service.d.ts.map +1 -0
  45. package/dist/ts/domain/services/model-service.normalization.test.d.ts +2 -0
  46. package/dist/ts/domain/services/model-service.normalization.test.d.ts.map +1 -0
  47. package/dist/ts/domain/services/model-service.test.d.ts +2 -0
  48. package/dist/ts/domain/services/model-service.test.d.ts.map +1 -0
  49. package/dist/ts/domain/services/read-only-model-service.d.ts +76 -0
  50. package/dist/ts/domain/services/read-only-model-service.d.ts.map +1 -0
  51. package/dist/ts/domain/services/read-only-model-service.test.d.ts +2 -0
  52. package/dist/ts/domain/services/read-only-model-service.test.d.ts.map +1 -0
  53. package/dist/ts/index.d.ts +18 -0
  54. package/dist/ts/index.d.ts.map +1 -0
  55. package/dist/ts/shared/utils/schema-inference.d.ts +23 -0
  56. package/dist/ts/shared/utils/schema-inference.d.ts.map +1 -0
  57. package/dist/ts/shared/utils/schema-inheritance.d.ts +24 -0
  58. package/dist/ts/shared/utils/schema-inheritance.d.ts.map +1 -0
  59. package/dist/ts/test/mock/models/mock-book-models.d.ts +42 -0
  60. package/dist/ts/test/mock/models/mock-book-models.d.ts.map +1 -0
  61. package/dist/ts/test/mock/repositories/mock-memory-repository.assign.test.d.ts +2 -0
  62. package/dist/ts/test/mock/repositories/mock-memory-repository.assign.test.d.ts.map +1 -0
  63. package/dist/ts/test/mock/repositories/mock-memory-repository.basic.test.d.ts +2 -0
  64. package/dist/ts/test/mock/repositories/mock-memory-repository.basic.test.d.ts.map +1 -0
  65. package/dist/ts/test/mock/repositories/mock-memory-repository.bulk-upsert.test.d.ts +2 -0
  66. package/dist/ts/test/mock/repositories/mock-memory-repository.bulk-upsert.test.d.ts.map +1 -0
  67. package/dist/ts/test/mock/repositories/mock-memory-repository.count.test.d.ts +2 -0
  68. package/dist/ts/test/mock/repositories/mock-memory-repository.count.test.d.ts.map +1 -0
  69. package/dist/ts/test/mock/repositories/mock-memory-repository.custom-lookup.test.d.ts +1 -0
  70. package/dist/ts/test/mock/repositories/mock-memory-repository.custom-lookup.test.d.ts.map +1 -0
  71. package/dist/ts/test/mock/repositories/mock-memory-repository.d.ts +62 -0
  72. package/dist/ts/test/mock/repositories/mock-memory-repository.d.ts.map +1 -0
  73. package/dist/ts/test/mock/repositories/mock-memory-repository.search.test.d.ts +2 -0
  74. package/dist/ts/test/mock/repositories/mock-memory-repository.search.test.d.ts.map +1 -0
  75. package/dist/ts/test/mock/repositories/mock-memory-repository.trash.test.d.ts +2 -0
  76. package/dist/ts/test/mock/repositories/mock-memory-repository.trash.test.d.ts.map +1 -0
  77. package/dist/ts/test/mock/repositories/mock-memory-repository.upsert.test.d.ts +2 -0
  78. package/dist/ts/test/mock/repositories/mock-memory-repository.upsert.test.d.ts.map +1 -0
  79. package/package.json +45 -42
  80. package/src/application/model-controller.test.ts +694 -0
  81. package/src/application/model-controller.ts +135 -0
  82. package/src/application/read-only-model-controller.test.ts +335 -0
  83. package/src/application/read-only-model-controller.ts +61 -0
  84. package/src/domain/events/domain-event.test.ts +82 -0
  85. package/src/domain/events/domain-event.ts +69 -0
  86. package/src/domain/events/event-types.ts +34 -0
  87. package/src/domain/events/mutation-event.test.ts +38 -0
  88. package/src/domain/events/mutation-event.ts +8 -0
  89. package/src/domain/events/query-event.test.ts +28 -0
  90. package/src/domain/events/query-event.ts +8 -0
  91. package/src/domain/events/request-event.test.ts +38 -0
  92. package/src/domain/events/request-event.ts +32 -0
  93. package/src/domain/interfaces/repository.ts +136 -0
  94. package/src/domain/models/pagination.ts +28 -0
  95. package/src/domain/services/base-model-service.ts +54 -0
  96. package/src/domain/services/model-service-args.ts +9 -0
  97. package/src/domain/services/model-service.normalization.test.ts +704 -0
  98. package/src/domain/services/model-service.test.ts +940 -0
  99. package/src/domain/services/model-service.ts +432 -0
  100. package/src/domain/services/read-only-model-service.test.ts +828 -0
  101. package/src/domain/services/read-only-model-service.ts +178 -0
  102. package/src/index.ts +17 -4
  103. package/src/shared/utils/schema-inference.ts +26 -0
  104. package/src/shared/utils/schema-inheritance.ts +28 -0
  105. package/src/test/mock/models/mock-book-models.ts +78 -0
  106. package/src/test/mock/repositories/mock-memory-repository.assign.test.ts +215 -0
  107. package/src/test/mock/repositories/mock-memory-repository.basic.test.ts +129 -0
  108. package/src/test/mock/repositories/mock-memory-repository.bulk-upsert.test.ts +159 -0
  109. package/src/test/mock/repositories/mock-memory-repository.count.test.ts +98 -0
  110. package/src/test/mock/repositories/mock-memory-repository.custom-lookup.test.ts +0 -0
  111. package/src/test/mock/repositories/mock-memory-repository.search.test.ts +265 -0
  112. package/src/test/mock/repositories/mock-memory-repository.trash.test.ts +736 -0
  113. package/src/test/mock/repositories/mock-memory-repository.ts +401 -0
  114. package/src/test/mock/repositories/mock-memory-repository.upsert.test.ts +108 -0
  115. package/dist/databaseConnection.d.ts +0 -24
  116. package/dist/datastoreAbstract.d.ts +0 -37
  117. package/dist/declaro-data.cjs +0 -1
  118. package/dist/declaro-data.mjs +0 -250
  119. package/dist/hydrateEntity.d.ts +0 -8
  120. package/dist/index.d.ts +0 -4
  121. package/dist/serverConnection.d.ts +0 -15
  122. package/dist/trackedStatus.d.ts +0 -15
  123. package/src/databaseConnection.ts +0 -137
  124. package/src/datastoreAbstract.ts +0 -190
  125. package/src/hydrateEntity.ts +0 -36
  126. package/src/placeholder.test.ts +0 -7
  127. package/src/serverConnection.ts +0 -74
  128. package/src/trackedStatus.ts +0 -35
  129. package/tsconfig.json +0 -10
  130. package/vite.config.ts +0 -23
@@ -0,0 +1,432 @@
1
+ import type { ActionDescriptor, AnyModelSchema, IActionDescriptor } from '@declaro/core'
2
+ import type {
3
+ InferDetail,
4
+ InferFilters,
5
+ InferInput,
6
+ InferLookup,
7
+ InferSummary,
8
+ } from '../../shared/utils/schema-inference'
9
+ import { ModelMutationAction, ModelQueryEvent } from '../events/event-types'
10
+ import { MutationEvent } from '../events/mutation-event'
11
+ import type { IModelServiceArgs } from './model-service-args'
12
+ import { ReadOnlyModelService, type ILoadOptions } from './read-only-model-service'
13
+ import type { IActionOptions } from './base-model-service'
14
+
15
+ export interface ICreateOptions extends IActionOptions {}
16
+ export interface IUpdateOptions extends IActionOptions {}
17
+
18
+ export interface INormalizeInputArgs<TSchema extends AnyModelSchema> {
19
+ existing?: InferDetail<TSchema>
20
+ descriptor: ActionDescriptor
21
+ }
22
+
23
+ export class ModelService<TSchema extends AnyModelSchema> extends ReadOnlyModelService<TSchema> {
24
+ constructor(args: IModelServiceArgs<TSchema>) {
25
+ super(args)
26
+ }
27
+
28
+ /**
29
+ * Normalizes input data before processing. This method can be overridden by subclasses
30
+ * to implement custom input normalization logic (e.g., trimming strings, setting defaults, etc.).
31
+ * By default, this method returns the input unchanged.
32
+ * @param input The input data to normalize.
33
+ * @returns The normalized input data.
34
+ */
35
+ protected async normalizeInput(
36
+ input: InferInput<TSchema>,
37
+ args: INormalizeInputArgs<TSchema>,
38
+ ): Promise<InferInput<TSchema>> {
39
+ return input
40
+ }
41
+
42
+ /**
43
+ * Removes a record by its lookup criteria.
44
+ * @param lookup The lookup criteria to find the record.
45
+ * @returns The removed record.
46
+ */
47
+ async remove(lookup: InferLookup<TSchema>, options?: ILoadOptions): Promise<InferSummary<TSchema>> {
48
+ // Emit the before remove event
49
+ const beforeRemoveEvent = new MutationEvent<InferSummary<TSchema>, InferLookup<TSchema>>(
50
+ this.getDescriptor(ModelMutationAction.BeforeRemove),
51
+ lookup,
52
+ )
53
+ await this.emitter.emitAsync(beforeRemoveEvent)
54
+
55
+ // Perform the removal
56
+ const result = await this.repository.remove(lookup, options)
57
+
58
+ // Emit the after remove event
59
+ const afterRemoveEvent = new MutationEvent<InferSummary<TSchema>, InferLookup<TSchema>>(
60
+ this.getDescriptor(ModelMutationAction.AfterRemove),
61
+ lookup,
62
+ ).setResult(result)
63
+ await this.emitter.emitAsync(afterRemoveEvent)
64
+
65
+ // Return the results of the removal
66
+ return await this.normalizeSummary(result)
67
+ }
68
+
69
+ /**
70
+ * Restores a record by its lookup criteria.
71
+ * If a soft-deleted copy exists, it will be restored.
72
+ * @param lookup The lookup criteria to find the record to restore.
73
+ * @returns
74
+ */
75
+ async restore(lookup: InferLookup<TSchema>, options?: ILoadOptions): Promise<InferSummary<TSchema>> {
76
+ // Emit the before restore event
77
+ const beforeRestoreEvent = new MutationEvent<InferSummary<TSchema>, InferLookup<TSchema>>(
78
+ this.getDescriptor(ModelMutationAction.BeforeRestore),
79
+ lookup,
80
+ )
81
+ await this.emitter.emitAsync(beforeRestoreEvent)
82
+
83
+ // Perform the restore operation
84
+ const result = await this.repository.restore(lookup, options)
85
+
86
+ // Emit the after restore event
87
+ const afterRestoreEvent = new MutationEvent<InferSummary<TSchema>, InferLookup<TSchema>>(
88
+ this.getDescriptor(ModelMutationAction.AfterRestore),
89
+ lookup,
90
+ ).setResult(result)
91
+ await this.emitter.emitAsync(afterRestoreEvent)
92
+
93
+ // Return the results of the restore operation
94
+ return await this.normalizeSummary(result)
95
+ }
96
+
97
+ async create(input: InferInput<TSchema>, options?: ICreateOptions): Promise<InferDetail<TSchema>> {
98
+ // Normalize the input data
99
+ const normalizedInput = await this.normalizeInput(input, {
100
+ descriptor: this.getDescriptor(ModelMutationAction.Create),
101
+ })
102
+
103
+ // Emit the before create event
104
+ const beforeCreateEvent = new MutationEvent<InferDetail<TSchema>, InferInput<TSchema>>(
105
+ this.getDescriptor(ModelMutationAction.BeforeCreate),
106
+ normalizedInput,
107
+ )
108
+ await this.emitter.emitAsync(beforeCreateEvent)
109
+
110
+ // Perform the creation
111
+ const result = await this.repository.create(normalizedInput, options)
112
+
113
+ // Emit the after create event
114
+ const afterCreateEvent = new MutationEvent<InferDetail<TSchema>, InferInput<TSchema>>(
115
+ this.getDescriptor(ModelMutationAction.AfterCreate),
116
+ normalizedInput,
117
+ ).setResult(result)
118
+ await this.emitter.emitAsync(afterCreateEvent)
119
+
120
+ // Return the results of the creation
121
+ return await this.normalizeDetail(result)
122
+ }
123
+
124
+ async update(
125
+ lookup: InferLookup<TSchema>,
126
+ input: InferInput<TSchema>,
127
+ options?: IUpdateOptions,
128
+ ): Promise<InferDetail<TSchema>> {
129
+ const existing = await this.repository.load(lookup, options)
130
+ // Normalize the input data
131
+ const normalizedInput = await this.normalizeInput(input, {
132
+ existing,
133
+ descriptor: this.getDescriptor(ModelMutationAction.Update),
134
+ })
135
+
136
+ // Emit the before update event
137
+ const beforeUpdateEvent = new MutationEvent<InferDetail<TSchema>, InferInput<TSchema>>(
138
+ this.getDescriptor(ModelMutationAction.BeforeUpdate),
139
+ normalizedInput,
140
+ )
141
+ await this.emitter.emitAsync(beforeUpdateEvent)
142
+
143
+ // Perform the update
144
+ const result = await this.repository.update(lookup, normalizedInput, options)
145
+
146
+ // Emit the after update event
147
+ const afterUpdateEvent = new MutationEvent<InferDetail<TSchema>, InferInput<TSchema>>(
148
+ this.getDescriptor(ModelMutationAction.AfterUpdate),
149
+ normalizedInput,
150
+ ).setResult(result)
151
+ await this.emitter.emitAsync(afterUpdateEvent)
152
+
153
+ // Return the results of the update
154
+ return await this.normalizeDetail(result)
155
+ }
156
+
157
+ /**
158
+ * Upserts a record (creates if it doesn't exist, updates if it does).
159
+ * @param input The input data for the upsert operation.
160
+ * @param options Optional create or update options.
161
+ * @returns The upserted record.
162
+ */
163
+ async upsert(input: InferInput<TSchema>, options?: ICreateOptions | IUpdateOptions): Promise<InferDetail<TSchema>> {
164
+ const primaryKeyValue = this.getPrimaryKeyValue(input)
165
+
166
+ let operation: ModelMutationAction
167
+ let beforeOperation: ModelMutationAction
168
+ let afterOperation: ModelMutationAction
169
+ let existingItem: InferDetail<TSchema> | undefined = undefined
170
+
171
+ if (primaryKeyValue === undefined) {
172
+ operation = ModelMutationAction.Create
173
+ beforeOperation = ModelMutationAction.BeforeCreate
174
+ afterOperation = ModelMutationAction.AfterCreate
175
+ } else {
176
+ existingItem = await this.load(
177
+ {
178
+ [this.entityMetadata.primaryKey]: primaryKeyValue,
179
+ } as InferLookup<TSchema>,
180
+ options,
181
+ )
182
+
183
+ if (existingItem) {
184
+ operation = ModelMutationAction.Update
185
+ beforeOperation = ModelMutationAction.BeforeUpdate
186
+ afterOperation = ModelMutationAction.AfterUpdate
187
+ } else {
188
+ operation = ModelMutationAction.Create
189
+ beforeOperation = ModelMutationAction.BeforeCreate
190
+ afterOperation = ModelMutationAction.AfterCreate
191
+ }
192
+ }
193
+
194
+ // Normalize the input data
195
+ const normalizedInput = await this.normalizeInput(input, {
196
+ descriptor: this.getDescriptor(operation),
197
+ existing: existingItem,
198
+ })
199
+
200
+ // Emit the before upsert event
201
+ const beforeUpsertEvent = new MutationEvent<InferDetail<TSchema>, InferInput<TSchema>>(
202
+ this.getDescriptor(beforeOperation),
203
+ normalizedInput,
204
+ )
205
+ await this.emitter.emitAsync(beforeUpsertEvent)
206
+
207
+ // Perform the upsert operation
208
+ const result = await this.repository.upsert(normalizedInput, options)
209
+
210
+ // Emit the after upsert event
211
+ const afterUpsertEvent = new MutationEvent<InferDetail<TSchema>, InferInput<TSchema>>(
212
+ this.getDescriptor(afterOperation),
213
+ normalizedInput,
214
+ ).setResult(result)
215
+ await this.emitter.emitAsync(afterUpsertEvent)
216
+
217
+ // Return the results of the upsert operation
218
+ return await this.normalizeDetail(result)
219
+ }
220
+
221
+ /**
222
+ * Bulk upserts multiple records (creates if they don't exist, updates if they do).
223
+ * @param inputs Array of input data for the bulk upsert operation.
224
+ * @param options Optional create or update options.
225
+ * @returns Array of upserted records.
226
+ */
227
+ async bulkUpsert(
228
+ inputs: InferInput<TSchema>[],
229
+ options?: ICreateOptions | IUpdateOptions,
230
+ ): Promise<InferDetail<TSchema>[]> {
231
+ if (inputs.length === 0) {
232
+ return []
233
+ }
234
+
235
+ // Keep track of input metadata for each position (preserves order and duplicates)
236
+ type InputInfo = {
237
+ input: InferInput<TSchema>
238
+ index: number
239
+ primaryKeyValue?: string | number
240
+ existingEntity?: InferDetail<TSchema>
241
+ operation?: ModelMutationAction
242
+ }
243
+
244
+ const inputInfos: InputInfo[] = []
245
+ const uniqueLookups = new Map<string | number, InferLookup<TSchema>>()
246
+
247
+ // Process each input and collect unique lookups
248
+ for (let i = 0; i < inputs.length; i++) {
249
+ const input = inputs[i]
250
+ const primaryKeyValue = this.getPrimaryKeyValue(input)
251
+
252
+ const inputInfo: InputInfo = {
253
+ input,
254
+ index: i,
255
+ primaryKeyValue,
256
+ }
257
+ inputInfos.push(inputInfo)
258
+
259
+ // Collect unique lookups for entities that have primary keys
260
+ if (primaryKeyValue !== undefined) {
261
+ uniqueLookups.set(primaryKeyValue, {
262
+ [this.entityMetadata.primaryKey]: primaryKeyValue,
263
+ } as InferLookup<TSchema>)
264
+ }
265
+ }
266
+
267
+ // Load existing entities for unique primary keys
268
+ const existingEntitiesMap = new Map<string | number, InferDetail<TSchema>>()
269
+ if (uniqueLookups.size > 0) {
270
+ const lookups = Array.from(uniqueLookups.values())
271
+ const existingEntities = await this.loadMany(lookups, options)
272
+ existingEntities.forEach((entity) => {
273
+ if (entity) {
274
+ const pkValue = this.getPrimaryKeyValue(entity)
275
+ if (pkValue !== undefined) {
276
+ existingEntitiesMap.set(pkValue, entity)
277
+ }
278
+ }
279
+ })
280
+ }
281
+
282
+ // Normalize all inputs and determine operations in parallel
283
+ const normalizationPromises = inputInfos.map(async (inputInfo) => {
284
+ // Set existing entity if found
285
+ if (inputInfo.primaryKeyValue !== undefined) {
286
+ inputInfo.existingEntity = existingEntitiesMap.get(inputInfo.primaryKeyValue)
287
+ }
288
+
289
+ // Determine operation type
290
+ inputInfo.operation = inputInfo.existingEntity
291
+ ? ModelMutationAction.BeforeUpdate
292
+ : ModelMutationAction.BeforeCreate
293
+
294
+ // Normalize the input
295
+ const normalizedInput = await this.normalizeInput(inputInfo.input, {
296
+ existing: inputInfo.existingEntity,
297
+ descriptor: this.getDescriptor(
298
+ inputInfo.existingEntity ? ModelMutationAction.Update : ModelMutationAction.Create,
299
+ ),
300
+ })
301
+
302
+ inputInfo.input = normalizedInput
303
+ return normalizedInput
304
+ })
305
+
306
+ const normalizedInputs = await Promise.all(normalizationPromises)
307
+
308
+ // Create before events
309
+ const beforeEvents: MutationEvent<InferDetail<TSchema>, InferInput<TSchema>>[] = []
310
+ for (const inputInfo of inputInfos) {
311
+ beforeEvents.push(
312
+ new MutationEvent<InferDetail<TSchema>, InferInput<TSchema>>(
313
+ this.getDescriptor(inputInfo.operation!),
314
+ inputInfo.input,
315
+ ),
316
+ )
317
+ }
318
+
319
+ // Emit all before events
320
+ await Promise.all(beforeEvents.map((event) => this.emitter.emitAsync(event)))
321
+
322
+ // Perform the bulk upsert operation with all normalized inputs
323
+ const results = await this.repository.bulkUpsert(normalizedInputs, options)
324
+
325
+ // Create after events and return results
326
+ const afterEvents: MutationEvent<InferDetail<TSchema>, InferInput<TSchema>>[] = []
327
+
328
+ for (let i = 0; i < inputInfos.length; i++) {
329
+ const inputInfo = inputInfos[i]
330
+ const result = results[i]
331
+
332
+ const afterOperation =
333
+ inputInfo.operation === ModelMutationAction.BeforeCreate
334
+ ? ModelMutationAction.AfterCreate
335
+ : ModelMutationAction.AfterUpdate
336
+
337
+ afterEvents.push(
338
+ new MutationEvent<InferDetail<TSchema>, InferInput<TSchema>>(
339
+ this.getDescriptor(afterOperation),
340
+ inputInfo.input,
341
+ ).setResult(result),
342
+ )
343
+ }
344
+
345
+ // Emit all after events
346
+ await Promise.all(afterEvents.map((event) => this.emitter.emitAsync(event)))
347
+
348
+ // Return normalized results
349
+ return await Promise.all(results.map((result) => this.normalizeDetail(result)))
350
+ }
351
+
352
+ /**
353
+ * Permanently deletes all items from trash, optionally filtered by the provided criteria.
354
+ * @param filters Optional filters to apply when selecting items to delete from trash.
355
+ * @returns The count of permanently deleted items.
356
+ */
357
+ async emptyTrash(filters?: InferFilters<TSchema>): Promise<number> {
358
+ // Emit the before empty trash event
359
+ const beforeEmptyTrashEvent = new MutationEvent<number, InferFilters<TSchema> | undefined>(
360
+ this.getDescriptor(ModelMutationAction.BeforeEmptyTrash),
361
+ filters,
362
+ )
363
+ await this.emitter.emitAsync(beforeEmptyTrashEvent)
364
+
365
+ // Perform the empty trash operation
366
+ const count = await this.repository.emptyTrash(filters)
367
+
368
+ // Emit the after empty trash event
369
+ const afterEmptyTrashEvent = new MutationEvent<number, InferFilters<TSchema> | undefined>(
370
+ this.getDescriptor(ModelMutationAction.AfterEmptyTrash),
371
+ filters,
372
+ ).setResult(count)
373
+ await this.emitter.emitAsync(afterEmptyTrashEvent)
374
+
375
+ // Return the count of deleted items
376
+ return count
377
+ }
378
+
379
+ /**
380
+ * Permanently deletes a specific item from trash based on the provided lookup.
381
+ * @param lookup The lookup criteria for the item to permanently delete from trash.
382
+ * @returns The permanently deleted item summary.
383
+ */
384
+ async permanentlyDeleteFromTrash(lookup: InferLookup<TSchema>): Promise<InferSummary<TSchema>> {
385
+ // Emit the before permanently delete from trash event
386
+ const beforePermanentlyDeleteFromTrashEvent = new MutationEvent<InferSummary<TSchema>, InferLookup<TSchema>>(
387
+ this.getDescriptor(ModelMutationAction.BeforePermanentlyDeleteFromTrash),
388
+ lookup,
389
+ )
390
+ await this.emitter.emitAsync(beforePermanentlyDeleteFromTrashEvent)
391
+
392
+ // Perform the permanent deletion from trash
393
+ const result = await this.repository.permanentlyDeleteFromTrash(lookup)
394
+
395
+ // Emit the after permanently delete from trash event
396
+ const afterPermanentlyDeleteFromTrashEvent = new MutationEvent<InferSummary<TSchema>, InferLookup<TSchema>>(
397
+ this.getDescriptor(ModelMutationAction.AfterPermanentlyDeleteFromTrash),
398
+ lookup,
399
+ ).setResult(result)
400
+ await this.emitter.emitAsync(afterPermanentlyDeleteFromTrashEvent)
401
+
402
+ // Return the results of the permanent deletion
403
+ return await this.normalizeSummary(result)
404
+ }
405
+
406
+ /**
407
+ * Permanently deletes an item based on the provided lookup, regardless of whether it is active or in trash.
408
+ * @param lookup The lookup criteria for the item to permanently delete.
409
+ * @returns The permanently deleted item summary.
410
+ */
411
+ async permanentlyDelete(lookup: InferLookup<TSchema>): Promise<InferSummary<TSchema>> {
412
+ // Emit the before permanently delete event
413
+ const beforePermanentlyDeleteEvent = new MutationEvent<InferSummary<TSchema>, InferLookup<TSchema>>(
414
+ this.getDescriptor(ModelMutationAction.BeforePermanentlyDelete),
415
+ lookup,
416
+ )
417
+ await this.emitter.emitAsync(beforePermanentlyDeleteEvent)
418
+
419
+ // Perform the permanent deletion
420
+ const result = await this.repository.permanentlyDelete(lookup)
421
+
422
+ // Emit the after permanently delete event
423
+ const afterPermanentlyDeleteEvent = new MutationEvent<InferSummary<TSchema>, InferLookup<TSchema>>(
424
+ this.getDescriptor(ModelMutationAction.AfterPermanentlyDelete),
425
+ lookup,
426
+ ).setResult(result)
427
+ await this.emitter.emitAsync(afterPermanentlyDeleteEvent)
428
+
429
+ // Return the results of the permanent deletion
430
+ return await this.normalizeSummary(result)
431
+ }
432
+ }