dinah 0.13.1 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cdk.d.cts +1 -1
- package/dist/cdk.d.mts +1 -1
- package/dist/index.cjs +80 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -14
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +34 -14
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +80 -35
- package/dist/index.mjs.map +1 -1
- package/dist/{table-CatYaz7J.d.mts → table-GSI2RvTa.d.cts} +5 -4
- package/dist/{table-CatYaz7J.d.mts.map → table-GSI2RvTa.d.cts.map} +1 -1
- package/dist/{table-D814Vwjm.d.cts → table-O4EneQbj.d.mts} +5 -4
- package/dist/{table-D814Vwjm.d.cts.map → table-O4EneQbj.d.mts.map} +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -4,8 +4,9 @@ import * as Lib from "@aws-sdk/lib-dynamodb";
|
|
|
4
4
|
//#region src/error.ts
|
|
5
5
|
function dinahErrorMessage(details) {
|
|
6
6
|
switch (details.type) {
|
|
7
|
-
case "NOT_FOUND": return `${details.resource ?? "Item"} not found
|
|
8
|
-
case "
|
|
7
|
+
case "NOT_FOUND": return `${details.resource ?? "Item"} not found.`;
|
|
8
|
+
case "ALREADY_EXISTS": return `${details.resource ?? "Item"} already exists.`;
|
|
9
|
+
case "CONDITIONAL_CHECK_FAILED": return `${details.resource ?? "Item"} condition check failed.`;
|
|
9
10
|
case "VALIDATION": return `Validation error: ${details.message}`;
|
|
10
11
|
case "DATA_INTEGRITY": return `Data integrity error: ${details.message}`;
|
|
11
12
|
case "TRANSACTION_CANCELED": return `Transaction canceled: ${details.reasons.map((r) => r.type).join(", ")}`;
|
|
@@ -114,7 +115,10 @@ var ExpressionBuilder = class {
|
|
|
114
115
|
const setDelOperations = [];
|
|
115
116
|
for (const [path, valOrOperation] of Object.entries(expression)) {
|
|
116
117
|
const placeholder = this.getPathSub(path);
|
|
117
|
-
if (valOrOperation === void 0)
|
|
118
|
+
if (valOrOperation === void 0) throw new DinahError({
|
|
119
|
+
type: "VALIDATION",
|
|
120
|
+
message: `Field "${path}" cannot be set to undefined in an update expression. Use { $remove: true } to remove an optional attribute.`
|
|
121
|
+
});
|
|
118
122
|
else if (isOperation(valOrOperation)) if (valOrOperation.$remove === true) removeOperations.push(placeholder);
|
|
119
123
|
else if (valOrOperation.$setAdd !== void 0 || valOrOperation.$setDel !== void 0) {
|
|
120
124
|
const operations = valOrOperation.$setAdd ? setAddOperations : setDelOperations;
|
|
@@ -202,7 +206,10 @@ var ExpressionBuilder = class {
|
|
|
202
206
|
const sets = [];
|
|
203
207
|
const removes = [];
|
|
204
208
|
const params = [];
|
|
205
|
-
for (const [path, valOrOp] of Object.entries(expression)) if (valOrOp === void 0)
|
|
209
|
+
for (const [path, valOrOp] of Object.entries(expression)) if (valOrOp === void 0) throw new DinahError({
|
|
210
|
+
type: "VALIDATION",
|
|
211
|
+
message: `Field "${path}" cannot be set to undefined in an update expression. Use { $remove: true } to remove an optional attribute.`
|
|
212
|
+
});
|
|
206
213
|
else if (isOperation(valOrOp)) if (valOrOp.$remove === true) removes.push(`"${path}"`);
|
|
207
214
|
else sets.push(`"${path}"=${this.resolvePartiQLSetOperand(path, valOrOp, params)}`);
|
|
208
215
|
else {
|
|
@@ -309,18 +316,25 @@ var Repo = class {
|
|
|
309
316
|
});
|
|
310
317
|
return this.applyTransformIfNeeded(result);
|
|
311
318
|
}
|
|
312
|
-
async create(item
|
|
313
|
-
const { condition: otherCondition, ...otherOptions } = options ?? {};
|
|
319
|
+
async create(item) {
|
|
314
320
|
const condition = { $and: [{ [this.table.def.partitionKey]: { $exists: false } }] };
|
|
315
|
-
if (otherCondition) condition.$and.push(otherCondition);
|
|
316
321
|
const normalizedItem = this.applyCreateTransforms(item);
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
322
|
+
let result;
|
|
323
|
+
try {
|
|
324
|
+
result = await this.db.put({
|
|
325
|
+
table: this.tableName,
|
|
326
|
+
item: normalizedItem,
|
|
327
|
+
resource: this.resourceName,
|
|
328
|
+
condition
|
|
329
|
+
});
|
|
330
|
+
} catch (err) {
|
|
331
|
+
if (err instanceof DinahError && err.details.type === "CONDITIONAL_CHECK_FAILED") throw new DinahError({
|
|
332
|
+
type: "ALREADY_EXISTS",
|
|
333
|
+
key: this.extractKey(item),
|
|
334
|
+
resource: this.resourceName
|
|
335
|
+
}, { cause: err });
|
|
336
|
+
throw err;
|
|
337
|
+
}
|
|
324
338
|
return this.applyTransformIfNeeded(result);
|
|
325
339
|
}
|
|
326
340
|
async delete(key, options) {
|
|
@@ -555,8 +569,8 @@ var Repo = class {
|
|
|
555
569
|
async trxUpdate(keys, update, options) {
|
|
556
570
|
return this.db.trxWrite(...keys.map((key) => this.trxUpdateRequest(key, update, options)));
|
|
557
571
|
}
|
|
558
|
-
async trxCreate(items
|
|
559
|
-
return this.db.trxWrite(...items.map((item) => this.trxCreateRequest(item
|
|
572
|
+
async trxCreate(items) {
|
|
573
|
+
return this.db.trxWrite(...items.map((item) => this.trxCreateRequest(item)));
|
|
560
574
|
}
|
|
561
575
|
trxGetRequest(key, options) {
|
|
562
576
|
return {
|
|
@@ -600,17 +614,14 @@ var Repo = class {
|
|
|
600
614
|
condition: this.withDiscriminatorCondition(key, options?.condition)
|
|
601
615
|
};
|
|
602
616
|
}
|
|
603
|
-
trxCreateRequest(item
|
|
604
|
-
const { condition: otherCondition, ...otherOptions } = options ?? {};
|
|
617
|
+
trxCreateRequest(item) {
|
|
605
618
|
const condition = { $and: [{ [this.table.def.partitionKey]: { $exists: false } }] };
|
|
606
|
-
if (otherCondition) condition.$and.push(otherCondition);
|
|
607
619
|
const normalizedItem = this.applyCreateTransforms(item);
|
|
608
620
|
return {
|
|
609
621
|
table: this.tableName,
|
|
610
622
|
type: "PUT",
|
|
611
623
|
item: normalizedItem,
|
|
612
|
-
condition
|
|
613
|
-
...otherOptions
|
|
624
|
+
condition
|
|
614
625
|
};
|
|
615
626
|
}
|
|
616
627
|
applyCreateTransforms(item) {
|
|
@@ -630,7 +641,7 @@ var Repo = class {
|
|
|
630
641
|
}
|
|
631
642
|
if (computedAttributes) for (const [key, def] of Object.entries(computedAttributes)) {
|
|
632
643
|
const { from, compute } = def;
|
|
633
|
-
const computed = compute(merged[from]);
|
|
644
|
+
const computed = compute(...Array.isArray(from) ? from.map((k) => merged[k]) : [merged[from]]);
|
|
634
645
|
if (computed !== void 0) merged[key] = computed;
|
|
635
646
|
else delete merged[key];
|
|
636
647
|
}
|
|
@@ -671,15 +682,42 @@ var Repo = class {
|
|
|
671
682
|
}
|
|
672
683
|
if (computedAttributes) for (const [computedKey, def] of Object.entries(computedAttributes)) {
|
|
673
684
|
const { from, compute } = def;
|
|
674
|
-
if (
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
685
|
+
if (Array.isArray(from)) {
|
|
686
|
+
const fromArr = from;
|
|
687
|
+
if (fromArr.filter((k) => k in result).length === 0) continue;
|
|
688
|
+
const missingKeys = fromArr.filter((k) => !(k in result));
|
|
689
|
+
if (missingKeys.length > 0) throw new DinahError({
|
|
690
|
+
type: "VALIDATION",
|
|
691
|
+
message: `Field "${computedKey}" is computed from [${fromArr.join(", ")}]. All source fields must be included in the update or none. Missing: [${missingKeys.join(", ")}].`
|
|
692
|
+
});
|
|
693
|
+
const args = [];
|
|
694
|
+
for (const key of fromArr) {
|
|
695
|
+
const val = result[key];
|
|
696
|
+
if (val === void 0 || isOperation(val) && val.$remove === true) args.push(void 0);
|
|
697
|
+
else if (!isOperation(val)) args.push(val);
|
|
698
|
+
else if (val.$set !== void 0) args.push(val.$set);
|
|
699
|
+
else throw new DinahError({
|
|
700
|
+
type: "VALIDATION",
|
|
701
|
+
message: `Field "${key}" drives computed field "${computedKey}" and cannot use arithmetic or list operators in updates.`
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
const computed = compute(...args);
|
|
705
|
+
result[computedKey] = computed !== void 0 ? computed : { $remove: true };
|
|
706
|
+
} else {
|
|
707
|
+
const fromKey = from;
|
|
708
|
+
if (!(fromKey in result)) continue;
|
|
709
|
+
const val = result[fromKey];
|
|
710
|
+
let resolvedVal;
|
|
711
|
+
if (val === void 0 || isOperation(val) && val.$remove === true) resolvedVal = void 0;
|
|
712
|
+
else if (!isOperation(val)) resolvedVal = val;
|
|
713
|
+
else if (val.$set !== void 0) resolvedVal = val.$set;
|
|
714
|
+
else throw new DinahError({
|
|
715
|
+
type: "VALIDATION",
|
|
716
|
+
message: `Field "${fromKey}" drives computed field "${computedKey}" and cannot use arithmetic or list operators in updates.`
|
|
717
|
+
});
|
|
718
|
+
const computed = compute(resolvedVal);
|
|
719
|
+
result[computedKey] = computed !== void 0 ? computed : { $remove: true };
|
|
720
|
+
}
|
|
683
721
|
}
|
|
684
722
|
return result;
|
|
685
723
|
}
|
|
@@ -723,12 +761,19 @@ var Db = class {
|
|
|
723
761
|
else {
|
|
724
762
|
const { endpoint, ...clientConfig } = clientOrConfig;
|
|
725
763
|
this.client = Lib.DynamoDBDocumentClient.from(new DynamoDBClient({
|
|
726
|
-
endpoint
|
|
764
|
+
...endpoint ? { endpoint } : {},
|
|
727
765
|
...clientConfig
|
|
728
766
|
}));
|
|
729
767
|
}
|
|
730
768
|
this.config = dbConfig;
|
|
731
769
|
}
|
|
770
|
+
normalizeUpdate(expression) {
|
|
771
|
+
const behavior = this.config?.updateUndefinedBehavior ?? "throw";
|
|
772
|
+
if (behavior === "throw") return expression;
|
|
773
|
+
const entries = Object.entries(expression);
|
|
774
|
+
if (behavior === "strip") return Object.fromEntries(entries.filter(([, v]) => v !== void 0));
|
|
775
|
+
return Object.fromEntries(entries.map(([k, v]) => [k, v === void 0 ? { $remove: true } : v]));
|
|
776
|
+
}
|
|
732
777
|
makeRepo(table, config) {
|
|
733
778
|
return new Repo(this, table, config);
|
|
734
779
|
}
|
|
@@ -805,7 +850,7 @@ var Db = class {
|
|
|
805
850
|
Key: data.key,
|
|
806
851
|
ReturnValues: "ALL_NEW",
|
|
807
852
|
ReturnValuesOnConditionCheckFailure: "ALL_OLD",
|
|
808
|
-
UpdateExpression: exp.update(data.update),
|
|
853
|
+
UpdateExpression: exp.update(this.normalizeUpdate(data.update)),
|
|
809
854
|
ConditionExpression: exp.condition(condition),
|
|
810
855
|
ExpressionAttributeNames: exp.attributeNames,
|
|
811
856
|
ExpressionAttributeValues: exp.attributeValues
|
|
@@ -1072,7 +1117,7 @@ var Db = class {
|
|
|
1072
1117
|
while (queue.length && retryCount < 5) {
|
|
1073
1118
|
const batch = queue.splice(0, batchSize || 1);
|
|
1074
1119
|
const statements = batch.map(([table, key, update]) => {
|
|
1075
|
-
const { sets, removes, params } = new ExpressionBuilder().partiqlUpdate(update);
|
|
1120
|
+
const { sets, removes, params } = new ExpressionBuilder().partiqlUpdate(this.normalizeUpdate(update));
|
|
1076
1121
|
const whereParts = [];
|
|
1077
1122
|
for (const [field, value] of Object.entries(key)) {
|
|
1078
1123
|
whereParts.push(`"${field}"=?`);
|
|
@@ -1157,7 +1202,7 @@ var Db = class {
|
|
|
1157
1202
|
return { Update: {
|
|
1158
1203
|
Key: request.key,
|
|
1159
1204
|
TableName: request.table,
|
|
1160
|
-
UpdateExpression: exp.update(request.update),
|
|
1205
|
+
UpdateExpression: exp.update(this.normalizeUpdate(request.update)),
|
|
1161
1206
|
ConditionExpression: exp.condition(request.condition),
|
|
1162
1207
|
ExpressionAttributeNames: exp.attributeNames,
|
|
1163
1208
|
ExpressionAttributeValues: exp.attributeValues
|