betterddb 0.8.0 → 0.8.2

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 (82) hide show
  1. package/dist/src/betterddb.d.ts +137 -0
  2. package/dist/src/betterddb.d.ts.map +1 -0
  3. package/dist/src/betterddb.js +165 -0
  4. package/dist/src/betterddb.js.map +1 -0
  5. package/dist/src/builders/batch-get-builder.d.ts +16 -0
  6. package/dist/src/builders/batch-get-builder.d.ts.map +1 -0
  7. package/dist/src/builders/batch-get-builder.js +54 -0
  8. package/dist/src/builders/batch-get-builder.js.map +1 -0
  9. package/dist/src/builders/create-builder.d.ts +12 -0
  10. package/dist/src/builders/create-builder.d.ts.map +1 -0
  11. package/dist/src/builders/create-builder.js +84 -0
  12. package/dist/src/builders/create-builder.js.map +1 -0
  13. package/dist/src/builders/delete-builder.d.ts +18 -0
  14. package/dist/src/builders/delete-builder.d.ts.map +1 -0
  15. package/dist/src/builders/delete-builder.js +75 -0
  16. package/dist/src/builders/delete-builder.js.map +1 -0
  17. package/dist/src/builders/get-builder.d.ts +18 -0
  18. package/dist/src/builders/get-builder.d.ts.map +1 -0
  19. package/dist/src/builders/get-builder.js +76 -0
  20. package/dist/src/builders/get-builder.js.map +1 -0
  21. package/dist/src/builders/index.d.ts +8 -0
  22. package/dist/src/builders/index.d.ts.map +1 -0
  23. package/{src/builders/index.ts → dist/src/builders/index.js} +1 -0
  24. package/dist/src/builders/index.js.map +1 -0
  25. package/dist/src/builders/query-builder.d.ts +29 -0
  26. package/dist/src/builders/query-builder.d.ts.map +1 -0
  27. package/dist/src/builders/query-builder.js +171 -0
  28. package/dist/src/builders/query-builder.js.map +1 -0
  29. package/dist/src/builders/scan-builder.d.ts +21 -0
  30. package/dist/src/builders/scan-builder.d.ts.map +1 -0
  31. package/dist/src/builders/scan-builder.js +76 -0
  32. package/dist/src/builders/scan-builder.js.map +1 -0
  33. package/dist/src/builders/update-builder.d.ts +37 -0
  34. package/dist/src/builders/update-builder.d.ts.map +1 -0
  35. package/dist/src/builders/update-builder.js +301 -0
  36. package/dist/src/builders/update-builder.js.map +1 -0
  37. package/dist/src/index.d.ts +5 -0
  38. package/dist/src/index.d.ts.map +1 -0
  39. package/{src/index.ts → dist/src/index.js} +1 -0
  40. package/dist/src/index.js.map +1 -0
  41. package/dist/src/operator.d.ts +3 -0
  42. package/dist/src/operator.d.ts.map +1 -0
  43. package/dist/src/operator.js +28 -0
  44. package/dist/src/operator.js.map +1 -0
  45. package/dist/src/types/index.d.ts +2 -0
  46. package/dist/src/types/index.d.ts.map +1 -0
  47. package/{src/types/index.ts → dist/src/types/index.js} +1 -0
  48. package/dist/src/types/index.js.map +1 -0
  49. package/dist/src/types/paginated-result.d.ts +6 -0
  50. package/dist/src/types/paginated-result.d.ts.map +1 -0
  51. package/dist/src/types/paginated-result.js +2 -0
  52. package/dist/src/types/paginated-result.js.map +1 -0
  53. package/dist/tsconfig.tsbuildinfo +1 -0
  54. package/package.json +9 -8
  55. package/.github/workflows/npm-publish.yml +0 -33
  56. package/.github/workflows/test.yml +0 -42
  57. package/CONTRIBUTING.md +0 -225
  58. package/LICENCSE +0 -21
  59. package/babel.config.cjs +0 -6
  60. package/docker-compose.yml +0 -16
  61. package/eslint.config.mjs +0 -29
  62. package/jest.config.cjs +0 -17
  63. package/prettier.config.js +0 -6
  64. package/src/betterddb.ts +0 -267
  65. package/src/builders/batch-get-builder.ts +0 -56
  66. package/src/builders/create-builder.ts +0 -97
  67. package/src/builders/delete-builder.ts +0 -87
  68. package/src/builders/get-builder.ts +0 -78
  69. package/src/builders/query-builder.ts +0 -242
  70. package/src/builders/scan-builder.ts +0 -98
  71. package/src/builders/update-builder.ts +0 -363
  72. package/src/operator.ts +0 -43
  73. package/src/types/paginated-result.ts +0 -6
  74. package/test/batch-get.test.ts +0 -122
  75. package/test/create.test.ts +0 -121
  76. package/test/delete.test.ts +0 -93
  77. package/test/get.test.ts +0 -98
  78. package/test/query.test.ts +0 -206
  79. package/test/scan.test.ts +0 -130
  80. package/test/update.test.ts +0 -355
  81. package/test/utils/table-setup.ts +0 -62
  82. package/tsconfig.json +0 -23
@@ -1,97 +0,0 @@
1
- import {
2
- type AttributeValue,
3
- type Put,
4
- type TransactWriteItem,
5
- } from "@aws-sdk/client-dynamodb";
6
- import { type BetterDDB } from "../betterddb.js";
7
- import { PutCommand, TransactWriteCommand } from "@aws-sdk/lib-dynamodb";
8
-
9
- export class CreateBuilder<T> {
10
- private extraTransactItems: TransactWriteItem[] = [];
11
-
12
- constructor(
13
- private parent: BetterDDB<T>,
14
- private item: T,
15
- ) {}
16
-
17
- public async execute(): Promise<T> {
18
- const validated = this.parent.getSchema().parse(this.item);
19
- if (this.extraTransactItems.length > 0) {
20
- // Build our update transaction item.
21
- const myTransactItem = this.toTransactPut();
22
- // Combine with extra transaction items.
23
- const allItems = [...this.extraTransactItems, myTransactItem];
24
- await this.parent.getClient().send(
25
- new TransactWriteCommand({
26
- TransactItems: allItems,
27
- }),
28
- );
29
- // After transaction, retrieve the updated item.
30
- const result = await this.parent.get(this.item).execute();
31
- if (result === null) {
32
- throw new Error("Item not found after transaction create");
33
- }
34
- return result;
35
- } else {
36
- let finalItem: T = {
37
- ...this.item,
38
- entityType: this.parent.getEntityType(),
39
- };
40
- if (this.parent.getTimestamps()) {
41
- const now = new Date().toISOString();
42
- finalItem = { ...finalItem, createdAt: now, updatedAt: now } as T;
43
- }
44
-
45
- // Compute and merge primary key.
46
- const computedKeys = this.parent.buildKey(validated as Partial<T>);
47
- finalItem = { ...finalItem, ...computedKeys };
48
-
49
- // Compute and merge index attributes.
50
- const indexAttributes = this.parent.buildIndexes(validated as Partial<T>);
51
- finalItem = { ...finalItem, ...indexAttributes };
52
-
53
- await this.parent.getClient().send(
54
- new PutCommand({
55
- TableName: this.parent.getTableName(),
56
- Item: finalItem as Record<string, AttributeValue>,
57
- }),
58
- );
59
-
60
- return validated as T;
61
- }
62
- }
63
-
64
- public transactWrite(ops: TransactWriteItem[] | TransactWriteItem): this {
65
- if (Array.isArray(ops)) {
66
- this.extraTransactItems.push(...ops);
67
- } else {
68
- this.extraTransactItems.push(ops);
69
- }
70
- return this;
71
- }
72
-
73
- public toTransactPut(): TransactWriteItem {
74
- const validated = this.parent.getSchema().parse(this.item);
75
- let finalItem: T = {
76
- ...this.item,
77
- entityType: this.parent.getEntityType(),
78
- };
79
- if (this.parent.getTimestamps()) {
80
- const now = new Date().toISOString();
81
- finalItem = { ...finalItem, createdAt: now, updatedAt: now } as T;
82
- }
83
-
84
- // Compute and merge primary key.
85
- const computedKeys = this.parent.buildKey(validated as Partial<T>);
86
- finalItem = { ...finalItem, ...computedKeys };
87
-
88
- // Compute and merge index attributes.
89
- const indexAttributes = this.parent.buildIndexes(validated as Partial<T>);
90
- finalItem = { ...finalItem, ...indexAttributes };
91
- const putItem: Put = {
92
- TableName: this.parent.getTableName(),
93
- Item: finalItem as Record<string, AttributeValue>,
94
- };
95
- return { Put: putItem };
96
- }
97
- }
@@ -1,87 +0,0 @@
1
- import { type BetterDDB } from "../betterddb.js";
2
- import {
3
- type TransactWriteItem,
4
- type DeleteItemInput,
5
- } from "@aws-sdk/client-dynamodb";
6
- import {
7
- type NativeAttributeValue,
8
- TransactWriteCommand,
9
- DeleteCommand,
10
- } from "@aws-sdk/lib-dynamodb";
11
- export class DeleteBuilder<T> {
12
- private condition?: {
13
- expression: string;
14
- attributeValues: Record<string, NativeAttributeValue>;
15
- };
16
- private extraTransactItems: TransactWriteItem[] = [];
17
- constructor(
18
- private parent: BetterDDB<T>,
19
- private key: Partial<T>,
20
- ) {}
21
-
22
- /**
23
- * Specify a condition expression for the delete operation.
24
- */
25
- public withCondition(
26
- expression: string,
27
- attributeValues: Record<string, NativeAttributeValue>,
28
- ): this {
29
- if (this.condition) {
30
- this.condition.expression += ` AND ${expression}`;
31
- Object.assign(this.condition.attributeValues, attributeValues);
32
- } else {
33
- this.condition = { expression, attributeValues };
34
- }
35
- return this;
36
- }
37
-
38
- public async execute(): Promise<void> {
39
- if (this.extraTransactItems.length > 0) {
40
- // Build our update transaction item.
41
- const myTransactItem = this.toTransactDelete();
42
- // Combine with extra transaction items.
43
- const allItems = [...this.extraTransactItems, myTransactItem];
44
- await this.parent.getClient().send(
45
- new TransactWriteCommand({
46
- TransactItems: allItems,
47
- }),
48
- );
49
- // After transaction, retrieve the updated item.
50
- const result = await this.parent.get(this.key).execute();
51
- if (result === null) {
52
- throw new Error("Item not found after transaction delete");
53
- }
54
- } else {
55
- const params: DeleteItemInput = {
56
- TableName: this.parent.getTableName(),
57
- Key: this.parent.buildKey(this.key),
58
- };
59
- if (this.condition) {
60
- params.ConditionExpression = this.condition.expression;
61
- params.ExpressionAttributeValues = this.condition.attributeValues;
62
- }
63
- await this.parent.getClient().send(new DeleteCommand(params));
64
- }
65
- }
66
-
67
- public transactWrite(ops: TransactWriteItem[] | TransactWriteItem): this {
68
- if (Array.isArray(ops)) {
69
- this.extraTransactItems.push(...ops);
70
- } else {
71
- this.extraTransactItems.push(ops);
72
- }
73
- return this;
74
- }
75
-
76
- public toTransactDelete(): TransactWriteItem {
77
- const deleteItem: DeleteItemInput = {
78
- TableName: this.parent.getTableName(),
79
- Key: this.parent.buildKey(this.key),
80
- };
81
- if (this.condition) {
82
- deleteItem.ConditionExpression = this.condition.expression;
83
- deleteItem.ExpressionAttributeValues = this.condition.attributeValues;
84
- }
85
- return { Delete: deleteItem };
86
- }
87
- }
@@ -1,78 +0,0 @@
1
- import { type BetterDDB } from "../betterddb.js";
2
- import { TransactGetCommand, GetCommand } from "@aws-sdk/lib-dynamodb";
3
- import {
4
- type GetItemInput,
5
- type TransactGetItem,
6
- } from "@aws-sdk/client-dynamodb";
7
- export class GetBuilder<T> {
8
- private projectionExpression?: string;
9
- private expressionAttributeNames: Record<string, string> = {};
10
- private extraTransactItems: TransactGetItem[] = [];
11
- constructor(
12
- private parent: BetterDDB<T>,
13
- private key: Partial<T>,
14
- ) {}
15
-
16
- /**
17
- * Specify a projection by providing an array of attribute names.
18
- */
19
- public withProjection(attributes: (keyof T)[]): this {
20
- this.projectionExpression = attributes
21
- .map((attr) => `#${String(attr)}`)
22
- .join(", ");
23
- for (const attr of attributes) {
24
- this.expressionAttributeNames[`#${String(attr)}`] = String(attr);
25
- }
26
- return this;
27
- }
28
-
29
- public async execute(): Promise<T | null> {
30
- if (this.extraTransactItems.length > 0) {
31
- // Build our update transaction item.
32
- const myTransactItem = this.toTransactGet();
33
- // Combine with extra transaction items.
34
- const allItems = [...this.extraTransactItems, myTransactItem];
35
- await this.parent.getClient().send(
36
- new TransactGetCommand({
37
- TransactItems: allItems,
38
- }),
39
- );
40
- // After transaction, retrieve the updated item.
41
- const result = await this.parent.get(this.key).execute();
42
- return result;
43
- } else {
44
- const params: GetItemInput = {
45
- TableName: this.parent.getTableName(),
46
- Key: this.parent.buildKey(this.key),
47
- };
48
- if (this.projectionExpression) {
49
- params.ProjectionExpression = this.projectionExpression;
50
- params.ExpressionAttributeNames = this.expressionAttributeNames;
51
- }
52
- const result = await this.parent.getClient().send(new GetCommand(params));
53
- if (!result.Item) return null;
54
- return this.parent.getSchema().parse(result.Item) as T;
55
- }
56
- }
57
-
58
- public transactGet(ops: TransactGetItem[] | TransactGetItem): this {
59
- if (Array.isArray(ops)) {
60
- this.extraTransactItems.push(...ops);
61
- } else {
62
- this.extraTransactItems.push(ops);
63
- }
64
- return this;
65
- }
66
-
67
- public toTransactGet(): TransactGetItem {
68
- const getItem: GetItemInput = {
69
- TableName: this.parent.getTableName(),
70
- Key: this.parent.buildKey(this.key),
71
- };
72
- if (this.projectionExpression) {
73
- getItem.ProjectionExpression = this.projectionExpression;
74
- getItem.ExpressionAttributeNames = this.expressionAttributeNames;
75
- }
76
- return { Get: getItem };
77
- }
78
- }
@@ -1,242 +0,0 @@
1
- import {
2
- type NativeAttributeValue,
3
- QueryCommand,
4
- type QueryCommandInput,
5
- } from "@aws-sdk/lib-dynamodb";
6
- import { type BetterDDB, type GSIConfig } from "../betterddb.js";
7
- import { getOperatorExpression, type Operator } from "../operator.js";
8
- import { type PaginatedResult } from "../types/paginated-result.js";
9
-
10
- export class QueryBuilder<T> {
11
- private keyConditions: string[] = [];
12
- private filterConditions: string[] = [];
13
- private expressionAttributeNames: Record<string, string> = {};
14
- private expressionAttributeValues: Record<string, NativeAttributeValue>;
15
- private index?: GSIConfig<T>;
16
- private limit?: number;
17
- private lastKey?: Record<string, NativeAttributeValue>;
18
- private ascending = true;
19
-
20
- constructor(
21
- private parent: BetterDDB<T>,
22
- private key: Partial<T>,
23
- ) {
24
- const keys = this.parent.getKeys();
25
- const pkName = keys.primary.name;
26
- const builtKey = this.parent.buildKey(this.key);
27
-
28
- this.expressionAttributeNames = {
29
- "#pk": pkName,
30
- };
31
- this.expressionAttributeValues = {
32
- ":pk_value": builtKey[pkName] as Record<string, NativeAttributeValue>,
33
- };
34
- }
35
-
36
- public usingIndex(indexName: string): this {
37
- if (!this.parent.getKeys().gsis) {
38
- throw new Error("No global secondary indexes defined for this table");
39
- }
40
- if (!(indexName in this.parent.getKeys().gsis!)) {
41
- throw new Error("index does not exist");
42
- }
43
-
44
- this.index = this.parent.getKeys().gsis![indexName];
45
-
46
- if (!this.index) {
47
- throw new Error("Failed to get index configuration");
48
- }
49
-
50
- const pkName = this.index.primary.name;
51
- const builtKey = this.parent.buildIndexes(this.key);
52
- this.expressionAttributeNames["#pk"] = pkName;
53
- this.expressionAttributeValues[":pk_value"] = builtKey[pkName] as Record<
54
- string,
55
- NativeAttributeValue
56
- >;
57
-
58
- return this;
59
- }
60
-
61
- public sortAscending(): this {
62
- this.ascending = true;
63
- return this;
64
- }
65
-
66
- public sortDescending(): this {
67
- this.ascending = false;
68
- return this;
69
- }
70
-
71
- public where(
72
- operator: Operator,
73
- values: Partial<T> | [Partial<T>, Partial<T>],
74
- ): this {
75
- const keys = this.parent.getKeys();
76
- // Determine the sort key name from either the index or the primary keys.
77
- const sortKeyName = this.index ? this.index.sort?.name : keys.sort?.name;
78
- if (!sortKeyName) {
79
- throw new Error("Sort key is not defined for this table/index.");
80
- }
81
- const nameKey = "#sk";
82
- this.expressionAttributeNames[nameKey] = sortKeyName;
83
-
84
- // Enforce that a complex sort key requires an object input.
85
- if (typeof values !== "object" || values === null) {
86
- throw new Error(
87
- `For complex sort keys, please provide an object with all necessary properties.`,
88
- );
89
- }
90
-
91
- if (operator === "between") {
92
- if (!Array.isArray(values) || values.length !== 2) {
93
- throw new Error(
94
- `For 'between' operator, values must be a tuple of two objects`,
95
- );
96
- }
97
- const valueKeyStart = ":sk_start";
98
- const valueKeyEnd = ":sk_end";
99
- // Use the key definition's build function to build the key from the full object.
100
- this.expressionAttributeValues[valueKeyStart] = this.index
101
- ? (this.parent.buildIndexes(values[0])[sortKeyName] as Record<
102
- string,
103
- NativeAttributeValue
104
- >)
105
- : (this.parent.buildKey(values[0])[sortKeyName] as Record<
106
- string,
107
- NativeAttributeValue
108
- >);
109
- this.expressionAttributeValues[valueKeyEnd] = this.index
110
- ? (this.parent.buildIndexes(values[1])[sortKeyName] as Record<
111
- string,
112
- NativeAttributeValue
113
- >)
114
- : (this.parent.buildKey(values[1])[sortKeyName] as Record<
115
- string,
116
- NativeAttributeValue
117
- >);
118
- this.keyConditions.push(
119
- `${nameKey} BETWEEN ${valueKeyStart} AND ${valueKeyEnd}`,
120
- );
121
- } else if (operator === "begins_with") {
122
- const valueKey = ":sk_value";
123
- this.expressionAttributeValues[valueKey] = this.index
124
- ? (this.parent.buildIndexes(values as Partial<T>)[
125
- sortKeyName
126
- ] as Record<string, NativeAttributeValue>)
127
- : (this.parent.buildKey(values as Partial<T>)[sortKeyName] as Record<
128
- string,
129
- NativeAttributeValue
130
- >);
131
- this.keyConditions.push(`begins_with(${nameKey}, ${valueKey})`);
132
- } else {
133
- // For eq, lt, lte, gt, gte:
134
- const valueKey = ":sk_value";
135
- this.expressionAttributeValues[valueKey] = this.index
136
- ? (this.parent.buildIndexes(values as Partial<T>)[
137
- sortKeyName
138
- ] as Record<string, NativeAttributeValue>)
139
- : (this.parent.buildKey(values as Partial<T>)[sortKeyName] as Record<
140
- string,
141
- NativeAttributeValue
142
- >);
143
- const condition = getOperatorExpression(operator, nameKey, valueKey);
144
- this.keyConditions.push(condition);
145
- }
146
- return this;
147
- }
148
-
149
- public filter(attribute: keyof T, operator: Operator, values: unknown): this {
150
- const attrStr = String(attribute);
151
- const randomString = Math.random().toString(36).substring(2, 15);
152
- const placeholderName = `#attr_${attrStr}_${randomString}`;
153
- this.expressionAttributeNames[placeholderName] = attrStr;
154
- if (operator === "between") {
155
- if (!Array.isArray(values) || values.length !== 2) {
156
- throw new Error(
157
- "For 'between' operator, values must be a tuple of two items",
158
- );
159
- }
160
- const placeholderValueStart = `:val_start_${attrStr}_${randomString}`;
161
- const placeholderValueEnd = `:val_end_${attrStr}_${randomString}`;
162
- this.expressionAttributeValues[placeholderValueStart] =
163
- values[0] as Record<string, NativeAttributeValue>;
164
- this.expressionAttributeValues[placeholderValueEnd] = values[1] as Record<
165
- string,
166
- NativeAttributeValue
167
- >;
168
- this.filterConditions.push(
169
- `${placeholderName} BETWEEN ${placeholderValueStart} AND ${placeholderValueEnd}`,
170
- );
171
- } else if (operator === "begins_with" || operator === "contains") {
172
- const placeholderValue = `:val_${attrStr}_${randomString}`;
173
- this.expressionAttributeValues[placeholderValue] = values as Record<
174
- string,
175
- NativeAttributeValue
176
- >;
177
- this.filterConditions.push(
178
- `${operator}(${placeholderName}, ${placeholderValue})`,
179
- );
180
- } else {
181
- const placeholderValue = `:val_${attrStr}_${randomString}`;
182
- this.expressionAttributeValues[placeholderValue] = values as Record<
183
- string,
184
- NativeAttributeValue
185
- >;
186
- const condition = getOperatorExpression(
187
- operator,
188
- placeholderName,
189
- placeholderValue,
190
- );
191
- this.filterConditions.push(condition);
192
- }
193
-
194
- return this;
195
- }
196
-
197
- public limitResults(limit: number): this {
198
- this.limit = limit;
199
- return this;
200
- }
201
-
202
- public startFrom(lastKey: Record<string, NativeAttributeValue>): this {
203
- this.lastKey = lastKey;
204
- return this;
205
- }
206
-
207
- /**
208
- * Executes the query and returns a Promise that resolves with an array of items.
209
- */
210
- public async execute(): Promise<PaginatedResult<T>> {
211
- this.keyConditions.unshift(`#pk = :pk_value`);
212
- const keyConditionExpression = this.keyConditions.join(" AND ");
213
-
214
- const params: QueryCommandInput = {
215
- TableName: this.parent.getTableName(),
216
- KeyConditionExpression: keyConditionExpression,
217
- ExpressionAttributeNames: this.expressionAttributeNames,
218
- ExpressionAttributeValues: this.expressionAttributeValues,
219
- ScanIndexForward: this.ascending,
220
- Limit: this.limit,
221
- ExclusiveStartKey: this.lastKey,
222
- IndexName: this.index?.name ?? undefined,
223
- };
224
-
225
- if (this.parent.getEntityType()) {
226
- this.filterConditions.push(`#entity = :entity_value`);
227
- this.expressionAttributeNames["#entity"] = "entityType";
228
- this.expressionAttributeValues[":entity_value"] =
229
- this.parent.getEntityType();
230
- }
231
- params.FilterExpression =
232
- this.filterConditions.length > 0
233
- ? this.filterConditions.join(" AND ")
234
- : undefined;
235
-
236
- const result = await this.parent.getClient().send(new QueryCommand(params));
237
- return {
238
- items: this.parent.getSchema().array().parse(result.Items) as T[],
239
- lastKey: result.LastEvaluatedKey ?? undefined,
240
- };
241
- }
242
- }
@@ -1,98 +0,0 @@
1
- import {
2
- ScanCommand,
3
- type ScanCommandInput,
4
- type NativeAttributeValue,
5
- } from "@aws-sdk/lib-dynamodb";
6
- import { type BetterDDB } from "../betterddb.js";
7
- import { getOperatorExpression, type Operator } from "../operator.js";
8
- import { type PaginatedResult } from "../types/paginated-result.js";
9
-
10
- export class ScanBuilder<T> {
11
- private filters: string[] = [];
12
- private expressionAttributeNames: Record<string, string> = {};
13
- private expressionAttributeValues: Record<string, NativeAttributeValue> = {};
14
- private limit?: number;
15
- private lastKey?: Record<string, NativeAttributeValue>;
16
-
17
- constructor(private parent: BetterDDB<T>) {}
18
-
19
- public where(attribute: keyof T, operator: Operator, values: unknown): this {
20
- const attrStr = String(attribute);
21
- const nameKey = `#attr_${attrStr}`;
22
- this.expressionAttributeNames[nameKey] = attrStr;
23
- if (operator === "between") {
24
- if (!Array.isArray(values) || values.length !== 2) {
25
- throw new Error(
26
- `For 'between' operator, values must be a tuple of two items`,
27
- );
28
- }
29
- const valueKeyStart = `:val_start_${attrStr}`;
30
- const valueKeyEnd = `:val_end_${attrStr}`;
31
- this.expressionAttributeValues[valueKeyStart] = values[0] as Record<
32
- string,
33
- NativeAttributeValue
34
- >;
35
- this.expressionAttributeValues[valueKeyEnd] = values[1] as Record<
36
- string,
37
- NativeAttributeValue
38
- >;
39
- this.filters.push(
40
- `${nameKey} BETWEEN ${valueKeyStart} AND ${valueKeyEnd}`,
41
- );
42
- } else if (operator === "begins_with" || operator === "contains") {
43
- const valueKey = `:val_${attrStr}`;
44
- this.expressionAttributeValues[valueKey] = values as Record<
45
- string,
46
- NativeAttributeValue
47
- >;
48
- this.filters.push(`${operator}(${nameKey}, ${valueKey})`);
49
- } else {
50
- const valueKey = `:val_${attrStr}`;
51
- this.expressionAttributeValues[valueKey] = values as Record<
52
- string,
53
- NativeAttributeValue
54
- >;
55
- const condition = getOperatorExpression(operator, nameKey, valueKey);
56
- this.filters.push(condition);
57
- }
58
- return this;
59
- }
60
-
61
- public limitResults(limit: number): this {
62
- this.limit = limit;
63
- return this;
64
- }
65
-
66
- public startFrom(lastKey: Record<string, NativeAttributeValue>): this {
67
- this.lastKey = lastKey;
68
- return this;
69
- }
70
-
71
- /**
72
- * Executes the scan and returns a Promise that resolves with an array of items.
73
- */
74
- public async execute(): Promise<PaginatedResult<T>> {
75
- const params: ScanCommandInput = {
76
- TableName: this.parent.getTableName(),
77
- ExpressionAttributeNames: this.expressionAttributeNames,
78
- ExpressionAttributeValues: this.expressionAttributeValues,
79
- Limit: this.limit,
80
- ExclusiveStartKey: this.lastKey,
81
- };
82
-
83
- if (this.parent.getEntityType()) {
84
- this.filters.push(`#entity = :entity_value`);
85
- this.expressionAttributeNames["#entity"] = "entityType";
86
- this.expressionAttributeValues[":entity_value"] =
87
- this.parent.getEntityType();
88
- }
89
- params.FilterExpression = this.filters.join(" AND ");
90
-
91
- const result = await this.parent.getClient().send(new ScanCommand(params));
92
-
93
- return {
94
- items: this.parent.getSchema().array().parse(result.Items) as T[],
95
- lastKey: result.LastEvaluatedKey ?? undefined,
96
- };
97
- }
98
- }