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/cdk.d.cts
CHANGED
package/dist/cdk.d.mts
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -6,8 +6,9 @@ _aws_sdk_lib_dynamodb = require_util.__toESM(_aws_sdk_lib_dynamodb, 1);
|
|
|
6
6
|
//#region src/error.ts
|
|
7
7
|
function dinahErrorMessage(details) {
|
|
8
8
|
switch (details.type) {
|
|
9
|
-
case "NOT_FOUND": return `${details.resource ?? "Item"} not found
|
|
10
|
-
case "
|
|
9
|
+
case "NOT_FOUND": return `${details.resource ?? "Item"} not found.`;
|
|
10
|
+
case "ALREADY_EXISTS": return `${details.resource ?? "Item"} already exists.`;
|
|
11
|
+
case "CONDITIONAL_CHECK_FAILED": return `${details.resource ?? "Item"} condition check failed.`;
|
|
11
12
|
case "VALIDATION": return `Validation error: ${details.message}`;
|
|
12
13
|
case "DATA_INTEGRITY": return `Data integrity error: ${details.message}`;
|
|
13
14
|
case "TRANSACTION_CANCELED": return `Transaction canceled: ${details.reasons.map((r) => r.type).join(", ")}`;
|
|
@@ -116,7 +117,10 @@ var ExpressionBuilder = class {
|
|
|
116
117
|
const setDelOperations = [];
|
|
117
118
|
for (const [path, valOrOperation] of Object.entries(expression)) {
|
|
118
119
|
const placeholder = this.getPathSub(path);
|
|
119
|
-
if (valOrOperation === void 0)
|
|
120
|
+
if (valOrOperation === void 0) throw new DinahError({
|
|
121
|
+
type: "VALIDATION",
|
|
122
|
+
message: `Field "${path}" cannot be set to undefined in an update expression. Use { $remove: true } to remove an optional attribute.`
|
|
123
|
+
});
|
|
120
124
|
else if (isOperation(valOrOperation)) if (valOrOperation.$remove === true) removeOperations.push(placeholder);
|
|
121
125
|
else if (valOrOperation.$setAdd !== void 0 || valOrOperation.$setDel !== void 0) {
|
|
122
126
|
const operations = valOrOperation.$setAdd ? setAddOperations : setDelOperations;
|
|
@@ -204,7 +208,10 @@ var ExpressionBuilder = class {
|
|
|
204
208
|
const sets = [];
|
|
205
209
|
const removes = [];
|
|
206
210
|
const params = [];
|
|
207
|
-
for (const [path, valOrOp] of Object.entries(expression)) if (valOrOp === void 0)
|
|
211
|
+
for (const [path, valOrOp] of Object.entries(expression)) if (valOrOp === void 0) throw new DinahError({
|
|
212
|
+
type: "VALIDATION",
|
|
213
|
+
message: `Field "${path}" cannot be set to undefined in an update expression. Use { $remove: true } to remove an optional attribute.`
|
|
214
|
+
});
|
|
208
215
|
else if (isOperation(valOrOp)) if (valOrOp.$remove === true) removes.push(`"${path}"`);
|
|
209
216
|
else sets.push(`"${path}"=${this.resolvePartiQLSetOperand(path, valOrOp, params)}`);
|
|
210
217
|
else {
|
|
@@ -311,18 +318,25 @@ var Repo = class {
|
|
|
311
318
|
});
|
|
312
319
|
return this.applyTransformIfNeeded(result);
|
|
313
320
|
}
|
|
314
|
-
async create(item
|
|
315
|
-
const { condition: otherCondition, ...otherOptions } = options ?? {};
|
|
321
|
+
async create(item) {
|
|
316
322
|
const condition = { $and: [{ [this.table.def.partitionKey]: { $exists: false } }] };
|
|
317
|
-
if (otherCondition) condition.$and.push(otherCondition);
|
|
318
323
|
const normalizedItem = this.applyCreateTransforms(item);
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
324
|
+
let result;
|
|
325
|
+
try {
|
|
326
|
+
result = await this.db.put({
|
|
327
|
+
table: this.tableName,
|
|
328
|
+
item: normalizedItem,
|
|
329
|
+
resource: this.resourceName,
|
|
330
|
+
condition
|
|
331
|
+
});
|
|
332
|
+
} catch (err) {
|
|
333
|
+
if (err instanceof DinahError && err.details.type === "CONDITIONAL_CHECK_FAILED") throw new DinahError({
|
|
334
|
+
type: "ALREADY_EXISTS",
|
|
335
|
+
key: this.extractKey(item),
|
|
336
|
+
resource: this.resourceName
|
|
337
|
+
}, { cause: err });
|
|
338
|
+
throw err;
|
|
339
|
+
}
|
|
326
340
|
return this.applyTransformIfNeeded(result);
|
|
327
341
|
}
|
|
328
342
|
async delete(key, options) {
|
|
@@ -557,8 +571,8 @@ var Repo = class {
|
|
|
557
571
|
async trxUpdate(keys, update, options) {
|
|
558
572
|
return this.db.trxWrite(...keys.map((key) => this.trxUpdateRequest(key, update, options)));
|
|
559
573
|
}
|
|
560
|
-
async trxCreate(items
|
|
561
|
-
return this.db.trxWrite(...items.map((item) => this.trxCreateRequest(item
|
|
574
|
+
async trxCreate(items) {
|
|
575
|
+
return this.db.trxWrite(...items.map((item) => this.trxCreateRequest(item)));
|
|
562
576
|
}
|
|
563
577
|
trxGetRequest(key, options) {
|
|
564
578
|
return {
|
|
@@ -602,17 +616,14 @@ var Repo = class {
|
|
|
602
616
|
condition: this.withDiscriminatorCondition(key, options?.condition)
|
|
603
617
|
};
|
|
604
618
|
}
|
|
605
|
-
trxCreateRequest(item
|
|
606
|
-
const { condition: otherCondition, ...otherOptions } = options ?? {};
|
|
619
|
+
trxCreateRequest(item) {
|
|
607
620
|
const condition = { $and: [{ [this.table.def.partitionKey]: { $exists: false } }] };
|
|
608
|
-
if (otherCondition) condition.$and.push(otherCondition);
|
|
609
621
|
const normalizedItem = this.applyCreateTransforms(item);
|
|
610
622
|
return {
|
|
611
623
|
table: this.tableName,
|
|
612
624
|
type: "PUT",
|
|
613
625
|
item: normalizedItem,
|
|
614
|
-
condition
|
|
615
|
-
...otherOptions
|
|
626
|
+
condition
|
|
616
627
|
};
|
|
617
628
|
}
|
|
618
629
|
applyCreateTransforms(item) {
|
|
@@ -632,7 +643,7 @@ var Repo = class {
|
|
|
632
643
|
}
|
|
633
644
|
if (computedAttributes) for (const [key, def] of Object.entries(computedAttributes)) {
|
|
634
645
|
const { from, compute } = def;
|
|
635
|
-
const computed = compute(merged[from]);
|
|
646
|
+
const computed = compute(...Array.isArray(from) ? from.map((k) => merged[k]) : [merged[from]]);
|
|
636
647
|
if (computed !== void 0) merged[key] = computed;
|
|
637
648
|
else delete merged[key];
|
|
638
649
|
}
|
|
@@ -673,15 +684,42 @@ var Repo = class {
|
|
|
673
684
|
}
|
|
674
685
|
if (computedAttributes) for (const [computedKey, def] of Object.entries(computedAttributes)) {
|
|
675
686
|
const { from, compute } = def;
|
|
676
|
-
if (
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
687
|
+
if (Array.isArray(from)) {
|
|
688
|
+
const fromArr = from;
|
|
689
|
+
if (fromArr.filter((k) => k in result).length === 0) continue;
|
|
690
|
+
const missingKeys = fromArr.filter((k) => !(k in result));
|
|
691
|
+
if (missingKeys.length > 0) throw new DinahError({
|
|
692
|
+
type: "VALIDATION",
|
|
693
|
+
message: `Field "${computedKey}" is computed from [${fromArr.join(", ")}]. All source fields must be included in the update or none. Missing: [${missingKeys.join(", ")}].`
|
|
694
|
+
});
|
|
695
|
+
const args = [];
|
|
696
|
+
for (const key of fromArr) {
|
|
697
|
+
const val = result[key];
|
|
698
|
+
if (val === void 0 || isOperation(val) && val.$remove === true) args.push(void 0);
|
|
699
|
+
else if (!isOperation(val)) args.push(val);
|
|
700
|
+
else if (val.$set !== void 0) args.push(val.$set);
|
|
701
|
+
else throw new DinahError({
|
|
702
|
+
type: "VALIDATION",
|
|
703
|
+
message: `Field "${key}" drives computed field "${computedKey}" and cannot use arithmetic or list operators in updates.`
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
const computed = compute(...args);
|
|
707
|
+
result[computedKey] = computed !== void 0 ? computed : { $remove: true };
|
|
708
|
+
} else {
|
|
709
|
+
const fromKey = from;
|
|
710
|
+
if (!(fromKey in result)) continue;
|
|
711
|
+
const val = result[fromKey];
|
|
712
|
+
let resolvedVal;
|
|
713
|
+
if (val === void 0 || isOperation(val) && val.$remove === true) resolvedVal = void 0;
|
|
714
|
+
else if (!isOperation(val)) resolvedVal = val;
|
|
715
|
+
else if (val.$set !== void 0) resolvedVal = val.$set;
|
|
716
|
+
else throw new DinahError({
|
|
717
|
+
type: "VALIDATION",
|
|
718
|
+
message: `Field "${fromKey}" drives computed field "${computedKey}" and cannot use arithmetic or list operators in updates.`
|
|
719
|
+
});
|
|
720
|
+
const computed = compute(resolvedVal);
|
|
721
|
+
result[computedKey] = computed !== void 0 ? computed : { $remove: true };
|
|
722
|
+
}
|
|
685
723
|
}
|
|
686
724
|
return result;
|
|
687
725
|
}
|
|
@@ -725,12 +763,19 @@ var Db = class {
|
|
|
725
763
|
else {
|
|
726
764
|
const { endpoint, ...clientConfig } = clientOrConfig;
|
|
727
765
|
this.client = _aws_sdk_lib_dynamodb.DynamoDBDocumentClient.from(new _aws_sdk_client_dynamodb.DynamoDBClient({
|
|
728
|
-
endpoint
|
|
766
|
+
...endpoint ? { endpoint } : {},
|
|
729
767
|
...clientConfig
|
|
730
768
|
}));
|
|
731
769
|
}
|
|
732
770
|
this.config = dbConfig;
|
|
733
771
|
}
|
|
772
|
+
normalizeUpdate(expression) {
|
|
773
|
+
const behavior = this.config?.updateUndefinedBehavior ?? "throw";
|
|
774
|
+
if (behavior === "throw") return expression;
|
|
775
|
+
const entries = Object.entries(expression);
|
|
776
|
+
if (behavior === "strip") return Object.fromEntries(entries.filter(([, v]) => v !== void 0));
|
|
777
|
+
return Object.fromEntries(entries.map(([k, v]) => [k, v === void 0 ? { $remove: true } : v]));
|
|
778
|
+
}
|
|
734
779
|
makeRepo(table, config) {
|
|
735
780
|
return new Repo(this, table, config);
|
|
736
781
|
}
|
|
@@ -807,7 +852,7 @@ var Db = class {
|
|
|
807
852
|
Key: data.key,
|
|
808
853
|
ReturnValues: "ALL_NEW",
|
|
809
854
|
ReturnValuesOnConditionCheckFailure: "ALL_OLD",
|
|
810
|
-
UpdateExpression: exp.update(data.update),
|
|
855
|
+
UpdateExpression: exp.update(this.normalizeUpdate(data.update)),
|
|
811
856
|
ConditionExpression: exp.condition(condition),
|
|
812
857
|
ExpressionAttributeNames: exp.attributeNames,
|
|
813
858
|
ExpressionAttributeValues: exp.attributeValues
|
|
@@ -1074,7 +1119,7 @@ var Db = class {
|
|
|
1074
1119
|
while (queue.length && retryCount < 5) {
|
|
1075
1120
|
const batch = queue.splice(0, batchSize || 1);
|
|
1076
1121
|
const statements = batch.map(([table, key, update]) => {
|
|
1077
|
-
const { sets, removes, params } = new ExpressionBuilder().partiqlUpdate(update);
|
|
1122
|
+
const { sets, removes, params } = new ExpressionBuilder().partiqlUpdate(this.normalizeUpdate(update));
|
|
1078
1123
|
const whereParts = [];
|
|
1079
1124
|
for (const [field, value] of Object.entries(key)) {
|
|
1080
1125
|
whereParts.push(`"${field}"=?`);
|
|
@@ -1159,7 +1204,7 @@ var Db = class {
|
|
|
1159
1204
|
return { Update: {
|
|
1160
1205
|
Key: request.key,
|
|
1161
1206
|
TableName: request.table,
|
|
1162
|
-
UpdateExpression: exp.update(request.update),
|
|
1207
|
+
UpdateExpression: exp.update(this.normalizeUpdate(request.update)),
|
|
1163
1208
|
ConditionExpression: exp.condition(request.condition),
|
|
1164
1209
|
ExpressionAttributeNames: exp.attributeNames,
|
|
1165
1210
|
ExpressionAttributeValues: exp.attributeValues
|