electrodb 1.11.1 → 2.0.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.js CHANGED
@@ -1,4 +1,5 @@
1
1
  const { Entity } = require("./src/entity");
2
2
  const { Service } = require("./src/service");
3
+ const { createCustomAttribute } = require('./src/schema');
3
4
 
4
- module.exports = { Entity, Service };
5
+ module.exports = { Entity, Service, createCustomAttribute };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "electrodb",
3
- "version": "1.11.1",
3
+ "version": "2.0.0",
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": {
package/src/clauses.js CHANGED
@@ -50,11 +50,11 @@ let clauses = {
50
50
  return state;
51
51
  }
52
52
  },
53
- children: ["params", "go", "page"],
53
+ children: ["params", "go"],
54
54
  },
55
55
  scan: {
56
56
  name: "scan",
57
- action(entity, state) {
57
+ action(entity, state, config) {
58
58
  if (state.getError() !== null) {
59
59
  return state;
60
60
  }
@@ -65,7 +65,7 @@ let clauses = {
65
65
  return state;
66
66
  }
67
67
  },
68
- children: ["params", "go", "page"],
68
+ children: ["params", "go"],
69
69
  },
70
70
  get: {
71
71
  name: "get",
@@ -397,6 +397,7 @@ let clauses = {
397
397
  return state;
398
398
  }
399
399
  try {
400
+ state.addOption('_isPagination', true);
400
401
  const attributes = state.getCompositeAttributes();
401
402
  return state
402
403
  .setMethod(MethodTypes.query)
@@ -410,7 +411,7 @@ let clauses = {
410
411
  return state;
411
412
  }
412
413
  },
413
- children: ["between", "gte", "gt", "lte", "lt", "begins", "params", "go", "page"],
414
+ children: ["between", "gte", "gt", "lte", "lt", "begins", "params", "go"],
414
415
  },
415
416
  between: {
416
417
  name: "between",
@@ -430,7 +431,7 @@ let clauses = {
430
431
  return state;
431
432
  }
432
433
  },
433
- children: ["go", "params", "page"],
434
+ children: ["go", "params"],
434
435
  },
435
436
  begins: {
436
437
  name: "begins",
@@ -450,7 +451,7 @@ let clauses = {
450
451
  return state;
451
452
  }
452
453
  },
453
- children: ["go", "params", "page"],
454
+ children: ["go", "params"],
454
455
  },
455
456
  gt: {
456
457
  name: "gt",
@@ -470,7 +471,7 @@ let clauses = {
470
471
  return state;
471
472
  }
472
473
  },
473
- children: ["go", "params", "page"],
474
+ children: ["go", "params"],
474
475
  },
475
476
  gte: {
476
477
  name: "gte",
@@ -490,7 +491,7 @@ let clauses = {
490
491
  return state;
491
492
  }
492
493
  },
493
- children: ["go", "params", "page"],
494
+ children: ["go", "params"],
494
495
  },
495
496
  lt: {
496
497
  name: "lt",
@@ -509,7 +510,7 @@ let clauses = {
509
510
  return state;
510
511
  }
511
512
  },
512
- children: ["go", "params", "page"],
513
+ children: ["go", "params"],
513
514
  },
514
515
  lte: {
515
516
  name: "lte",
@@ -528,7 +529,7 @@ let clauses = {
528
529
  return state;
529
530
  }
530
531
  },
531
- children: ["go", "params", "page"],
532
+ children: ["go", "params"],
532
533
  },
533
534
  params: {
534
535
  name: "params",
@@ -589,28 +590,6 @@ let clauses = {
589
590
  },
590
591
  children: [],
591
592
  },
