dyno-table 2.5.2 → 2.6.1

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 (76) hide show
  1. package/dist/builders/batch-builder.d.ts +249 -0
  2. package/dist/builders/builder-types.d.ts +123 -0
  3. package/dist/builders/condition-check-builder.d.ts +149 -0
  4. package/dist/builders/delete-builder.d.ts +208 -0
  5. package/dist/builders/entity-aware-builders.d.ts +127 -0
  6. package/dist/builders/filter-builder.d.ts +294 -0
  7. package/dist/builders/get-builder.d.ts +191 -0
  8. package/dist/builders/index.d.ts +14 -0
  9. package/dist/builders/paginator.d.ts +151 -0
  10. package/dist/builders/put-builder.d.ts +317 -0
  11. package/dist/builders/query-builder.d.ts +218 -0
  12. package/dist/builders/result-iterator.d.ts +55 -0
  13. package/dist/builders/scan-builder.d.ts +109 -0
  14. package/dist/builders/transaction-builder.d.ts +462 -0
  15. package/dist/builders/types.d.ts +25 -0
  16. package/dist/builders/update-builder.d.ts +372 -0
  17. package/dist/builders.cjs +3648 -43
  18. package/dist/builders.d.ts +1 -4
  19. package/dist/builders.js +3648 -3
  20. package/dist/conditions.cjs +60 -67
  21. package/dist/conditions.d.ts +705 -3
  22. package/dist/conditions.js +46 -1
  23. package/dist/entity/ddb-indexing.d.ts +45 -0
  24. package/dist/entity/entity.d.ts +188 -0
  25. package/dist/entity/index-utils.d.ts +24 -0
  26. package/dist/entity.cjs +1126 -15
  27. package/dist/entity.d.ts +1 -261
  28. package/dist/entity.js +1127 -3
  29. package/dist/errors.d.ts +212 -0
  30. package/dist/expression.d.ts +9 -0
  31. package/dist/index-definition.d.ts +10 -0
  32. package/dist/index.cjs +5388 -270
  33. package/dist/index.d.ts +16 -273
  34. package/dist/index.js +5332 -6
  35. package/dist/operation-types.d.ts +8 -0
  36. package/dist/standard-schema.d.ts +2 -4
  37. package/dist/table.cjs +4311 -7
  38. package/dist/table.d.ts +13 -8
  39. package/dist/table.js +4315 -4
  40. package/dist/types.d.ts +6 -9
  41. package/dist/utils/chunk-array.d.ts +9 -0
  42. package/dist/utils/debug-expression.d.ts +32 -0
  43. package/dist/utils/debug-transaction.d.ts +17 -0
  44. package/dist/utils/error-factory.d.ts +162 -0
  45. package/dist/utils/error-utils.d.ts +170 -0
  46. package/dist/utils/index.d.ts +7 -0
  47. package/dist/utils/partition-key-template.d.ts +30 -0
  48. package/dist/utils/sort-key-template.d.ts +33 -0
  49. package/dist/utils.cjs +28 -10
  50. package/dist/utils.d.ts +1 -66
  51. package/dist/utils.js +29 -1
  52. package/package.json +53 -66
  53. package/dist/builders.d.cts +0 -4
  54. package/dist/chunk-2WIBY7PZ.js +0 -46
  55. package/dist/chunk-3DR6VOFW.cjs +0 -3349
  56. package/dist/chunk-42LH2UEM.js +0 -577
  57. package/dist/chunk-7UJJ7JXM.cjs +0 -63
  58. package/dist/chunk-ELULXDSB.cjs +0 -564
  59. package/dist/chunk-FF7FYGDH.js +0 -543
  60. package/dist/chunk-G5ERTQFX.cjs +0 -843
  61. package/dist/chunk-NYJGW3XH.js +0 -3334
  62. package/dist/chunk-PB7BBCZO.cjs +0 -32
  63. package/dist/chunk-QVRMYGC4.js +0 -29
  64. package/dist/chunk-RNX2DAHA.js +0 -818
  65. package/dist/chunk-ZUBCW3LA.cjs +0 -579
  66. package/dist/conditions-BSAcZswY.d.ts +0 -731
  67. package/dist/conditions-C8bM__Pn.d.cts +0 -731
  68. package/dist/conditions.d.cts +0 -3
  69. package/dist/entity.d.cts +0 -261
  70. package/dist/index-Bc-ra0im.d.ts +0 -3042
  71. package/dist/index-CPCmWsEv.d.cts +0 -3042
  72. package/dist/index.d.cts +0 -273
  73. package/dist/standard-schema.d.cts +0 -57
  74. package/dist/table.d.cts +0 -165
  75. package/dist/types.d.cts +0 -29
  76. package/dist/utils.d.cts +0 -66
