@moicky/dynamodb 3.0.4 → 3.1.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.
@@ -11,6 +11,7 @@ export declare class Transaction {
11
11
  private updatedAt;
12
12
  private operations;
13
13
  private shouldSplitTransactions;
14
+ private hasBeenExecuted;
14
15
  constructor({ tableName, createdAt, updatedAt, shouldSplitTransactions, }?: {
15
16
  tableName?: string;
16
17
  createdAt?: any;
@@ -19,8 +20,8 @@ export declare class Transaction {
19
20
  });
20
21
  private getItemKey;
21
22
  create<T extends ItemWithKey>(item: WithoutReferences<T>, args?: CreateOperation["args"]): CreateOperations<T>;
22
- update<T extends DynamoDBItemKey>(item: OnlyKey<T>, args?: UpdateOperation["args"]): UpdateOperations<T>;
23
- delete(item: DynamoDBItemKey, args?: DeleteOperation["args"]): void;
23
+ update<T extends DynamoDBItemKey>(item: T extends any ? OnlyKey<T> : T, args?: UpdateOperation["args"]): UpdateOperations<T>;
24
+ delete(item: DynamoDBItemKey, args?: DeleteOperation["args"]): this;
24
25
  addConditionFor<T extends DynamoDBItemKey>(item: T, args?: Partial<ConditionOperation["args"]>): ConditionOperations<T>;
25
26
  private handleOperation;
26
27
  execute(args?: Partial<Omit<TransactWriteItemsCommandInput, "TransactItems">>): Promise<Record<string, ResponseItem[]>>;
@@ -12,6 +12,7 @@ class Transaction {
12
12
  updatedAt;
13
13
  operations = {};
14
14
  shouldSplitTransactions;
15
+ hasBeenExecuted = false;
15
16
  constructor({ tableName, createdAt, updatedAt, shouldSplitTransactions, } = {}) {
16
17
  this.tableName = tableName ?? (0, __1.getDefaultTable)();
17
18
  this.createdAt = createdAt ?? (0, __1.getItemModificationTimestamp)("createdAt");
@@ -25,6 +26,9 @@ class Transaction {
25
26
  return (0, __1.getItemKey)(item, { TableName: tableName || this.tableName });
26
27
  }
27
28
  create(item, args) {
29
+ if (this.hasBeenExecuted) {
30
+ throw new Error("[@moicky/dynamodb]: Transaction has already been executed");
31
+ }
28
32
  const itemKey = this.getItemKey(item, args?.TableName);
29
33
  const createOperation = {
30
34
  _type: "create",
@@ -35,26 +39,41 @@ class Transaction {
35
39
  return new operations_1.CreateOperations(createOperation, this);
36
40
  }
37
41
  update(item, args) {
42
+ if (this.hasBeenExecuted) {
43
+ throw new Error("[@moicky/dynamodb]: Transaction has already been executed");
44
+ }
38
45
  const itemKey = this.getItemKey(item, args?.TableName);
46
+ const actions = [];
47
+ const existingOperation = this.operations[itemKey];
48
+ if (existingOperation && existingOperation._type === "update") {
49
+ actions.push(...existingOperation.actions);
50
+ }
39
51
  const updateOperation = {
40
52
  _type: "update",
41
53
  item,
42
- actions: [{ _type: "set", values: { updatedAt: this.updatedAt } }],
54
+ actions,
43
55
  args: { TableName: this.tableName, ...args },
44
56
  };
45
57
  this.operations[itemKey] = updateOperation;
46
58
  return new operations_1.UpdateOperations(updateOperation, this);
47
59
  }
48
60
  delete(item, args) {
61
+ if (this.hasBeenExecuted) {
62
+ throw new Error("[@moicky/dynamodb]: Transaction has already been executed");
63
+ }
49
64
  const itemKey = this.getItemKey(item, args?.TableName);
50
65
  this.operations[itemKey] = {
51
66
  _type: "delete",
52
67
  item,
53
68
  args: { TableName: this.tableName, ...args },
54
69
  };
70
+ return this;
55
71
  }
56
72
  addConditionFor(item, args) {
57
- return new operations_1.ConditionOperations(this.operations, item, {
73
+ if (this.hasBeenExecuted) {
74
+ throw new Error("[@moicky/dynamodb]: Transaction has already been executed");
75
+ }
76
+ return new operations_1.ConditionOperations(this, this.operations, item, {
58
77
  TableName: this.tableName,
59
78
  ...args,
60
79
  });
@@ -80,32 +99,49 @@ class Transaction {
80
99
  };
81
100
  const { ExpressionAttributeValues, ExpressionAttributeNames, ...otherArgs } = args;
82
101
  const attr = new __1.ExpressionAttributes(ExpressionAttributeValues, ExpressionAttributeNames);
102
+ let hasSetUpdatedAt = false;
83
103
  actions.forEach((action) => {
84
104
  switch (action._type) {
85
105
  case "set":
86
106
  attr.appendBoth(action.values);
107
+ if ("updatedAt" in action.values) {
108
+ hasSetUpdatedAt = true;
109
+ }
87
110
  expressions.set.push(Object.keys(action.values)
88
111
  .map((key) => `${attr.getName(key)} = ${attr.getValue(key)}`)
89
112
  .join(", "));
90
113
  break;
91
114
  case "remove":
92
115
  attr.appendNames(action.attributes);
116
+ if (action.attributes.includes("updatedAt")) {
117
+ hasSetUpdatedAt = true;
118
+ }
93
119
  expressions.remove.push(action.attributes.map((key) => attr.getName(key)).join(", "));
94
120
  break;
95
121
  case "add":
96
122
  attr.appendBoth(action.values);
123
+ if ("updatedAt" in action.values) {
124
+ hasSetUpdatedAt = true;
125
+ }
97
126
  expressions.add.push(Object.keys(action.values)
98
127
  .map((key) => `${attr.getName(key)} ${attr.getValue(key)}`)
99
128
  .join(", "));
100
129
  break;
101
130
  case "delete":
102
131
  attr.appendBoth(action.values);
132
+ if ("updatedAt" in action.values) {
133
+ hasSetUpdatedAt = true;
134
+ }
103
135
  expressions.delete.push(Object.keys(action.values)
104
136
  .map((key) => `${attr.getName(key)} ${attr.getValue(key)}`)
105
137
  .join(", "));
106
138
  break;
107
139
  }
108
140
  });
141
+ if (!hasSetUpdatedAt) {
142
+ attr.appendBoth({ updatedAt: this.updatedAt });
143
+ expressions.set.push(`${attr.getName("updatedAt")} = ${attr.getValue("updatedAt")}`);
144
+ }
109
145
  const joinedExpressions = Object.entries(expressions)
110
146
  .filter(([, value]) => value?.length)
111
147
  .reduce((acc, [t, v]) => [...acc, `${t.toUpperCase()} ${v.join(", ")}`], [])
@@ -132,6 +168,10 @@ class Transaction {
132
168
  async execute(args) {
133
169
  args = (0, __1.withDefaults)(args, "transactWriteItems");
134
170
  return new Promise(async (resolve, reject) => {
171
+ if (this.hasBeenExecuted) {
172
+ reject(new Error("[@moicky/dynamodb]: Transaction has already been executed"));
173
+ }
174
+ this.hasBeenExecuted = true;
135
175
  const operations = Object.values(this.operations);
136
176
  if (operations.length === 0 ||
137
177
  (operations.length > OPERATIONS_LIMIT &&
@@ -1,3 +1,4 @@
1
+ import { TransactWriteItemsCommandInput } from "@aws-sdk/client-dynamodb";
1
2
  import { Transaction } from ".";
2
3
  import { DynamoDBItem } from "../types";
3
4
  import { DynamoDBReference } from "./references/types";
@@ -7,6 +8,9 @@ export declare class CreateOperations<U extends ItemWithKey> {
7
8
  private transaction;
8
9
  constructor(operation: CreateOperation, transaction: Transaction);
9
10
  setReferences(refs: SetReferencesParams<U>): void;
11
+ execute(args?: Partial<Omit<TransactWriteItemsCommandInput, "TransactItems">>): Promise<Record<string, (Pick<import("@aws-sdk/client-dynamodb").ItemCollectionMetrics, "SizeEstimateRangeGB"> & {
12
+ Key: Record<string, any>;
13
+ })[]>>;
10
14
  }
11
15
  export declare class UpdateOperations<U extends DynamoDBItem> {
12
16
  private operation;
@@ -15,25 +19,32 @@ export declare class UpdateOperations<U extends DynamoDBItem> {
15
19
  setReferences(refs: SetReferencesParams<U>): void;
16
20
  set(values: WithoutKey<Partial<U>> & NestedParams): this;
17
21
  adjustNumber(values: Prettify<StrictDeepNumberUpdates<U>>): this;
18
- removeAttributes(...attributes: string[]): this;
22
+ removeAttributes(...attributes: Array<Exclude<Extract<keyof U, string>, keyof DynamoDBItemKey> | `${string}.${string}` | `${string}.${string}.${string}`>): UpdateOperations<U>;
19
23
  addItemsToSet(values: Prettify<DeepSetUpdates<U>>): this;
20
24
  deleteItemsFromSet(values: Prettify<DeepSetUpdates<U>>): this;
21
25
  onCondition({ expression, values, }: {
22
26
  expression: string;
23
27
  values: Partial<U> & Record<string, any>;
24
28
  }): void;
29
+ execute(args?: Partial<Omit<TransactWriteItemsCommandInput, "TransactItems">>): Promise<Record<string, (Pick<import("@aws-sdk/client-dynamodb").ItemCollectionMetrics, "SizeEstimateRangeGB"> & {
30
+ Key: Record<string, any>;
31
+ })[]>>;
25
32
  }
26
33
  export declare class ConditionOperations<U extends DynamoDBItem> {
34
+ private transaction;
27
35
  private operations;
28
36
  private item;
29
37
  private args;
30
- constructor(operations: Transaction["operations"], item: DynamoDBItemKey, args: Partial<ConditionOperation["args"]> & {
38
+ constructor(transaction: Transaction, operations: Transaction["operations"], item: DynamoDBItemKey, args: Partial<ConditionOperation["args"]> & {
31
39
  TableName: string;
32
40
  });
33
41
  matches({ expression, values, }: {
34
42
  expression: string;
35
43
  values: Partial<U> & Record<string, any>;
36
44
  }): void;
45
+ execute(args?: Partial<Omit<TransactWriteItemsCommandInput, "TransactItems">>): Promise<Record<string, (Pick<import("@aws-sdk/client-dynamodb").ItemCollectionMetrics, "SizeEstimateRangeGB"> & {
46
+ Key: Record<string, any>;
47
+ })[]>>;
37
48
  }
38
49
  type SetReferencesParams<U extends DynamoDBItem> = TypedParams<U, DynamoDBReference, DynamoDBItemKey> & TypedParams<U, Set<DynamoDBReference>, DynamoDBItemKey[] | Set<DynamoDBItemKey>> & NestedParams<DynamoDBItemKey | DynamoDBItemKey[] | Set<DynamoDBItemKey>>;
39
50
  export {};
@@ -19,7 +19,7 @@ class CreateOperations {
19
19
  onAttribute: attributeName,
20
20
  };
21
21
  const refData = ref instanceof Set
22
- ? [...ref].map((references) => (0, references_1.createReference)({ references, ...refArgs }, this.transaction))
22
+ ? Array.from(ref).map((references) => (0, references_1.createReference)({ references, ...refArgs }, this.transaction))
23
23
  : (0, references_1.createReference)({ references: ref, ...refArgs }, this.transaction);
24
24
  const parts = attributeName.split(".");
25
25
  parts.reduce((acc, part, index) => {
@@ -33,6 +33,9 @@ class CreateOperations {
33
33
  }, this.operation.item);
34
34
  });
35
35
  }
36
+ execute(args) {
37
+ return this.transaction.execute(args);
38
+ }
36
39
  }
37
40
  exports.CreateOperations = CreateOperations;
38
41
  class UpdateOperations {
@@ -54,7 +57,7 @@ class UpdateOperations {
54
57
  _type: "set",
55
58
  values: {
56
59
  [attributeName]: ref instanceof Set
57
- ? [...ref].map((references) => (0, references_1.createReference)({ references, ...refArgs }, this.transaction))
60
+ ? Array.from(ref).map((references) => (0, references_1.createReference)({ references, ...refArgs }, this.transaction))
58
61
  : (0, references_1.createReference)({ references: ref, ...refArgs }, this.transaction),
59
62
  },
60
63
  });
@@ -75,7 +78,10 @@ class UpdateOperations {
75
78
  removeAttributes(...attributes) {
76
79
  if (attributes.length === 0)
77
80
  return this;
78
- this.operation.actions.push({ _type: "remove", attributes });
81
+ this.operation.actions.push({
82
+ _type: "remove",
83
+ attributes: attributes.map(String),
84
+ });
79
85
  return this;
80
86
  }
81
87
  addItemsToSet(values) {
@@ -109,13 +115,18 @@ class UpdateOperations {
109
115
  }, {}),
110
116
  };
111
117
  }
118
+ execute(args) {
119
+ return this.transaction.execute(args);
120
+ }
112
121
  }
113
122
  exports.UpdateOperations = UpdateOperations;
114
123
  class ConditionOperations {
124
+ transaction;
115
125
  operations;
116
126
  item;
117
127
  args;
118
- constructor(operations, item, args) {
128
+ constructor(transaction, operations, item, args) {
129
+ this.transaction = transaction;
119
130
  this.operations = operations;
120
131
  this.item = item;
121
132
  this.args = args;
@@ -136,6 +147,9 @@ class ConditionOperations {
136
147
  },
137
148
  };
138
149
  }
150
+ execute(args) {
151
+ return this.transaction.execute(args);
152
+ }
139
153
  }
140
154
  exports.ConditionOperations = ConditionOperations;
141
155
  const arraysToSets = (values) => {
@@ -39,13 +39,15 @@ const getRefsToResolve = (item) => {
39
39
  for (const value of Object.values(item)) {
40
40
  if (typeof value === "object") {
41
41
  if (value instanceof Set || Array.isArray(value)) {
42
- refs.push(...[...value].map((v) => getRefsToResolve(v)).flat());
42
+ refs.push(...Array.from(value)
43
+ .map((v) => getRefsToResolve(v))
44
+ .flat());
43
45
  }
44
46
  else if (value?._type === "dynamodb:reference") {
45
47
  refs.push(value?._target);
46
48
  }
47
49
  else {
48
- refs.push(...getRefsToResolve(value));
50
+ refs.push(...getRefsToResolve(value).flat());
49
51
  }
50
52
  }
51
53
  }
@@ -62,7 +64,7 @@ const injectRefs = (item, refs) => {
62
64
  for (const [key, value] of Object.entries(item)) {
63
65
  if (typeof value === "object") {
64
66
  if (value instanceof Set || Array.isArray(value)) {
65
- item[key] = new Set([...value].map((v) => injectRefs(v, refs)));
67
+ item[key] = new Set(Array.from(value).map((v) => injectRefs(v, refs)));
66
68
  }
67
69
  else if (value?._type === "dynamodb:reference") {
68
70
  item[key] = refs[itemToStringKey(value._target)];
@@ -51,10 +51,6 @@ export type TypedParams<T extends DynamoDBItem, ValueType, ParamType = ValueType
51
51
  export type NestedParams<T = any> = Record<`${string}.${string}`, T>;
52
52
  export type NestedTypedParams<T extends DynamoDBItem, ValueType, ParamType = ValueType> = TypedParams<T, ValueType, ParamType> & NestedParams<ParamType>;
53
53
  type Prev = [never, 0, 1, 2, 3];
54
- type PathEntry = {
55
- path: string;
56
- type: any;
57
- };
58
54
  type DeepNumberPathEntries<T, Prefix extends string = "", D extends number = 3> = [D] extends [never] ? never : {
59
55
  [K in keyof T & string]-?: NonNullable<T[K]> extends number ? {
60
56
  path: `${Prefix}${K}`;
@@ -62,7 +58,11 @@ type DeepNumberPathEntries<T, Prefix extends string = "", D extends number = 3>
62
58
  } : NonNullable<T[K]> extends any[] | Set<any> | Date | Function | Uint8Array ? never : NonNullable<T[K]> extends object ? DeepNumberPathEntries<NonNullable<T[K]>, `${Prefix}${K}.`, Prev[D]> : never;
63
59
  }[keyof T & string];
64
60
  export type StrictDeepNumberUpdates<T> = {
65
- [Entry in DeepNumberPathEntries<T> & PathEntry as Entry["path"]]?: Entry["type"];
61
+ [E in DeepNumberPathEntries<T> as E extends {
62
+ path: string;
63
+ } ? E["path"] : never]?: E extends {
64
+ type: infer V;
65
+ } ? V : never;
66
66
  };
67
67
  type DeepSetPathEntries<T, Prefix extends string = "", D extends number = 3> = [
68
68
  D
@@ -73,6 +73,10 @@ type DeepSetPathEntries<T, Prefix extends string = "", D extends number = 3> = [
73
73
  } : NonNullable<T[K]> extends Date | Function | Uint8Array ? never : NonNullable<T[K]> extends object ? DeepSetPathEntries<NonNullable<T[K]>, `${Prefix}${K}.`, Prev[D]> : never;
74
74
  }[keyof T & string];
75
75
  export type DeepSetUpdates<T> = {
76
- [Entry in DeepSetPathEntries<T> & PathEntry as Entry["path"]]?: Entry["type"][] | Set<Entry["type"]>;
76
+ [E in DeepSetPathEntries<T> as E extends {
77
+ path: string;
78
+ } ? E["path"] : never]?: E extends {
79
+ type: infer V;
80
+ } ? V[] | Set<V> : never;
77
81
  };
78
82
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moicky/dynamodb",
3
- "version": "3.0.4",
3
+ "version": "3.1.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "description": "Contains a collection of convenience functions for working with AWS DynamoDB",