@jzhuo3/dynamodb-lib 0.1.1 → 0.1.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.
@@ -135,6 +135,20 @@ export declare function ListPrepend(attribute: string, value: Array<any>, upsert
135
135
  export declare function Remove(attribute: string): DynamoDBExpression;
136
136
  export declare function SetAdd(attribute: string, value: Set<any>): DynamoDBExpression;
137
137
  export declare function SetDelete(attribute: string, value: Set<any>): DynamoDBExpression;
138
+ /** JSON-compatible value used by expression descriptions. */
139
+ export type DynamoDBExpressionSerializedValue = null | boolean | number | string | DynamoDBExpressionSerializedValue[] | {
140
+ [key: string]: DynamoDBExpressionSerializedValue;
141
+ };
142
+ export type DynamoDBExpressionDescription = {
143
+ /** DynamoDB expression string, e.g. `#attr0 = :val0`. */
144
+ expression: string;
145
+ /** Placeholder -> attribute name (e.g. `#attr0` -> `VersionStamp`). */
146
+ expressionAttributeNames: Record<string, string>;
147
+ /** Placeholder -> JSON-compatible value; native non-JSON values use explicit `$type` tags. */
148
+ expressionAttributeValues: Record<string, DynamoDBExpressionSerializedValue>;
149
+ /** Which update clause the expression belongs to (SET/REMOVE/ADD/DELETE). */
150
+ updateExpressionGroup?: 'SET' | 'REMOVE' | 'ADD' | 'DELETE';
151
+ };
138
152
  export declare class DynamoDBExpression {
139
153
  expressionAttributeNameMap: Map<string, string>;
140
154
  expressionAttributeValueMap: Map<string, any> | null;
@@ -143,6 +157,14 @@ export declare class DynamoDBExpression {
143
157
  constructor(expressionAttributeNameMap?: Map<string, string>, // e.g. #attr0 -> attribute
144
158
  expressionAttributeValueMap?: Map<string, any> | null, // e.g. :val0 -> value
145
159
  expression?: string, updateExpressionGroup?: "SET" | "REMOVE" | "ADD" | "DELETE" | undefined);
160
+ /**
161
+ * Detached, JSON-serializable description of the expression. Intended for logging,
162
+ * debugging, and unit tests that need to assert on a built expression
163
+ * without reaching into the internal Maps.
164
+ * Sets, bigint, binary, Maps, NumberValue, undefined and non-finite numbers
165
+ * use explicit `$type` tags. Circular and unsupported values throw TypeError.
166
+ */
167
+ describe(): DynamoDBExpressionDescription;
146
168
  }
147
169
  export declare enum DynamoDBTransactionMode {
148
170
  READ = "READ",
package/dist/esm/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Agent } from 'https';
2
2
  import { DynamoDBClient, ReturnConsumedCapacity, } from '@aws-sdk/client-dynamodb';
3
- import { DynamoDBDocumentClient, GetCommand, BatchGetCommand, QueryCommand, PutCommand, BatchWriteCommand, UpdateCommand, DeleteCommand, TransactGetCommand, TransactWriteCommand, } from '@aws-sdk/lib-dynamodb';
3
+ import { DynamoDBDocumentClient, NumberValue, GetCommand, BatchGetCommand, QueryCommand, PutCommand, BatchWriteCommand, UpdateCommand, DeleteCommand, TransactGetCommand, TransactWriteCommand, } from '@aws-sdk/lib-dynamodb';
4
4
  import { diff, camel, snake } from 'radash';
5
5
  const silentLogger = { debug() { }, warn() { }, error() { } };
6
6
  export class DynamoDBError extends Error {
@@ -324,6 +324,55 @@ export function SetDelete(attribute, value) {
324
324
  const expression = `${path} :val0`;
325
325
  return new DynamoDBExpression(expressionAttributeNameMap, expressionAttributeValueMap, expression, 'DELETE');
326
326
  }
327
+ function serializeExpressionValue(value, ancestors = new Set()) {
328
+ if (value === null || typeof value === 'string' || typeof value === 'boolean')
329
+ return value;
330
+ if (typeof value === 'number') {
331
+ return Number.isFinite(value) ? value : { $type: 'Number', value: String(value) };
332
+ }
333
+ if (typeof value === 'bigint')
334
+ return { $type: 'BigInt', value: value.toString() };
335
+ if (value === undefined)
336
+ return { $type: 'Undefined' };
337
+ if (typeof value !== 'object') {
338
+ throw new TypeError(`Cannot describe expression value of type ${typeof value}`);
339
+ }
340
+ if (ancestors.has(value))
341
+ throw new TypeError('Cannot describe circular expression value');
342
+ ancestors.add(value);
343
+ try {
344
+ if (value instanceof NumberValue)
345
+ return { $type: 'NumberValue', value: value.toString() };
346
+ if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
347
+ const bytes = value instanceof ArrayBuffer
348
+ ? Buffer.from(value)
349
+ : Buffer.from(value.buffer, value.byteOffset, value.byteLength);
350
+ return { $type: 'Binary', encoding: 'base64', value: bytes.toString('base64') };
351
+ }
352
+ if (value instanceof Set) {
353
+ return { $type: 'Set', values: Array.from(value, item => serializeExpressionValue(item, ancestors)) };
354
+ }
355
+ if (Array.isArray(value))
356
+ return Array.from(value, item => serializeExpressionValue(item, ancestors));
357
+ if (value instanceof Map) {
358
+ return { $type: 'Map', entries: Array.from(value, ([key, item]) => [
359
+ serializeExpressionValue(key, ancestors), serializeExpressionValue(item, ancestors),
360
+ ]) };
361
+ }
362
+ if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
363
+ throw new TypeError('Cannot describe unsupported expression value object');
364
+ }
365
+ if (Object.getOwnPropertySymbols(value).some(key => Object.prototype.propertyIsEnumerable.call(value, key))) {
366
+ throw new TypeError('Cannot describe expression value with symbol keys');
367
+ }
368
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [
369
+ key, serializeExpressionValue(item, ancestors),
370
+ ]));
371
+ }
372
+ finally {
373
+ ancestors.delete(value);
374
+ }
375
+ }
327
376
  export class DynamoDBExpression {
328
377
  expressionAttributeNameMap;
329
378
  expressionAttributeValueMap;
@@ -337,6 +386,27 @@ export class DynamoDBExpression {
337
386
  this.expression = expression;
338
387
  this.updateExpressionGroup = updateExpressionGroup;
339
388
  }
389
+ /**
390
+ * Detached, JSON-serializable description of the expression. Intended for logging,
391
+ * debugging, and unit tests that need to assert on a built expression
392
+ * without reaching into the internal Maps.
393
+ * Sets, bigint, binary, Maps, NumberValue, undefined and non-finite numbers
394
+ * use explicit `$type` tags. Circular and unsupported values throw TypeError.
395
+ */
396
+ describe() {
397
+ return {
398
+ expression: this.expression,
399
+ expressionAttributeNames: Object.fromEntries(this.expressionAttributeNameMap),
400
+ expressionAttributeValues: this.expressionAttributeValueMap
401
+ ? Object.fromEntries(Array.from(this.expressionAttributeValueMap, ([key, value]) => [
402
+ key, serializeExpressionValue(value),
403
+ ]))
404
+ : {},
405
+ ...(this.updateExpressionGroup !== undefined
406
+ ? { updateExpressionGroup: this.updateExpressionGroup }
407
+ : {}),
408
+ };
409
+ }
340
410
  }
341
411
  export var DynamoDBTransactionMode;
342
412
  (function (DynamoDBTransactionMode) {