package/dist/entity.js CHANGED
@@ -1,3 +1,1127 @@
1
- export { createIndex, createQueries, defineEntity } from './chunk-RNX2DAHA.js';
2
- import './chunk-FF7FYGDH.js';
3
- import './chunk-2WIBY7PZ.js';
1
+ // src/builders/entity-aware-builders.ts
2
+ function createEntityAwareBuilder(builder, entityName) {
3
+ return new Proxy(builder, {
4
+ get(target, prop, receiver) {
5
+ if (prop === "entityName") {
6
+ return entityName;
7
+ }
8
+ if (prop === "withBatch" && typeof target[prop] === "function") {
9
+ return (batch, entityType) => {
10
+ const typeToUse = entityType ?? entityName;
11
+ const fn = target[prop];
12
+ return fn.call(target, batch, typeToUse);
13
+ };
14
+ }
15
+ return Reflect.get(target, prop, receiver);
16
+ }
17
+ });
18
+ }
19
+ function createEntityAwarePutBuilder(builder, entityName) {
20
+ return createEntityAwareBuilder(builder, entityName);
21
+ }
22
+ function createEntityAwareGetBuilder(builder, entityName) {
23
+ return createEntityAwareBuilder(builder, entityName);
24
+ }
25
+ function createEntityAwareDeleteBuilder(builder, entityName) {
26
+ return createEntityAwareBuilder(builder, entityName);
27
+ }
28
+ var EntityAwareUpdateBuilder = class {
29
+ forceRebuildIndexes = [];
30
+ entityName;
31
+ builder;
32
+ entityConfig;
33
+ updateDataApplied = false;
34
+ constructor(builder, entityName) {
35
+ this.builder = builder;
36
+ this.entityName = entityName;
37
+ }
38
+ /**
39
+ * Configure entity-specific logic for automatic timestamp generation and index updates
40
+ */
41
+ configureEntityLogic(config) {
42
+ this.entityConfig = config;
43
+ }
44
+ /**
45
+ * Forces a rebuild of one or more readonly indexes during the update operation.
46
+ *
47
+ * By default, readonly indexes are not updated during entity updates to prevent
48
+ * errors when required index attributes are missing. This method allows you to
49
+ * override that behavior and force specific indexes to be rebuilt.
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * // Force rebuild a single readonly index
54
+ * const result = await repo.update({ id: 'TREX-001' }, { status: 'ACTIVE' })
55
+ * .forceIndexRebuild('gsi1')
56
+ * .execute();
57
+ *
58
+ * // Force rebuild multiple readonly indexes
59
+ * const result = await repo.update({ id: 'TREX-001' }, { status: 'ACTIVE' })
60
+ * .forceIndexRebuild(['gsi1', 'gsi2'])
61
+ * .execute();
62
+ *
63
+ * // Chain with other update operations
64
+ * const result = await repo.update({ id: 'TREX-001' }, { status: 'ACTIVE' })
65
+ * .set('lastUpdated', new Date().toISOString())
66
+ * .forceIndexRebuild('gsi1')
67
+ * .condition(op => op.eq('status', 'INACTIVE'))
68
+ * .execute();
69
+ * ```
70
+ *
71
+ * @param indexes - A single index name or array of index names to force rebuild
72
+ * @returns The builder instance for method chaining
73
+ */
74
+ forceIndexRebuild(indexes) {
75
+ if (Array.isArray(indexes)) {
76
+ this.forceRebuildIndexes = [...this.forceRebuildIndexes, ...indexes];
77
+ } else {
78
+ this.forceRebuildIndexes.push(indexes);
79
+ }
80
+ return this;
81
+ }
82
+ /**
83
+ * Gets the list of indexes that should be force rebuilt.
84
+ * This is used internally by entity update logic.
85
+ *
86
+ * @returns Array of index names to force rebuild
87
+ */
88
+ getForceRebuildIndexes() {
89
+ return [...this.forceRebuildIndexes];
90
+ }
91
+ /**
92
+ * Apply entity-specific update data (timestamps and index updates)
93
+ * This is called automatically when needed
94
+ */
95
+ applyEntityUpdates() {
96
+ if (!this.entityConfig || this.updateDataApplied) return;
97
+ const timestamps = this.entityConfig.generateTimestamps();
98
+ const updatedItem = { ...this.entityConfig.key, ...this.entityConfig.data, ...timestamps };
99
+ const indexUpdates = this.entityConfig.buildIndexUpdates(
100
+ this.entityConfig.key,
101
+ updatedItem,
102
+ this.entityConfig.table,
103
+ this.entityConfig.indexes,
104
+ this.forceRebuildIndexes
105
+ );
106
+ this.builder.set({ ...this.entityConfig.data, ...timestamps, ...indexUpdates });
107
+ this.updateDataApplied = true;
108
+ }
109
+ set(valuesOrPath, value) {
110
+ if (typeof valuesOrPath === "object") {
111
+ this.builder.set(valuesOrPath);
112
+ } else {
113
+ this.builder.set(valuesOrPath, value);
114
+ }
115
+ return this;
116
+ }
117
+ remove(path) {
118
+ this.builder.remove(path);
119
+ return this;
120
+ }
121
+ add(path, value) {
122
+ this.builder.add(path, value);
123
+ return this;
124
+ }
125
+ deleteElementsFromSet(path, value) {
126
+ this.builder.deleteElementsFromSet(path, value);
127
+ return this;
128
+ }
129
+ condition(condition) {
130
+ this.builder.condition(condition);
131
+ return this;
132
+ }
133
+ returnValues(returnValues) {
134
+ this.builder.returnValues(returnValues);
135
+ return this;
136
+ }
137
+ toDynamoCommand() {
138
+ return this.builder.toDynamoCommand();
139
+ }
140
+ withTransaction(transaction) {
141
+ this.applyEntityUpdates();
142
+ this.builder.withTransaction(transaction);
143
+ }
144
+ debug() {
145
+ return this.builder.debug();
146
+ }
147
+ async execute() {
148
+ this.updateDataApplied = false;
149
+ this.applyEntityUpdates();
150
+ return this.builder.execute();
151
+ }
152
+ };
153
+ function createEntityAwareUpdateBuilder(builder, entityName) {
154
+ return new EntityAwareUpdateBuilder(builder, entityName);
155
+ }
156
+
157
+ // src/conditions.ts
158
+ var createComparisonCondition = (type) => (attr, value) => ({
159
+ type,
160
+ attr,
161
+ value
162
+ });
163
+ var eq = createComparisonCondition("eq");
164
+
165
+ // src/errors.ts
166
+ var DynoTableError = class extends Error {
167
+ /**
168
+ * Machine-readable error code for programmatic error handling
169
+ * @example "KEY_GENERATION_FAILED", "VALIDATION_ERROR", etc.
170
+ */
171
+ code;
172
+ /**
173
+ * Additional context about the error
174
+ * Contains operation-specific details like entity names, table names,
175
+ * expressions, conditions, and other relevant debugging information
176
+ */
177
+ context;
178
+ /**
179
+ * The original error that caused this error (if wrapping another error)
180
+ * Useful for preserving AWS SDK errors or other underlying errors
181
+ */
182
+ cause;
183
+ constructor(message, code, context = {}, cause) {
184
+ super(message);
185
+ this.name = "DynoTableError";
186
+ this.code = code;
187
+ this.context = context;
188
+ this.cause = cause;
189
+ if (Error.captureStackTrace) {
190
+ Error.captureStackTrace(this, this.constructor);
191
+ }
192
+ }
193
+ };
194
+ var ValidationError = class extends DynoTableError {
195
+ constructor(message, code, context = {}, cause) {
196
+ super(message, code, context, cause);
197
+ this.name = "ValidationError";
198
+ }
199
+ };
200
+ var OperationError = class extends DynoTableError {
201
+ constructor(message, code, context = {}, cause) {
202
+ super(message, code, context, cause);
203
+ this.name = "OperationError";
204
+ }
205
+ };
206
+ var ExpressionError = class extends DynoTableError {
207
+ constructor(message, code, context = {}, cause) {
208
+ super(message, code, context, cause);
209
+ this.name = "ExpressionError";
210
+ }
211
+ };
212
+ var ConfigurationError = class extends DynoTableError {
213
+ constructor(message, code, context = {}, cause) {
214
+ super(message, code, context, cause);
215
+ this.name = "ConfigurationError";
216
+ }
217
+ };
218
+ var EntityError = class extends DynoTableError {
219
+ constructor(message, code, context = {}, cause) {
220
+ super(message, code, context, cause);
221
+ this.name = "EntityError";
222
+ }
223
+ };
224
+ var KeyGenerationError = class extends EntityError {
225
+ constructor(message, code, context = {}, cause) {
226
+ super(message, code, context, cause);
227
+ this.name = "KeyGenerationError";
228
+ }
229
+ };
230
+ var IndexGenerationError = class extends EntityError {
231
+ constructor(message, code, context = {}, cause) {
232
+ super(message, code, context, cause);
233
+ this.name = "IndexGenerationError";
234
+ }
235
+ };
236
+ var EntityValidationError = class extends ValidationError {
237
+ constructor(message, code, context = {}, cause) {
238
+ super(message, code, context, cause);
239
+ this.name = "EntityValidationError";
240
+ }
241
+ };
242
+ var ErrorCodes = {
243
+ // Key Generation Errors
244
+ KEY_GENERATION_FAILED: "KEY_GENERATION_FAILED",
245
+ KEY_MISSING_ATTRIBUTES: "KEY_MISSING_ATTRIBUTES",
246
+ KEY_INVALID_FORMAT: "KEY_INVALID_FORMAT",
247
+ // Index Errors
248
+ INDEX_GENERATION_FAILED: "INDEX_GENERATION_FAILED",
249
+ INDEX_MISSING_ATTRIBUTES: "INDEX_MISSING_ATTRIBUTES",
250
+ INDEX_NOT_FOUND: "INDEX_NOT_FOUND",
251
+ INDEX_READONLY_UPDATE_FAILED: "INDEX_READONLY_UPDATE_FAILED",
252
+ INDEX_UNDEFINED_VALUES: "INDEX_UNDEFINED_VALUES",
253
+ // Validation Errors
254
+ ENTITY_VALIDATION_FAILED: "ENTITY_VALIDATION_FAILED",
255
+ ASYNC_VALIDATION_NOT_SUPPORTED: "ASYNC_VALIDATION_NOT_SUPPORTED",
256
+ QUERY_INPUT_VALIDATION_FAILED: "QUERY_INPUT_VALIDATION_FAILED",
257
+ SCHEMA_VALIDATION_FAILED: "SCHEMA_VALIDATION_FAILED",
258
+ UNDEFINED_VALUE: "UNDEFINED_VALUE",
259
+ // Operation Errors
260
+ QUERY_FAILED: "QUERY_FAILED",
261
+ SCAN_FAILED: "SCAN_FAILED",
262
+ GET_FAILED: "GET_FAILED",
263
+ PUT_FAILED: "PUT_FAILED",
264
+ DELETE_FAILED: "DELETE_FAILED",
265
+ UPDATE_FAILED: "UPDATE_FAILED",
266
+ BATCH_GET_FAILED: "BATCH_GET_FAILED",
267
+ BATCH_WRITE_FAILED: "BATCH_WRITE_FAILED",
268
+ NO_UPDATE_ACTIONS: "NO_UPDATE_ACTIONS",
269
+ // Configuration Errors
270
+ GSI_NOT_FOUND: "GSI_NOT_FOUND",
271
+ SORT_KEY_REQUIRED: "SORT_KEY_REQUIRED",
272
+ SORT_KEY_NOT_DEFINED: "SORT_KEY_NOT_DEFINED",
273
+ PRIMARY_KEY_MISSING: "PRIMARY_KEY_MISSING",
274
+ INVALID_CHUNK_SIZE: "INVALID_CHUNK_SIZE",
275
+ CONDITION_REQUIRED: "CONDITION_REQUIRED",
276
+ CONDITION_GENERATION_FAILED: "CONDITION_GENERATION_FAILED",
277
+ PK_EXTRACTION_FAILED: "PK_EXTRACTION_FAILED"};
278
+
279
+ // src/utils/error-factory.ts
280
+ var ValidationErrors = {
281
+ indexSchemaValidationFailed: (validationIssues, keyType) => {
282
+ const keyLabel = keyType === "partition" ? "partition key" : keyType === "sort" ? "sort key" : "partition/sort key";
283
+ return new ValidationError(
284
+ `Index validation failed while generating ${keyLabel}: missing required attribute(s) or invalid values.`,
285
+ ErrorCodes.SCHEMA_VALIDATION_FAILED,
286
+ {
287
+ keyType,
288
+ validationIssues,
289
+ suggestion: `Provide the required attributes to construct the index ${keyLabel}`
290
+ }
291
+ );
292
+ },
293
+ noUpdateActions: (tableName, key) => new ValidationError("No update actions specified", ErrorCodes.NO_UPDATE_ACTIONS, {
294
+ tableName,
295
+ key,
296
+ suggestion: "Use set(), remove(), add(), or delete() to specify update actions"
297
+ }),
298
+ conditionRequired: (tableName, key) => new ValidationError("Condition is required for condition check operations", ErrorCodes.CONDITION_REQUIRED, {
299
+ tableName,
300
+ key,
301
+ suggestion: "Use the condition() method to specify a condition"
302
+ }),
303
+ queryInputValidationFailed: (entityName, queryName, validationIssues, providedInput) => new ValidationError(
304
+ `Query input validation failed for "${queryName}" on entity "${entityName}"`,
305
+ ErrorCodes.QUERY_INPUT_VALIDATION_FAILED,
306
+ {
307
+ entityName,
308
+ queryName,
309
+ validationIssues,
310
+ providedInput,
311
+ suggestion: "Ensure the query input matches the expected schema"
312
+ }
313
+ ),
314
+ undefinedValue: (path, tableName, key) => new ValidationError(`Cannot set undefined value for attribute "${path}"`, ErrorCodes.UNDEFINED_VALUE, {
315
+ path,
316
+ tableName,
317
+ key,
318
+ suggestion: "DynamoDB does not support undefined values. Use remove() to delete an attribute, or provide a valid value (null, string, number, etc.)"
319
+ })
320
+ };
321
+ var ConfigurationErrors = {
322
+ invalidChunkSize: (size) => new ConfigurationError("Chunk size must be greater than 0", ErrorCodes.INVALID_CHUNK_SIZE, {
323
+ size,
324
+ suggestion: "Provide a chunk size greater than 0"
325
+ }),
326
+ sortKeyRequired: (tableName, partitionKey, sortKey) => new ConfigurationError("Sort key is required for this operation", ErrorCodes.SORT_KEY_REQUIRED, {
327
+ tableName,
328
+ partitionKey,
329
+ sortKey,
330
+ suggestion: "Provide a sort key value or use a table with only a partition key"
331
+ }),
332
+ sortKeyNotDefined: (tableName, partitionKey, indexName) => new ConfigurationError("Sort key is not defined for this table/index", ErrorCodes.SORT_KEY_NOT_DEFINED, {
333
+ tableName,
334
+ partitionKey,
335
+ indexName,
336
+ suggestion: "This operation requires a table/index with a sort key defined"
337
+ }),
338
+ gsiNotFound: (indexName, tableName, availableIndexes) => new ConfigurationError(`GSI "${indexName}" not found in table configuration`, ErrorCodes.GSI_NOT_FOUND, {
339
+ indexName,
340
+ tableName,
341
+ availableIndexes,
342
+ suggestion: `Use one of the available indexes: ${availableIndexes.join(", ")}`
343
+ }),
344
+ primaryKeyMissing: (tableName, partitionKeyName, providedItem) => new ConfigurationError(`Primary key value for '${partitionKeyName}' is missing`, ErrorCodes.PRIMARY_KEY_MISSING, {
345
+ tableName,
346
+ partitionKeyName,
347
+ providedItem,
348
+ suggestion: `Ensure the item includes a value for '${partitionKeyName}'`
349
+ }),
350
+ pkExtractionFailed: (tableName, indexName, item, cause) => new ConfigurationError(
351
+ `Failed to extract partition key from item for index "${indexName}"`,
352
+ ErrorCodes.PK_EXTRACTION_FAILED,
353
+ {
354
+ tableName,
355
+ indexName,
356
+ item,
357
+ suggestion: "Ensure the item has the required partition key attribute"
358
+ },
359
+ cause
360
+ ),
361
+ conditionGenerationFailed: (condition, suggestion) => new ExpressionError("Failed to generate condition expression", ErrorCodes.CONDITION_GENERATION_FAILED, {
362
+ condition,
363
+ suggestion: suggestion || "Check that the condition is properly formed"
364
+ })
365
+ };
366
+ var OperationErrors = {
367
+ queryFailed: (tableName, context, cause) => new OperationError(
368
+ `Query operation failed on table "${tableName}"`,
369
+ ErrorCodes.QUERY_FAILED,
370
+ {
371
+ tableName,
372
+ operation: "query",
373
+ ...context
374
+ },
375
+ cause
376
+ ),
377
+ scanFailed: (tableName, context, cause) => new OperationError(
378
+ `Scan operation failed on table "${tableName}"`,
379
+ ErrorCodes.SCAN_FAILED,
380
+ {
381
+ tableName,
382
+ operation: "scan",
383
+ ...context
384
+ },
385
+ cause
386
+ ),
387
+ getFailed: (tableName, key, cause) => new OperationError(
388
+ `Get operation failed on table "${tableName}"`,
389
+ ErrorCodes.GET_FAILED,
390
+ {
391
+ tableName,
392
+ operation: "get",
393
+ key
394
+ },
395
+ cause
396
+ ),
397
+ putFailed: (tableName, item, cause) => new OperationError(
398
+ `Put operation failed on table "${tableName}"`,
399
+ ErrorCodes.PUT_FAILED,
400
+ {
401
+ tableName,
402
+ operation: "put",
403
+ item
404
+ },
405
+ cause
406
+ ),
407
+ updateFailed: (tableName, key, cause) => new OperationError(
408
+ `Update operation failed on table "${tableName}"`,
409
+ ErrorCodes.UPDATE_FAILED,
410
+ {
411
+ tableName,
412
+ operation: "update",
413
+ key
414
+ },
415
+ cause
416
+ ),
417
+ deleteFailed: (tableName, key, cause) => new OperationError(
418
+ `Delete operation failed on table "${tableName}"`,
419
+ ErrorCodes.DELETE_FAILED,
420
+ {
421
+ tableName,
422
+ operation: "delete",
423
+ key
424
+ },
425
+ cause
426
+ ),
427
+ batchGetFailed: (tableName, context, cause) => new OperationError(
428
+ `Batch get operation failed on table "${tableName}"`,
429
+ ErrorCodes.BATCH_GET_FAILED,
430
+ {
431
+ tableName,
432
+ operation: "batchGet",
433
+ ...context
434
+ },
435
+ cause
436
+ ),
437
+ batchWriteFailed: (tableName, context, cause) => new OperationError(
438
+ `Batch write operation failed on table "${tableName}"`,
439
+ ErrorCodes.BATCH_WRITE_FAILED,
440
+ {
441
+ tableName,
442
+ operation: "batchWrite",
443
+ ...context
444
+ },
445
+ cause
446
+ )
447
+ };
448
+ var EntityErrors = {
449
+ validationFailed: (entityName, operation, validationIssues, providedData) => new EntityValidationError(
450
+ `Validation failed for entity "${entityName}" during ${operation} operation`,
451
+ ErrorCodes.ENTITY_VALIDATION_FAILED,
452
+ {
453
+ entityName,
454
+ operation,
455
+ validationIssues,
456
+ providedData,
457
+ suggestion: "Check that all required fields are provided and match the schema"
458
+ }
459
+ ),
460
+ queryInputValidationFailed: (entityName, queryName, validationIssues, providedInput) => new EntityValidationError(
461
+ `Query input validation failed for "${queryName}" on entity "${entityName}"`,
462
+ ErrorCodes.QUERY_INPUT_VALIDATION_FAILED,
463
+ {
464
+ entityName,
465
+ queryName,
466
+ validationIssues,
467
+ providedInput,
468
+ suggestion: "Ensure the query input matches the expected schema"
469
+ }
470
+ ),
471
+ asyncValidationNotSupported: (entityName, operation) => new EntityValidationError(
472
+ `Entity "${entityName}" uses async validation which is not supported in transactions/batches`,
473
+ ErrorCodes.ASYNC_VALIDATION_NOT_SUPPORTED,
474
+ {
475
+ entityName,
476
+ operation,
477
+ suggestion: "Use .execute() for async validation or switch to synchronous schema validation"
478
+ }
479
+ ),
480
+ keyGenerationFailed: (entityName, operation, providedData, requiredAttributes, cause) => new KeyGenerationError(
481
+ `Failed to generate primary key for entity "${entityName}"`,
482
+ ErrorCodes.KEY_GENERATION_FAILED,
483
+ {
484
+ entityName,
485
+ operation,
486
+ providedData,
487
+ requiredAttributes,
488
+ suggestion: requiredAttributes ? `Ensure these attributes are provided: ${requiredAttributes.join(", ")}` : "Check that all required attributes for key generation are provided"
489
+ },
490
+ cause
491
+ ),
492
+ keyInvalidFormat: (entityName, operation, providedData, generatedKey) => new KeyGenerationError(
493
+ `Primary key generation for entity "${entityName}" produced undefined/null partition key`,
494
+ ErrorCodes.KEY_INVALID_FORMAT,
495
+ {
496
+ entityName,
497
+ operation,
498
+ providedData,
499
+ generatedKey,
500
+ suggestion: "Ensure the key generation function returns valid pk (and sk if applicable) values"
501
+ }
502
+ ),
503
+ keyMissingAttributes: (entityName, operation, missingAttributes, providedData) => new KeyGenerationError(
504
+ `Missing required attributes for key generation in entity "${entityName}": ${missingAttributes.join(", ")}`,
505
+ ErrorCodes.KEY_MISSING_ATTRIBUTES,
506
+ {
507
+ entityName,
508
+ operation,
509
+ missingAttributes,
510
+ providedData,
511
+ suggestion: `Provide the following attributes: ${missingAttributes.join(", ")}`
512
+ }
513
+ )
514
+ };
515
+ var IndexErrors = {
516
+ generationFailed: (indexName, operation, providedItem, partitionKeyAttribute, sortKeyAttribute, cause) => new IndexGenerationError(
517
+ `Failed to generate key for index "${indexName}"`,
518
+ ErrorCodes.INDEX_GENERATION_FAILED,
519
+ {
520
+ indexName,
521
+ operation,
522
+ providedItem,
523
+ partitionKeyAttribute,
524
+ sortKeyAttribute,
525
+ suggestion: "Ensure all attributes required by the index are present in the item"
526
+ },
527
+ cause
528
+ ),
529
+ missingAttributes: (indexName, operation, missingAttributes, providedData, isReadOnly) => new IndexGenerationError(
530
+ `Cannot regenerate readonly index "${indexName}" - missing required attributes: ${missingAttributes.join(", ")}`,
531
+ ErrorCodes.INDEX_MISSING_ATTRIBUTES,
532
+ {
533
+ indexName,
534
+ operation,
535
+ missingAttributes,
536
+ providedData,
537
+ isReadOnly,
538
+ suggestion: isReadOnly ? "For readonly indexes, provide all attributes or use forceIndexRebuild() with complete data" : `Provide the following attributes: ${missingAttributes.join(", ")}`
539
+ }
540
+ ),
541
+ undefinedValues: (indexName, operation, generatedKey, providedItem) => new IndexGenerationError(`Index "${indexName}" generated undefined values`, ErrorCodes.INDEX_UNDEFINED_VALUES, {
542
+ indexName,
543
+ operation,
544
+ generatedKey,
545
+ providedItem,
546
+ suggestion: "Ensure all attributes required by the index are present in the item"
547
+ }),
548
+ notFound: (requestedIndexes, availableIndexes, entityName, tableName) => new IndexGenerationError(
549
+ `Requested indexes not found: ${requestedIndexes.join(", ")}`,
550
+ ErrorCodes.INDEX_NOT_FOUND,
551
+ {
552
+ requestedIndexes,
553
+ availableIndexes,
554
+ entityName,
555
+ tableName,
556
+ suggestion: `Available indexes are: ${availableIndexes.join(", ")}`
557
+ }
558
+ ),
559
+ readonlyUpdateFailed: (indexName, operation, providedData) => new IndexGenerationError(
560
+ `Cannot update readonly index "${indexName}" without forcing rebuild`,
561
+ ErrorCodes.INDEX_READONLY_UPDATE_FAILED,
562
+ {
563
+ indexName,
564
+ operation,
565
+ providedData,
566
+ isReadOnly: true,
567
+ suggestion: "Use forceIndexRebuild() to update readonly indexes, or provide all required attributes"
568
+ }
569
+ )
570
+ };
571
+
572
+ // src/utils/error-utils.ts
573
+ function getAwsErrorMessage(error) {
574
+ if (error instanceof Error) {
575
+ return error.message;
576
+ }
577
+ if (typeof error === "object" && error !== null && "message" in error) {
578
+ return String(error.message);
579
+ }
580
+ return void 0;
581
+ }
582
+ function extractRequiredAttributes(error) {
583
+ const message = getAwsErrorMessage(error);
584
+ if (!message) return void 0;
585
+ const patterns = [
586
+ /(?:missing|required)\s+(?:attribute|field|property)(?:s)?[:\s]+([a-zA-Z0-9_,\s]+)/i,
587
+ /(?:attribute|field|property)[:\s]+([a-zA-Z0-9_]+)\s+is\s+(?:missing|required)/i,
588
+ /"([a-zA-Z0-9_]+)"\s+is\s+(?:missing|required)/i
589
+ ];
590
+ for (const pattern of patterns) {
591
+ const match = message.match(pattern);
592
+ if (match?.[1]) {
593
+ return match[1].split(",").map((attr) => attr.trim()).filter((attr) => attr.length > 0);
594
+ }
595
+ }
596
+ return void 0;
597
+ }
598
+
599
+ // src/entity/ddb-indexing.ts
600
+ var IndexBuilder = class {
601
+ /**
602
+ * Creates a new IndexBuilder instance
603
+ *
604
+ * @param table - The DynamoDB table instance
605
+ * @param indexes - The index definitions
606
+ */
607
+ constructor(table, indexes = {}) {
608
+ this.table = table;
609
+ this.indexes = indexes;
610
+ }
611
+ /**
612
+ * Build index attributes for item creation
613
+ *
614
+ * @param item - The item to generate indexes for
615
+ * @param options - Options for building indexes
616
+ * @returns Record of GSI attribute names to their values
617
+ */
618
+ buildForCreate(item, options = {}) {
619
+ const attributes = {};
620
+ for (const [indexName, indexDef] of Object.entries(this.indexes)) {
621
+ if (options.excludeReadOnly && indexDef.isReadOnly) {
622
+ continue;
623
+ }
624
+ let key;
625
+ try {
626
+ key = indexDef.generateKey(item);
627
+ if (this.hasUndefinedValues(key)) {
628
+ throw IndexErrors.undefinedValues(indexName, "create", key, item);
629
+ }
630
+ } catch (error) {
631
+ if (error instanceof DynoTableError) throw error;
632
+ throw IndexErrors.generationFailed(
633
+ indexName,
634
+ "create",
635
+ item,
636
+ indexDef.partitionKey,
637
+ indexDef.sortKey,
638
+ error instanceof Error ? error : void 0
639
+ );
640
+ }
641
+ const gsiConfig = this.table.gsis[indexName];
642
+ if (!gsiConfig) {
643
+ throw ConfigurationErrors.gsiNotFound(indexName, this.table.tableName, Object.keys(this.table.gsis));
644
+ }
645
+ if (key.pk) {
646
+ attributes[gsiConfig.partitionKey] = key.pk;
647
+ }
648
+ if (key.sk && gsiConfig.sortKey) {
649
+ attributes[gsiConfig.sortKey] = key.sk;
650
+ }
651
+ }
652
+ return attributes;
653
+ }
654
+ /**
655
+ * Build index attributes for item updates
656
+ *
657
+ * @param currentData - The current data before update
658
+ * @param updates - The update data
659
+ * @param options - Options for building indexes
660
+ * @returns Record of GSI attribute names to their updated values
661
+ */
662
+ buildForUpdate(currentData, updates, options = {}) {
663
+ const attributes = {};
664
+ const updatedItem = { ...currentData, ...updates };
665
+ if (options.forceRebuildIndexes && options.forceRebuildIndexes.length > 0) {
666
+ const invalidIndexes = options.forceRebuildIndexes.filter((indexName) => !this.indexes[indexName]);
667
+ if (invalidIndexes.length > 0) {
668
+ throw IndexErrors.notFound(invalidIndexes, Object.keys(this.indexes), void 0, this.table.tableName);
669
+ }
670
+ }
671
+ for (const [indexName, indexDef] of Object.entries(this.indexes)) {
672
+ const isForced = options.forceRebuildIndexes?.includes(indexName);
673
+ if (indexDef.isReadOnly && !isForced) {
674
+ continue;
675
+ }
676
+ if (!isForced) {
677
+ let shouldUpdateIndex = false;
678
+ try {
679
+ const currentKey = indexDef.generateKey(currentData);
680
+ const updatedKey = indexDef.generateKey(updatedItem);
681
+ if (currentKey.pk !== updatedKey.pk || currentKey.sk !== updatedKey.sk) {
682
+ shouldUpdateIndex = true;
683
+ }
684
+ } catch {
685
+ shouldUpdateIndex = true;
686
+ }
687
+ if (!shouldUpdateIndex) {
688
+ continue;
689
+ }
690
+ }
691
+ let key;
692
+ try {
693
+ key = indexDef.generateKey(updatedItem);
694
+ } catch (error) {
695
+ if (error instanceof DynoTableError) throw error;
696
+ throw IndexErrors.missingAttributes(
697
+ indexName,
698
+ "update",
699
+ [],
700
+ // We don't know which specific attributes are missing from the error
701
+ updates,
702
+ indexDef.isReadOnly
703
+ );
704
+ }
705
+ if (this.hasUndefinedValues(key)) {
706
+ throw IndexErrors.undefinedValues(indexName, "update", key, updates);
707
+ }
708
+ const gsiConfig = this.table.gsis[indexName];
709
+ if (!gsiConfig) {
710
+ throw ConfigurationErrors.gsiNotFound(indexName, this.table.tableName, Object.keys(this.table.gsis));
711
+ }
712
+ if (key.pk) {
713
+ attributes[gsiConfig.partitionKey] = key.pk;
714
+ }
715
+ if (key.sk && gsiConfig.sortKey) {
716
+ attributes[gsiConfig.sortKey] = key.sk;
717
+ }
718
+ }
719
+ return attributes;
720
+ }
721
+ /**
722
+ * Check if a key has undefined values
723
+ *
724
+ * @param key - The index key to check
725
+ * @returns True if the key contains undefined values, false otherwise
726
+ */
727
+ hasUndefinedValues(key) {
728
+ return (key.pk?.includes("undefined") ?? false) || (key.sk?.includes("undefined") ?? false);
729
+ }
730
+ };
731
+
732
+ // src/entity/index-utils.ts
733
+ function buildIndexes(dataForKeyGeneration, table, indexes, excludeReadOnly = false) {
734
+ if (!indexes) {
735
+ return {};
736
+ }
737
+ const indexBuilder = new IndexBuilder(table, indexes);
738
+ return indexBuilder.buildForCreate(dataForKeyGeneration, { excludeReadOnly });
739
+ }
740
+ function buildIndexUpdates(currentData, updates, table, indexes, forceRebuildIndexes) {
741
+ if (!indexes) {
742
+ return {};
743
+ }
744
+ const indexBuilder = new IndexBuilder(table, indexes);
745
+ return indexBuilder.buildForUpdate(currentData, updates, { forceRebuildIndexes });
746
+ }
747
+
748
+ // src/entity/entity.ts
749
+ function defineEntity(config) {
750
+ const entityTypeAttributeName = config.settings?.entityTypeAttributeName ?? "entityType";
751
+ const buildIndexes2 = (dataForKeyGeneration, table, excludeReadOnly = false) => {
752
+ return buildIndexes(dataForKeyGeneration, table, config.indexes, excludeReadOnly);
753
+ };
754
+ const wrapMethodWithPreparation = (originalMethod, prepareFn, context) => {
755
+ const wrappedMethod = (...args) => {
756
+ prepareFn();
757
+ return originalMethod.call(context, ...args);
758
+ };
759
+ Object.setPrototypeOf(wrappedMethod, originalMethod);
760
+ const propertyNames = Object.getOwnPropertyNames(originalMethod);
761
+ for (let i = 0; i < propertyNames.length; i++) {
762
+ const prop = propertyNames[i];
763
+ if (prop !== "length" && prop !== "name" && prop !== "prototype") {
764
+ const descriptor = Object.getOwnPropertyDescriptor(originalMethod, prop);
765
+ if (descriptor && descriptor.writable !== false && !descriptor.get) {
766
+ wrappedMethod[prop] = originalMethod[prop];
767
+ }
768
+ }
769
+ }
770
+ return wrappedMethod;
771
+ };
772
+ const generateTimestamps = (timestampsToGenerate, data) => {
773
+ if (!config.settings?.timestamps) return {};
774
+ const timestamps = {};
775
+ const now = /* @__PURE__ */ new Date();
776
+ const unixTime = Math.floor(Date.now() / 1e3);
777
+ const { createdAt, updatedAt } = config.settings.timestamps;
778
+ if (createdAt && timestampsToGenerate.includes("createdAt") && !data.createdAt) {
779
+ const name = createdAt.attributeName ?? "createdAt";
780
+ timestamps[name] = createdAt.format === "UNIX" ? unixTime : now.toISOString();
781
+ }
782
+ if (updatedAt && timestampsToGenerate.includes("updatedAt") && !data.updatedAt) {
783
+ const name = updatedAt.attributeName ?? "updatedAt";
784
+ timestamps[name] = updatedAt.format === "UNIX" ? unixTime : now.toISOString();
785
+ }
786
+ return timestamps;
787
+ };
788
+ return {
789
+ name: config.name,
790
+ createRepository: (table) => {
791
+ const repository = {
792
+ create: (data) => {
793
+ const builder = table.create({});
794
+ const prepareValidatedItemAsync = async () => {
795
+ const validatedData = await config.schema["~standard"].validate(data);
796
+ if ("issues" in validatedData && validatedData.issues) {
797
+ throw EntityErrors.validationFailed(config.name, "create", validatedData.issues, data);
798
+ }
799
+ const dataForKeyGeneration = {
800
+ ...validatedData.value,
801
+ ...generateTimestamps(["createdAt", "updatedAt"], validatedData.value)
802
+ };
803
+ let primaryKey;
804
+ try {
805
+ primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
806
+ if (primaryKey.pk === void 0 || primaryKey.pk === null) {
807
+ throw EntityErrors.keyInvalidFormat(config.name, "create", dataForKeyGeneration, primaryKey);
808
+ }
809
+ } catch (error) {
810
+ if (error instanceof DynoTableError) throw error;
811
+ throw EntityErrors.keyGenerationFailed(
812
+ config.name,
813
+ "create",
814
+ dataForKeyGeneration,
815
+ extractRequiredAttributes(error),
816
+ error instanceof Error ? error : void 0
817
+ );
818
+ }
819
+ const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
820
+ const validatedItem = {
821
+ ...dataForKeyGeneration,
822
+ [entityTypeAttributeName]: config.name,
823
+ [table.partitionKey]: primaryKey.pk,
824
+ ...table.sortKey ? { [table.sortKey]: primaryKey.sk } : {},
825
+ ...indexes
826
+ };
827
+ Object.assign(builder, { item: validatedItem });
828
+ return validatedItem;
829
+ };
830
+ const prepareValidatedItemSync = () => {
831
+ const validationResult = config.schema["~standard"].validate(data);
832
+ if (validationResult instanceof Promise) {
833
+ throw EntityErrors.asyncValidationNotSupported(config.name, "create");
834
+ }
835
+ if ("issues" in validationResult && validationResult.issues) {
836
+ throw EntityErrors.validationFailed(config.name, "create", validationResult.issues, data);
837
+ }
838
+ const dataForKeyGeneration = {
839
+ ...validationResult.value,
840
+ ...generateTimestamps(["createdAt", "updatedAt"], validationResult.value)
841
+ };
842
+ let primaryKey;
843
+ try {
844
+ primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
845
+ if (primaryKey.pk === void 0 || primaryKey.pk === null) {
846
+ throw EntityErrors.keyInvalidFormat(config.name, "create", dataForKeyGeneration, primaryKey);
847
+ }
848
+ } catch (error) {
849
+ if (error instanceof DynoTableError) throw error;
850
+ throw EntityErrors.keyGenerationFailed(
851
+ config.name,
852
+ "create",
853
+ dataForKeyGeneration,
854
+ extractRequiredAttributes(error),
855
+ error instanceof Error ? error : void 0
856
+ );
857
+ }
858
+ const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
859
+ const validatedItem = {
860
+ ...dataForKeyGeneration,
861
+ [entityTypeAttributeName]: config.name,
862
+ [table.partitionKey]: primaryKey.pk,
863
+ ...table.sortKey ? { [table.sortKey]: primaryKey.sk } : {},
864
+ ...indexes
865
+ };
866
+ Object.assign(builder, { item: validatedItem });
867
+ return validatedItem;
868
+ };
869
+ const originalExecute = builder.execute;
870
+ builder.execute = async () => {
871
+ await prepareValidatedItemAsync();
872
+ return await originalExecute.call(builder);
873
+ };
874
+ const originalWithTransaction = builder.withTransaction;
875
+ if (originalWithTransaction) {
876
+ builder.withTransaction = wrapMethodWithPreparation(
877
+ originalWithTransaction,
878
+ prepareValidatedItemSync,
879
+ builder
880
+ );
881
+ }
882
+ const originalWithBatch = builder.withBatch;
883
+ if (originalWithBatch) {
884
+ builder.withBatch = wrapMethodWithPreparation(originalWithBatch, prepareValidatedItemSync, builder);
885
+ }
886
+ return createEntityAwarePutBuilder(builder, config.name);
887
+ },
888
+ upsert: (data) => {
889
+ const builder = table.put({});
890
+ const prepareValidatedItemAsync = async () => {
891
+ const validatedData = await config.schema["~standard"].validate(data);
892
+ if ("issues" in validatedData && validatedData.issues) {
893
+ throw EntityErrors.validationFailed(config.name, "upsert", validatedData.issues, data);
894
+ }
895
+ const dataForKeyGeneration = {
896
+ ...validatedData.value,
897
+ ...generateTimestamps(["createdAt", "updatedAt"], validatedData.value)
898
+ };
899
+ let primaryKey;
900
+ try {
901
+ primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
902
+ if (primaryKey.pk === void 0 || primaryKey.pk === null) {
903
+ throw EntityErrors.keyInvalidFormat(config.name, "upsert", dataForKeyGeneration, primaryKey);
904
+ }
905
+ } catch (error) {
906
+ if (error instanceof DynoTableError) throw error;
907
+ throw EntityErrors.keyGenerationFailed(
908
+ config.name,
909
+ "upsert",
910
+ dataForKeyGeneration,
911
+ extractRequiredAttributes(error),
912
+ error instanceof Error ? error : void 0
913
+ );
914
+ }
915
+ const indexes = buildIndexes2(dataForKeyGeneration, table, false);
916
+ const validatedItem = {
917
+ [table.partitionKey]: primaryKey.pk,
918
+ ...table.sortKey ? { [table.sortKey]: primaryKey.sk } : {},
919
+ ...dataForKeyGeneration,
920
+ [entityTypeAttributeName]: config.name,
921
+ ...indexes
922
+ };
923
+ Object.assign(builder, { item: validatedItem });
924
+ return validatedItem;
925
+ };
926
+ const prepareValidatedItemSync = () => {
927
+ const validationResult = config.schema["~standard"].validate(data);
928
+ if (validationResult instanceof Promise) {
929
+ throw EntityErrors.asyncValidationNotSupported(config.name, "upsert");
930
+ }
931
+ if ("issues" in validationResult && validationResult.issues) {
932
+ throw EntityErrors.validationFailed(config.name, "upsert", validationResult.issues, data);
933
+ }
934
+ const dataForKeyGeneration = {
935
+ ...validationResult.value,
936
+ ...generateTimestamps(["createdAt", "updatedAt"], validationResult.value)
937
+ };
938
+ let primaryKey;
939
+ try {
940
+ primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
941
+ if (primaryKey.pk === void 0 || primaryKey.pk === null) {
942
+ throw EntityErrors.keyInvalidFormat(config.name, "upsert", dataForKeyGeneration, primaryKey);
943
+ }
944
+ } catch (error) {
945
+ if (error instanceof DynoTableError) throw error;
946
+ throw EntityErrors.keyGenerationFailed(
947
+ config.name,
948
+ "upsert",
949
+ dataForKeyGeneration,
950
+ extractRequiredAttributes(error),
951
+ error instanceof Error ? error : void 0
952
+ );
953
+ }
954
+ const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
955
+ const validatedItem = {
956
+ [table.partitionKey]: primaryKey.pk,
957
+ ...table.sortKey ? { [table.sortKey]: primaryKey.sk } : {},
958
+ ...dataForKeyGeneration,
959
+ [entityTypeAttributeName]: config.name,
960
+ ...indexes
961
+ };
962
+ Object.assign(builder, { item: validatedItem });
963
+ return validatedItem;
964
+ };
965
+ const originalExecute = builder.execute;
966
+ builder.execute = async () => {
967
+ const validatedItem = await prepareValidatedItemAsync();
968
+ await originalExecute.call(builder);
969
+ return validatedItem;
970
+ };
971
+ const originalWithTransaction = builder.withTransaction;
972
+ if (originalWithTransaction) {
973
+ builder.withTransaction = wrapMethodWithPreparation(
974
+ originalWithTransaction,
975
+ prepareValidatedItemSync,
976
+ builder
977
+ );
978
+ }
979
+ const originalWithBatch = builder.withBatch;
980
+ if (originalWithBatch) {
981
+ builder.withBatch = wrapMethodWithPreparation(originalWithBatch, prepareValidatedItemSync, builder);
982
+ }
983
+ return createEntityAwarePutBuilder(builder, config.name);
984
+ },
985
+ get: (key) => {
986
+ const builder = table.get(config.primaryKey.generateKey(key));
987
+ return createEntityAwareGetBuilder(builder, config.name);
988
+ },
989
+ update: (key, data) => {
990
+ const primaryKeyObj = config.primaryKey.generateKey(key);
991
+ const builder = table.update(primaryKeyObj);
992
+ builder.condition(eq(entityTypeAttributeName, config.name));
993
+ const entityAwareBuilder = createEntityAwareUpdateBuilder(builder, config.name);
994
+ entityAwareBuilder.configureEntityLogic({
995
+ data,
996
+ key,
997
+ table,
998
+ indexes: config.indexes,
999
+ generateTimestamps: () => generateTimestamps(["updatedAt"], data),
1000
+ buildIndexUpdates
1001
+ });
1002
+ return entityAwareBuilder;
1003
+ },
1004
+ delete: (key) => {
1005
+ const builder = table.delete(config.primaryKey.generateKey(key));
1006
+ builder.condition(eq(entityTypeAttributeName, config.name));
1007
+ return createEntityAwareDeleteBuilder(builder, config.name);
1008
+ },
1009
+ query: Object.entries(config.queries || {}).reduce(
1010
+ (acc, [key, inputCallback]) => {
1011
+ acc[key] = (input) => {
1012
+ const queryEntity = {
1013
+ scan: repository.scan,
1014
+ get: (key2) => createEntityAwareGetBuilder(table.get(key2), config.name),
1015
+ query: (keyCondition) => {
1016
+ return table.query(keyCondition);
1017
+ }
1018
+ };
1019
+ const queryBuilderCallback = inputCallback(input);
1020
+ const builder = queryBuilderCallback(queryEntity);
1021
+ if (builder && typeof builder === "object" && "filter" in builder && typeof builder.filter === "function") {
1022
+ builder.filter(eq(entityTypeAttributeName, config.name));
1023
+ }
1024
+ if (builder && typeof builder === "object" && "execute" in builder) {
1025
+ const originalExecute = builder.execute;
1026
+ builder.execute = async () => {
1027
+ const queryFn = config.queries[key];
1028
+ if (queryFn && typeof queryFn === "function") {
1029
+ const schema = queryFn.schema;
1030
+ if (schema?.["~standard"]?.validate && typeof schema["~standard"].validate === "function") {
1031
+ const validationResult = schema["~standard"].validate(input);
1032
+ if ("issues" in validationResult && validationResult.issues) {
1033
+ throw EntityErrors.queryInputValidationFailed(config.name, key, validationResult.issues, input);
1034
+ }
1035
+ }
1036
+ }
1037
+ const result = await originalExecute.call(builder);
1038
+ if (!result) {
1039
+ throw OperationErrors.queryFailed(config.name, { queryName: key }, void 0);
1040
+ }
1041
+ return result;
1042
+ };
1043
+ }
1044
+ return builder;
1045
+ };
1046
+ return acc;
1047
+ },
1048
+ {}
1049
+ ),
1050
+ scan: () => {
1051
+ const builder = table.scan();
1052
+ builder.filter(eq(entityTypeAttributeName, config.name));
1053
+ return builder;
1054
+ }
1055
+ };
1056
+ return repository;
1057
+ }
1058
+ };
1059
+ }
1060
+ function createQueries() {
1061
+ return {
1062
+ input: (schema) => ({
1063
+ query: (handler) => {
1064
+ const queryFn = (input) => (entity) => handler({ input, entity });
1065
+ queryFn.schema = schema;
1066
+ return queryFn;
1067
+ }
1068
+ })
1069
+ };
1070
+ }
1071
+ function createIndex() {
1072
+ return {
1073
+ input: (schema) => {
1074
+ const createIndexBuilder = (isReadOnly = false) => ({
1075
+ partitionKey: (pkFn) => ({
1076
+ sortKey: (skFn) => {
1077
+ const index = {
1078
+ name: "custom",
1079
+ partitionKey: "pk",
1080
+ sortKey: "sk",
1081
+ isReadOnly,
1082
+ generateKey: (item) => {
1083
+ const data = schema["~standard"].validate(item);
1084
+ if ("issues" in data && data.issues) {
1085
+ throw ValidationErrors.indexSchemaValidationFailed(data.issues, "both");
1086
+ }
1087
+ const validData = "value" in data ? data.value : item;
1088
+ return { pk: pkFn(validData), sk: skFn(validData) };
1089
+ }
1090
+ };
1091
+ return Object.assign(index, {
1092
+ readOnly: (value = false) => ({
1093
+ ...index,
1094
+ isReadOnly: value
1095
+ })
1096
+ });
1097
+ },
1098
+ withoutSortKey: () => {
1099
+ const index = {
1100
+ name: "custom",
1101
+ partitionKey: "pk",
1102
+ isReadOnly,
1103
+ generateKey: (item) => {
1104
+ const data = schema["~standard"].validate(item);
1105
+ if ("issues" in data && data.issues) {
1106
+ throw ValidationErrors.indexSchemaValidationFailed(data.issues, "partition");
1107
+ }
1108
+ const validData = "value" in data ? data.value : item;
1109
+ return { pk: pkFn(validData) };
1110
+ }
1111
+ };
1112
+ return Object.assign(index, {
1113
+ readOnly: (value = true) => ({
1114
+ ...index,
1115
+ isReadOnly: value
1116
+ })
1117
+ });
1118
+ }
1119
+ }),
1120
+ readOnly: (value = true) => createIndexBuilder(value)
1121
+ });
1122
+ return createIndexBuilder(false);
1123
+ }
1124
+ };
1125
+ }
1126
+
1127
+ export { createIndex, createQueries, defineEntity };