electrodb 2.10.1 → 2.10.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "electrodb",
3
- "version": "2.10.1",
3
+ "version": "2.10.3",
4
4
  "description": "A library to more easily create and interact with multiple entities and heretical relationships in dynamodb",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -8,7 +8,7 @@
8
8
  "build:browser": "browserify playground/browser.js -o playground/bundle.js",
9
9
  "test": "./test.sh",
10
10
  "test:ci": "npm install && npm test",
11
- "test:run": "npm run test:format && npm run test:types && npm run test:init && npm run test:unit",
11
+ "test:run": "npm run test:types && npm run test:init && npm run test:unit",
12
12
  "test:init": "node ./test/init.js",
13
13
  "test:unit": "mocha -r ts-node/register ./test/**.spec.*",
14
14
  "test:types": "tsd",
@@ -16,15 +16,16 @@
16
16
  "coverage": "npm run test:init && nyc npm run test:unit && nyc report --reporter=text-lcov | coveralls",
17
17
  "coverage:local:coveralls": "npm run test:init && nyc npm run test:unit && nyc report --reporter=text-lcov | coveralls",
18
18
  "coverage:local:html": "npm run test:init && nyc npm run test:unit && nyc report --reporter=html",
19
- "ddb:start": "docker compose up -d",
19
+ "ddb:start": "docker compose up -d dynamodb",
20
20
  "ddb:load": "docker compose exec electro npm run test:init",
21
21
  "ddb:stop": "docker compose stop",
22
22
  "examples:load:library": "npm run ddb:start && npm run local:init && ts-node ./examples/library/load.ts",
23
23
  "examples:query:library": "npm run ddb:start && npm run local:init && ts-node ./examples/library/query.ts",
24
- "examples:load:taskmanager": "npm run ddb:start && npm run local:init && ts-node ./examples/taskmanager/load.ts",
25
- "examples:query:taskmanager": "npm run ddb:start && npm run local:init && ts-node ./examples/taskmanager/query.ts",
26
- "examples:load:versioncontrol": "npm run ddb:start && npm run local:init && ts-node ./examples/versioncontrol/load.ts",
27
- "examples:query:versioncontrol": "npm run ddb:start && npm run local:init && ts-node ./examples/versioncontrol/query.ts",
24
+ "examples:load:taskmanager": "npm run ddb:start && npm run local:init && ts-node ./examples/taskManager/load.ts",
25
+ "examples:query:taskmanager": "npm run ddb:start && npm run local:init && ts-node ./examples/taskManager/query.ts",
26
+ "examples:load:versioncontrol": "npm run ddb:start && npm run local:init && ts-node ./examples/versionControl/load.ts",
27
+ "examples:query:versioncontrol": "npm run ddb:start && npm run local:init && ts-node ./examples/versionControl/query.ts",
28
+ "examples:provisiontable": "npm run ddb:start && npm run local:init && ts-node ./examples/provisionTable",
28
29
  "examples:locks": "npm run ddb:start && npm run local:init && ts-node ./examples/locks",
29
30
  "local:init": "LOCAL_DYNAMO_ENDPOINT='http://localhost:8000' npm run test:init",
30
31
  "local:start": "npm run ddb:start && npm run local:init",
@@ -64,10 +65,12 @@
64
65
  "prettier": "^3.0.3",
65
66
  "prettier-plugin-astro": "^0.12.0",
66
67
  "source-map-support": "^0.5.19",
68
+ "sst": "^2.28.0",
67
69
  "ts-node": "^10.9.1",
68
70
  "tsd": "^0.28.1",
69
71
  "typescript": "^5.2.2",
70
- "uuid": "7.0.1"
72
+ "uuid": "7.0.1",
73
+ "aws-cdk-lib": "^2.100.0"
71
74
  },
72
75
  "keywords": [
73
76
  "electrodb",
package/src/clauses.js CHANGED
@@ -973,12 +973,21 @@ let clauses = {
973
973
  sk,
974
974
  pk,
975
975
  );
976
+
977
+ const accessPattern =
978
+ entity.model.translations.indexes.fromIndexToAccessPattern[
979
+ state.query.index
980
+ ];
981
+
982
+ if (!entity.model.indexes[accessPattern].sk.isFieldRef) {
983
+ state.filterProperties(FilterOperationNames.lte, endingSk.composites);
984
+ }
985
+
976
986
  return state
977
987
  .setType(QueryTypes.and)
978
988
  .setSK(endingSk.composites)
979
989
  .setType(QueryTypes.between)
980
- .setSK(startingSk.composites)
981
- .filterProperties(FilterOperationNames.lte, endingSk.composites);
990
+ .setSK(startingSk.composites);
982
991
  } catch (err) {
983
992
  state.setError(err);
984
993
  return state;
@@ -1019,9 +1028,14 @@ let clauses = {
1019
1028
  pk,
1020
1029
  );
1021
1030
  state.setSK(composites);
1022
- state.filterProperties(FilterOperationNames.gt, {
1023
- ...composites,
1024
- });
1031
+ const accessPattern =
1032
+ entity.model.translations.indexes.fromIndexToAccessPattern[
1033
+ state.query.index
1034
+ ];
1035
+
1036
+ if (!entity.model.indexes[accessPattern].sk.isFieldRef) {
1037
+ state.filterProperties(FilterOperationNames.gt, composites);
1038
+ }
1025
1039
  });
