@aurios/mizzle 1.1.2 → 1.1.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 (116) hide show
  1. package/.turbo/turbo-build.log +37 -0
  2. package/LICENSE +21 -0
  3. package/README.md +166 -57
  4. package/dist/chunk-AQVECMXP.js +1 -0
  5. package/dist/chunk-DU7UPWBW.js +1 -0
  6. package/dist/chunk-GPYZK4WY.js +1 -0
  7. package/dist/chunk-NPPZW6VT.js +1 -0
  8. package/dist/chunk-TOYV2M4M.js +1 -0
  9. package/dist/chunk-UM3YF5EC.js +1 -0
  10. package/dist/columns.d.ts +1 -0
  11. package/dist/columns.js +1 -0
  12. package/dist/db-zHIHBm1E.d.ts +815 -0
  13. package/dist/db.d.ts +3 -0
  14. package/dist/db.js +1 -0
  15. package/dist/diff.d.ts +18 -0
  16. package/dist/diff.js +1 -0
  17. package/dist/index.d.ts +42 -0
  18. package/dist/index.js +1 -0
  19. package/dist/introspection.d.ts +7 -0
  20. package/dist/introspection.js +1 -0
  21. package/dist/operators-BVreW0ky.d.ts +719 -0
  22. package/dist/snapshot.d.ts +24 -0
  23. package/dist/snapshot.js +1 -0
  24. package/dist/table.d.ts +1 -0
  25. package/dist/table.js +1 -0
  26. package/dist/transaction-RE7LXTGV.js +1 -0
  27. package/package.json +82 -66
  28. package/src/builders/base.ts +53 -56
  29. package/src/builders/batch-get.ts +63 -58
  30. package/src/builders/batch-write.ts +81 -78
  31. package/src/builders/delete.ts +46 -53
  32. package/src/builders/insert.ts +158 -150
  33. package/src/builders/query-promise.ts +26 -35
  34. package/src/builders/relational-builder.ts +214 -191
  35. package/src/builders/select.ts +250 -237
  36. package/src/builders/transaction.ts +170 -152
  37. package/src/builders/update.ts +197 -192
  38. package/src/columns/binary-set.ts +29 -38
  39. package/src/columns/binary.ts +25 -35
  40. package/src/columns/boolean.ts +25 -30
  41. package/src/columns/date.ts +57 -64
  42. package/src/columns/index.ts +15 -15
  43. package/src/columns/json.ts +39 -48
  44. package/src/columns/list.ts +26 -36
  45. package/src/columns/map.ts +26 -34
  46. package/src/columns/number-set.ts +29 -38
  47. package/src/columns/number.ts +33 -40
  48. package/src/columns/string-set.ts +38 -47
  49. package/src/columns/string.ts +37 -49
  50. package/src/columns/uuid.ts +26 -33
  51. package/src/core/client.ts +9 -9
  52. package/src/core/column-builder.ts +194 -220
  53. package/src/core/column.ts +127 -135
  54. package/src/core/diff.ts +40 -34
  55. package/src/core/errors.ts +20 -17
  56. package/src/core/introspection.ts +62 -58
  57. package/src/core/operations.ts +17 -23
  58. package/src/core/parser.ts +82 -89
  59. package/src/core/relations.ts +164 -154
  60. package/src/core/retry.ts +52 -52
  61. package/src/core/snapshot.ts +131 -130
  62. package/src/core/strategies.ts +222 -218
  63. package/src/core/table.ts +189 -202
  64. package/src/core/validation.ts +52 -52
  65. package/src/db.ts +211 -209
  66. package/src/expressions/actions.ts +26 -26
  67. package/src/expressions/builder.ts +62 -54
  68. package/src/expressions/operators.ts +48 -48
  69. package/src/expressions/update-builder.ts +78 -76
  70. package/src/index.ts +1 -1
  71. package/src/indexes.ts +8 -8
  72. package/test/batch-resilience.test.ts +138 -0
  73. package/test/builders/delete.test.ts +100 -0
  74. package/test/builders/insert.test.ts +216 -0
  75. package/test/builders/relational-types.test.ts +55 -0
  76. package/test/builders/relational.integration.test.ts +291 -0
  77. package/test/builders/relational.test.ts +66 -0
  78. package/test/builders/select.test.ts +411 -0
  79. package/test/builders/transaction-errors.test.ts +46 -0
  80. package/test/builders/transaction-execution.test.ts +99 -0
  81. package/test/builders/transaction-proxy.test.ts +41 -0
  82. package/test/builders/update-expression.test.ts +106 -0
  83. package/test/builders/update.test.ts +179 -0
  84. package/test/core/diff.test.ts +152 -0
  85. package/test/core/expressions.test.ts +64 -0
  86. package/test/core/introspection.test.ts +47 -0
  87. package/test/core/parser.test.ts +69 -0
  88. package/test/core/snapshot-gen.test.ts +155 -0
  89. package/test/core/snapshot.test.ts +52 -0
  90. package/test/date-column.test.ts +159 -0
  91. package/test/fluent-writes.integration.test.ts +148 -0
  92. package/test/integration-retry.test.ts +77 -0
  93. package/test/integration.test.ts +105 -0
  94. package/test/item-size-error.test.ts +16 -0
  95. package/test/item-size-validation.test.ts +82 -0
  96. package/test/item-size.test.ts +47 -0
  97. package/test/iterator-pagination.integration.test.ts +132 -0
  98. package/test/jsdoc-builders.test.ts +55 -0
  99. package/test/jsdoc-schema.test.ts +107 -0
  100. package/test/json-column.test.ts +51 -0
  101. package/test/metadata.test.ts +54 -0
  102. package/test/mizzle-package.test.ts +20 -0
  103. package/test/relational-centralized.test.ts +83 -0
  104. package/test/relational-definition.test.ts +75 -0
  105. package/test/relational-init.test.ts +30 -0
  106. package/test/relational-proxy.test.ts +52 -0
  107. package/test/relations.test.ts +45 -0
  108. package/test/resilience-config.test.ts +34 -0
  109. package/test/retry-handler.test.ts +63 -0
  110. package/test/transaction.integration.test.ts +153 -0
  111. package/test/unified-select.integration.test.ts +153 -0
  112. package/test/unified-update.integration.test.ts +139 -0
  113. package/test/update.integration.test.ts +132 -0
  114. package/tsconfig.json +12 -8
  115. package/tsup.config.ts +11 -11
  116. package/vitest.config.ts +8 -0
@@ -1,14 +1,8 @@
1
- import {
2
- GetCommand,
3
- QueryCommand,
4
- ScanCommand,
5
- } from "@aws-sdk/lib-dynamodb";
6
- import { ENTITY_SYMBOLS } from "@mizzle/shared";
1
+ import { GetCommand, QueryCommand, ScanCommand } from "@aws-sdk/lib-dynamodb";
2
+ import { ENTITY_SYMBOLS } from "@repo/shared";
7
3
  import { Column } from "../core/column";
8
4
  import type { SelectedFields as SelectedFieldsBase } from "../core/operations";
9
- import {
10
- type Expression,
11
- } from "../expressions/operators";
5
+ import { type Expression } from "../expressions/operators";
12
6
  import { Entity, type InferSelectModel, type PhysicalTable } from "../core/table";
13
7
  import { BaseBuilder } from "./base";
14
8
  import type { StrategyResolution } from "../core/strategies";
@@ -18,262 +12,281 @@ import { buildExpression } from "../expressions/builder";
18
12
  export type SelectedFields = SelectedFieldsBase<Column, PhysicalTable>;
19
13
 
20
14
  export class SelectBuilder<TSelection extends SelectedFields | undefined> {
21
- constructor(
22
- private client: IMizzleClient,
23
- private fields?: TSelection,
24
- ) {}
15
+ constructor(
16
+ private client: IMizzleClient,
17
+ private fields?: TSelection,
18
+ ) {}
25
19
 
26
- /**
27
- * Specifies the entity to select from.
28
- *
29
- * @example
30
- * ```ts
31
- * const results = await db.select().from(users).execute();
32
- * ```
33
- *
34
- * @param entity The Mizzle entity (table) to query.
35
- * @returns A SelectBase instance to further chain the query.
36
- */
37
- from<TEntity extends Entity>(entity: TEntity) {
38
- return new SelectBase(entity, this.client, this.fields);
39
- }
20
+ /**
21
+ * Specifies the entity to select from.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * const results = await db.select().from(users).execute();
26
+ * ```
27
+ *
28
+ * @param entity The Mizzle entity (table) to query.
29
+ * @returns A SelectBase instance to further chain the query.
30
+ */
31
+ from<TEntity extends Entity>(entity: TEntity) {
32
+ return new SelectBase(entity, this.client, this.fields);
33
+ }
40
34
  }
