@lafken/dynamo 0.14.2 → 0.14.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.
@@ -27,6 +27,13 @@ interface IndexBase<T extends Function> {
27
27
  */
28
28
  projection?: (keyof T['prototype'])[] | 'ALL';
29
29
  }
30
+ /**
31
+ * Filter expression for a single DynamoDB attribute.
32
+ *
33
+ * Supports exact matches, `anything-but`, `exists`, `prefix`, and empty-string
34
+ * conditions. Values can be provided as a single value, an array of values,
35
+ * or null.
36
+ */
30
37
  type AttributeFilter<T> = {
31
38
  [key in keyof T]?: (T[key] | {
32
39
  'anything-but': T[key][];
@@ -359,18 +366,47 @@ export interface ExternalTableMetadata extends Omit<ExternalTableProps<any>, 'na
359
366
  name: string;
360
367
  }
361
368
  export type TableMetadata = InternalTableMetadata | ExternalTableMetadata;
369
+ /**
370
+ * Base properties for a DynamoDB table field definition.
371
+ */
362
372
  export interface FieldProps {
373
+ /** The TypeScript constructor that determines the field's DynamoDB type. */
363
374
  type?: StringConstructor | NumberConstructor | BooleanConstructor | [Function] | Function;
364
375
  }
376
+ /**
377
+ * Resolved metadata for a DynamoDB table field after the `@Field`
378
+ * decorator is processed.
379
+ */
365
380
  export interface FieldMetadata {
381
+ /** Property name as declared on the class. */
366
382
  name: string;
383
+ /** Resolved DynamoDB type (e.g. `'string'`, `'number'`, `'boolean'`). */
367
384
  type: FieldTypes;
368
385
  }
386
+ /**
387
+ * Map of property names to their resolved DynamoDB field metadata.
388
+ */
369
389
  export type FieldsMetadata = Record<string, FieldMetadata>;
390
+ /**
391
+ * Sentinel type used internally to mark DynamoDB partition key properties.
392
+ * Properties typed with `PrimaryPartition<T>` are recognized as partition
393
+ * key candidates during table metadata resolution.
394
+ */
370
395
  type Partition = {
371
396
  __special: unknown;
372
397
  };
398
+ /**
399
+ * Marks a type as a DynamoDB partition key candidate.
400
+ *
401
+ * @typeParam T - The type to mark as a partition key.
402
+ */
373
403
  export type PrimaryPartition<T = never> = T | (T & Partition);
404
+ /**
405
+ * Extracts only the partition key properties from a DynamoDB table class.
406
+ *
407
+ * Filters the keys of `T` to include only those whose type includes the
408
+ * {@link Partition} sentinel, giving the list of valid partition key fields.
409
+ */
374
410
  export type TablePartition<T> = {
375
411
  [K in keyof T as [Extract<T[K], Partition>] extends [never] ? never : K]: T[K];
376
412
  };
@@ -5,6 +5,25 @@ const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
5
5
  const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
6
6
  const base_1 = require("../base/base");
7
7
  const update_utils_1 = require("./update.utils");
8
+ /**
9
+ * Recursively removes undefined values from an object.
10
+ * This prevents DynamoDB expression errors when undefined values
11
+ * are passed in setValues or replaceValues.
12
+ */
13
+ function removeUndefinedValues(obj) {
14
+ const result = {};
15
+ for (const key in obj) {
16
+ if (obj[key] !== undefined) {
17
+ if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
18
+ result[key] = removeUndefinedValues(obj[key]);
19
+ }
20
+ else {
21
+ result[key] = obj[key];
22
+ }
23
+ }
24
+ }
25
+ return result;
26
+ }
8
27
  class UpdateBuilder extends base_1.QueryBuilderBase {
9
28
  queryOptions;
10
29
  command;
@@ -30,17 +49,21 @@ class UpdateBuilder extends base_1.QueryBuilderBase {
30
49
  }
31
50
  prepare() {
32
51
  const { removeValues = {}, replaceValues = {}, setValues = {}, condition, } = this.queryOptions.inputProps;
52
+ // Remove undefined values from setValues and replaceValues to prevent
53
+ // DynamoDB expression errors when undefined values are passed
54
+ const filteredSetValues = removeUndefinedValues(setValues);
55
+ const filteredReplaceValues = removeUndefinedValues(replaceValues);
33
56
  if (Object.keys(removeValues).length > 0 &&
34
- Object.keys(replaceValues).length > 0 &&
35
- Object.keys(setValues).length > 0) {
57
+ Object.keys(filteredReplaceValues).length > 0 &&
58
+ Object.keys(filteredSetValues).length > 0) {
36
59
  throw new Error('You must assign a value to update');
37
60
  }
38
61
  let setExpression = '';
39
- if (Object.keys(setValues).length > 0) {
40
- setExpression = this.setValues(setValues, true, [], this.expressionGroupCounter++);
62
+ if (Object.keys(filteredSetValues).length > 0) {
63
+ setExpression = this.setValues(filteredSetValues, true, [], this.expressionGroupCounter++);
41
64
  }
42
- if (Object.keys(replaceValues).length > 0) {
43
- setExpression += `${setExpression ? ',' : ''} ${this.setValues(replaceValues, false, [], this.expressionGroupCounter++)}`;
65
+ if (Object.keys(filteredReplaceValues).length > 0) {
66
+ setExpression += `${setExpression ? ',' : ''} ${this.setValues(filteredReplaceValues, false, [], this.expressionGroupCounter++)}`;
44
67
  }
45
68
  const removeExpression = this.removeValues(removeValues);
46
69
  const keyCondition = this.queryOptions.inputProps.keyCondition;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lafken/dynamo",
3
- "version": "0.14.2",
3
+ "version": "0.14.4",
4
4
  "private": false,
5
5
  "description": "Define DynamoDB tables using TypeScript decorators - type-safe, declarative infrastructure with Lafken",
6
6
  "keywords": [
@@ -59,7 +59,7 @@
59
59
  "@aws-sdk/client-dynamodb": "^3.1106.0",
60
60
  "@aws-sdk/util-dynamodb": "^3.996.7",
61
61
  "reflect-metadata": "^0.2.2",
62
- "@lafken/resolver": "0.14.2"
62
+ "@lafken/resolver": "0.14.4"
63
63
  },
64
64
  "devDependencies": {
65
65
  "@cdktn/provider-aws": "^25.0.0",
@@ -68,18 +68,18 @@
68
68
  "@vitest/runner": "^4.1.10",
69
69
  "aws-sdk-client-mock": "^4.1.0",
70
70
  "cdktn": "^0.24.0",
71
- "cdktn-vitest": "^1.0.0",
71
+ "@cdktn/vitest": "^0.1.0",
72
72
  "constructs": "^10.8.0",
73
73
  "typescript": "7.0.2",
74
74
  "unplugin-swc": "^1.5.10",
75
75
  "vitest": "^4.1.10",
76
- "@lafken/common": "0.14.2"
76
+ "@lafken/common": "0.14.4"
77
77
  },
78
78
  "peerDependencies": {
79
79
  "@cdktn/provider-aws": ">=23.0.0",
80
80
  "cdktn": ">=0.22.0",
81
81
  "constructs": ">=10.7.0",
82
- "@lafken/common": "0.14.2"
82
+ "@lafken/common": "0.14.4"
83
83
  },
84
84
  "engines": {
85
85
  "node": ">=20.19"