1026
1040
  } catch (err) {
1027
1041
  state.setError(err);
@@ -1086,9 +1100,13 @@ let clauses = {
1086
1100
  pk,
1087
1101
  );
1088
1102
  state.setSK(composites);
1089
- state.filterProperties(FilterOperationNames.lte, {
1090
- ...composites,
1091
- });
1103
+ const accessPattern =
1104
+ entity.model.translations.indexes.fromIndexToAccessPattern[
1105
+ state.query.index
1106
+ ];
1107
+ if (!entity.model.indexes[accessPattern].sk.isFieldRef) {
1108
+ state.filterProperties(FilterOperationNames.lte, composites);
1109
+ }
1092
1110
  });
1093
1111
  } catch (err) {
1094
1112
  state.setError(err);
package/src/entity.js CHANGED
@@ -184,36 +184,6 @@ class Entity {
184
184
  return this.model.version;
185
185
  }
186
186
 
187
- // ownsItem(item) {
188
- // return (
189
- // item &&
190
- // this.getName() === item[this.identifiers.entity] &&
191
- // this.getVersion() === item[this.identifiers.version] &&
192
- // validations.isStringHasLength(item[this.identifiers.entity]) &&
193
- // validations.isStringHasLength(item[this.identifiers.version])
194
- // ) || !!this.ownsKeys(item)
195
- // }
196
-
197
- // ownsKeys({keys = {}}) {
198
- // let {pk, sk} = this.model.prefixes[TableIndex];
199
- // let hasSK = this.model.lookup.indexHasSortKeys[TableIndex];
200
- // let pkMatch = typeof keys[pk.field] === "string" && keys[pk.field].startsWith(pk.prefix);
201
- // let skMatch = pkMatch && !hasSK;
202
- // if (pkMatch && hasSK) {
203
- // skMatch = typeof keys[sk.field] === "string" && keys[sk.field].startsWith(sk.prefix);
204
- // }
205
- //
206
- // return (pkMatch && skMatch &&
207
- // this._formatKeysToItem(TableIndex, key) !== null);
208
- // }
209
-
210
- // ownsCursor({ cursor }) {
211
- // if (typeof cursor === 'string') {
212
- // cursor = u.cursorFormatter.deserialize(cursor);
213
- // }
214
- // return this.ownsKeys({ keys: cursor });
215
- // }
216
-
217
187
  ownsItem(item) {
218
188
  return (
219
189
  item &&
@@ -246,13 +216,18 @@ class Entity {
246
216
  ownsKeys(key = {}) {
247
217
  let { pk, sk } = this.model.prefixes[TableIndex];
248
218
  let hasSK = this.model.lookup.indexHasSortKeys[TableIndex];
249
- let pkMatch =
250
- typeof key[pk.field] === "string" && key[pk.field].startsWith(pk.prefix);
219
+ const typeofPkProvided = typeof key[pk.field];
220
+ const pkPrefixMatch =
221
+ typeofPkProvided === "string" && key[pk.field].startsWith(pk.prefix);
222
+ const isNumericPk = typeofPkProvided === "number" && pk.cast === "number";
223
+ let pkMatch = pkPrefixMatch || isNumericPk;
251
224
  let skMatch = pkMatch && !hasSK;
252
225
  if (pkMatch && hasSK) {
253
- skMatch =
254
- typeof key[sk.field] === "string" &&
255
- key[sk.field].startsWith(sk.prefix);
226
+ const typeofSkProvided = typeof key[sk.field];
227
+ const skPrefixMatch =
228
+ typeofSkProvided === "string" && key[sk.field].startsWith(sk.prefix);
229
+ const isNumericSk = typeofSkProvided === "number" && sk.cast === "number";
230
+ skMatch = skPrefixMatch || isNumericSk;
256
231
  }
257
232
 
258
233
  return (
@@ -1481,7 +1456,7 @@ class Entity {
1481
1456
  }
1482
1457
 
1483
1458
  _createKeyDeconstructor(prefixes = {}, labels = [], attributes = {}) {
1484
- let { prefix, isCustom, postfix } = prefixes;
1459
+ let { prefix, isCustom, postfix, cast } = prefixes;
1485
1460
  let names = [];
1486
1461
  let types = [];
1487
1462
  let pattern = `^${this._regexpEscape(prefix || "")}`;
@@ -1512,16 +1487,19 @@ class Entity {
1512
1487
  let regex = new RegExp(pattern, "i");
1513
1488
 
1514
1489
  return ({ key } = {}) => {
1515
- if (!["string", "number"].includes(typeof key)) {
1490
+ const typeofKey = typeof key;
1491
+ if (!["string", "number"].includes(typeofKey)) {
1516
1492
  return null;
1517
1493
  }
1518
1494
  key = `${key}`;
1495
+ const isNumeric =
1496
+ cast === CastKeyOptions.number && typeofKey === "number";
1519
1497
  let match = key.match(regex);
1520
1498
  let results = {};
1521
- if (match) {
1499
+ if (match || isNumeric) {
1522
1500
  for (let i = 0; i < names.length; i++) {
1523
- let key = names[i];
1524
- let value = match[i + 1];
1501
+ let keyName = names[i];
1502
+ let value = isNumeric ? key : match[i + 1];
1525
1503
  let type = types[i];
1526
1504
  switch (type) {
1527
1505
  case "number": {
@@ -1533,8 +1511,8 @@ class Entity {
1533
1511
  break;
1534
1512
  }
1535
1513
  }
1536
- if (key && value !== undefined) {
1537
- results[key] = value;
1514
+ if (keyName && value !== undefined) {
1515
+ results[keyName] = value;
1538
1516
  }
1539
1517
  }
1540
1518
  } else {
@@ -1565,7 +1543,7 @@ class Entity {
1565
1543
  let skComposites = {};
1566
1544
  if (indexHasSortKey) {
1567
1545
  const sk = keys[skName];
1568
- if (!sk) {
1546
+ if (sk === undefined) {
1569
1547
  return null;
1570
1548
  }
1571
1549
  skComposites = deconstructors.sk({ key: sk });
@@ -2724,8 +2702,9 @@ class Entity {
2724
2702
 
2725
2703
  if (
2726
2704
  this.model.lookup.indexHasSortKeys[index] &&
2727
- typeof keyExpressions.ExpressionAttributeValues[":sk1"] === "string" &&
2728
- keyExpressions.ExpressionAttributeValues[":sk1"].length > 0
2705
+ (typeof keyExpressions.ExpressionAttributeValues[":sk1"] === "number" ||
2706
+ (typeof keyExpressions.ExpressionAttributeValues[":sk1"] === "string" &&
2707
+ keyExpressions.ExpressionAttributeValues[":sk1"].length > 0))
2729
2708
  ) {
2730
2709
  if (type === QueryTypes.is) {
2731
2710
  KeyConditionExpression = `${KeyConditionExpression} and #sk1 = :sk1`;
@@ -2834,17 +2813,18 @@ class Entity {
2834
2813
  filter = {},
2835
2814
  indexKeys = {},
2836
2815
  ) {
2837
- const { pk, fulfilled } = indexKeys;
2816
+ const { pk } = indexKeys;
2838
2817
  const sk = indexKeys.sk[0];
2839
- let operator = PartialComparisons[comparison];
2840
- // fulfilled
2841
- // ? Comparisons[comparison]
2842
- // : PartialComparisons[comparison];
2818
+
2819
+ let operator =
2820
+ typeof sk === "number"
2821
+ ? Comparisons[comparison]
2822
+ : PartialComparisons[comparison];
2843
2823
 
2844
2824
  if (!operator) {
2845
2825
  throw new Error(
2846
2826
  `Unexpected comparison operator "${comparison}", expected ${u.commaSeparatedString(
2847
- Object.values(Comparisons),
2827
+ Object.values(PartialComparisons),
2848
2828
  )}`,
2849
2829
  );
2850
2830
  }
@@ -2853,6 +2833,7 @@ class Entity {
2853
2833
  pk,
2854
2834
  sk,
2855
2835
  );
2836
+
2856
2837
  let params = {
2857
2838
  TableName: this.getTableName(),
2858
2839
  ExpressionAttributeNames: this._mergeExpressionsAttributes(
@@ -4409,6 +4390,7 @@ class Entity {
4409
4390
  getClient: () => this.client,
4410
4391
  isRoot: true,
4411
4392
  });
4393
+
4412
4394
  let filters = this._normalizeFilters(model.filters);
4413
4395
  // todo: consider a rename
4414
4396
  let prefixes = this._normalizeKeyFixings({
@@ -4445,6 +4427,12 @@ class Entity {
4445
4427
  labels,
4446
4428
  attributes,
4447
4429
  );
4430
+ for (let attributeName in schema.attributes) {
4431
+ const { field } = schema.attributes[attributeName];
4432
+ if (indexes[accessPattern][keyType].field === field) {
4433
+ indexes[accessPattern][keyType].isFieldRef = true;
4434
+ }
4435
+ }
4448
4436
  }
4449
4437
  }
4450
4438
 
package/tsconfig.json CHANGED
@@ -7,6 +7,7 @@
7
7
  "target": "ESNext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
8
8
  "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
9
9
  "lib": ["ESNext"], /* Specify library files to be included in the compilation. */
10
+ "resolveJsonModule": true,
10
11
  // "allowJs": true, /* Allow javascript files to be compiled. */
11
12
  // "checkJs": true, /* Report errors in .js files. */
12
13
  // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */