electrodb 1.8.3 → 1.10.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "electrodb",
3
- "version": "1.8.3",
3
+ "version": "1.10.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": {
@@ -8,7 +8,7 @@
8
8
  "test-ts": "mocha -r ts-node/register ./test/**.spec.ts",
9
9
  "test-all": "mocha ./test/**.spec.js",
10
10
  "test-all-local": "LOCAL_DYNAMO_ENDPOINT=http://localhost:8000 node ./test/init.js && LOCAL_DYNAMO_ENDPOINT=http://localhost:8000 mocha ./test/**.spec.js && npm run test-types && LOCAL_DYNAMO_ENDPOINT=http://localhost:8000 npm run test-ts",
11
- "test-types": "node ./node_modules/tsd/dist/cli.js",
11
+ "test-types": "tsd",
12
12
  "coverage": "nyc npm run test-all && nyc report --reporter=text-lcov | coveralls",
13
13
  "coverage-coveralls-local": "nyc npm run test-all-local && nyc report --reporter=text-lcov | coveralls",
14
14
  "coverage-html-local": "nyc npm run test-all-local && nyc report --reporter=html",
@@ -43,9 +43,9 @@
43
43
  "moment": "2.24.0",
44
44
  "nyc": "^15.1.0",
45
45
  "source-map-support": "^0.5.19",
46
- "ts-node": "^9.0.0",
47
- "tsd": "^0.14.0",
48
- "typescript": "^4.2.4",
46
+ "ts-node": "^10.8.1",
47
+ "tsd": "^0.21.0",
48
+ "typescript": "^4.7.4",
49
49
  "uuid": "7.0.1"
50
50
  },
51
51
  "keywords": [
@@ -61,7 +61,7 @@
61
61
  "directory": "test"
62
62
  },
63
63
  "dependencies": {
64
- "jsonschema": "1.2.7",
65
- "@aws-sdk/lib-dynamodb": "^3.54.1"
64
+ "@aws-sdk/lib-dynamodb": "^3.54.1",
65
+ "jsonschema": "1.2.7"
66
66
  }
67
67
  }
