betterddb 0.6.0 → 0.6.4

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.
Files changed (49) hide show
  1. package/.eslintrc.cjs +41 -0
  2. package/lib/betterddb.js +29 -17
  3. package/lib/builders/batch-get-builder.js +6 -2
  4. package/lib/builders/create-builder.js +14 -6
  5. package/lib/builders/delete-builder.js +8 -5
  6. package/lib/builders/get-builder.js +11 -6
  7. package/lib/builders/query-builder.js +38 -31
  8. package/lib/builders/scan-builder.js +17 -11
  9. package/lib/builders/update-builder.js +27 -18
  10. package/lib/operator.js +10 -10
  11. package/lib/{betterddb.d.ts → types/betterddb.d.ts} +13 -9
  12. package/lib/types/betterddb.d.ts.map +1 -0
  13. package/lib/{builders → types/builders}/batch-get-builder.d.ts +2 -1
  14. package/lib/types/builders/batch-get-builder.d.ts.map +1 -0
  15. package/lib/{builders → types/builders}/create-builder.d.ts +3 -2
  16. package/lib/types/builders/create-builder.d.ts.map +1 -0
  17. package/lib/{builders → types/builders}/delete-builder.d.ts +3 -2
  18. package/lib/types/builders/delete-builder.d.ts.map +1 -0
  19. package/lib/{builders → types/builders}/get-builder.d.ts +3 -2
  20. package/lib/types/builders/get-builder.d.ts.map +1 -0
  21. package/lib/{builders → types/builders}/query-builder.d.ts +4 -3
  22. package/lib/types/builders/query-builder.d.ts.map +1 -0
  23. package/lib/{builders → types/builders}/scan-builder.d.ts +4 -3
  24. package/lib/types/builders/scan-builder.d.ts.map +1 -0
  25. package/lib/{builders → types/builders}/update-builder.d.ts +3 -2
  26. package/lib/types/builders/update-builder.d.ts.map +1 -0
  27. package/lib/types/index.d.ts +2 -0
  28. package/lib/types/index.d.ts.map +1 -0
  29. package/lib/types/operator.d.ts +3 -0
  30. package/lib/types/operator.d.ts.map +1 -0
  31. package/lib/types/{paginated-result.d.ts → types/paginated-result.d.ts} +1 -0
  32. package/lib/types/types/paginated-result.d.ts.map +1 -0
  33. package/package.json +15 -5
  34. package/prettier.config.js +6 -0
  35. package/src/betterddb.ts +46 -37
  36. package/src/builders/batch-get-builder.ts +11 -6
  37. package/src/builders/create-builder.ts +42 -27
  38. package/src/builders/delete-builder.ts +28 -17
  39. package/src/builders/get-builder.ts +26 -19
  40. package/src/builders/query-builder.ts +64 -44
  41. package/src/builders/scan-builder.ts +18 -14
  42. package/src/builders/update-builder.ts +53 -27
  43. package/src/index.ts +1 -1
  44. package/src/operator.ts +21 -21
  45. package/src/types/paginated-result.ts +1 -1
  46. package/test/update.test.ts +6 -0
  47. package/tsconfig.json +25 -11
  48. package/lib/index.d.ts +0 -1
  49. package/lib/operator.d.ts +0 -2
