electrodb 3.2.0 → 3.4.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/index.d.ts +4 -2
- package/package.json +1 -1
- package/src/entity.js +67 -23
- package/src/errors.js +6 -0
- package/src/types.js +3 -0
- package/src/util.js +18 -2
package/index.d.ts
CHANGED
|
@@ -3648,6 +3648,8 @@ export type AccessPatternCollection<C extends string> = C | ReadonlyArray<C>;
|
|
|
3648
3648
|
|
|
3649
3649
|
export type KeyCastOption = "string" | "number";
|
|
3650
3650
|
|
|
3651
|
+
export type KeyCasingOption = "upper" | "lower" | "none" | "default";
|
|
3652
|
+
|
|
3651
3653
|
export interface Schema<A extends string, F extends string, C extends string> {
|
|
3652
3654
|
readonly model: {
|
|
3653
3655
|
readonly entity: string;
|
|
@@ -3666,14 +3668,14 @@ export interface Schema<A extends string, F extends string, C extends string> {
|
|
|
3666
3668
|
readonly collection?: AccessPatternCollection<C>;
|
|
3667
3669
|
readonly condition?: (composite: Record<string, unknown>) => boolean;
|
|
3668
3670
|
readonly pk: {
|
|
3669
|
-
readonly casing?:
|
|
3671
|
+
readonly casing?: KeyCasingOption;
|
|
3670
3672
|
readonly field: string;
|
|
3671
3673
|
readonly composite: ReadonlyArray<F>;
|
|
3672
3674
|
readonly template?: string;
|
|
3673
3675
|
readonly cast?: KeyCastOption;
|
|
3674
3676
|
};
|
|
3675
3677
|
readonly sk?: {
|
|
3676
|
-
readonly casing?:
|
|
3678
|
+
readonly casing?: KeyCasingOption;
|
|
3677
3679
|
readonly field: string;
|
|
3678
3680
|
readonly composite: ReadonlyArray<F>;
|
|
3679
3681
|
readonly template?: string;
|
package/package.json
CHANGED
package/src/entity.js
CHANGED
|
@@ -3,6 +3,7 @@ const { Schema } = require("./schema");
|
|
|
3
3
|
const {
|
|
4
4
|
AllPages,
|
|
5
5
|
KeyCasing,
|
|
6
|
+
DefaultKeyCasing,
|
|
6
7
|
TableIndex,
|
|
7
8
|
FormatToReturnValues,
|
|
8
9
|
ReturnValues,
|
|
@@ -1369,10 +1370,6 @@ class Entity {
|
|
|
1369
1370
|
return this.config.table;
|
|
1370
1371
|
}
|
|
1371
1372
|
|
|
1372
|
-
getTableName() {
|
|
1373
|
-
return this.config.table;
|
|
1374
|
-
}
|
|
1375
|
-
|
|
1376
1373
|
_chain(state, clauses, clause) {
|
|
1377
1374
|
let current = {};
|
|
1378
1375
|
for (let child of clause.children) {
|
|
@@ -3519,6 +3516,7 @@ class Entity {
|
|
|
3519
3516
|
modelVersion,
|
|
3520
3517
|
isClustered,
|
|
3521
3518
|
schema,
|
|
3519
|
+
prefixes = {},
|
|
3522
3520
|
}) {
|
|
3523
3521
|
/*
|
|
3524
3522
|
Collections will prefix the sort key so they can be queried with
|
|
@@ -3527,7 +3525,6 @@ class Entity {
|
|
|
3527
3525
|
of a customKey AND a collection, the collection is ignored to favor
|
|
3528
3526
|
the custom key.
|
|
3529
3527
|
*/
|
|
3530
|
-
|
|
3531
3528
|
let keys = {
|
|
3532
3529
|
pk: {
|
|
3533
3530
|
prefix: "",
|
|
@@ -3545,6 +3542,28 @@ class Entity {
|
|
|
3545
3542
|
},
|
|
3546
3543
|
};
|
|
3547
3544
|
|
|
3545
|
+
let previouslyDefinedPk = null;
|
|
3546
|
+
let previouslyDefinedSk = null;
|
|
3547
|
+
for (const [indexName, definition] of Object.entries(prefixes)) {
|
|
3548
|
+
if (definition.pk.field === tableIndex.pk.field) {
|
|
3549
|
+
previouslyDefinedPk = { indexName, definition: definition.pk };
|
|
3550
|
+
} else if (definition.sk && definition.sk.field === tableIndex.pk.field) {
|
|
3551
|
+
previouslyDefinedPk = { indexName, definition: definition.sk };
|
|
3552
|
+
}
|
|
3553
|
+
|
|
3554
|
+
if (tableIndex.sk) {
|
|
3555
|
+
if (definition.pk.field === tableIndex.sk.field) {
|
|
3556
|
+
previouslyDefinedSk = { indexName, definition: definition.pk };
|
|
3557
|
+
} else if (definition.sk && definition.sk.field === tableIndex.sk.field) {
|
|
3558
|
+
previouslyDefinedSk = { indexName, definition: definition.sk };
|
|
3559
|
+
}
|
|
3560
|
+
}
|
|
3561
|
+
|
|
3562
|
+
if (previouslyDefinedPk && (previouslyDefinedSk || !tableIndex.sk)) {
|
|
3563
|
+
break;
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
|
|
3548
3567
|
let pk = `$${service}`;
|
|
3549
3568
|
let sk = "";
|
|
3550
3569
|
let entityKeys = "";
|
|
@@ -3636,6 +3655,37 @@ class Entity {
|
|
|
3636
3655
|
}
|
|
3637
3656
|
}
|
|
3638
3657
|
|
|
3658
|
+
if (previouslyDefinedPk) {
|
|
3659
|
+
const casingMatch = u.toKeyCasingOption(keys.pk.casing) === u.toKeyCasingOption(previouslyDefinedPk.definition.casing);
|
|
3660
|
+
if (!casingMatch) {
|
|
3661
|
+
throw new e.ElectroError(
|
|
3662
|
+
e.ErrorCodes.IncompatibleKeyCasing,
|
|
3663
|
+
`Partition Key (pk) on Access Pattern '${u.formatIndexNameForDisplay(
|
|
3664
|
+
tableIndex.index,
|
|
3665
|
+
)}' is defined with the casing ${keys.pk.casing}, but the accessPattern '${u.formatIndexNameForDisplay(
|
|
3666
|
+
previouslyDefinedPk.indexName,
|
|
3667
|
+
)}' defines the same index field with the ${previouslyDefinedPk.definition.casing === DefaultKeyCasing ? '(default)' : ''} casing ${previouslyDefinedPk.definition.casing}. Key fields must have the same casing definitions across all indexes they are involved with.`,
|
|
3668
|
+
);
|
|
3669
|
+
}
|
|
3670
|
+
|
|
3671
|
+
keys.pk = previouslyDefinedPk.definition;
|
|
3672
|
+
}
|
|
3673
|
+
|
|
3674
|
+
if (previouslyDefinedSk) {
|
|
3675
|
+
const casingMatch = u.toKeyCasingOption(keys.sk.casing) === u.toKeyCasingOption(previouslyDefinedSk.definition.casing);
|
|
3676
|
+
if (!casingMatch) {
|
|
3677
|
+
throw new e.ElectroError(
|
|
3678
|
+
e.ErrorCodes.IncompatibleKeyCasing,
|
|
3679
|
+
`Sort Key (sk) on Access Pattern '${u.formatIndexNameForDisplay(
|
|
3680
|
+
tableIndex.index,
|
|
3681
|
+
)}' is defined with the casing ${keys.sk.casing}, but the accessPattern '${u.formatIndexNameForDisplay(
|
|
3682
|
+
previouslyDefinedSk.indexName,
|
|
3683
|
+
)}' defines the same index field with the ${previouslyDefinedSk.definition.casing === DefaultKeyCasing ? '(default)' : ''} casing ${previouslyDefinedSk.definition.casing}. Key fields must have the same casing definitions across all indexes they are involved with.`,
|
|
3684
|
+
);
|
|
3685
|
+
}
|
|
3686
|
+
keys.sk = previouslyDefinedSk.definition;
|
|
3687
|
+
}
|
|
3688
|
+
|
|
3639
3689
|
return keys;
|
|
3640
3690
|
}
|
|
3641
3691
|
|
|
@@ -3772,17 +3822,21 @@ class Entity {
|
|
|
3772
3822
|
if (!skAttributes.length) {
|
|
3773
3823
|
skAttributes.push({});
|
|
3774
3824
|
}
|
|
3825
|
+
|
|
3775
3826
|
let facets = this.model.facets.byIndex[index];
|
|
3827
|
+
|
|
3776
3828
|
let prefixes = this.model.prefixes[index];
|
|
3777
3829
|
if (!prefixes) {
|
|
3778
3830
|
throw new Error(`Invalid index: ${index}`);
|
|
3779
3831
|
}
|
|
3832
|
+
|
|
3780
3833
|
let pk = this._makeKey(
|
|
3781
3834
|
prefixes.pk,
|
|
3782
3835
|
facets.pk,
|
|
3783
3836
|
pkAttributes,
|
|
3784
3837
|
this.model.facets.labels[index].pk,
|
|
3785
3838
|
);
|
|
3839
|
+
|
|
3786
3840
|
let sk = [];
|
|
3787
3841
|
let fulfilled = false;
|
|
3788
3842
|
if (this.model.lookup.indexHasSortKeys[index]) {
|
|
@@ -3805,6 +3859,7 @@ class Entity {
|
|
|
3805
3859
|
}
|
|
3806
3860
|
}
|
|
3807
3861
|
}
|
|
3862
|
+
|
|
3808
3863
|
return {
|
|
3809
3864
|
pk: pk.key,
|
|
3810
3865
|
sk,
|
|
@@ -3866,8 +3921,9 @@ class Entity {
|
|
|
3866
3921
|
for (let i = 0; i < labels.length; i++) {
|
|
3867
3922
|
const { name, label } = labels[i];
|
|
3868
3923
|
const attribute = this.model.schema.getAttribute(name);
|
|
3924
|
+
|
|
3869
3925
|
let value = supplied[name];
|
|
3870
|
-
if (
|
|
3926
|
+
if (value === undefined && excludeLabelTail) {
|
|
3871
3927
|
break;
|
|
3872
3928
|
}
|
|
3873
3929
|
|
|
@@ -3880,11 +3936,14 @@ class Entity {
|
|
|
3880
3936
|
} else {
|
|
3881
3937
|
key = `${key}#${label}_`;
|
|
3882
3938
|
}
|
|
3939
|
+
|
|
3883
3940
|
// Undefined facet value means we cant build any more of the key
|
|
3884
3941
|
if (supplied[name] === undefined) {
|
|
3885
3942
|
break;
|
|
3886
3943
|
}
|
|
3944
|
+
|
|
3887
3945
|
foundCount++;
|
|
3946
|
+
|
|
3888
3947
|
key = `${key}${value}`;
|
|
3889
3948
|
}
|
|
3890
3949
|
|
|
@@ -4248,6 +4307,7 @@ class Entity {
|
|
|
4248
4307
|
pk: false,
|
|
4249
4308
|
sk: false,
|
|
4250
4309
|
};
|
|
4310
|
+
|
|
4251
4311
|
const pkCasing =
|
|
4252
4312
|
KeyCasing[index.pk.casing] === undefined
|
|
4253
4313
|
? KeyCasing.default
|
|
@@ -4462,23 +4522,6 @@ class Entity {
|
|
|
4462
4522
|
}' as the field name for both the PK and SK. Fields used for indexes need to be unique to avoid conflicts.`,
|
|
4463
4523
|
);
|
|
4464
4524
|
} else if (seenIndexFields[sk.field] !== undefined) {
|
|
4465
|
-
const isAlsoDefinedAsPK = seenIndexFields[sk.field].find(
|
|
4466
|
-
(field) => field.type === "pk",
|
|
4467
|
-
);
|
|
4468
|
-
|
|
4469
|
-
if (isAlsoDefinedAsPK && !sk.isCustom) {
|
|
4470
|
-
throw new e.ElectroError(
|
|
4471
|
-
e.ErrorCodes.InconsistentIndexDefinition,
|
|
4472
|
-
`The Sort Key (sk) on Access Pattern '${u.formatIndexNameForDisplay(
|
|
4473
|
-
accessPattern,
|
|
4474
|
-
)}' references the field '${
|
|
4475
|
-
pk.field
|
|
4476
|
-
}' which is already referenced by the Access Pattern(s) '${u.formatIndexNameForDisplay(
|
|
4477
|
-
isAlsoDefinedAsPK.accessPattern,
|
|
4478
|
-
)}' as a Partition Key. Fields mapped to Partition Keys cannot be also mapped to Sort Keys unless their format is defined with a 'template'.`,
|
|
4479
|
-
);
|
|
4480
|
-
}
|
|
4481
|
-
|
|
4482
4525
|
const definition = Object.values(facets.byField[sk.field]).find(
|
|
4483
4526
|
(definition) => definition.index !== indexName,
|
|
4484
4527
|
);
|
|
@@ -4640,6 +4683,7 @@ class Entity {
|
|
|
4640
4683
|
modelVersion,
|
|
4641
4684
|
isClustered: clusteredIndexes.has(accessPattern),
|
|
4642
4685
|
schema,
|
|
4686
|
+
prefixes,
|
|
4643
4687
|
});
|
|
4644
4688
|
}
|
|
4645
4689
|
return prefixes;
|
package/src/errors.js
CHANGED
|
@@ -127,6 +127,12 @@ const ErrorCodes = {
|
|
|
127
127
|
name: "InvalidIndexCompositeWithAttributeName",
|
|
128
128
|
sym: ErrorCode,
|
|
129
129
|
},
|
|
130
|
+
IncompatibleKeyCasing: {
|
|
131
|
+
code: 1020,
|
|
132
|
+
section: "incompatible-key-casing",
|
|
133
|
+
name: "IncompatibleKeyCasing",
|
|
134
|
+
sym: ErrorCode,
|
|
135
|
+
},
|
|
130
136
|
InvalidListenerProvided: {
|
|
131
137
|
code: 1020,
|
|
132
138
|
section: "invalid-listener-provided",
|
package/src/types.js
CHANGED
|
@@ -295,6 +295,8 @@ const KeyCasing = {
|
|
|
295
295
|
default: "default",
|
|
296
296
|
};
|
|
297
297
|
|
|
298
|
+
const DefaultKeyCasing = KeyCasing.lower;
|
|
299
|
+
|
|
298
300
|
const EventSubscriptionTypes = ["query", "results"];
|
|
299
301
|
|
|
300
302
|
const TerminalOperation = {
|
|
@@ -378,4 +380,5 @@ module.exports = {
|
|
|
378
380
|
TransactionMethods,
|
|
379
381
|
UpsertOperations,
|
|
380
382
|
BatchWriteTypes,
|
|
383
|
+
DefaultKeyCasing,
|
|
381
384
|
};
|
package/src/util.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const t = require("./types");
|
|
2
|
-
const e = require("./errors");
|
|
3
2
|
const v = require("./validations");
|
|
4
3
|
|
|
5
4
|
function parseJSONPath(path = "") {
|
|
@@ -105,8 +104,24 @@ function formatStringCasing(str, casing, defaultCase) {
|
|
|
105
104
|
}
|
|
106
105
|
}
|
|
107
106
|
|
|
107
|
+
function toKeyCasingOption(casing) {
|
|
108
|
+
switch(casing) {
|
|
109
|
+
case t.KeyCasing.upper:
|
|
110
|
+
return t.KeyCasing.upper;
|
|
111
|
+
case t.KeyCasing.none:
|
|
112
|
+
return t.KeyCasing.none;
|
|
113
|
+
case t.KeyCasing.lower:
|
|
114
|
+
return t.KeyCasing.lower;
|
|
115
|
+
case t.KeyCasing.default:
|
|
116
|
+
case undefined:
|
|
117
|
+
return t.DefaultKeyCasing;
|
|
118
|
+
default:
|
|
119
|
+
throw new Error(`Unknown casing option: ${casing}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
108
123
|
function formatKeyCasing(str, casing) {
|
|
109
|
-
return formatStringCasing(str, casing, t.
|
|
124
|
+
return formatStringCasing(str, casing, t.DefaultKeyCasing);
|
|
110
125
|
}
|
|
111
126
|
|
|
112
127
|
function formatAttributeCasing(str, casing) {
|
|
@@ -268,6 +283,7 @@ module.exports = {
|
|
|
268
283
|
getModelVersion,
|
|
269
284
|
formatKeyCasing,
|
|
270
285
|
cursorFormatter,
|
|
286
|
+
toKeyCasingOption,
|
|
271
287
|
genericizeJSONPath,
|
|
272
288
|
commaSeparatedString,
|
|
273
289
|
formatAttributeCasing,
|