dinah 0.2.0 → 0.3.1

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/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
- import { ConditionalCheckFailedException, CreateTableCommand, DeleteTableCommand, DynamoDB, DynamoDBClient, ListTablesCommand, UpdateTimeToLiveCommand } from "@aws-sdk/client-dynamodb";
1
+ import { i as resolveAttrType, n as extractTableDesc, r as removeUndefined, t as chunk } from "./util-B4dDuxlC.mjs";
2
+ import { CreateTableCommand, DeleteTableCommand, DynamoDB, DynamoDBClient, ListTablesCommand } from "@aws-sdk/client-dynamodb";
2
3
  import * as Lib from "@aws-sdk/lib-dynamodb";
3
4
  import sift from "sift";
4
- import * as T from "typebox";
5
5
  //#region src/expression-builder.ts
6
6
  const attrTypes = [
7
7
  "S",
@@ -182,18 +182,22 @@ var ExpressionBuilder = class {
182
182
  };
183
183
  //#endregion
184
184
  //#region src/repo.ts
185
- var Repository = class {
186
- table;
185
+ var AbstractRepo = class {
187
186
  db;
188
- constructor(table, db) {
189
- this.table = table;
187
+ constructor(db) {
190
188
  this.db = db;
191
189
  }
192
190
  get tableName() {
193
191
  return `${this.db.config?.tableNamePrefix ?? ""}${this.table.def.name}`;
194
192
  }
195
- get def() {
196
- return this.table.def;
193
+ get defaultPutData() {
194
+ return {};
195
+ }
196
+ get defaultUpdateData() {
197
+ return {};
198
+ }
199
+ transformItem(item) {
200
+ return item;
197
201
  }
198
202
  extractKey(item) {
199
203
  const { partitionKey, sortKey } = this.table.def;
@@ -204,140 +208,141 @@ var Repository = class {
204
208
  return { [partitionKey]: item[partitionKey] };
205
209
  }
206
210
  async get(key, options) {
207
- return this.db.get({
211
+ const item = await this.db.get({
208
212
  table: this.tableName,
209
213
  key: this.extractKey(key),
210
214
  ...options
211
215
  });
216
+ return item && this.applyTransformIfNeeded(item, options);
212
217
  }
213
218
  async getOrThrow(key, options) {
214
- return this.db.getOrThrow({
219
+ const item = await this.db.getOrThrow({
215
220
  table: this.tableName,
216
221
  key: this.extractKey(key),
217
222
  ...options
218
223
  });
224
+ return this.applyTransformIfNeeded(item, options);
219
225
  }
220
226
  async put(item, options) {
221
- const itemWithDefaults = this.table.def.beforePut ? {
222
- ...this.table.def.beforePut(item),
227
+ const itemWithDefaults = {
228
+ ...this.defaultPutData,
223
229
  ...item
224
- } : item;
225
- return this.db.put({
230
+ };
231
+ const result = await this.db.put({
226
232
  table: this.tableName,
227
233
  item: itemWithDefaults,
228
234
  ...options
229
235
  });
236
+ return this.applyTransformIfNeeded(result);
230
237
  }
231
238
  async update(key, update, options) {
232
- const updateWithDefaults = this.table.def.beforeUpdate ? {
233
- ...this.table.def.beforeUpdate(update),
239
+ const updateWithDefaults = {
240
+ ...this.defaultUpdateData,
234
241
  ...update
235
- } : update;
236
- return this.db.update({
242
+ };
243
+ const result = await this.db.update({
237
244
  table: this.tableName,
238
245
  key: this.extractKey(key),
239
246
  update: updateWithDefaults,
240
247
  ...options
241
248
  });
249
+ return this.applyTransformIfNeeded(result);
242
250
  }
243
251
  async create(item, options) {
244
- const itemWithDefaults = this.table.def.beforePut ? {
245
- ...this.table.def.beforePut(item),
246
- ...item
247
- } : item;
248
- return this.db.create({
249
- table: this.tableName,
250
- item: itemWithDefaults,
251
- partitionKeyName: this.table.def.partitionKey,
252
- ...options
253
- });
254
- }
255
- async upsert(data) {
256
- const { update, item, key, ...options } = data;
257
- const updateWithDefaults = this.table.def.beforeUpdate ? {
258
- ...this.table.def.beforeUpdate(update),
259
- ...update
260
- } : update;
261
- const itemWithDefaults = this.table.def.beforePut ? {
262
- ...this.table.def.beforePut(item),
263
- ...item
264
- } : item;
265
- return this.db.upsert({
266
- table: this.tableName,
267
- key: this.extractKey(key),
268
- update: updateWithDefaults,
269
- item: itemWithDefaults,
270
- ...options
252
+ const { condition: otherCondition, ...otherOptions } = options ?? {};
253
+ const condition = { $and: [{ [this.table.def.partitionKey]: { $exists: false } }] };
254
+ if (otherCondition) condition.$and.push(otherCondition);
255
+ return this.put(item, {
256
+ condition,
257
+ ...otherOptions
271
258
  });
272
259
  }
273
260
  async delete(key, options) {
274
- return this.db.delete({
261
+ const item = await this.db.delete({
275
262
  table: this.tableName,
276
263
  key: this.extractKey(key),
277
264
  ...options
278
265
  });
266
+ return item && this.applyTransformIfNeeded(item);
279
267
  }
280
268
  async deleteOrThrow(key, options) {
281
- return this.db.deleteOrThrow({
269
+ const item = await this.db.deleteOrThrow({
282
270
  table: this.tableName,
283
271
  key: this.extractKey(key),
284
272
  ...options
285
273
  });
274
+ return this.applyTransformIfNeeded(item);
286
275
  }
287
276
  async query(query, options) {
288
- return this.db.query({
277
+ const items = await this.db.query({
289
278
  table: this.tableName,
290
279
  query,
291
280
  ...options
292
281
  });
282
+ return this.applyTransformsIfNeeded(items, options);
293
283
  }
294
284
  async *queryPaged(query, options) {
295
- yield* this.db.queryPaged({
285
+ for await (const page of this.db.queryPaged({
296
286
  table: this.tableName,
297
287
  query,
298
288
  ...options
299
- });
289
+ })) yield this.applyTransformsIfNeeded(page, options);
300
290
  }
301
291
  async queryGsi(gsi, query, options) {
302
- return this.db.query({
292
+ const items = await this.db.query({
303
293
  table: this.tableName,
304
294
  index: gsi,
305
295
  query,
306
296
  ...options
307
297
  });
298
+ return this.applyTransformsIfNeeded(items, {
299
+ ...options,
300
+ gsi
301
+ });
308
302
  }
309
303
  async *queryGsiPaged(gsi, query, options) {
310
- yield* this.db.queryPaged({
304
+ for await (const page of this.db.queryPaged({
311
305
  table: this.tableName,
312
306
  index: gsi,
313
307
  query,
314
308
  ...options
309
+ })) yield this.applyTransformsIfNeeded(page, {
310
+ ...options,
311
+ gsi
315
312
  });
316
313
  }
317
314
  async scan(options) {
318
- return this.db.scan({
315
+ const items = await this.db.scan({
319
316
  table: this.tableName,
320
317
  ...options
321
318
  });
319
+ return this.applyTransformsIfNeeded(items, options);
322
320
  }
323
321
  async *scanPaged(options) {
324
- yield* this.db.scanPaged({
322
+ for await (const page of this.db.scanPaged({
325
323
  table: this.tableName,
326
324
  ...options
327
- });
325
+ })) yield this.applyTransformsIfNeeded(page, options);
328
326
  }
329
327
  async scanGsi(gsi, options) {
330
- return this.db.scan({
328
+ const items = await this.db.scan({
331
329
  table: this.tableName,
332
330
  index: gsi,
333
331
  ...options
334
332
  });
333
+ return this.applyTransformsIfNeeded(items, {
334
+ ...options,
335
+ gsi
336
+ });
335
337
  }
336
338
  async *scanGsiPaged(gsi, options) {
337
- yield* this.db.scanPaged({
339
+ for await (const page of this.db.scanPaged({
338
340
  table: this.tableName,
339
341
  index: gsi,
340
342
  ...options
343
+ })) yield this.applyTransformsIfNeeded(page, {
344
+ ...options,
345
+ gsi
341
346
  });
342
347
  }
343
348
  async exists(options) {
@@ -360,16 +365,18 @@ var Repository = class {
360
365
  keys: keys.map((key) => this.extractKey(key)),
361
366
  ...options
362
367
  } });
368
+ const tableItems = items[this.tableName];
363
369
  return {
364
- items: items[this.tableName],
370
+ items: tableItems && this.applyTransformsIfNeeded(tableItems, options),
365
371
  unprocessed: unprocessed?.[this.tableName]?.keys
366
372
  };
367
373
  }
368
374
  async batchGetOrThrow(keys, options) {
369
- return (await this.db.batchGetOrThrow({ [this.tableName]: {
375
+ const result = await this.db.batchGetOrThrow({ [this.tableName]: {
370
376
  keys: keys.map((key) => this.extractKey(key)),
371
377
  ...options
372
- } }))[this.tableName];
378
+ } });
379
+ return this.applyTransformsIfNeeded(result[this.tableName] ?? [], options);
373
380
  }
374
381
  async batchWrite(requests) {
375
382
  const { items, unprocessed } = await this.db.batchWrite({ [this.tableName]: requests.map((request) => {
@@ -379,10 +386,10 @@ var Repository = class {
379
386
  };
380
387
  else return {
381
388
  type: "PUT",
382
- item: this.table.def.beforePut ? {
383
- ...this.table.def.beforePut(request.item),
389
+ item: {
390
+ ...this.defaultPutData,
384
391
  ...request.item
385
- } : request.item
392
+ }
386
393
  };
387
394
  }) });
388
395
  return {
@@ -391,18 +398,19 @@ var Repository = class {
391
398
  };
392
399
  }
393
400
  async trxGet(keys, options) {
394
- return this.db.trxGet(...keys.map((key) => ({
401
+ return (await this.db.trxGet(...keys.map((key) => ({
395
402
  table: this.tableName,
396
403
  key: this.extractKey(key),
397
404
  ...options
398
- })));
405
+ })))).map((item) => item && this.applyTransformIfNeeded(item, options));
399
406
  }
400
407
  async trxGetOrThrow(keys, options) {
401
- return this.db.trxGetOrThrow(...keys.map((key) => ({
408
+ const items = await this.db.trxGetOrThrow(...keys.map((key) => ({
402
409
  table: this.tableName,
403
410
  key: this.extractKey(key),
404
411
  ...options
405
412
  })));
413
+ return this.applyTransformsIfNeeded(items, options);
406
414
  }
407
415
  async trxWrite(...requests) {
408
416
  await this.db.trxWrite(...requests.map((request) => {
@@ -464,10 +472,10 @@ var Repository = class {
464
472
  };
465
473
  }
466
474
  trxPutRequest(item, options) {
467
- const itemWithDefaults = this.table.def.beforePut ? {
468
- ...this.table.def.beforePut(item),
475
+ const itemWithDefaults = {
476
+ ...this.defaultPutData,
469
477
  ...item
470
- } : item;
478
+ };
471
479
  return {
472
480
  table: this.tableName,
473
481
  type: "PUT",
@@ -476,10 +484,10 @@ var Repository = class {
476
484
  };
477
485
  }
478
486
  trxUpdateRequest(key, update, options) {
479
- const updateWithDefaults = this.table.def.beforeUpdate ? {
480
- ...this.table.def.beforeUpdate(update),
487
+ const updateWithDefaults = {
488
+ ...this.defaultUpdateData,
481
489
  ...update
482
- } : update;
490
+ };
483
491
  return {
484
492
  table: this.tableName,
485
493
  type: "UPDATE",
@@ -492,10 +500,10 @@ var Repository = class {
492
500
  const { condition: otherCondition, ...otherOptions } = options ?? {};
493
501
  const condition = { $and: [{ [this.table.def.partitionKey]: { $exists: false } }] };
494
502
  if (otherCondition) condition.$and.push(otherCondition);
495
- const itemWithDefaults = this.table.def.beforePut ? {
496
- ...this.table.def.beforePut(item),
503
+ const itemWithDefaults = {
504
+ ...this.defaultPutData,
497
505
  ...item
498
- } : item;
506
+ };
499
507
  return {
500
508
  table: this.tableName,
501
509
  type: "PUT",
@@ -504,78 +512,25 @@ var Repository = class {
504
512
  ...otherOptions
505
513
  };
506
514
  }
515
+ applyTransformsIfNeeded(items, options) {
516
+ if (options?.projection?.length) return items;
517
+ if (options?.gsi) {
518
+ const gsiProj = this.table.def.gsis?.[options.gsi]?.projection;
519
+ if (gsiProj === "KEYS_ONLY" || Array.isArray(gsiProj)) return items;
520
+ }
521
+ return items.map((item) => this.transformItem(item));
522
+ }
523
+ applyTransformIfNeeded(item, options) {
524
+ const [transformedItem] = this.applyTransformsIfNeeded([item], options);
525
+ return transformedItem;
526
+ }
507
527
  };
508
- //#endregion
509
- //#region src/util.ts
510
- const resolveAttrType = (schema, attrName) => {
511
- if (T.IsString(schema) || T.IsLiteralString(schema)) return T.IsOptional(schema) ? ["S", void 0] : ["S"];
512
- if (T.IsNumber(schema) || T.IsLiteralNumber(schema)) return T.IsOptional(schema) ? ["N", void 0] : ["N"];
513
- if (T.IsObject(schema)) {
514
- const attrSchema = schema.properties[attrName];
515
- if (!attrSchema) return [void 0];
516
- return resolveAttrType(attrSchema, attrName);
517
- }
518
- if (T.IsIntersect(schema)) return schema.allOf.flatMap((s) => resolveAttrType(s, attrName));
519
- if (T.IsUnion(schema)) return schema.anyOf.flatMap((s) => resolveAttrType(s, attrName));
520
- return [];
521
- };
522
- const getAttrType = (schema, attrName, allowedTypes = [
523
- "S",
524
- "N",
525
- void 0
526
- ]) => {
527
- const resolvedType = resolveAttrType(schema, attrName);
528
- if (resolvedType.includes("S") && resolvedType.includes("N")) throw new Error(`Attribute ${attrName} cannot be both a number and a string.`);
529
- if (!allowedTypes.includes(void 0) && resolvedType.includes(void 0)) throw new Error(`Attribute ${attrName} cannot be optional.`);
530
- return resolvedType.includes("S") ? "S" : "N";
531
- };
532
- const extractAttribute = (schema, attrName, allowedTypes) => {
533
- return {
534
- AttributeName: attrName,
535
- AttributeType: getAttrType(schema, attrName, allowedTypes)
536
- };
537
- };
538
- const extractTableKey = (schema, attrName, allowedTypes) => {
539
- return {
540
- name: attrName,
541
- type: getAttrType(schema, attrName, allowedTypes)
542
- };
543
- };
544
- const extractTableDesc = (schema, def) => {
545
- const gsis = Object.entries(def.gsis ?? {}).map(([indexName, gsi]) => {
546
- return {
547
- indexName,
548
- partitionKey: extractTableKey(schema, gsi.partitionKey),
549
- sortKey: gsi.sortKey ? extractTableKey(schema, gsi.sortKey) : void 0,
550
- projectionType: Array.isArray(gsi.projection) ? "INCLUDE" : gsi.projection ?? "ALL",
551
- nonKeyAttributes: Array.isArray(gsi.projection) ? gsi.projection : void 0
552
- };
553
- });
554
- return {
555
- tableName: def.name,
556
- billingMode: def.billingMode ?? "PAY_PER_REQUEST",
557
- stream: def.stream,
558
- partitionKey: extractTableKey(schema, def.partitionKey, ["N", "S"]),
559
- sortKey: def.sortKey ? extractTableKey(schema, def.sortKey, ["N", "S"]) : void 0,
560
- gsis: gsis.length ? gsis : void 0
561
- };
562
- };
563
- const chunk = (arr, chunkSize) => {
564
- if (chunkSize <= 0) return [];
565
- const chunks = [];
566
- for (let i = 0; i < arr.length; i += chunkSize) chunks.push(arr.slice(i, i + chunkSize));
567
- return chunks;
568
- };
569
- const removeUndefined = (obj) => {
570
- const stack = [obj];
571
- while (stack.length) {
572
- const currentObj = stack.pop();
573
- if (currentObj !== void 0) Object.entries(currentObj).forEach(([k, v]) => {
574
- if (v && v instanceof Object) stack.push(v);
575
- else if (v === void 0) delete currentObj[k];
576
- });
528
+ var Repo = class extends AbstractRepo {
529
+ table;
530
+ constructor(db, table) {
531
+ super(db);
532
+ this.table = table;
577
533
  }
578
- return obj;
579
534
  };
580
535
  //#endregion
581
536
  //#region src/db.ts
@@ -594,7 +549,13 @@ var Db = class {
594
549
  this.config = dbConfig;
595
550
  }
596
551
  createRepo(table) {
597
- return new Repository(table, this);
552
+ return new Repo(this, table);
553
+ }
554
+ async createTable(table) {
555
+ await this.client.send(new CreateTableCommand(extractTableDesc(table)));
556
+ }
557
+ async deleteTable(tableName) {
558
+ await this.client.send(new DeleteTableCommand({ TableName: tableName }));
598
559
  }
599
560
  async listTables(data) {
600
561
  const tables = [];
@@ -631,17 +592,18 @@ var Db = class {
631
592
  }
632
593
  async put(data) {
633
594
  const exp = new ExpressionBuilder();
595
+ const item = removeUndefined(data.item);
634
596
  const input = new Lib.PutCommand({
635
597
  TableName: data.table,
636
- Item: removeUndefined(data.item),
637
- ReturnValues: data.return === "ALL_OLD" ? "ALL_OLD" : void 0,
598
+ Item: item,
599
+ ReturnValues: data.returnOld ? "ALL_OLD" : "NONE",
638
600
  ReturnValuesOnConditionCheckFailure: "ALL_OLD",
639
601
  ConditionExpression: exp.condition(data.condition),
640
602
  ExpressionAttributeNames: exp.attributeNames,
641
603
  ExpressionAttributeValues: exp.attributeValues
642
604
  });
643
605
  const output = await this.client.send(input);
644
- return data.return === "ALL_OLD" ? output.Attributes : data;
606
+ return data.returnOld ? output.Attributes : item;
645
607
  }
646
608
  async update(data) {
647
609
  const exp = new ExpressionBuilder();
@@ -650,7 +612,7 @@ var Db = class {
650
612
  const input = new Lib.UpdateCommand({
651
613
  TableName: data.table,
652
614
  Key: data.key,
653
- ReturnValues: data.return ?? "ALL_NEW",
615
+ ReturnValues: "ALL_NEW",
654
616
  ReturnValuesOnConditionCheckFailure: "ALL_OLD",
655
617
  UpdateExpression: exp.update(data.update),
656
618
  ConditionExpression: exp.condition(condition),
@@ -659,40 +621,12 @@ var Db = class {
659
621
  });
660
622
  return (await this.client.send(input)).Attributes;
661
623
  }
662
- async create(data) {
663
- const { condition: otherCondition, partitionKeyName, ...options } = data;
664
- const condition = { $and: [{ [partitionKeyName]: { $exists: false } }] };
665
- if (otherCondition) condition.$and.push(otherCondition);
666
- return this.put({
667
- ...options,
668
- condition,
669
- return: "ALL_NEW"
670
- });
671
- }
672
- async upsert(data) {
673
- const { key, item, update, ...options } = data;
674
- try {
675
- return await this.update({
676
- ...options,
677
- key,
678
- update,
679
- return: "ALL_NEW"
680
- });
681
- } catch (e) {
682
- if (e instanceof ConditionalCheckFailedException && !e.Item) return this.create({
683
- item,
684
- partitionKeyName: Object.keys(key)[0] ?? "",
685
- ...options
686
- });
687
- throw e;
688
- }
689
- }
690
624
  async delete(data) {
691
625
  const exp = new ExpressionBuilder();
692
626
  const input = new Lib.DeleteCommand({
693
627
  TableName: data.table,
694
628
  Key: data.key,
695
- ReturnValues: data.return ?? "ALL_OLD",
629
+ ReturnValues: "ALL_OLD",
696
630
  ConditionExpression: exp.condition(data.condition),
697
631
  ReturnValuesOnConditionCheckFailure: "ALL_OLD",
698
632
  ExpressionAttributeNames: exp.attributeNames,
@@ -973,73 +907,8 @@ var Table = class {
973
907
  this.schema = schema;
974
908
  this.def = def;
975
909
  }
976
- async drop(client) {
977
- await client.send(new DeleteTableCommand({ TableName: this.def.name }));
978
- }
979
- async createTable(client) {
980
- const desc = extractTableDesc(this.schema, this.def);
981
- const attributes = /* @__PURE__ */ new Map();
982
- const setAttribute = (tableKey) => {
983
- attributes.set(tableKey.name, {
984
- AttributeName: tableKey.name,
985
- AttributeType: tableKey.type
986
- });
987
- };
988
- const keySchema = [{
989
- AttributeName: desc.partitionKey.name,
990
- KeyType: "HASH"
991
- }];
992
- setAttribute(desc.partitionKey);
993
- if (desc.sortKey) {
994
- keySchema.push({
995
- AttributeName: desc.sortKey.name,
996
- KeyType: "RANGE"
997
- });
998
- setAttribute(desc.sortKey);
999
- }
1000
- const gsis = desc.gsis?.map((gsi) => {
1001
- const gsiKeySchema = [{
1002
- AttributeName: gsi.partitionKey.name,
1003
- KeyType: "HASH"
1004
- }];
1005
- setAttribute(gsi.partitionKey);
1006
- if (gsi.sortKey) {
1007
- gsiKeySchema.push({
1008
- AttributeName: gsi.sortKey.name,
1009
- KeyType: "RANGE"
1010
- });
1011
- setAttribute(gsi.sortKey);
1012
- }
1013
- return {
1014
- IndexName: gsi.indexName,
1015
- KeySchema: gsiKeySchema,
1016
- Projection: {
1017
- ProjectionType: gsi.projectionType,
1018
- NonKeyAttributes: gsi.nonKeyAttributes
1019
- }
1020
- };
1021
- });
1022
- await client.send(new CreateTableCommand({
1023
- TableName: desc.tableName,
1024
- KeySchema: keySchema,
1025
- AttributeDefinitions: [...attributes.values()],
1026
- BillingMode: desc.billingMode,
1027
- GlobalSecondaryIndexes: gsis,
1028
- StreamSpecification: desc.stream ? {
1029
- StreamEnabled: true,
1030
- StreamViewType: desc.stream
1031
- } : void 0
1032
- }));
1033
- if (desc.ttlAttribute) await client.send(new UpdateTimeToLiveCommand({
1034
- TableName: desc.tableName,
1035
- TimeToLiveSpecification: {
1036
- Enabled: true,
1037
- AttributeName: desc.ttlAttribute
1038
- }
1039
- }));
1040
- }
1041
910
  };
1042
911
  //#endregion
1043
- export { Db, Repository, Table, chunk, extractAttribute, extractTableDesc, extractTableKey, getAttrType, removeUndefined, resolveAttrType };
912
+ export { AbstractRepo, Db, Repo, Table, chunk, extractTableDesc, removeUndefined, resolveAttrType };
1044
913
 
1045
914
  //# sourceMappingURL=index.mjs.map