@farming-labs/orm-dynamodb 0.0.37

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.
package/dist/index.cjs ADDED
@@ -0,0 +1,1064 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ createDynamodbDriver: () => createDynamodbDriver,
24
+ normalizeDynamoDbDocumentClient: () => normalizeDynamoDbDocumentClient
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+ var import_node_crypto = require("crypto");
28
+ var import_lib_dynamodb = require("@aws-sdk/lib-dynamodb");
29
+ var import_orm = require("@farming-labs/orm");
30
+ var ormPrimaryKey = "__orm_pk";
31
+ var ormKindKey = "__orm_kind";
32
+ var ormTargetKey = "__orm_target";
33
+ var ormRecordKind = "record";
34
+ var ormUniqueKind = "unique";
35
+ var manifestCache = /* @__PURE__ */ new WeakMap();
36
+ function getManifest(schema) {
37
+ const cached = manifestCache.get(schema);
38
+ if (cached) return cached;
39
+ const next = (0, import_orm.createManifest)(schema);
40
+ manifestCache.set(schema, next);
41
+ return next;
42
+ }
43
+ function isRecord(value) {
44
+ return !!value && typeof value === "object";
45
+ }
46
+ function normalizeDecimalString(value) {
47
+ const trimmed = value.trim();
48
+ const match = /^(-?\d+)(?:\.(\d+))?$/.exec(trimmed);
49
+ if (!match) {
50
+ return trimmed;
51
+ }
52
+ const [, integerPart, fractionalPart] = match;
53
+ if (!fractionalPart) {
54
+ return integerPart;
55
+ }
56
+ const normalizedFraction = fractionalPart.replace(/0+$/g, "");
57
+ return normalizedFraction.length ? `${integerPart}.${normalizedFraction}` : integerPart;
58
+ }
59
+ function isDocumentClientLike(client) {
60
+ const constructorName = client?.constructor?.name ?? "";
61
+ return constructorName.includes("DocumentClient") || isRecord(client.config) && "translateConfig" in client.config;
62
+ }
63
+ function createDocumentClient(client, override) {
64
+ if (override) return override;
65
+ if (isDocumentClientLike(client)) {
66
+ return client;
67
+ }
68
+ return import_lib_dynamodb.DynamoDBDocumentClient.from(client, {
69
+ marshallOptions: {
70
+ removeUndefinedValues: true
71
+ }
72
+ });
73
+ }
74
+ function applyDefault(value, field) {
75
+ if (value !== void 0) return value;
76
+ if (field.generated === "id") return (0, import_node_crypto.randomUUID)();
77
+ if (field.generated === "now") return /* @__PURE__ */ new Date();
78
+ if (typeof field.defaultValue === "function") {
79
+ return field.defaultValue();
80
+ }
81
+ return field.defaultValue;
82
+ }
83
+ function isComparable(value) {
84
+ return value instanceof Date || typeof value === "number" || typeof value === "string" || typeof value === "bigint";
85
+ }
86
+ function evaluateFilter(value, filter) {
87
+ if (!(0, import_orm.isOperatorFilterObject)(filter)) {
88
+ return (0, import_orm.equalValues)(value, filter);
89
+ }
90
+ if ("eq" in filter && !(0, import_orm.equalValues)(value, filter.eq)) return false;
91
+ if ("not" in filter && (0, import_orm.equalValues)(value, filter.not)) return false;
92
+ if ("in" in filter) {
93
+ const values = Array.isArray(filter.in) ? filter.in : [];
94
+ if (!values.some((candidate) => (0, import_orm.equalValues)(candidate, value))) return false;
95
+ }
96
+ if ("contains" in filter) {
97
+ if (typeof value !== "string" || typeof filter.contains !== "string") return false;
98
+ if (!value.includes(filter.contains)) return false;
99
+ }
100
+ if ("gt" in filter) {
101
+ if (!isComparable(value) || value <= filter.gt) return false;
102
+ }
103
+ if ("gte" in filter) {
104
+ if (!isComparable(value) || value < filter.gte) return false;
105
+ }
106
+ if ("lt" in filter) {
107
+ if (!isComparable(value) || value >= filter.lt) return false;
108
+ }
109
+ if ("lte" in filter) {
110
+ if (!isComparable(value) || value > filter.lte) return false;
111
+ }
112
+ return true;
113
+ }
114
+ function sortRows(rows, orderBy) {
115
+ if (!orderBy) return rows;
116
+ const entries = Object.entries(orderBy);
117
+ if (!entries.length) return rows;
118
+ return [...rows].sort((left, right) => {
119
+ for (const [field, direction] of entries) {
120
+ const a = left.data[field];
121
+ const b = right.data[field];
122
+ if ((0, import_orm.equalValues)(a, b)) continue;
123
+ if (a === void 0) return direction === "asc" ? -1 : 1;
124
+ if (b === void 0) return direction === "asc" ? 1 : -1;
125
+ if (a == null) return direction === "asc" ? -1 : 1;
126
+ if (b == null) return direction === "asc" ? 1 : -1;
127
+ if (a < b) return direction === "asc" ? -1 : 1;
128
+ if (a > b) return direction === "asc" ? 1 : -1;
129
+ }
130
+ return 0;
131
+ });
132
+ }
133
+ function pageRows(rows, skip, take) {
134
+ const start = skip ?? 0;
135
+ const end = take === void 0 ? void 0 : start + take;
136
+ return rows.slice(start, end);
137
+ }
138
+ function dynamodbConstraintError(target) {
139
+ const fields = Array.isArray(target) ? target.join(", ") : target ?? "unique fields";
140
+ const error = new Error(`DynamoDB unique constraint violation on ${fields}.`);
141
+ Object.assign(error, {
142
+ code: "ConditionalCheckFailedException",
143
+ target
144
+ });
145
+ return error;
146
+ }
147
+ function isUnsupportedTransactionWrite(error) {
148
+ if (!isRecord(error)) return false;
149
+ return error.name === "UnknownOperationException" || error.code === "UnknownOperationException" || String(error.message ?? "").includes("UnknownOperation");
150
+ }
151
+ function recordPk(docId) {
152
+ return `record#${docId}`;
153
+ }
154
+ function getStoredItemPk(item) {
155
+ const value = item[ormPrimaryKey];
156
+ return typeof value === "string" ? value : void 0;
157
+ }
158
+ function normalizeDynamoDbDocumentClient(client, documentClient) {
159
+ return createDocumentClient(client, documentClient);
160
+ }
161
+ function createDynamoDbDriverInternal(config) {
162
+ const documentClient = createDocumentClient(config.client, config.documentClient);
163
+ function getSupportedManifest(schema) {
164
+ const manifest = getManifest(schema);
165
+ for (const model of Object.values(manifest.models)) {
166
+ if (model.schema) {
167
+ throw new Error(
168
+ `The DynamoDB runtime does not support schema-qualified tables for model "${model.name}". Use flat table names instead.`
169
+ );
170
+ }
171
+ const idField = model.fields.id;
172
+ if (idField?.kind === "id" && idField.idType === "integer" && idField.generated === "increment") {
173
+ throw new Error(
174
+ `The DynamoDB runtime does not support generated integer ids for model "${model.name}". Use manual numeric ids or a string id instead.`
175
+ );
176
+ }
177
+ }
178
+ return manifest;
179
+ }
180
+ function getTableName(schema, modelName) {
181
+ const manifest = getSupportedManifest(schema);
182
+ return config.tables?.[modelName] ?? manifest.models[modelName].table;
183
+ }
184
+ function fieldTransform(modelName, fieldName) {
185
+ return config.transforms?.[modelName]?.[fieldName];
186
+ }
187
+ function encodeValue(modelName, field, value) {
188
+ if (value === void 0) return value;
189
+ if (value === null) return null;
190
+ const transform = fieldTransform(modelName, field.name);
191
+ if (transform?.encode) {
192
+ return transform.encode(value);
193
+ }
194
+ if (field.kind === "id" && field.idType === "integer") {
195
+ return Number(value);
196
+ }
197
+ if (field.kind === "enum") {
198
+ return String(value);
199
+ }
200
+ if (field.kind === "boolean") {
201
+ return Boolean(value);
202
+ }
203
+ if (field.kind === "integer") {
204
+ return Number(value);
205
+ }
206
+ if (field.kind === "bigint") {
207
+ return typeof value === "bigint" ? value.toString() : BigInt(value).toString();
208
+ }
209
+ if (field.kind === "decimal") {
210
+ return typeof value === "string" ? normalizeDecimalString(value) : String(value);
211
+ }
212
+ if (field.kind === "datetime") {
213
+ if (value instanceof Date) return value.toISOString();
214
+ return new Date(value).toISOString();
215
+ }
216
+ return value;
217
+ }
218
+ function decodeValue(modelName, field, value, docId) {
219
+ const transform = fieldTransform(modelName, field.name);
220
+ if (transform?.decode) {
221
+ return transform.decode(value);
222
+ }
223
+ if (value === void 0 && field.kind === "id" && docId !== void 0) {
224
+ if (field.idType === "integer") {
225
+ const numeric = Number(docId);
226
+ return Number.isFinite(numeric) ? numeric : void 0;
227
+ }
228
+ return docId;
229
+ }
230
+ if (value === void 0 || value === null) return value ?? null;
231
+ if (field.kind === "id" && field.idType === "integer") {
232
+ return Number(value);
233
+ }
234
+ if (field.kind === "enum") {
235
+ return String(value);
236
+ }
237
+ if (field.kind === "boolean") {
238
+ return Boolean(value);
239
+ }
240
+ if (field.kind === "integer") {
241
+ return Number(value);
242
+ }
243
+ if (field.kind === "bigint") {
244
+ return typeof value === "bigint" ? value : BigInt(value);
245
+ }
246
+ if (field.kind === "decimal") {
247
+ return normalizeDecimalString(String(value));
248
+ }
249
+ if (field.kind === "datetime") {
250
+ return value instanceof Date ? value : new Date(value);
251
+ }
252
+ return value;
253
+ }
254
+ function buildStoredRow(model, data) {
255
+ const stored = {};
256
+ const decoded = {};
257
+ for (const field of Object.values(model.fields)) {
258
+ const value = applyDefault(data[field.name], field);
259
+ if (value === void 0) continue;
260
+ const encoded = encodeValue(model.name, field, value);
261
+ stored[field.column] = encoded;
262
+ decoded[field.name] = decodeValue(model.name, field, encoded);
263
+ }
264
+ const idField = model.fields.id;
265
+ if (!idField) {
266
+ return {
267
+ docId: (0, import_node_crypto.randomUUID)(),
268
+ stored,
269
+ decoded
270
+ };
271
+ }
272
+ let idValue = decoded[idField.name];
273
+ if (idValue === void 0 || idValue === null) {
274
+ if (idField.idType === "integer") {
275
+ throw new Error(
276
+ `The DynamoDB runtime requires an explicit numeric id for model "${model.name}" when using manual integer ids.`
277
+ );
278
+ }
279
+ idValue = (0, import_node_crypto.randomUUID)();
280
+ const encodedId = encodeValue(model.name, idField, idValue);
281
+ stored[idField.column] = encodedId;
282
+ decoded[idField.name] = decodeValue(model.name, idField, encodedId);
283
+ idValue = decoded[idField.name];
284
+ }
285
+ return {
286
+ docId: String(idValue),
287
+ stored,
288
+ decoded
289
+ };
290
+ }
291
+ function buildUpdatedRow(model, current, data) {
292
+ const stored = {
293
+ ...current.stored
294
+ };
295
+ const decoded = {
296
+ ...current.data
297
+ };
298
+ for (const [fieldName, value] of Object.entries(data)) {
299
+ if (value === void 0) continue;
300
+ const field = model.fields[fieldName];
301
+ if (!field) {
302
+ throw new Error(`Unknown field "${fieldName}" on model "${model.name}".`);
303
+ }
304
+ if (field.name === "id" && !(0, import_orm.equalValues)(current.data.id, value)) {
305
+ throw new Error(
306
+ `The DynamoDB runtime does not support updating the id field for model "${model.name}".`
307
+ );
308
+ }
309
+ const encoded = encodeValue(model.name, field, value);
310
+ stored[field.column] = encoded;
311
+ decoded[field.name] = decodeValue(model.name, field, encoded);
312
+ }
313
+ return {
314
+ docId: current.docId,
315
+ stored,
316
+ decoded
317
+ };
318
+ }
319
+ async function scanAllItems(tableName) {
320
+ const items = [];
321
+ let exclusiveStartKey;
322
+ do {
323
+ const result = await documentClient.send(
324
+ new import_lib_dynamodb.ScanCommand({
325
+ TableName: tableName,
326
+ ExclusiveStartKey: exclusiveStartKey
327
+ })
328
+ );
329
+ items.push(...result.Items ?? []);
330
+ exclusiveStartKey = result.LastEvaluatedKey;
331
+ } while (exclusiveStartKey);
332
+ return items;
333
+ }
334
+ async function loadRowByPk(model, tableName, pk) {
335
+ const result = await documentClient.send(
336
+ new import_lib_dynamodb.GetCommand({
337
+ TableName: tableName,
338
+ Key: {
339
+ [ormPrimaryKey]: pk
340
+ }
341
+ })
342
+ );
343
+ const stored = result.Item;
344
+ if (!stored || stored[ormKindKey] !== ormRecordKind) {
345
+ return null;
346
+ }
347
+ const decoded = {};
348
+ const docId = pk.slice("record#".length);
349
+ for (const field of Object.values(model.fields)) {
350
+ decoded[field.name] = decodeValue(model.name, field, stored[field.column], docId);
351
+ }
352
+ return {
353
+ docId,
354
+ pk,
355
+ data: decoded,
356
+ stored
357
+ };
358
+ }
359
+ async function loadRows(schema, modelName) {
360
+ const model = getSupportedManifest(schema).models[modelName];
361
+ const tableName = getTableName(schema, modelName);
362
+ const items = await scanAllItems(tableName);
363
+ return items.filter((item) => item[ormKindKey] === ormRecordKind).map((stored) => {
364
+ const pk = getStoredItemPk(stored) ?? "";
365
+ const docId = pk.slice("record#".length);
366
+ const decoded = {};
367
+ for (const field of Object.values(model.fields)) {
368
+ decoded[field.name] = decodeValue(model.name, field, stored[field.column], docId);
369
+ }
370
+ return {
371
+ docId,
372
+ pk,
373
+ data: decoded,
374
+ stored
375
+ };
376
+ });
377
+ }
378
+ function normalizeFilterValue(model, fieldName, value) {
379
+ const field = model.fields[fieldName];
380
+ if (!field || value === void 0 || value === null) {
381
+ return value;
382
+ }
383
+ return decodeValue(model.name, field, encodeValue(model.name, field, value));
384
+ }
385
+ function evaluateModelFilter(model, fieldName, value, filter) {
386
+ if (!(0, import_orm.isOperatorFilterObject)(filter)) {
387
+ return (0, import_orm.equalValues)(value, normalizeFilterValue(model, fieldName, filter));
388
+ }
389
+ const normalized = {
390
+ ...filter.eq !== void 0 ? { eq: normalizeFilterValue(model, fieldName, filter.eq) } : {},
391
+ ...filter.not !== void 0 ? { not: normalizeFilterValue(model, fieldName, filter.not) } : {},
392
+ ...filter.in !== void 0 ? {
393
+ in: (Array.isArray(filter.in) ? filter.in : []).map(
394
+ (entry) => normalizeFilterValue(model, fieldName, entry)
395
+ )
396
+ } : {},
397
+ ...filter.contains !== void 0 ? { contains: String(filter.contains) } : {},
398
+ ...filter.gt !== void 0 ? { gt: normalizeFilterValue(model, fieldName, filter.gt) } : {},
399
+ ...filter.gte !== void 0 ? { gte: normalizeFilterValue(model, fieldName, filter.gte) } : {},
400
+ ...filter.lt !== void 0 ? { lt: normalizeFilterValue(model, fieldName, filter.lt) } : {},
401
+ ...filter.lte !== void 0 ? { lte: normalizeFilterValue(model, fieldName, filter.lte) } : {}
402
+ };
403
+ return evaluateFilter(value, normalized);
404
+ }
405
+ function matchesModelWhere(model, record, where) {
406
+ if (!where) return true;
407
+ if (where.AND && !where.AND.every((clause) => matchesModelWhere(model, record, clause))) {
408
+ return false;
409
+ }
410
+ if (where.OR && !where.OR.some((clause) => matchesModelWhere(model, record, clause))) {
411
+ return false;
412
+ }
413
+ if (where.NOT && matchesModelWhere(model, record, where.NOT)) {
414
+ return false;
415
+ }
416
+ for (const [key, filter] of Object.entries(where)) {
417
+ if (key === "AND" || key === "OR" || key === "NOT") continue;
418
+ if (!evaluateModelFilter(model, key, record[key], filter)) return false;
419
+ }
420
+ return true;
421
+ }
422
+ function applyModelQuery(model, rows, args = {}) {
423
+ const filtered = rows.filter((row) => matchesModelWhere(model, row.data, args.where));
424
+ const sorted = sortRows(filtered, args.orderBy);
425
+ return pageRows(sorted, args.skip, args.take);
426
+ }
427
+ function serializeUniqueValue(model, fieldName, value) {
428
+ const field = model.fields[fieldName];
429
+ const normalized = decodeValue(model.name, field, encodeValue(model.name, field, value));
430
+ if (normalized instanceof Date) {
431
+ return normalized.toISOString();
432
+ }
433
+ if (typeof normalized === "bigint") {
434
+ return normalized.toString();
435
+ }
436
+ return JSON.stringify(normalized);
437
+ }
438
+ function uniqueLockPk(model, fields, row) {
439
+ const values = fields.map((fieldName) => row[fieldName]);
440
+ if (values.some((value) => value === void 0 || value === null)) {
441
+ return null;
442
+ }
443
+ const encodedValues = fields.map(
444
+ (fieldName) => encodeURIComponent(serializeUniqueValue(model, fieldName, row[fieldName]))
445
+ );
446
+ return `unique#${model.name}#${fields.join("+")}#${encodedValues.join("#")}`;
447
+ }
448
+ function uniqueLocksForRow(model, row, docId) {
449
+ const target = recordPk(docId);
450
+ const locks = [];
451
+ for (const field of Object.values(model.fields)) {
452
+ if (!field.unique) continue;
453
+ const pk = uniqueLockPk(model, [field.name], row);
454
+ if (!pk) continue;
455
+ locks.push({
456
+ pk,
457
+ target,
458
+ fields: [field.name]
459
+ });
460
+ }
461
+ for (const constraint of model.constraints.unique) {
462
+ const pk = uniqueLockPk(model, [...constraint.fields], row);
463
+ if (!pk) continue;
464
+ locks.push({
465
+ pk,
466
+ target,
467
+ fields: [...constraint.fields]
468
+ });
469
+ }
470
+ return locks;
471
+ }
472
+ async function loadUniqueRow(schema, modelName, where) {
473
+ const manifest = getSupportedManifest(schema);
474
+ const model = manifest.models[modelName];
475
+ const lookup = (0, import_orm.requireUniqueLookup)(model, where, "FindUnique");
476
+ const tableName = getTableName(schema, modelName);
477
+ if (lookup.kind === "id") {
478
+ return loadRowByPk(model, tableName, recordPk(String(lookup.values[lookup.fields[0].name])));
479
+ }
480
+ const normalizedLookupRow = Object.fromEntries(
481
+ lookup.fields.map((field) => [
482
+ field.name,
483
+ decodeValue(model.name, field, encodeValue(model.name, field, lookup.values[field.name]))
484
+ ])
485
+ );
486
+ const lockPk = uniqueLockPk(
487
+ model,
488
+ lookup.fields.map((field) => field.name),
489
+ normalizedLookupRow
490
+ );
491
+ if (!lockPk) {
492
+ return null;
493
+ }
494
+ const lockResult = await documentClient.send(
495
+ new import_lib_dynamodb.GetCommand({
496
+ TableName: tableName,
497
+ Key: {
498
+ [ormPrimaryKey]: lockPk
499
+ }
500
+ })
501
+ );
502
+ const target = typeof lockResult.Item?.[ormTargetKey] === "string" ? lockResult.Item[ormTargetKey] : void 0;
503
+ if (!target) {
504
+ return null;
505
+ }
506
+ return loadRowByPk(model, tableName, target);
507
+ }
508
+ function findUniqueConflict(model, candidate, existingRows, ignoreDocIds = /* @__PURE__ */ new Set()) {
509
+ const idField = model.fields.id;
510
+ for (const row of existingRows) {
511
+ if (ignoreDocIds.has(row.docId)) continue;
512
+ if (idField && candidate[idField.name] !== void 0 && candidate[idField.name] !== null && row.data[idField.name] !== void 0 && row.data[idField.name] !== null && (0, import_orm.equalValues)(candidate[idField.name], row.data[idField.name])) {
513
+ return [idField.name];
514
+ }
515
+ for (const field of Object.values(model.fields)) {
516
+ if (!field.unique) continue;
517
+ if (candidate[field.name] === void 0 || candidate[field.name] === null) continue;
518
+ if (row.data[field.name] === void 0 || row.data[field.name] === null) continue;
519
+ if ((0, import_orm.equalValues)(candidate[field.name], row.data[field.name])) {
520
+ return [field.name];
521
+ }
522
+ }
523
+ for (const constraint of model.constraints.unique) {
524
+ if (!constraint.fields.every(
525
+ (fieldName) => candidate[fieldName] !== void 0 && candidate[fieldName] !== null && row.data[fieldName] !== void 0 && row.data[fieldName] !== null
526
+ )) {
527
+ continue;
528
+ }
529
+ if (constraint.fields.every(
530
+ (fieldName) => (0, import_orm.equalValues)(candidate[fieldName], row.data[fieldName])
531
+ )) {
532
+ return [...constraint.fields];
533
+ }
534
+ }
535
+ }
536
+ return null;
537
+ }
538
+ function buildRecordItem(row) {
539
+ return {
540
+ [ormPrimaryKey]: recordPk(row.docId),
541
+ [ormKindKey]: ormRecordKind,
542
+ ...row.stored
543
+ };
544
+ }
545
+ function putRecordCreateItem(tableName, row) {
546
+ return {
547
+ Put: {
548
+ TableName: tableName,
549
+ Item: buildRecordItem(row),
550
+ ConditionExpression: "attribute_not_exists(#pk)",
551
+ ExpressionAttributeNames: {
552
+ "#pk": ormPrimaryKey
553
+ }
554
+ }
555
+ };
556
+ }
557
+ function putRecordUpdateItem(tableName, row) {
558
+ return {
559
+ Put: {
560
+ TableName: tableName,
561
+ Item: buildRecordItem(row)
562
+ }
563
+ };
564
+ }
565
+ function putUniqueItem(tableName, lock) {
566
+ return {
567
+ Put: {
568
+ TableName: tableName,
569
+ Item: {
570
+ [ormPrimaryKey]: lock.pk,
571
+ [ormKindKey]: ormUniqueKind,
572
+ [ormTargetKey]: lock.target
573
+ },
574
+ ConditionExpression: "attribute_not_exists(#pk)",
575
+ ExpressionAttributeNames: {
576
+ "#pk": ormPrimaryKey
577
+ }
578
+ }
579
+ };
580
+ }
581
+ function deleteItem(tableName, pk) {
582
+ return {
583
+ Delete: {
584
+ TableName: tableName,
585
+ Key: {
586
+ [ormPrimaryKey]: pk
587
+ }
588
+ }
589
+ };
590
+ }
591
+ async function putRecordSequential(tableName, row, conditionallyCreate) {
592
+ await documentClient.send(
593
+ new import_lib_dynamodb.PutCommand({
594
+ TableName: tableName,
595
+ Item: buildRecordItem(row),
596
+ ...conditionallyCreate ? {
597
+ ConditionExpression: "attribute_not_exists(#pk)",
598
+ ExpressionAttributeNames: {
599
+ "#pk": ormPrimaryKey
600
+ }
601
+ } : {}
602
+ })
603
+ );
604
+ }
605
+ async function putUniqueSequential(tableName, lock) {
606
+ await documentClient.send(
607
+ new import_lib_dynamodb.PutCommand({
608
+ TableName: tableName,
609
+ Item: {
610
+ [ormPrimaryKey]: lock.pk,
611
+ [ormKindKey]: ormUniqueKind,
612
+ [ormTargetKey]: lock.target
613
+ },
614
+ ConditionExpression: "attribute_not_exists(#pk)",
615
+ ExpressionAttributeNames: {
616
+ "#pk": ormPrimaryKey
617
+ }
618
+ })
619
+ );
620
+ }
621
+ async function deleteSequential(tableName, pk) {
622
+ await documentClient.send(
623
+ new import_lib_dynamodb.DeleteCommand({
624
+ TableName: tableName,
625
+ Key: {
626
+ [ormPrimaryKey]: pk
627
+ }
628
+ })
629
+ );
630
+ }
631
+ async function acquireUniqueLocksSequential(tableName, locks) {
632
+ const acquired = [];
633
+ try {
634
+ for (const lock of locks) {
635
+ await putUniqueSequential(tableName, lock);
636
+ acquired.push(lock);
637
+ }
638
+ } catch (error) {
639
+ await releaseUniqueLocksSequential(tableName, acquired);
640
+ throw error;
641
+ }
642
+ return acquired;
643
+ }
644
+ async function releaseUniqueLocksSequential(tableName, locks) {
645
+ for (const lock of [...locks].reverse()) {
646
+ try {
647
+ await deleteSequential(tableName, lock.pk);
648
+ } catch {
649
+ }
650
+ }
651
+ }
652
+ async function createRecordWithLocks(tableName, row, model) {
653
+ const locks = uniqueLocksForRow(model, row.decoded, row.docId);
654
+ const transactItems = [
655
+ putRecordCreateItem(tableName, row),
656
+ ...locks.map((lock) => putUniqueItem(tableName, lock))
657
+ ];
658
+ try {
659
+ await documentClient.send(
660
+ new import_lib_dynamodb.TransactWriteCommand({
661
+ TransactItems: transactItems
662
+ })
663
+ );
664
+ } catch (error) {
665
+ if (!isUnsupportedTransactionWrite(error)) {
666
+ throw error;
667
+ }
668
+ const acquiredLocks = await acquireUniqueLocksSequential(tableName, locks);
669
+ try {
670
+ await putRecordSequential(tableName, row, true);
671
+ } catch (recordError) {
672
+ await releaseUniqueLocksSequential(tableName, acquiredLocks);
673
+ throw recordError;
674
+ }
675
+ }
676
+ }
677
+ async function updateRecordWithLocks(tableName, model, current, next) {
678
+ const currentLocks = new Map(
679
+ uniqueLocksForRow(model, current.data, current.docId).map((lock) => [lock.pk, lock])
680
+ );
681
+ const nextLocks = new Map(
682
+ uniqueLocksForRow(model, next.decoded, next.docId).map((lock) => [lock.pk, lock])
683
+ );
684
+ const deleteOps = [...currentLocks.keys()].filter((pk) => !nextLocks.has(pk)).map((pk) => deleteItem(tableName, pk));
685
+ const putOps = [...nextLocks.entries()].filter(([pk]) => !currentLocks.has(pk)).map(([, lock]) => putUniqueItem(tableName, lock));
686
+ try {
687
+ await documentClient.send(
688
+ new import_lib_dynamodb.TransactWriteCommand({
689
+ TransactItems: [putRecordUpdateItem(tableName, next), ...deleteOps, ...putOps]
690
+ })
691
+ );
692
+ } catch (error) {
693
+ if (!isUnsupportedTransactionWrite(error)) {
694
+ throw error;
695
+ }
696
+ const addedLocks = [...nextLocks.values()].filter((lock) => !currentLocks.has(lock.pk));
697
+ const acquiredLocks = await acquireUniqueLocksSequential(tableName, addedLocks);
698
+ try {
699
+ await putRecordSequential(tableName, next, false);
700
+ } catch (recordError) {
701
+ await releaseUniqueLocksSequential(tableName, acquiredLocks);
702
+ throw recordError;
703
+ }
704
+ for (const operation of deleteOps) {
705
+ await deleteSequential(tableName, operation.Delete.Key[ormPrimaryKey]);
706
+ }
707
+ }
708
+ }
709
+ async function deleteRecordWithLocks(tableName, model, current) {
710
+ const locks = uniqueLocksForRow(model, current.data, current.docId);
711
+ try {
712
+ await documentClient.send(
713
+ new import_lib_dynamodb.TransactWriteCommand({
714
+ TransactItems: [
715
+ deleteItem(tableName, current.pk),
716
+ ...locks.map((lock) => deleteItem(tableName, lock.pk))
717
+ ]
718
+ })
719
+ );
720
+ } catch (error) {
721
+ if (!isUnsupportedTransactionWrite(error)) {
722
+ throw error;
723
+ }
724
+ await deleteSequential(tableName, current.pk);
725
+ for (const lock of locks) {
726
+ await deleteSequential(tableName, lock.pk);
727
+ }
728
+ }
729
+ }
730
+ async function projectRow(schema, modelName, row, select) {
731
+ const modelDefinition = schema.models[modelName];
732
+ const output = {};
733
+ if (!select) {
734
+ for (const fieldName of Object.keys(modelDefinition.fields)) {
735
+ output[fieldName] = row[fieldName];
736
+ }
737
+ return output;
738
+ }
739
+ for (const [key, value] of Object.entries(select)) {
740
+ if (value !== true && value === void 0) continue;
741
+ if (key in modelDefinition.fields && value === true) {
742
+ output[key] = row[key];
743
+ continue;
744
+ }
745
+ if (key in modelDefinition.relations) {
746
+ output[key] = await resolveRelation(
747
+ schema,
748
+ modelName,
749
+ key,
750
+ row,
751
+ value
752
+ );
753
+ }
754
+ }
755
+ return output;
756
+ }
757
+ async function resolveRelation(schema, modelName, relationName, row, value) {
758
+ const relation = schema.models[modelName].relations[relationName];
759
+ const relationArgs = value === true ? {} : value;
760
+ if (relation.kind === "belongsTo") {
761
+ const foreignValue = row[relation.foreignKey];
762
+ const targetRows2 = (await loadRows(schema, relation.target)).filter(
763
+ (item) => (0, import_orm.equalValues)(item.data.id, foreignValue)
764
+ );
765
+ const target = applyModelQuery(
766
+ relationTargetManifest(schema, relation.target),
767
+ targetRows2,
768
+ relationArgs
769
+ )[0];
770
+ return target ? projectRow(
771
+ schema,
772
+ relation.target,
773
+ target.data,
774
+ relationArgs.select
775
+ ) : null;
776
+ }
777
+ if (relation.kind === "hasOne") {
778
+ const targetRows2 = (await loadRows(schema, relation.target)).filter(
779
+ (item) => (0, import_orm.equalValues)(item.data[relation.foreignKey], row.id)
780
+ );
781
+ const target = applyModelQuery(
782
+ relationTargetManifest(schema, relation.target),
783
+ targetRows2,
784
+ relationArgs
785
+ )[0];
786
+ return target ? projectRow(
787
+ schema,
788
+ relation.target,
789
+ target.data,
790
+ relationArgs.select
791
+ ) : null;
792
+ }
793
+ if (relation.kind === "hasMany") {
794
+ const targetRows2 = (await loadRows(schema, relation.target)).filter(
795
+ (item) => (0, import_orm.equalValues)(item.data[relation.foreignKey], row.id)
796
+ );
797
+ const matchedRows2 = applyModelQuery(
798
+ relationTargetManifest(schema, relation.target),
799
+ targetRows2,
800
+ relationArgs
801
+ );
802
+ return Promise.all(
803
+ matchedRows2.map(
804
+ (item) => projectRow(schema, relation.target, item.data, relationArgs.select)
805
+ )
806
+ );
807
+ }
808
+ const throughRows = (await loadRows(schema, relation.through)).filter(
809
+ (item) => (0, import_orm.equalValues)(item.data[relation.from], row.id)
810
+ );
811
+ const targetIds = throughRows.map((item) => item.data[relation.to]);
812
+ const targetRows = (await loadRows(schema, relation.target)).filter(
813
+ (item) => targetIds.some((targetId) => (0, import_orm.equalValues)(targetId, item.data.id))
814
+ );
815
+ const matchedRows = applyModelQuery(
816
+ relationTargetManifest(schema, relation.target),
817
+ targetRows,
818
+ relationArgs
819
+ );
820
+ return Promise.all(
821
+ matchedRows.map(
822
+ (item) => projectRow(schema, relation.target, item.data, relationArgs.select)
823
+ )
824
+ );
825
+ }
826
+ function relationTargetManifest(schema, modelName) {
827
+ return getSupportedManifest(schema).models[modelName];
828
+ }
829
+ let driver;
830
+ driver = {
831
+ handle: (0, import_orm.createDriverHandle)({
832
+ kind: "dynamodb",
833
+ client: {
834
+ client: config.client,
835
+ documentClient,
836
+ tables: config.tables
837
+ },
838
+ capabilities: {
839
+ supportsNumericIds: true,
840
+ numericIds: "manual",
841
+ supportsJSON: true,
842
+ supportsDates: true,
843
+ supportsBooleans: true,
844
+ supportsTransactions: false,
845
+ supportsSchemaNamespaces: false,
846
+ supportsTransactionalDDL: false,
847
+ supportsJoin: false,
848
+ nativeRelationLoading: "none",
849
+ textComparison: "case-sensitive",
850
+ textMatching: {
851
+ equality: "case-sensitive",
852
+ contains: "case-sensitive",
853
+ ordering: "case-sensitive"
854
+ },
855
+ upsert: "emulated",
856
+ returning: {
857
+ create: true,
858
+ update: true,
859
+ delete: false
860
+ },
861
+ returningMode: {
862
+ create: "record",
863
+ update: "record",
864
+ delete: "none"
865
+ },
866
+ nativeRelations: {
867
+ singularChains: false,
868
+ hasMany: false,
869
+ manyToMany: false,
870
+ filtered: false,
871
+ ordered: false,
872
+ paginated: false
873
+ }
874
+ }
875
+ }),
876
+ async findMany(schema, modelName, args) {
877
+ const model = getSupportedManifest(schema).models[modelName];
878
+ const rows = applyModelQuery(model, await loadRows(schema, modelName), args);
879
+ return Promise.all(rows.map((row) => projectRow(schema, modelName, row.data, args.select)));
880
+ },
881
+ async findFirst(schema, modelName, args) {
882
+ const model = getSupportedManifest(schema).models[modelName];
883
+ const row = applyModelQuery(model, await loadRows(schema, modelName), args)[0];
884
+ if (!row) return null;
885
+ return projectRow(schema, modelName, row.data, args.select);
886
+ },
887
+ async findUnique(schema, modelName, args) {
888
+ const model = getSupportedManifest(schema).models[modelName];
889
+ const row = await loadUniqueRow(schema, modelName, args.where);
890
+ if (!row || !matchesModelWhere(model, row.data, args.where)) {
891
+ return null;
892
+ }
893
+ return projectRow(schema, modelName, row.data, args.select);
894
+ },
895
+ async count(schema, modelName, args) {
896
+ const model = getSupportedManifest(schema).models[modelName];
897
+ return applyModelQuery(model, await loadRows(schema, modelName), args).length;
898
+ },
899
+ async create(schema, modelName, args) {
900
+ const model = getSupportedManifest(schema).models[modelName];
901
+ const existingRows = await loadRows(schema, modelName);
902
+ const built = buildStoredRow(model, args.data);
903
+ const conflict = findUniqueConflict(model, built.decoded, existingRows);
904
+ if (conflict) {
905
+ throw dynamodbConstraintError(conflict);
906
+ }
907
+ await createRecordWithLocks(getTableName(schema, modelName), built, model);
908
+ return projectRow(schema, modelName, built.decoded, args.select);
909
+ },
910
+ async createMany(schema, modelName, args) {
911
+ const model = getSupportedManifest(schema).models[modelName];
912
+ const existingRows = await loadRows(schema, modelName);
913
+ const created = [];
914
+ for (const entry of args.data) {
915
+ const built = buildStoredRow(model, entry);
916
+ const conflict = findUniqueConflict(model, built.decoded, [
917
+ ...existingRows,
918
+ ...created.map((row) => ({
919
+ docId: row.docId,
920
+ pk: recordPk(row.docId),
921
+ data: row.decoded,
922
+ stored: row.stored
923
+ }))
924
+ ]);
925
+ if (conflict) {
926
+ throw dynamodbConstraintError(conflict);
927
+ }
928
+ created.push(built);
929
+ }
930
+ for (const row of created) {
931
+ await createRecordWithLocks(getTableName(schema, modelName), row, model);
932
+ }
933
+ return Promise.all(
934
+ created.map((row) => projectRow(schema, modelName, row.decoded, args.select))
935
+ );
936
+ },
937
+ async update(schema, modelName, args) {
938
+ const model = getSupportedManifest(schema).models[modelName];
939
+ const rows = await loadRows(schema, modelName);
940
+ const current = applyModelQuery(model, rows, {
941
+ where: args.where,
942
+ take: 1
943
+ })[0];
944
+ if (!current) return null;
945
+ const next = buildUpdatedRow(model, current, args.data);
946
+ const conflict = findUniqueConflict(model, next.decoded, rows, /* @__PURE__ */ new Set([current.docId]));
947
+ if (conflict) {
948
+ throw dynamodbConstraintError(conflict);
949
+ }
950
+ await updateRecordWithLocks(getTableName(schema, modelName), model, current, next);
951
+ return projectRow(schema, modelName, next.decoded, args.select);
952
+ },
953
+ async updateMany(schema, modelName, args) {
954
+ const model = getSupportedManifest(schema).models[modelName];
955
+ const rows = await loadRows(schema, modelName);
956
+ const matched = applyModelQuery(model, rows, {
957
+ where: args.where
958
+ });
959
+ if (!matched.length) return 0;
960
+ const nextRows = matched.map(
961
+ (row) => buildUpdatedRow(model, row, args.data)
962
+ );
963
+ const keepIds = new Set(matched.map((row) => row.docId));
964
+ const remaining = rows.filter((row) => !keepIds.has(row.docId));
965
+ const pending = [];
966
+ for (const next of nextRows) {
967
+ const conflict = findUniqueConflict(model, next.decoded, [...remaining, ...pending]);
968
+ if (conflict) {
969
+ throw dynamodbConstraintError(conflict);
970
+ }
971
+ pending.push({
972
+ docId: next.docId,
973
+ pk: recordPk(next.docId),
974
+ data: next.decoded,
975
+ stored: next.stored
976
+ });
977
+ }
978
+ for (let index = 0; index < matched.length; index += 1) {
979
+ await updateRecordWithLocks(
980
+ getTableName(schema, modelName),
981
+ model,
982
+ matched[index],
983
+ nextRows[index]
984
+ );
985
+ }
986
+ return nextRows.length;
987
+ },
988
+ async upsert(schema, modelName, args) {
989
+ const model = getSupportedManifest(schema).models[modelName];
990
+ const lookup = (0, import_orm.requireUniqueLookup)(model, args.where, "Upsert");
991
+ (0, import_orm.validateUniqueLookupUpdateData)(
992
+ model,
993
+ args.update,
994
+ lookup,
995
+ "Upsert"
996
+ );
997
+ const current = await loadUniqueRow(schema, modelName, args.where);
998
+ if (current && matchesModelWhere(model, current.data, args.where)) {
999
+ const rows2 = await loadRows(schema, modelName);
1000
+ const next = buildUpdatedRow(
1001
+ model,
1002
+ current,
1003
+ args.update
1004
+ );
1005
+ const conflict2 = findUniqueConflict(model, next.decoded, rows2, /* @__PURE__ */ new Set([current.docId]));
1006
+ if (conflict2) {
1007
+ throw dynamodbConstraintError(conflict2);
1008
+ }
1009
+ await updateRecordWithLocks(getTableName(schema, modelName), model, current, next);
1010
+ return projectRow(schema, modelName, next.decoded, args.select);
1011
+ }
1012
+ const created = buildStoredRow(
1013
+ model,
1014
+ (0, import_orm.mergeUniqueLookupCreateData)(
1015
+ model,
1016
+ args.create,
1017
+ lookup,
1018
+ "Upsert"
1019
+ )
1020
+ );
1021
+ const rows = await loadRows(schema, modelName);
1022
+ const conflict = findUniqueConflict(model, created.decoded, rows);
1023
+ if (conflict) {
1024
+ throw dynamodbConstraintError(conflict);
1025
+ }
1026
+ await createRecordWithLocks(getTableName(schema, modelName), created, model);
1027
+ return projectRow(schema, modelName, created.decoded, args.select);
1028
+ },
1029
+ async delete(schema, modelName, args) {
1030
+ const model = getSupportedManifest(schema).models[modelName];
1031
+ const row = applyModelQuery(model, await loadRows(schema, modelName), {
1032
+ where: args.where,
1033
+ take: 1
1034
+ })[0];
1035
+ if (!row) return 0;
1036
+ await deleteRecordWithLocks(getTableName(schema, modelName), model, row);
1037
+ return 1;
1038
+ },
1039
+ async deleteMany(schema, modelName, args) {
1040
+ const model = getSupportedManifest(schema).models[modelName];
1041
+ const rows = applyModelQuery(model, await loadRows(schema, modelName), {
1042
+ where: args.where
1043
+ });
1044
+ for (const row of rows) {
1045
+ await deleteRecordWithLocks(getTableName(schema, modelName), model, row);
1046
+ }
1047
+ return rows.length;
1048
+ },
1049
+ async transaction(schema, run) {
1050
+ getSupportedManifest(schema);
1051
+ return run(driver);
1052
+ }
1053
+ };
1054
+ return driver;
1055
+ }
1056
+ function createDynamodbDriver(config) {
1057
+ return createDynamoDbDriverInternal(config);
1058
+ }
1059
+ // Annotate the CommonJS export names for ESM import in node:
1060
+ 0 && (module.exports = {
1061
+ createDynamodbDriver,
1062
+ normalizeDynamoDbDocumentClient
1063
+ });
1064
+ //# sourceMappingURL=index.cjs.map