41
35
 
42
36
  export class SelectBase<
43
- TEntity extends Entity,
44
- TSelection extends SelectedFields | undefined = undefined,
45
- TResult = TSelection extends undefined ? InferSelectModel<TEntity> : Record<string, unknown>,
37
+ TEntity extends Entity,
38
+ TSelection extends SelectedFields | undefined = undefined,
39
+ TResult = TSelection extends undefined ? InferSelectModel<TEntity> : Record<string, unknown>,
46
40
  > extends BaseBuilder<TEntity, TResult[]> {
47
- static readonly [ENTITY_SYMBOLS.ENTITY_KIND]: string = "SelectBase";
41
+ static readonly [ENTITY_SYMBOLS.ENTITY_KIND]: string = "SelectBase";
48
42
 
49
- private _whereClause?: Expression;
50
- private _limitVal?: number;
51
- private _pageSizeVal?: number;
52
- private _consistentReadVal?: boolean;
53
- private _sortForward: boolean = true;
54
- private _forcedIndexName?: string;
43
+ private _whereClause?: Expression;
44
+ private _limitVal?: number;
45
+ private _pageSizeVal?: number;
46
+ private _consistentReadVal?: boolean;
47
+ private _sortForward: boolean = true;
48
+ private _forcedIndexName?: string;
55
49
 
56
- constructor(
57
- entity: TEntity,
58
- client: IMizzleClient,
59
- private fields?: TSelection,
60
- ) {
61
- super(entity, client);
62
- }
50
+ constructor(
51
+ entity: TEntity,
52
+ client: IMizzleClient,
53
+ private fields?: TSelection,
54
+ ) {
55
+ super(entity, client);
56
+ }
63
57
 
64
- /**
65
- * Adds a filter criteria to the query.
66
- *
67
- * For DynamoDB, this will be used as a `KeyConditionExpression` if the
68
- * primary keys are provided, otherwise it will be used as a `FilterExpression`.
69
- *
70
- * @example
71
- * ```ts
72
- * import { eq, and, gt } from "@aurios/mizzle";
73
- *
74
- * const results = await db.select()
75
- * .from(users)
76
- * .where(and(eq(users.id, 1), gt(users.age, 18)))
77
- * .execute();
78
- * ```
79
- *
80
- * @param expression The expression to filter by.
81
- * @returns The current builder instance for chaining.
82
- */
83
- where(expression: Expression): this {
84
- this._whereClause = expression;
85
- return this;
86
- }
58
+ /**
59
+ * Adds a filter criteria to the query.
60
+ *
61
+ * For DynamoDB, this will be used as a `KeyConditionExpression` if the
62
+ * primary keys are provided, otherwise it will be used as a `FilterExpression`.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * import { eq, and, gt } from "@aurios/mizzle";
67
+ *
68
+ * const results = await db.select()
69
+ * .from(users)
70
+ * .where(and(eq(users.id, 1), gt(users.age, 18)))
71
+ * .execute();
72
+ * ```
73
+ *
74
+ * @param expression The expression to filter by.
75
+ * @returns The current builder instance for chaining.
76
+ */
77
+ where(expression: Expression): this {
78
+ this._whereClause = expression;
79
+ return this;
80
+ }
87
81
 
88
- /**
89
- * Limits the total number of items returned by the query.
90
- *
91
- * @example
92
- * ```ts
93
- * const results = await db.select().from(users).limit(10).execute();
94
- * ```
95
- *
96
- * @param val The maximum number of items to return.
97
- * @returns The current builder instance for chaining.
98
- */
99
- limit(val: number): this {
100
- this._limitVal = val;
101
- return this;
102
- }
82
+ /**
83
+ * Limits the total number of items returned by the query.
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * const results = await db.select().from(users).limit(10).execute();
88
+ * ```
89
+ *
90
+ * @param val The maximum number of items to return.
91
+ * @returns The current builder instance for chaining.
92
+ */
93
+ limit(val: number): this {
94
+ this._limitVal = val;
95
+ return this;
96
+ }
103
97
 
104
- /**
105
- * Sets the page size for the underlying DynamoDB requests.
106
- * Use this with `.iterator()` to control how many items are fetched in each network request.
107
- *
108
- * @param val The number of items per page.
109
- * @returns The current builder instance for chaining.
110
- */
111
- pageSize(val: number): this {
112
- this._pageSizeVal = val;
113
- return this;
114
- }
98
+ /**
99
+ * Sets the page size for the underlying DynamoDB requests.
100
+ * Use this with `.iterator()` to control how many items are fetched in each network request.
101
+ *
102
+ * @param val The number of items per page.
103
+ * @returns The current builder instance for chaining.
104
+ */
105
+ pageSize(val: number): this {
106
+ this._pageSizeVal = val;
107
+ return this;
108
+ }
115
109
 
116
- /**
117
- * Enables or disables consistent reads for this query.
118
- *
119
- * @param enabled Whether consistent read is enabled. Defaults to true.
120
- * @returns The current builder instance for chaining.
121
- */
122
- consistentRead(enabled: boolean = true): this {
123
- this._consistentReadVal = enabled;
124
- return this;
125
- }
110
+ /**
111
+ * Enables or disables consistent reads for this query.
112
+ *
113
+ * @param enabled Whether consistent read is enabled. Defaults to true.
114
+ * @returns The current builder instance for chaining.
115
+ */
116
+ consistentRead(enabled: boolean = true): this {
117
+ this._consistentReadVal = enabled;
118
+ return this;
119
+ }
126
120
 
127
- /**
128
- * Specifies the sort order for the query (only applicable for `Query` operations).
129
- *
130
- * @param forward If true (default), results are sorted in ascending order. If false, descending.
131
- * @returns The current builder instance for chaining.
132
- */
133
- sort(forward: boolean): this {
134
- this._sortForward = forward;
135
- return this;
136
- }
121
+ /**
122
+ * Specifies the sort order for the query (only applicable for `Query` operations).
123
+ *
124
+ * @param forward If true (default), results are sorted in ascending order. If false, descending.
125
+ * @returns The current builder instance for chaining.
126
+ */
127
+ sort(forward: boolean): this {
128
+ this._sortForward = forward;
129
+ return this;
130
+ }
137
131
 
138
- /**
139
- * Forces the use of a specific Global Secondary Index (GSI) or Local Secondary Index (LSI).
140
- *
141
- * @param name The name of the index to use.
142
- * @returns The current builder instance for chaining.
143
- */
144
- index(name: string): this {
145
- this._forcedIndexName = name;
146
- return this;
147
- }
132
+ /**
133
+ * Forces the use of a specific Global Secondary Index (GSI) or Local Secondary Index (LSI).
134
+ *
135
+ * @param name The name of the index to use.
136
+ * @returns The current builder instance for chaining.
137
+ */
138
+ index(name: string): this {
139
+ this._forcedIndexName = name;
140
+ return this;
141
+ }
148
142
 
149
- /**
150
- * Returns an async iterator that automatically handles pagination.
151
- *
152
- * @example
153
- * ```ts
154
- * for await (const user of db.select().from(users).iterator()) {
155
- * console.log(user.name);
156
- * }
157
- * ```
158
- *
159
- * @returns An AsyncIterableIterator for the query results.
160
- */
161
- iterator(): AsyncIterableIterator<TResult> {
162
- return (async function* (this: SelectBase<TEntity, TSelection, TResult>) {
163
- let lastEvaluatedKey: Record<string, unknown> | undefined;
164
- let count = 0;
143
+ /**
144
+ * Returns an async iterator that automatically handles pagination.
145
+ *
146
+ * @example
147
+ * ```ts
148
+ * for await (const user of db.select().from(users).iterator()) {
149
+ * console.log(user.name);
150
+ * }
151
+ * ```
152
+ *
153
+ * @returns An AsyncIterableIterator for the query results.
154
+ */
155
+ iterator(): AsyncIterableIterator<TResult> {
156
+ return async function* (this: SelectBase<TEntity, TSelection, TResult>) {
157
+ let lastEvaluatedKey: Record<string, unknown> | undefined;
158
+ let count = 0;
165
159
 
166
- do {
167
- const result = await this.fetchPage(lastEvaluatedKey);
168
-
169
- for (const item of result.items) {
170
- yield item;
171
- count++;
172
- if (this._limitVal !== undefined && count >= this._limitVal) {
173
- return;
174
- }
175
- }
176
-
177
- lastEvaluatedKey = result.lastEvaluatedKey;
178
- } while (lastEvaluatedKey);
179
- }).bind(this)();
180
- }
160
+ do {
161
+ const result = await this.fetchPage(lastEvaluatedKey);
181
162
 
182
- private async fetchPage(exclusiveStartKey?: Record<string, unknown>): Promise<{ items: TResult[], lastEvaluatedKey?: Record<string, unknown> }> {
183
- const resolution = this.resolveKeys(this._whereClause, undefined, this._forcedIndexName);
184
-
185
- if (resolution.hasPartitionKey && resolution.hasSortKey && !resolution.indexName && !exclusiveStartKey) {
186
- const items = await this.executeGet(resolution.keys);
187
- return { items };
188
- } else if (resolution.hasPartitionKey || resolution.indexName) {
189
- return await this.executeQuery(resolution, exclusiveStartKey);
190
- } else {
191
- return await this.executeScan(exclusiveStartKey);
163
+ for (const item of result.items) {
164
+ yield item;
165
+ count++;
166
+ if (this._limitVal !== undefined && count >= this._limitVal) {
167
+ return;
168
+ }
192
169
  }
193
- }
194
170
 
195
- /**
196
- * Executes the query and returns all matching items.
197
- *
198
- * @returns A promise that resolves to an array of items.
199
- * @throws Error if the query fails.
200
- */
201
- override async execute(): Promise<TResult[]> {
202
- const { items } = await this.fetchPage();
203
- return items;
204
- }
171
+ lastEvaluatedKey = result.lastEvaluatedKey;
172
+ } while (lastEvaluatedKey);
173
+ }.bind(this)();
174
+ }
205
175
 
206
- private async executeGet(keys: Record<string, unknown>): Promise<TResult[]> {
207
- const command = new GetCommand({
208
- TableName: this.tableName,
209
- Key: keys,
210
- ConsistentRead: this._consistentReadVal,
211
- });
176
+ private async fetchPage(
177
+ exclusiveStartKey?: Record<string, unknown>,
178
+ ): Promise<{ items: TResult[]; lastEvaluatedKey?: Record<string, unknown> }> {
179
+ const resolution = this.resolveKeys(this._whereClause, undefined, this._forcedIndexName);
212
180
 
213
- const result = await this.client.send(command) as any;
214
- return result.Item ? ([this.mapToLogical(result.Item)] as TResult[]) : [];
181
+ if (
182
+ resolution.hasPartitionKey &&
183
+ resolution.hasSortKey &&
184
+ !resolution.indexName &&
185
+ !exclusiveStartKey
186
+ ) {
187
+ const items = await this.executeGet(resolution.keys);
188
+ return { items };
189
+ } else if (resolution.hasPartitionKey || resolution.indexName) {
190
+ return await this.executeQuery(resolution, exclusiveStartKey);
191
+ } else {
192
+ return await this.executeScan(exclusiveStartKey);
215
193
  }
194
+ }
216
195
 
217
- private async executeQuery(
218
- resolution: StrategyResolution,
219
- exclusiveStartKey?: Record<string, unknown>,
220
- ): Promise<{ items: TResult[], lastEvaluatedKey?: Record<string, unknown> }> {
221
- const { expressionAttributeNames, expressionAttributeValues, addName, addValue } = this.createExpressionContext();
196
+ /**
197
+ * Executes the query and returns all matching items.
198
+ *
199
+ * @returns A promise that resolves to an array of items.
200
+ * @throws Error if the query fails.
201
+ */
202
+ override async execute(): Promise<TResult[]> {
203
+ const { items } = await this.fetchPage();
204
+ return items;
205
+ }
222
206
 
223
- const keyParts: string[] = [];
224
- const keyAttrNames = new Set<string>();
225
- for (const [key, value] of Object.entries(resolution.keys)) {
226
- keyParts.push(`${addName(key)} = ${addValue(value)}`);
227
- keyAttrNames.add(key);
228
- }
229
- const keyConditionExpression = keyParts.join(" AND ");
230
-
231
- // DynamoDB does NOT allow primary key attributes in FilterExpression
232
- const filterExpression = this._whereClause
233
- ? buildExpression(this._whereClause, addName, addValue, keyAttrNames)
234
- : undefined;
207
+ private async executeGet(keys: Record<string, unknown>): Promise<TResult[]> {
208
+ const command = new GetCommand({
209
+ TableName: this.tableName,
210
+ Key: keys,
211
+ ConsistentRead: this._consistentReadVal,
212
+ });
235
213
 
236
- const command = new QueryCommand({
237
- TableName: this.tableName,
238
- IndexName: resolution.indexName,
239
- KeyConditionExpression: keyConditionExpression,
240
- FilterExpression: filterExpression || undefined,
241
- ExpressionAttributeNames: Object.keys(expressionAttributeNames).length > 0 ? expressionAttributeNames : undefined,
242
- ExpressionAttributeValues: Object.keys(expressionAttributeValues).length > 0 ? expressionAttributeValues : undefined,
243
- Limit: this._pageSizeVal ?? this._limitVal,
244
- ScanIndexForward: this._sortForward,
245
- ConsistentRead: resolution.indexName ? undefined : this._consistentReadVal,
246
- ExclusiveStartKey: exclusiveStartKey,
247
- });
214
+ const result = (await this.client.send(command)) as any;
215
+ return result.Item ? ([this.mapToLogical(result.Item)] as TResult[]) : [];
216
+ }
248
217
 
249
- const response = await this.client.send(command) as any;
250
- return {
251
- items: (response.Items || []).map((item: Record<string, unknown>) => this.mapToLogical(item)) as TResult[],
252
- lastEvaluatedKey: response.LastEvaluatedKey,
253
- };
218
+ private async executeQuery(
219
+ resolution: StrategyResolution,
220
+ exclusiveStartKey?: Record<string, unknown>,
221
+ ): Promise<{ items: TResult[]; lastEvaluatedKey?: Record<string, unknown> }> {
222
+ const { expressionAttributeNames, expressionAttributeValues, addName, addValue } =
223
+ this.createExpressionContext();
224
+
225
+ const keyParts: string[] = [];
226
+ const keyAttrNames = new Set<string>();
227
+ for (const [key, value] of Object.entries(resolution.keys)) {
228
+ keyParts.push(`${addName(key)} = ${addValue(value)}`);
229
+ keyAttrNames.add(key);
254
230
  }
231
+ const keyConditionExpression = keyParts.join(" AND ");
255
232
 
256
- private async executeScan(exclusiveStartKey?: Record<string, unknown>): Promise<{ items: TResult[], lastEvaluatedKey?: Record<string, unknown> }> {
257
- const { expressionAttributeNames, expressionAttributeValues, addName, addValue } = this.createExpressionContext();
233
+ // DynamoDB does NOT allow primary key attributes in FilterExpression
234
+ const filterExpression = this._whereClause
235
+ ? buildExpression(this._whereClause, addName, addValue, keyAttrNames)
236
+ : undefined;
258
237
 
259
- const filterExpression = this._whereClause
260
- ? buildExpression(this._whereClause, addName, addValue)
261
- : undefined;
238
+ const command = new QueryCommand({
239
+ TableName: this.tableName,
240
+ IndexName: resolution.indexName,
241
+ KeyConditionExpression: keyConditionExpression,
242
+ FilterExpression: filterExpression || undefined,
243
+ ExpressionAttributeNames:
244
+ Object.keys(expressionAttributeNames).length > 0 ? expressionAttributeNames : undefined,
245
+ ExpressionAttributeValues:
246
+ Object.keys(expressionAttributeValues).length > 0 ? expressionAttributeValues : undefined,
247
+ Limit: this._pageSizeVal ?? this._limitVal,
248
+ ScanIndexForward: this._sortForward,
249
+ ConsistentRead: resolution.indexName ? undefined : this._consistentReadVal,
250
+ ExclusiveStartKey: exclusiveStartKey,
251
+ });
262
252
 
263
- const command = new ScanCommand({
264
- TableName: this.tableName,
265
- FilterExpression: filterExpression,
266
- ExpressionAttributeNames: Object.keys(expressionAttributeNames).length > 0 ? expressionAttributeNames : undefined,
267
- ExpressionAttributeValues: Object.keys(expressionAttributeValues).length > 0 ? expressionAttributeValues : undefined,
268
- Limit: this._pageSizeVal ?? this._limitVal,
269
- ConsistentRead: this._consistentReadVal,
270
- ExclusiveStartKey: exclusiveStartKey,
271
- });
253
+ const response = (await this.client.send(command)) as any;
254
+ return {
255
+ items: (response.Items || []).map((item: Record<string, unknown>) =>
256
+ this.mapToLogical(item),
257
+ ) as TResult[],
258
+ lastEvaluatedKey: response.LastEvaluatedKey,
259
+ };
260
+ }
272
261
 
273
- const response = await this.client.send(command) as any;
274
- return {
275
- items: (response.Items || []).map((item: Record<string, unknown>) => this.mapToLogical(item)) as TResult[],
276
- lastEvaluatedKey: response.LastEvaluatedKey,
277
- };
278
- }
262
+ private async executeScan(
263
+ exclusiveStartKey?: Record<string, unknown>,
264
+ ): Promise<{ items: TResult[]; lastEvaluatedKey?: Record<string, unknown> }> {
265
+ const { expressionAttributeNames, expressionAttributeValues, addName, addValue } =
266
+ this.createExpressionContext();
267
+
268
+ const filterExpression = this._whereClause
269
+ ? buildExpression(this._whereClause, addName, addValue)
270
+ : undefined;
271
+
272
+ const command = new ScanCommand({
273
+ TableName: this.tableName,
274
+ FilterExpression: filterExpression,
275
+ ExpressionAttributeNames:
276
+ Object.keys(expressionAttributeNames).length > 0 ? expressionAttributeNames : undefined,
277
+ ExpressionAttributeValues:
278
+ Object.keys(expressionAttributeValues).length > 0 ? expressionAttributeValues : undefined,
279
+ Limit: this._pageSizeVal ?? this._limitVal,
280
+ ConsistentRead: this._consistentReadVal,
281
+ ExclusiveStartKey: exclusiveStartKey,
282
+ });
283
+
284
+ const response = (await this.client.send(command)) as any;
285
+ return {
286
+ items: (response.Items || []).map((item: Record<string, unknown>) =>
287
+ this.mapToLogical(item),
288
+ ) as TResult[],
289
+ lastEvaluatedKey: response.LastEvaluatedKey,
290
+ };
291
+ }
279
292
  }