592
- page: {
593
- name: "page",
594
- action(entity, state, page = null, options = {}) {
595
- if (state.getError() !== null) {
596
- return Promise.reject(state.error);
597
- }
598
- try {
599
- if (entity.client === undefined) {
600
- throw new e.ElectroError(e.ErrorCodes.NoClientDefined, "No client defined on model");
601
- }
602
- options.page = page;
603
- options._isPagination = true;
604
- options.terminalOperation = TerminalOperation.page;
605
- let params = clauses.params.action(entity, state, options);
606
- let {config} = entity._applyParameterOptions({}, state.getOptions(), options);
607
- return entity.go(state.getMethod(), params, config);
608
- } catch(err) {
609
- return Promise.reject(err);
610
- }
611
- },
612
- children: []
613
- },
614
593
  };
615
594
 
616
595
  class ChainState {
@@ -677,6 +656,10 @@ class ChainState {
677
656
  return this.query.options;
678
657
  }
679
658
 
659
+ addOption(key, value) {
660
+ this.query.options[key] = value;
661
+ }
662
+
680
663
  _appendProvided(type, attributes) {
681
664
  const newAttributes = Object.keys(attributes).map(attribute => {
682
665
  return {
package/src/entity.js CHANGED
@@ -1,6 +1,27 @@
1
1
  "use strict";
2
2
  const { Schema } = require("./schema");
3
- const { KeyCasing, TableIndex, FormatToReturnValues, ReturnValues, EntityVersions, ItemOperations, UnprocessedTypes, Pager, ElectroInstance, KeyTypes, QueryTypes, MethodTypes, Comparisons, ExpressionTypes, ModelVersions, ElectroInstanceTypes, MaxBatchItems, TerminalOperation } = require("./types");
3
+ const { AllPages,
4
+ KeyCasing,
5
+ TableIndex,
6
+ FormatToReturnValues,
7
+ ReturnValues,
8
+ EntityVersions,
9
+ ItemOperations,
10
+ UnprocessedTypes,
11
+ Pager,
12
+ ElectroInstance,
13
+ KeyTypes,
14
+ QueryTypes,
15
+ MethodTypes,
16
+ Comparisons,
17
+ ExpressionTypes,
18
+ ModelVersions,
19
+ ElectroInstanceTypes,
20
+ MaxBatchItems,
21
+ TerminalOperation,
22
+ ResultOrderOption,
23
+ ResultOrderParam
24
+ } = require("./types");
4
25
  const { FilterFactory } = require("./filters");
5
26
  const { FilterOperations } = require("./operations");
6
27
  const { WhereFactory } = require("./where");
@@ -10,6 +31,7 @@ const validations = require("./validations");
10
31
  const c = require('./client');
11
32
  const u = require("./util");
12
33
  const e = require("./errors");
34
+ const { validate } = require("jsonschema");
13
35
 
14
36
  class Entity {
15
37
  constructor(model, config = {}) {
@@ -30,7 +52,7 @@ class Entity {
30
52
  this._whereBuilder = new WhereFactory(this.model.schema.attributes, FilterOperations);
31
53
  this._clausesWithFilters = this._filterBuilder.injectFilterClauses(clauses, this.model.filters);
32
54
  this._clausesWithFilters = this._whereBuilder.injectWhereClauses(this._clausesWithFilters);
33
- this.scan = this._makeChain(TableIndex, this._clausesWithFilters, clauses.index).scan();
55
+ this.scan = this._makeChain(TableIndex, this._clausesWithFilters, clauses.index, {_isPagination: true}).scan();
34
56
  this.query = {};
35
57
  for (let accessPattern in this.model.indexes) {
36
58
  let index = this.model.indexes[accessPattern].index;
@@ -84,14 +106,30 @@ class Entity {
84
106
  return pkMatch;
85
107
  }
86
108
 
109
+ ownsCursor(cursor) {
110
+ if (typeof cursor === 'string') {
111
+ cursor = u.cursorFormatter.deserialize(cursor);
112
+ }
113
+ return this.ownsLastEvaluatedKey(cursor);
114
+ }
115
+
116
+ serializeCursor(key) {
117
+ return u.cursorFormatter.serialize(key);
118
+ }
119
+
120
+ deserializeCursor(cursor) {
121
+ return u.cursorFormatter.deserialize(cursor);
122
+ }
123
+
124
+ /** @depricated pagers no longer exist, use the new cursor api */
87
125
  ownsPager(pager, index = TableIndex) {
88
126
  if (pager === null) {
89
127
  return false;
90
128
  }
91
- let tableIndexFacets = this.model.facets.byIndex[TableIndex];
129
+ let tableIndexFacets = this.model.facets.byIndex[index];
92
130
  // todo: is the fact it doesn't use the provided index a bug?
93
131
  // feels like collections may have played a roll into why this is this way
94
- let indexFacets = this.model.facets.byIndex[TableIndex];
132
+ let indexFacets = this.model.facets.byIndex[index];
95
133
 
96
134
  // Unknown index
97
135
  if (tableIndexFacets === undefined || indexFacets === undefined) {
@@ -114,9 +152,10 @@ class Entity {
114
152
  }
115
153
 
116
154
  match(facets = {}) {
117
- let match = this._findBestIndexKeyMatch(facets);
155
+ const options = { _isPagination: true };
156
+ const match = this._findBestIndexKeyMatch(facets);
118
157
  if (match.shouldScan) {
119
- return this._makeChain(TableIndex, this._clausesWithFilters, clauses.index)
158
+ return this._makeChain(TableIndex, this._clausesWithFilters, clauses.index, options)
120
159
  .scan()
121
160
  .filter(attr => {
122
161
  let eqFilters = [];
@@ -128,7 +167,7 @@ class Entity {
128
167
  return eqFilters.join(" AND ");
129
168
  });
130
169
  } else {
131
- return this._makeChain(match.index, this._clausesWithFilters, clauses.index)
170
+ return this._makeChain(match.index, this._clausesWithFilters, clauses.index, options)
132
171
  .query(facets)
133
172
  .filter(attr => {
134
173
  let eqFilters = [];
@@ -143,16 +182,17 @@ class Entity {
143
182
  }
144
183
 
145
184
  find(facets = {}) {
146
- let match = this._findBestIndexKeyMatch(facets);
185
+ const options = { _isPagination: true };
186
+ const match = this._findBestIndexKeyMatch(facets);
147
187
  if (match.shouldScan) {
148
- return this._makeChain(TableIndex, this._clausesWithFilters, clauses.index).scan();
188
+ return this._makeChain(TableIndex, this._clausesWithFilters, clauses.index, options).scan();
149
189
  } else {
150
- return this._makeChain(match.index, this._clausesWithFilters, clauses.index).query(facets);
190
+ return this._makeChain(match.index, this._clausesWithFilters, clauses.index, options).query(facets);
151
191
  }
152
192
  }
153
193
 
154
194
  collection(collection = "", clauses = {}, facets = {}, {expressions = {}, parse} = {}) {
155
- let options = {
195
+ const options = {
156
196
  parse,
157
197
  expressions: {
158
198
  names: expressions.names || {},
@@ -239,7 +279,8 @@ class Entity {
239
279
  case MethodTypes.batchGet:
240
280
  return await this.executeBulkGet(parameters, config);
241
281
  case MethodTypes.query:
242
- return await this.executeQuery(parameters, config)
282
+ case MethodTypes.scan:
283
+ return await this.executeQuery(method, parameters, config);
243
284
  default:
244
285
  return await this.executeOperation(method, parameters, config);
245
286
  }
@@ -305,20 +346,20 @@ class Entity {
305
346
  await Promise.all(operation.map(async params => {
306
347
  let response = await this._exec(MethodTypes.batchWrite, params, config);
307
348
  if (validations.isFunction(config.parse)) {
308
- let parsed = await config.parse(config, response);
349
+ let parsed = config.parse(config, response);
309
350
  if (parsed) {
310
351
  results.push(parsed);
311
352
  }
312
- return;
313
- }
314
- let unprocessed = this.formatBulkWriteResponse(response, config);
315
- for (let u of unprocessed) {
316
- results.push(u);
353
+ } else {
354
+ let {unprocessed} = this.formatBulkWriteResponse(response, config);
355
+ for (let u of unprocessed) {
356
+ results.push(u);
357
+ }
317
358
  }
318
359
  }));
319
360
  }
320
361
 
321
- return results;
362
+ return { unprocessed: results };
322
363
  }
323
364
 
324
365
  _createNewBatchGetOrderMaintainer(config = {}) {
@@ -356,26 +397,29 @@ class Entity {
356
397
  await Promise.all(operation.map(async params => {
357
398
  let response = await this._exec(MethodTypes.batchGet, params, config);
358
399
  if (validations.isFunction(config.parse)) {
359
- resultsAll.push(await config.parse(config, response));
360
- return;
400
+ resultsAll.push(config.parse(config, response));
401
+ } else {
402
+ this.applyBulkGetResponseFormatting({
403
+ orderMaintainer,
404
+ resultsAll,
405
+ unprocessedAll,
406
+ response,
407
+ config
408
+ });
361
409
  }
362
- this.applyBulkGetResponseFormatting({
363
- orderMaintainer,
364
- resultsAll,
365
- unprocessedAll,
366
- response,
367
- config
368
- });
369
410
  }));
370
411
  }
371
- return [resultsAll, unprocessedAll];
412
+ return { data: resultsAll, unprocessed: unprocessedAll };
372
413
  }
373
414
 
374
- async executeQuery(parameters, config = {}) {
415
+ async executeQuery(method, parameters, config = {}) {
375
416
  let results = config._isCollectionQuery
376
417
  ? {}
377
418
  : [];
378
- let ExclusiveStartKey;
419
+ let ExclusiveStartKey = this._formatExclusiveStartKey(config);
420
+ if (ExclusiveStartKey === null) {
421
+ ExclusiveStartKey = undefined;
422
+ }
379
423
  let pages = this._normalizePagesValue(config.pages);
380
424
  let max = this._normalizeLimitValue(config.limit);
381
425
  let iterations = 0;
@@ -384,53 +428,48 @@ class Entity {
384
428
  let limit = max === undefined
385
429
  ? parameters.Limit
386
430
  : max - count;
387
- let response = await this._exec("query", {ExclusiveStartKey, ...parameters, Limit: limit}, config);
388
-
431
+ let response = await this._exec(method, {ExclusiveStartKey, ...parameters, Limit: limit}, config);
389
432
  ExclusiveStartKey = response.LastEvaluatedKey;
390
-
391
- if (validations.isFunction(config.parse)) {
392
- response = config.parse(config, response);
393
- } else {
394
- response = this.formatResponse(response, parameters.IndexName, config);
395
- }
396
-
397
- if (config.raw || config._isPagination) {
433
+ response = this.formatResponse(response, parameters.IndexName, config);
434
+ if (config.raw) {
398
435
  return response;
399
436
  } else if (config._isCollectionQuery) {
400
- for (const entity in response) {
437
+ for (const entity in response.data) {
401
438
  if (max) {
402
- count += response[entity].length;
439
+ count += response.data[entity].length;
403
440
  }
404
441
  results[entity] = results[entity] || [];
405
- results[entity] = [...results[entity], ...response[entity]];
442
+ results[entity] = [...results[entity], ...response.data[entity]];
406
443
  }
407
- } else if (Array.isArray(response)) {
444
+ } else if (Array.isArray(response.data)) {
408
445
  if (max) {
409
- count += response.length;
446
+ count += response.data.length;
410
447
  }
411
- results = [...results, ...response];
448
+ results = [...results, ...response.data];
412
449
  } else {
413
450
  return response;
414
451
  }
415
-
416
452
  iterations++;
417
- } while(ExclusiveStartKey && iterations < pages && (max === undefined || count < max));
418
- return results;
453
+ } while(
454
+ ExclusiveStartKey &&
455
+ (pages === AllPages || iterations < pages) &&
456
+ (max === undefined || count < max)
457
+ );
458
+
459
+ const cursor = this._formatReturnPager(config, ExclusiveStartKey);
460
+ return { data: results, cursor };
419
461
  }
420
462
 
421
463
  async executeOperation(method, parameters, config) {
422
464
  let response = await this._exec(method, parameters, config);
423
- if (validations.isFunction(config.parse)) {
424
- return config.parse(config, response);
425
- }
426
465
  switch (parameters.ReturnValues) {
427
466
  case FormatToReturnValues.none:
428
- return null;
467
+ return { data: null };
429
468
  case FormatToReturnValues.all_new:
430
469
  case FormatToReturnValues.all_old:
431
470
  case FormatToReturnValues.updated_new:
432
471
  case FormatToReturnValues.updated_old:
433
- return this.formatResponse(response, parameters.IndexName, config);
472
+ return this.formatResponse(response, config);
434
473
  case FormatToReturnValues.default:
435
474
  default:
436
475
  return this._formatDefaultResponse(method, parameters.IndexName, parameters, config, response);
@@ -475,9 +514,9 @@ class Entity {
475
514
  const index = TableIndex;
476
515
  let unprocessed = response.UnprocessedItems[table];
477
516
  if (Array.isArray(unprocessed) && unprocessed.length) {
478
- return unprocessed.map(request => {
517
+ unprocessed = unprocessed.map(request => {
479
518
  if (request.PutRequest) {
480
- return this.formatResponse(request.PutRequest, index, config);
519
+ return this.formatResponse(request.PutRequest, index, config).data;
481
520
  } else if (request.DeleteRequest) {
482
521
  if (config.unprocessed === UnprocessedTypes.raw) {
483
522
  return request.DeleteRequest.Key;
@@ -487,10 +526,12 @@ class Entity {
487
526
  } else {
488
527
  throw new Error("Unknown response format");
489
528
  }
490
- })
529
+ });
491
530
  } else {
492
- return []
531
+ unprocessed = [];
493
532
  }
533
+
534
+ return { unprocessed };
494
535
  }
495
536
 
496
537
  applyBulkGetResponseFormatting({
@@ -526,9 +567,9 @@ class Entity {
526
567
  const slot = orderMaintainer.getOrder(item);
527
568
  const formatted = this.formatResponse({Item: item}, index, config);
528
569
  if (slot !== -1) {
529
- resultsAll[slot] = formatted;
570
+ resultsAll[slot] = formatted.data;
530
571
  } else {
531
- resultsAll.push(formatted);
572
+ resultsAll.push(formatted.data);
532
573
  }
533
574
  }
534
575
  }
@@ -541,14 +582,16 @@ class Entity {
541
582
  }
542
583
  try {
543
584
  let results = {};
544
- if (config.raw && !config._isPagination) {
585
+ if (validations.isFunction(config.parse)) {
586
+ results = config.parse(config, response);
587
+ } else if (config.raw && !config._isPagination) {
545
588
  if (response.TableName) {
546
589
  results = {};
547
590
  } else {
548
591
  results = response;
549
592
  }
550
593
  } else if (config.raw && (config._isPagination || config.lastEvaluatedKeyRaw)) {
551
- return [response.LastEvaluatedKey || null, response];
594
+ results = response;
552
595
  } else {
553
596
  if (response.Item) {
554
597
  if (config.ignoreOwnership || this.ownsItem(response.Item)) {
@@ -573,18 +616,18 @@ class Entity {
573
616
  results = null;
574
617
  }
575
618
  } else if (config._objectOnEmpty) {
576
- return {};
619
+ return { data: {} };
577
620
  } else {
578
621
  results = null;
579
622
  }
580
623
  }
581
624
 
582
- if (config._isPagination) {
583
- let nextPage = this._formatReturnPager(config, index, response.LastEvaluatedKey, results[results.length - 1]);
584
- results = [nextPage, results];
625
+ if (config._isPagination || response.LastEvaluatedKey) {
626
+ const nextPage = this._formatReturnPager(config, response.LastEvaluatedKey);
627
+ return { cursor: nextPage ?? null, data: results };
585
628
  }
586
629
 
587
- return results;
630
+ return { data: results };
588
631
 
589
632
  } catch (err) {
590
633
  if (config.originalErr || stackTrace === undefined) {
@@ -608,16 +651,20 @@ class Entity {
608
651
  return this.formatResponse(item, TableIndex, config);
609
652
  }
610
653
 
611
- _formatReturnPager(config, index, lastEvaluatedKey, lastReturned) {
612
- let page = lastEvaluatedKey || null;
613
- if (config.pager !== Pager.raw) {
614
- page = this._formatKeysToItem(index, page, lastReturned);
615
- if (page && config.pager === Pager.named) {
616
- page[this.identifiers.entity] = this.getName();
617
- page[this.identifiers.version] = this.getVersion();
618
- }
654
+ _formatReturnPager(config, lastEvaluatedKey) {
655
+ let page = lastEvaluatedKey ?? null;
656
+ if (config.raw || config.pager === Pager.raw) {
657
+ return page;
619
658
  }
620
- return page;
659
+ return config.formatCursor.serialize(page) ?? null;
660
+ }
661
+
662
+ _formatExclusiveStartKey(config) {
663
+ let exclusiveStartKey = config.cursor;
664
+ if (config.raw || config.pager === Pager.raw) {
665
+ return exclusiveStartKey ?? null;
666
+ }
667
+ return config.formatCursor.deserialize(exclusiveStartKey) ?? null;
621
668
  }
622
669
 
623
670
  _getTableName() {
@@ -674,10 +721,13 @@ class Entity {
674
721
  return value;
675
722
  }
676
723
 
677
- _normalizePagesValue(value = Number.MAX_SAFE_INTEGER) {
724
+ _normalizePagesValue(value) {
725
+ if (value === AllPages) {
726
+ return value;
727
+ }
678
728
  value = parseInt(value);
679
729
  if (isNaN(value) || value < 1) {
680
- throw new e.ElectroError(e.ErrorCodes.InvalidPagesOption, "Query option 'pages' must be of type 'number' and greater than zero.");
730
+ throw new e.ElectroError(e.ErrorCodes.InvalidPagesOption, `Query option 'pages' must be of type 'number' and greater than zero or the string value '${AllPages}'`);
681
731
  }
682
732
  return value;
683
733
  }
@@ -819,17 +869,34 @@ class Entity {
819
869
  pager: Pager.named,
820
870
  unprocessed: UnprocessedTypes.item,
821
871
  response: 'default',
872
+ cursor: null,
873
+ data: 'attributes',
822
874
  ignoreOwnership: false,
823
875
  _isPagination: false,
824
876
  _isCollectionQuery: false,
825
- pages: undefined,
877
+ pages: 1,
826
878
  listeners: [],
827
879
  preserveBatchOrder: false,
828
880
  attributes: [],
829
881
  terminalOperation: undefined,
882
+ formatCursor: u.cursorFormatter,
883
+ order: undefined,
830
884
  };
831
885
 
832
886
  config = options.reduce((config, option) => {
887
+ if (typeof option.order === 'string') {
888
+ switch (option.order.toLowerCase()) {
889
+ case 'asc':
890
+ config.params[ResultOrderParam] = ResultOrderOption.asc;
891
+ break;
892
+ case 'desc':
893
+ config.params[ResultOrderParam] = ResultOrderOption.desc;
894
+ break;
895
+ default:
896
+ throw new e.ElectroError(e.ErrorCodes.InvalidOptions, `Invalid value for query option "order" provided. Valid options include 'asc' and 'desc, received: "${option.order}"`);
897
+ }
898
+ }
899
+
833
900
  if (typeof option.response === 'string' && option.response.length) {
834
901
  const format = ReturnValues[option.response];
835
902
  if (format === undefined) {
@@ -839,6 +906,18 @@ class Entity {
839
906
  config.params.ReturnValues = FormatToReturnValues[format];
840
907
  }
841
908
 
909
+ if (option.formatCursor) {
910
+ const isValid = ['serialize', 'deserialize'].every(method =>
911
+ method in option.formatCursor &&
912
+ validate.isFunction(option.formatCursor[method])
913
+ );
914
+ if (isValid) {
915
+ config.formatCursor = option.formatCursor;
916
+ } else {
917
+ throw new e.ElectroError(e.ErrorCodes.InvalidOptions, `Invalid value for query option "formatCursor" provided. Formatter interface must have serialize and deserialize functions`);
918
+ }
919
+ }
920
+
842
921
  if (option.terminalOperation in TerminalOperation) {
843
922
  config.terminalOperation = TerminalOperation[option.terminalOperation];
844
923
  }
@@ -881,6 +960,22 @@ class Entity {
881
960
  config.unprocessed = UnprocessedTypes.raw;
882
961
  }
883
962
 
963
+ if (option.cursor) {
964
+ config.cursor = option.cursor;
965
+ }
966
+
967
+ if (option.data) {
968
+ config.data = option.data;
969
+ switch(option.data) {
970
+ case 'raw':
971
+ config.raw = true;
972
+ break;
973
+ case 'includeKeys':
974
+ config.includeKeys = true;
975
+ break;
976
+ }
977
+ }
978
+
884
979
  if (option.limit !== undefined) {
885
980
  config.limit = option.limit;
886
981
  config.params.Limit = option.limit;
@@ -945,15 +1040,7 @@ class Entity {
945
1040
  }
946
1041
  }
947
1042
 
948
- if (Object.keys(config.page || {}).length) {
949
- if (config.raw || config.pager === Pager.raw) {
950
- parameters.ExclusiveStartKey = config.page;
951
- } else {
952
- parameters.ExclusiveStartKey = this._formatSuppliedPager(params.IndexName, config.page);
953
- }
954
- }
955
-
956
- return {parameters, config};
1043
+ return { parameters, config };
957
1044
  }
958
1045
 
959
1046
  addListeners(logger) {
package/src/errors.js CHANGED
@@ -217,6 +217,12 @@ const ErrorCodes = {
217
217
  name: "NoOwnerForPager",
218
218
  sym: ErrorCode,
219
219
  },
220
+ NoOwnerForCursor: {
221
+ code: 5004,
222
+ section: "no-owner-for-pager",
223
+ name: "NoOwnerForCursor",
224
+ sym: ErrorCode,
225
+ },
220
226
  PagerNotUnique: {
221
227
  code: 5005,
222
228
  section: "pager-not-unique",
package/src/filters.js CHANGED
@@ -84,7 +84,7 @@ class FilterFactory {
84
84
  injected[name] = {
85
85
  name: name,
86
86
  action: this.buildClause(filter),
87
- children: ["params", "go", "page", "filter", ...modelFilters],
87
+ children: ["params", "go", "filter", ...modelFilters],
88
88
  };
89
89
  }
90
90
  filterChildren.push("filter");
@@ -93,7 +93,7 @@ class FilterFactory {
93
93
  action: (entity, state, fn) => {
94
94
  return this.buildClause(fn)(entity, state);
95
95
  },
96
- children: ["params", "go", "page", "filter", ...modelFilters],
96
+ children: ["params", "go", "filter", ...modelFilters],
97
97
  };
98
98
  for (let parent of filterParents) {
99
99
  injected[parent] = { ...injected[parent] };
package/src/operations.js CHANGED
@@ -62,7 +62,13 @@ const UpdateOperations = {
62
62
  template: function add(options, attr, path, value) {
63
63
  let operation = "";
64
64
  let expression = "";
65
- switch(attr.type) {
65
+ let type = attr.type;
66
+ if (type === AttributeTypes.any) {
67
+ type = typeof value === 'number'
68
+ ? AttributeTypes.number
69
+ : AttributeTypes.any;
70
+ }
71
+ switch(type) {
66
72
  case AttributeTypes.any:
67
73
  case AttributeTypes.set:
68
74
  operation = ItemOperations.add;