@@ -1,8 +1,11 @@
1
-
2
-
3
- import { TransactWriteCommand, UpdateCommand } from '@aws-sdk/lib-dynamodb';
4
- import { BetterDDB } from '../betterddb';
5
- import { TransactWriteItem, Update, UpdateItemInput } from '@aws-sdk/client-dynamodb';
1
+ import { TransactWriteCommand, UpdateCommand } from "@aws-sdk/lib-dynamodb";
2
+ import { BetterDDB } from "../betterddb";
3
+ import {
4
+ TransactWriteItem,
5
+ Update,
6
+ UpdateItemInput,
7
+ } from "@aws-sdk/client-dynamodb";
8
+ import { z } from "zod";
6
9
  interface UpdateActions<T> {
7
10
  set?: Partial<T>;
8
11
  remove?: (keyof T)[];
@@ -12,16 +15,26 @@ interface UpdateActions<T> {
12
15
 
13
16
  export class UpdateBuilder<T> {
14
17
  private actions: UpdateActions<T> = {};
15
- private condition?: { expression: string; attributeValues: Record<string, any> };
18
+ private condition?: {
19
+ expression: string;
20
+ attributeValues: Record<string, any>;
21
+ };
16
22
  // When using transaction mode, we store extra transaction items.
17
23
  private extraTransactItems: TransactWriteItem[] = [];
18
24
 
19
25
  // Reference to the parent BetterDDB instance and key.
20
- constructor(private parent: BetterDDB<T>, private key: Partial<T>) {}
26
+ constructor(
27
+ private parent: BetterDDB<T>,
28
+ private key: Partial<T>,
29
+ ) {}
21
30
 
22
31
  // Chainable methods:
23
32
  public set(attrs: Partial<T>): this {
24
- this.actions.set = { ...this.actions.set, ...attrs };
33
+ const partialSchema = (
34
+ this.parent.getSchema() as unknown as z.ZodObject<any>
35
+ ).partial();
36
+ const validated = partialSchema.parse(attrs);
37
+ this.actions.set = { ...this.actions.set, ...validated };
25
38
  return this;
26
39
  }
27
40
 
@@ -31,7 +44,11 @@ export class UpdateBuilder<T> {
31
44
  }
32
45
 
33
46
  public add(attrs: Partial<Record<keyof T, number | Set<any>>>): this {
34
- this.actions.add = { ...this.actions.add, ...attrs };
47
+ const partialSchema = (
48
+ this.parent.getSchema() as unknown as z.ZodObject<any>
49
+ ).partial();
50
+ const validated = partialSchema.parse(attrs);
51
+ this.actions.add = { ...this.actions.add, ...validated };
35
52
  return this;
36
53
  }
37
54
 
@@ -43,7 +60,10 @@ export class UpdateBuilder<T> {
43
60
  /**
44
61
  * Adds a condition expression to the update.
45
62
  */
46
- public setCondition(expression: string, attributeValues: Record<string, any>): this {
63
+ public setCondition(
64
+ expression: string,
65
+ attributeValues: Record<string, any>,
66
+ ): this {
47
67
  if (this.condition) {
48
68
  // Merge conditions with AND.
49
69
  this.condition.expression += ` AND ${expression}`;
@@ -89,18 +109,18 @@ export class UpdateBuilder<T> {
89
109
  setParts.push(`${nameKey} = ${valueKey}`);
90
110
  }
91
111
  if (setParts.length > 0) {
92
- clauses.push(`SET ${setParts.join(', ')}`);
112
+ clauses.push(`SET ${setParts.join(", ")}`);
93
113
  }
94
114
  }
95
115
 
96
116
  // Build REMOVE clause.
97
117
  if (this.actions.remove && this.actions.remove.length > 0) {
98
- const removeParts = this.actions.remove.map(attr => {
118
+ const removeParts = this.actions.remove.map((attr) => {
99
119
  const nameKey = `#remove_${String(attr)}`;
100
120
  ExpressionAttributeNames[nameKey] = String(attr);
101
121
  return nameKey;
102
122
  });
103
- clauses.push(`REMOVE ${removeParts.join(', ')}`);
123
+ clauses.push(`REMOVE ${removeParts.join(", ")}`);
104
124
  }
105
125
 
106
126
  // Build ADD clause.
@@ -114,7 +134,7 @@ export class UpdateBuilder<T> {
114
134
  addParts.push(`${nameKey} ${valueKey}`);
115
135
  }
116
136
  if (addParts.length > 0) {
117
- clauses.push(`ADD ${addParts.join(', ')}`);
137
+ clauses.push(`ADD ${addParts.join(", ")}`);
118
138
  }
119
139
  }
120
140
 
@@ -129,7 +149,7 @@ export class UpdateBuilder<T> {
129
149
  deleteParts.push(`${nameKey} ${valueKey}`);
130
150
  }
131
151
  if (deleteParts.length > 0) {
132
- clauses.push(`DELETE ${deleteParts.join(', ')}`);
152
+ clauses.push(`DELETE ${deleteParts.join(", ")}`);
133
153
  }
134
154
  }
135
155
 
@@ -139,9 +159,9 @@ export class UpdateBuilder<T> {
139
159
  }
140
160
 
141
161
  return {
142
- updateExpression: clauses.join(' '),
162
+ updateExpression: clauses.join(" "),
143
163
  attributeNames: ExpressionAttributeNames,
144
- attributeValues: ExpressionAttributeValues
164
+ attributeValues: ExpressionAttributeValues,
145
165
  };
146
166
  }
147
167
 
@@ -149,13 +169,14 @@ export class UpdateBuilder<T> {
149
169
  * Returns a transaction update item that can be included in a transactWrite call.
150
170
  */
151
171
  public toTransactUpdate(): TransactWriteItem {
152
- const { updateExpression, attributeNames, attributeValues } = this.buildExpression();
172
+ const { updateExpression, attributeNames, attributeValues } =
173
+ this.buildExpression();
153
174
  const updateItem: Update = {
154
175
  TableName: this.parent.getTableName(),
155
176
  Key: this.parent.buildKey(this.key),
156
177
  UpdateExpression: updateExpression,
157
178
  ExpressionAttributeNames: attributeNames,
158
- ExpressionAttributeValues: attributeValues
179
+ ExpressionAttributeValues: attributeValues,
159
180
  };
160
181
  if (this.condition && this.condition.expression) {
161
182
  updateItem.ConditionExpression = this.condition.expression;
@@ -172,32 +193,37 @@ export class UpdateBuilder<T> {
172
193
  const myTransactItem = this.toTransactUpdate();
173
194
  // Combine with extra transaction items.
174
195
  const allItems = [...this.extraTransactItems, myTransactItem];
175
- await this.parent.getClient().send(new TransactWriteCommand({
176
- TransactItems: allItems
177
- }));
196
+ await this.parent.getClient().send(
197
+ new TransactWriteCommand({
198
+ TransactItems: allItems,
199
+ }),
200
+ );
178
201
  // After transaction, retrieve the updated item.
179
202
  const result = await this.parent.get(this.key).execute();
180
203
  if (result === null) {
181
- throw new Error('Item not found after transaction update');
204
+ throw new Error("Item not found after transaction update");
182
205
  }
183
206
  return result;
184
207
  } else {
185
208
  // Normal update flow.
186
- const { updateExpression, attributeNames, attributeValues } = this.buildExpression();
209
+ const { updateExpression, attributeNames, attributeValues } =
210
+ this.buildExpression();
187
211
  const params: UpdateItemInput = {
188
212
  TableName: this.parent.getTableName(),
189
213
  Key: this.parent.buildKey(this.key),
190
214
  UpdateExpression: updateExpression,
191
215
  ExpressionAttributeNames: attributeNames,
192
216
  ExpressionAttributeValues: attributeValues,
193
- ReturnValues: 'ALL_NEW'
217
+ ReturnValues: "ALL_NEW",
194
218
  };
195
219
  if (this.condition && this.condition.expression) {
196
220
  params.ConditionExpression = this.condition.expression;
197
221
  }
198
- const result = await this.parent.getClient().send(new UpdateCommand(params));
222
+ const result = await this.parent
223
+ .getClient()
224
+ .send(new UpdateCommand(params));
199
225
  if (!result.Attributes) {
200
- throw new Error('No attributes returned after update');
226
+ throw new Error("No attributes returned after update");
201
227
  }
202
228
  return this.parent.getSchema().parse(result.Attributes) as T;
203
229
  }
package/src/index.ts CHANGED
@@ -1 +1 @@
1
- export * from './betterddb';
1
+ export * from "./betterddb";
package/src/operator.ts CHANGED
@@ -1,43 +1,43 @@
1
1
  export type Operator =
2
- | '=='
3
- | '!='
4
- | '<'
5
- | '<='
6
- | '>'
7
- | '>='
8
- | 'begins_with'
9
- | 'between'
10
- | 'contains';
2
+ | "=="
3
+ | "!="
4
+ | "<"
5
+ | "<="
6
+ | ">"
7
+ | ">="
8
+ | "begins_with"
9
+ | "between"
10
+ | "contains";
11
11
 
12
12
  export function getOperatorExpression(
13
13
  operator: Operator,
14
14
  nameKey: string,
15
15
  valueKey: string,
16
- secondValueKey?: string
16
+ secondValueKey?: string,
17
17
  ): string {
18
18
  switch (operator) {
19
- case '==':
19
+ case "==":
20
20
  return `${nameKey} = ${valueKey}`;
21
- case '!=':
21
+ case "!=":
22
22
  return `${nameKey} <> ${valueKey}`;
23
- case '<':
23
+ case "<":
24
24
  return `${nameKey} < ${valueKey}`;
25
- case '<=':
25
+ case "<=":
26
26
  return `${nameKey} <= ${valueKey}`;
27
- case '>':
27
+ case ">":
28
28
  return `${nameKey} > ${valueKey}`;
29
- case '>=':
29
+ case ">=":
30
30
  return `${nameKey} >= ${valueKey}`;
31
- case 'begins_with':
31
+ case "begins_with":
32
32
  return `begins_with(${nameKey}, ${valueKey})`;
33
- case 'between':
33
+ case "between":
34
34
  if (!secondValueKey) {
35
35
  throw new Error("The 'between' operator requires two value keys");
36
36
  }
37
37
  return `${nameKey} BETWEEN ${valueKey} AND ${secondValueKey}`;
38
- case 'contains':
38
+ case "contains":
39
39
  return `contains(${nameKey}, ${valueKey})`;
40
40
  default:
41
- throw new Error(`Unsupported operator: ${operator}`);
41
+ throw new Error("Unsupported operator");
42
42
  }
43
- }
43
+ }
@@ -1,4 +1,4 @@
1
1
  export type PaginatedResult<T> = {
2
2
  items: T[];
3
3
  lastKey: Record<string, any> | undefined;
4
- }
4
+ };
@@ -75,4 +75,10 @@ describe('BetterDDB - Update Operation', () => {
75
75
  expect(updatedUser.name).toBe('Jane Doe');
76
76
  expect(updatedUser.email).toBe('john@example.com');
77
77
  });
78
+
79
+ // it('should update an existing item using UpdateBuilder with null values throws', async () => {
80
+ // const updates = { name: 'Jane Doe', email: null };
81
+ // // @ts-ignore
82
+ // expect(await userDdb.update({ id: 'user-123', email: 'john@example.com' }).set(updates).execute()).rejects.toThrow();
83
+ // });
78
84
  });
package/tsconfig.json CHANGED
@@ -1,12 +1,26 @@
1
1
  {
2
- "compilerOptions": {
3
- "target": "ES2019",
4
- "module": "commonjs",
5
- "declaration": true,
6
- "outDir": "lib",
7
- "strict": true,
8
- "esModuleInterop": true
9
- },
10
- "include": ["src"]
11
- }
12
-
2
+ "compilerOptions": {
3
+ "lib": ["ES2022", "DOM"],
4
+ "target": "ES2022",
5
+ "module": "NodeNext",
6
+ "moduleResolution": "NodeNext",
7
+ "outDir": "./lib/",
8
+ "declarationDir": "./lib/types",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "skipLibCheck": true,
13
+ "checkJs": true,
14
+ "allowJs": true,
15
+ "declaration": true,
16
+ "declarationMap": true,
17
+ "allowSyntheticDefaultImports": true
18
+ },
19
+ /* Path Aliases */
20
+ "baseUrl": ".",
21
+ "paths": {
22
+ "~/*": ["./src/*"]
23
+ },
24
+ "files": ["./src/index.ts"],
25
+ "exclude": ["node_modules"]
26
+ }
package/lib/index.d.ts DELETED
@@ -1 +0,0 @@
1
- export * from './betterddb';
package/lib/operator.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export type Operator = '==' | '!=' | '<' | '<=' | '>' | '>=' | 'begins_with' | 'between' | 'contains';
2
- export declare function getOperatorExpression(operator: Operator, nameKey: string, valueKey: string, secondValueKey?: string): string;