@@ -0,0 +1,94 @@
1
+ import {
2
+ ParseOptions
3
+ } from './types/options';
4
+ import {
5
+ TableIndexCompositeAttributes,
6
+ AllTableIndexCompositeAttributes,
7
+ ResponseItem,
8
+ Item,
9
+ Schema,
10
+ SetItem,
11
+ AddItem,
12
+ SubtractItem,
13
+ AppendItem,
14
+ DeleteItem,
15
+ RemoveItem,
16
+ PutItem,
17
+ } from './types/schema';
18
+
19
+ import {
20
+ Queries,
21
+ SetRecord,
22
+ RemoveRecord,
23
+ DataUpdateMethodRecord,
24
+ RecordsActionOptions,
25
+ SingleRecordOperationOptions,
26
+ BulkRecordOperationOptions,
27
+ DeleteRecordOperationOptions,
28
+ PutRecordOperationOptions,
29
+ ParseSingleInput,
30
+ ParseMultiInput
31
+ } from './types/model';
32
+
33
+ import { ElectroEventListener } from './types/events';
34
+ import { DocumentClient } from './types/client';
35
+
36
+ export type Resolve<T> = T extends Function | string | number | boolean
37
+ ? T : {[Key in keyof T]: Resolve<T[Key]>}
38
+
39
+ export type EntityConfiguration = {
40
+ table?: string;
41
+ client?: DocumentClient;
42
+ listeners?: Array<ElectroEventListener>;
43
+ logger?: ElectroEventListener;
44
+ };
45
+
46
+ export class Entity<A extends string, F extends string, C extends string, S extends Schema<A,F,C>> {
47
+ readonly schema: S;
48
+ private config?: EntityConfiguration;
49
+ constructor(schema: S, config?: EntityConfiguration);
50
+
51
+ get(key: AllTableIndexCompositeAttributes<A,F,C,S>): SingleRecordOperationOptions<A,F,C,S, ResponseItem<A,F,C,S> | null>;
52
+ get(key: AllTableIndexCompositeAttributes<A,F,C,S>[]): BulkRecordOperationOptions<A,F,C,S, [Array<Resolve<ResponseItem<A,F,C,S>>>, Array<Resolve<AllTableIndexCompositeAttributes<A,F,C,S>>>], [Array<Resolve<ResponseItem<A,F,C,S>> | null>, Array<Resolve<AllTableIndexCompositeAttributes<A,F,C,S>>>]>;
53
+
54
+ delete(key: AllTableIndexCompositeAttributes<A,F,C,S>): DeleteRecordOperationOptions<A,F,C,S, ResponseItem<A,F,C,S>>;
55
+ delete(key: AllTableIndexCompositeAttributes<A,F,C,S>[]): BulkRecordOperationOptions<A,F,C,S, AllTableIndexCompositeAttributes<A,F,C,S>[], AllTableIndexCompositeAttributes<A,F,C,S>[]>;
56
+
57
+ put(record: PutItem<A,F,C,S>): PutRecordOperationOptions<A,F,C,S, ResponseItem<A,F,C,S>>;
58
+ put(record: PutItem<A,F,C,S>[]): BulkRecordOperationOptions<A,F,C,S, AllTableIndexCompositeAttributes<A,F,C,S>[], AllTableIndexCompositeAttributes<A,F,C,S>[]>;
59
+
60
+ remove(key: AllTableIndexCompositeAttributes<A,F,C,S>): DeleteRecordOperationOptions<A,F,C,S, ResponseItem<A,F,C,S>>
61
+ update(key: AllTableIndexCompositeAttributes<A,F,C,S>): {
62
+ set: SetRecord<A,F,C,S, SetItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
63
+ remove: RemoveRecord<A,F,C,S, RemoveItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
64
+ add: SetRecord<A,F,C,S, AddItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
65
+ subtract: SetRecord<A,F,C,S, SubtractItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
66
+ append: SetRecord<A,F,C,S, AppendItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
67
+ delete: SetRecord<A,F,C,S, DeleteItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
68
+ data: DataUpdateMethodRecord<A,F,C,S, Item<A,F,C,S,S["attributes"]>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
69
+ };
70
+
71
+ patch(key: AllTableIndexCompositeAttributes<A,F,C,S>): {
72
+ set: SetRecord<A,F,C,S, SetItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
73
+ remove: RemoveRecord<A,F,C,S, RemoveItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
74
+ add: SetRecord<A,F,C,S, AddItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
75
+ subtract: SetRecord<A,F,C,S, SubtractItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
76
+ append: SetRecord<A,F,C,S, AppendItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
77
+ delete: SetRecord<A,F,C,S, DeleteItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
78
+ data: DataUpdateMethodRecord<A,F,C,S, Item<A,F,C,S,S["attributes"]>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
79
+ };
80
+
81
+ create(record: PutItem<A,F,C,S>): PutRecordOperationOptions<A,F,C,S, ResponseItem<A,F,C,S>>
82
+
83
+ find(record: Partial<Item<A,F,C,S,S["attributes"]>>): RecordsActionOptions<A,F,C,S, ResponseItem<A,F,C,S>[], AllTableIndexCompositeAttributes<A,F,C,S>>;
84
+
85
+ match(record: Partial<Item<A,F,C,S,S["attributes"]>>): RecordsActionOptions<A,F,C,S, ResponseItem<A,F,C,S>[], AllTableIndexCompositeAttributes<A,F,C,S>>;
86
+
87
+ scan: RecordsActionOptions<A,F,C,S, ResponseItem<A,F,C,S>[], TableIndexCompositeAttributes<A,F,C,S>>;
88
+ query: Queries<A,F,C,S>;
89
+
90
+ parse(item: ParseSingleInput, options?: ParseOptions): ResponseItem<A,F,C,S> | null;
91
+ parse(item: ParseMultiInput, options?: ParseOptions): ResponseItem<A,F,C,S>[];
92
+ setIdentifier(type: "entity" | "version", value: string): void;
93
+ client: any;
94
+ }
package/src/entity.js CHANGED
@@ -277,6 +277,7 @@ class Entity {
277
277
  results,
278
278
  }, config.listeners);
279
279
  }
280
+
280
281
  return this.client[method](params).promise()
281
282
  .then((results) => {
282
283
  notifyQuery();
@@ -317,14 +318,36 @@ class Entity {
317
318
  return results;
318
319
  }
319
320
 
321
+ _createNewBatchGetOrderMaintainer(config = {}) {
322
+ const pkName = this.model.translations.keys[TableIndex].pk;
323
+ const skName = this.model.translations.keys[TableIndex].sk;
324
+ const enabled = !!config.preserveBatchOrder;
325
+ const table = this.config.table;
326
+ const keyFormatter = ((record = {}) => {
327
+ const pk = record[pkName];
328
+ const sk = record[skName];
329
+ return `${pk}${sk}`;
330
+ });
331
+
332
+ return new u.BatchGetOrderMaintainer({
333
+ table,
334
+ enabled,
335
+ keyFormatter,
336
+ });
337
+ }
338
+
320
339
  async executeBulkGet(parameters, config) {
321
340
  if (!Array.isArray(parameters)) {
322
341
  parameters = [parameters];
323
342
  }
324
- let concurrent = this._normalizeConcurrencyValue(config.concurrent)
325
- let concurrentOperations = u.batchItems(parameters, concurrent);
326
343
 
327
- let resultsAll = [];
344
+ const orderMaintainer = this._createNewBatchGetOrderMaintainer(config);
345
+ orderMaintainer.defineOrder(parameters);
346
+ let concurrent = this._normalizeConcurrencyValue(config.concurrent);
347
+ let concurrentOperations = u.batchItems(parameters, concurrent);
348
+ let resultsAll = config.preserveBatchOrder
349
+ ? new Array(orderMaintainer.getSize()).fill(null)
350
+ : [];
328
351
  let unprocessedAll = [];
329
352
  for (let operation of concurrentOperations) {
330
353
  await Promise.all(operation.map(async params => {
@@ -333,13 +356,13 @@ class Entity {
333
356
  resultsAll.push(await config.parse(config, response));
334
357
  return;
335
358
  }
336
- let [results, unprocessed] = this.formatBulkGetResponse(response, config);
337
- for (let r of results) {
338
- resultsAll.push(r);
339
- }
340
- for (let u of unprocessed) {
341
- unprocessedAll.push(u);
342
- }
359
+ this.applyBulkGetResponseFormatting({
360
+ orderMaintainer,
361
+ resultsAll,
362
+ unprocessedAll,
363
+ response,
364
+ config
365
+ });
343
366
  }));
344
367
  }
345
368
  return [resultsAll, unprocessedAll];
@@ -467,20 +490,26 @@ class Entity {
467
490
  }
468
491
  }
469
492
 
470
- formatBulkGetResponse(response = {}, config = {}) {
471
- let unprocessed = [];
472
- let results = [];
493
+ applyBulkGetResponseFormatting({
494
+ resultsAll,
495
+ unprocessedAll,
496
+ orderMaintainer,
497
+ response = {},
498
+ config = {},
499
+ }) {
473
500
  const table = config.table || this._getTableName();
474
501
  const index = TableIndex;
502
+
475
503
  if (!response.UnprocessedKeys || !response.Responses) {
476
504
  throw new Error("Unknown response format");
477
505
  }
506
+
478
507
  if (response.UnprocessedKeys[table] && response.UnprocessedKeys[table].Keys && Array.isArray(response.UnprocessedKeys[table].Keys)) {
479
508
  for (let value of response.UnprocessedKeys[table].Keys) {
480
509
  if (config && config.unprocessed === UnprocessedTypes.raw) {
481
- unprocessed.push(value);
510
+ unprocessedAll.push(value);
482
511
  } else {
483
- unprocessed.push(
512
+ unprocessedAll.push(
484
513
  this._formatKeysToItem(index, value)
485
514
  );
486
515
  }
@@ -488,10 +517,18 @@ class Entity {
488
517
  }
489
518
 
490
519
  if (response.Responses[table] && Array.isArray(response.Responses[table])) {
491
- results = this.formatResponse({Items: response.Responses[table]}, index, config);
520
+ const responses = response.Responses[table];
521
+ for (let i = 0; i < responses.length; i++) {
522
+ const item = responses[i];
523
+ const slot = orderMaintainer.getOrder(item);
524
+ const formatted = this.formatResponse({Item: item}, index, config);
525
+ if (slot !== -1) {
526
+ resultsAll[slot] = formatted;
527
+ } else {
528
+ resultsAll.push(formatted);
529
+ }
530
+ }
492
531
  }
493
-
494
- return [results, unprocessed];
495
532
  }
496
533
 
497
534
  formatResponse(response, index, config = {}) {
@@ -782,6 +819,7 @@ class Entity {
782
819
  _isCollectionQuery: false,
783
820
  pages: undefined,
784
821
  listeners: [],
822
+ preserveBatchOrder: false,
785
823
  };
786
824
 
787
825
  config = options.reduce((config, option) => {
@@ -794,6 +832,10 @@ class Entity {
794
832
  config.params.ReturnValues = FormatToReturnValues[format];
795
833
  }
796
834
 
835
+ if (option.preserveBatchOrder === true) {
836
+ config.preserveBatchOrder = true;
837
+ }
838
+
797
839
  if (option.pages !== undefined) {
798
840
  config.pages = option.pages;
799
841
  }
@@ -2257,22 +2299,6 @@ class Entity {
2257
2299
  facets.fields.push(sk.field);
2258
2300
  }
2259
2301
 
2260
- if (seenIndexFields[pk.field] !== undefined) {
2261
- throw new e.ElectroError(e.ErrorCodes.DuplicateIndexFields, `Partition Key (pk) on Access Pattern '${accessPattern}' references the field '${pk.field}' which is already referenced by the Access Pattern '${seenIndexFields[pk.field]}'. Fields used for indexes need to be unique to avoid conflicts.`);
2262
- } else {
2263
- seenIndexFields[pk.field] = accessPattern;
2264
- }
2265
-
2266
- if (sk.field) {
2267
- if (sk.field === pk.field) {
2268
- throw new e.ElectroError(e.ErrorCodes.DuplicateIndexFields, `The Access Pattern '${accessPattern}' references the field '${sk.field}' as the field name for both the PK and SK. Fields used for indexes need to be unique to avoid conflicts.`);
2269
- } else if (seenIndexFields[sk.field] !== undefined) {
2270
- throw new e.ElectroError(e.ErrorCodes.DuplicateIndexFields, `Sort Key (sk) on Access Pattern '${accessPattern}' references the field '${sk.field}' which is already referenced by the Access Pattern '${seenIndexFields[sk.field]}'. Fields used for indexes need to be unique to avoid conflicts.`);
2271
- }else {
2272
- seenIndexFields[sk.field] = accessPattern;
2273
- }
2274
- }
2275
-
2276
2302
  if (Array.isArray(sk.facets)) {
2277
2303
  let duplicates = pk.facets.filter(facet => sk.facets.includes(facet));
2278
2304
  if (duplicates.length !== 0) {
@@ -2362,6 +2388,37 @@ class Entity {
2362
2388
  facets.byField[sk.field][indexName] = sk;
2363
2389
  }
2364
2390
 
2391
+ if (seenIndexFields[pk.field] !== undefined) {
2392
+ const definition = Object.values(facets.byField[pk.field]).find(definition => definition.index !== indexName)
2393
+ const definitionsMatch = validations.stringArrayMatch(pk.facets, definition.facets);
2394
+ if (!definitionsMatch) {
2395
+ throw new e.ElectroError(e.ErrorCodes.InconsistentIndexDefinition, `Partition Key (pk) on Access Pattern '${u.formatIndexNameForDisplay(accessPattern)}' is defined with the composite attribute(s) ${u.commaSeparatedString(pk.facets)}, but the accessPattern '${u.formatIndexNameForDisplay(definition.index)}' defines this field with the composite attributes ${u.commaSeparatedString(definition.facets)}'. Key fields must have the same composite attribute definitions across all indexes they are involved with`);
2396
+ }
2397
+ seenIndexFields[pk.field].push({accessPattern, type: 'pk'});
2398
+ } else {
2399
+ seenIndexFields[pk.field] = [];
2400
+ seenIndexFields[pk.field].push({accessPattern, type: 'pk'});
2401
+ }
2402
+
2403
+ if (sk.field) {
2404
+ if (sk.field === pk.field) {
2405
+ throw new e.ElectroError(e.ErrorCodes.DuplicateIndexFields, `The Access Pattern '${u.formatIndexNameForDisplay(accessPattern)}' references the field '${sk.field}' as the field name for both the PK and SK. Fields used for indexes need to be unique to avoid conflicts.`);
2406
+ } else if (seenIndexFields[sk.field] !== undefined) {
2407
+ const isAlsoDefinedAsPK = seenIndexFields[sk.field].find(field => field.type === "pk");
2408
+ if (isAlsoDefinedAsPK) {
2409
+ throw new e.ElectroError(e.ErrorCodes.InconsistentIndexDefinition, `The Sort Key (sk) on Access Pattern '${u.formatIndexNameForDisplay(accessPattern)}' references the field '${pk.field}' which is already referenced by the Access Pattern(s) '${u.formatIndexNameForDisplay(isAlsoDefinedAsPK.accessPattern)}' as a Partition Key. Fields mapped to Partition Keys cannot be also mapped to Sort Keys.`);
2410
+ }
2411
+ const definition = Object.values(facets.byField[sk.field]).find(definition => definition.index !== indexName)
2412
+ const definitionsMatch = validations.stringArrayMatch(sk.facets, definition.facets);
2413
+ if (!definitionsMatch) {
2414
+ throw new e.ElectroError(e.ErrorCodes.DuplicateIndexFields, `Sort Key (sk) on Access Pattern '${u.formatIndexNameForDisplay(accessPattern)}' is defined with the composite attribute(s) ${u.commaSeparatedString(sk.facets)}, but the accessPattern '${u.formatIndexNameForDisplay(definition.index)}' defines this field with the composite attributes ${u.commaSeparatedString(definition.facets)}'. Key fields must have the same composite attribute definitions across all indexes they are involved with`);
2415
+ }
2416
+ seenIndexFields[sk.field].push({accessPattern, type: 'sk'});
2417
+ } else {
2418
+ seenIndexFields[sk.field] = [];
2419
+ seenIndexFields[sk.field].push({accessPattern, type: 'sk'});
2420
+ }
2421
+ }
2365
2422
 
2366
2423
  attributes.forEach(({index, type, name}, j) => {
2367
2424
  let next = attributes[j + 1] !== undefined ? attributes[j + 1].name : "";
@@ -0,0 +1,110 @@
1
+ import { Entity } from './entity';
2
+
3
+ const entityWithSK = new Entity({
4
+ model: {
5
+ entity: "abc",
6
+ service: "myservice",
7
+ version: "myversion"
8
+ },
9
+ attributes: {
10
+ attr1: {
11
+ type: "string",
12
+ default: "abc",
13
+ get: (val) => val + 123,
14
+ set: (val) => (val ?? "") + 456,
15
+ validate: (val) => !!val,
16
+ },
17
+ attr2: {
18
+ type: "string",
19
+ // default: () => "sfg",
20
+ // required: false,
21
+ validate: (val) => val.length > 0
22
+ },
23
+ attr3: {
24
+ type: ["123", "def", "ghi"] as const,
25
+ default: "def"
26
+ },
27
+ attr4: {
28
+ type: ["abc", "ghi"] as const,
29
+ required: true
30
+ },
31
+ attr5: {
32
+ type: "string"
33
+ },
34
+ attr6: {
35
+ type: "number",
36
+ default: () => 100,
37
+ get: (val) => val + 5,
38
+ set: (val) => (val ?? 0) + 5,
39
+ validate: (val) => true,
40
+ },
41
+ attr7: {
42
+ type: "any",
43
+ default: () => false,
44
+ get: (val) => ({key: "value"}),
45
+ set: (val) => (val ?? 0) + 5,
46
+ validate: (val) => true,
47
+ },
48
+ attr8: {
49
+ type: "boolean",
50
+ required: true,
51
+ get: (val) => !!val,
52
+ set: (val) => !!val,
53
+ validate: (val) => !!val,
54
+ },
55
+ attr9: {
56
+ type: "number"
57
+ },
58
+ attr10: {
59
+ type: "boolean"
60
+ },
61
+ attr11: {
62
+ type: 'list',
63
+ items: {
64
+ type: 'string'
65
+ }
66
+ }
67
+ },
68
+ indexes: {
69
+ myIndex: {
70
+ collection: "mycollection2",
71
+ pk: {
72
+ field: "pk",
73
+ composite: ["attr1"]
74
+ },
75
+ sk: {
76
+ field: "sk",
77
+ composite: ["attr2"]
78
+ }
79
+ },
80
+ myIndex2: {
81
+ collection: "mycollection1",
82
+ index: "gsi1",
83
+ pk: {
84
+ field: "gsipk1",
85
+ composite: ["attr6", "attr9"]
86
+ },
87
+ sk: {
88
+ field: "gsisk1",
89
+ composite: ["attr4", "attr5"]
90
+ }
91
+ },
92
+ myIndex3: {
93
+ collection: "mycollection",
94
+ index: "gsi2",
95
+ pk: {
96
+ field: "gsipk2",
97
+ composite: ["attr5"]
98
+ },
99
+ sk: {
100
+ field: "gsisk2",
101
+ composite: ["attr4", "attr3", "attr9"]
102
+ }
103
+ }
104
+ }
105
+ });
106
+
107
+ entityWithSK.update({
108
+ attr1: 'abc',
109
+ attr2: 'def'
110
+ }).append({})
package/src/errors.js CHANGED
@@ -139,6 +139,12 @@ const ErrorCodes = {
139
139
  name: "InvalidClientProvided",
140
140
  sym: ErrorCode,
141
141
  },
142
+ InconsistentIndexDefinition: {
143
+ code: 1022,
144
+ section: "inconsistent-index-definition",
145
+ name: "InvalidClientProvided",
146
+ sym: ErrorCode,
147
+ },
142
148
  MissingAttribute: {
143
149
  code: 2001,
144
150
  section: "missing-attribute",
@@ -0,0 +1,17 @@
1
+ import { Entity } from './entity';
2
+ import { DocumentClient } from './types/client';
3
+ import { ElectroEventListener } from './types/events';
4
+ import { CollectionQueries, CollectionAssociations } from './types/collections';
5
+
6
+ export type ServiceConfiguration = {
7
+ table?: string;
8
+ client?: DocumentClient;
9
+ listeners?: Array<ElectroEventListener>;
10
+ logger?: ElectroEventListener;
11
+ };
12
+
13
+ export class Service<E extends {[name: string]: Entity<any, any, any, any>}> {
14
+ entities: E;
15
+ collections: CollectionQueries<E, CollectionAssociations<E>>
16
+ constructor(entities: E, config?: ServiceConfiguration);
17
+ }
@@ -0,0 +1,15 @@
1
+ type DocumentClientMethod = (parameters: any) => {promise: () => Promise<any>};
2
+
3
+ export type DocumentClient = {
4
+ get: DocumentClientMethod;
5
+ put: DocumentClientMethod;
6
+ delete: DocumentClientMethod;
7
+ update: DocumentClientMethod;
8
+ batchWrite: DocumentClientMethod;
9
+ batchGet: DocumentClientMethod;
10
+ scan: DocumentClientMethod;
11
+ transactGet: DocumentClientMethod;
12
+ transactWrite: DocumentClientMethod;
13
+ } | {
14
+ send: (command: any) => Promise<any>;
15
+ }