@ftschopp/dynatable-core 1.4.3 → 1.4.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.
- package/CHANGELOG.md +7 -0
- package/dist/builders/update/create-update-builder.d.ts.map +1 -1
- package/dist/builders/update/create-update-builder.js +25 -0
- package/dist/utils/model-utils.d.ts +9 -3
- package/dist/utils/model-utils.d.ts.map +1 -1
- package/dist/utils/model-utils.js +15 -7
- package/package.json +9 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## @ftschopp/dynatable-core [1.4.4](https://github.com/ftschopp/dynatable/compare/@ftschopp/dynatable-core@1.4.3...@ftschopp/dynatable-core@1.4.4) (2026-05-09)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **core:** detect SET-collisions on auto-recomputed index keys; stripInternalKeys recurses ([#38](https://github.com/ftschopp/dynatable/issues/38)) ([d609c5b](https://github.com/ftschopp/dynatable/commit/d609c5bbeb566145ec034ec9883246a6d77d7cf6)), closes [#17](https://github.com/ftschopp/dynatable/issues/17) [#GSI1](https://github.com/ftschopp/dynatable/issues/GSI1) [#GSI1](https://github.com/ftschopp/dynatable/issues/GSI1)
|
|
7
|
+
|
|
1
8
|
## @ftschopp/dynatable-core [1.4.3](https://github.com/ftschopp/dynatable/compare/@ftschopp/dynatable-core@1.4.2...@ftschopp/dynatable-core@1.4.3) (2026-05-09)
|
|
2
9
|
|
|
3
10
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-update-builder.d.ts","sourceRoot":"","sources":["../../../src/builders/update/create-update-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,EAAgC,SAAS,EAA4B,MAAM,WAAW,CAAC;AAC9F,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"create-update-builder.d.ts","sourceRoot":"","sources":["../../../src/builders/update/create-update-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,EAAgC,SAAS,EAA4B,MAAM,WAAW,CAAC;AAC9F,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAa7D;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EACvC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC,EACnB,MAAM,EAAE,cAAc,EACtB,cAAc,GAAE,SAAS,EAAO,EAChC,aAAa,GAAE;IACb,GAAG,EAAE,YAAY,EAAE,CAAC;IACpB,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,GAAG,EAAE,YAAY,EAAE,CAAC;IACpB,MAAM,EAAE,YAAY,EAAE,CAAC;CACuB,EAChD,UAAU,GAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,aAAsB,EACnF,YAAY,SAAI,EAChB,gBAAgB,UAAQ,EACxB,MAAM,CAAC,EAAE,cAAc,EACvB,YAAY,CAAC,EAAE,YAAY,EAC3B,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,GAClC,aAAa,CAAC,KAAK,CAAC,CAsWtB"}
|
|
@@ -4,6 +4,15 @@ exports.createUpdateBuilder = createUpdateBuilder;
|
|
|
4
4
|
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
|
|
5
5
|
const shared_1 = require("../shared");
|
|
6
6
|
const model_utils_1 = require("../../utils/model-utils");
|
|
7
|
+
/**
|
|
8
|
+
* Pull the LHS attribute name out of a SET-action expression like
|
|
9
|
+
* `#GSI1PK = :v_0` → `'GSI1PK'`. Returns `undefined` for shapes the
|
|
10
|
+
* builder doesn't emit so callers can ignore them safely.
|
|
11
|
+
*/
|
|
12
|
+
function extractAttrName(action) {
|
|
13
|
+
const m = action.expression.match(/^\s*#([A-Za-z0-9_]+)\s*=/);
|
|
14
|
+
return m?.[1];
|
|
15
|
+
}
|
|
7
16
|
/**
|
|
8
17
|
* Creates an UpdateBuilder for an item key and table.
|
|
9
18
|
*
|
|
@@ -120,6 +129,22 @@ function createUpdateBuilder(tableName, key, client, prevConditions = [], update
|
|
|
120
129
|
`but the templates cannot be fully resolved from the update payload. ` +
|
|
121
130
|
`Include the missing fields in .set():\n${details}`);
|
|
122
131
|
}
|
|
132
|
+
// If the user's `.set()` already targets the same index-key
|
|
133
|
+
// attribute (e.g. `.set('GSI1PK', 'foo')`), refuse to silently
|
|
134
|
+
// emit a second SET against the same path. DynamoDB rejects
|
|
135
|
+
// `SET #GSI1PK = :a, #GSI1PK = :b` outright, and namespacing the
|
|
136
|
+
// placeholders only hides which one wins. Force the caller to
|
|
137
|
+
// resolve the conflict explicitly.
|
|
138
|
+
const userSetKeys = new Set(actionsToProcess.set.map(extractAttrName));
|
|
139
|
+
userSetKeys.delete(undefined);
|
|
140
|
+
const conflicts = Object.keys(idxActions).filter((k) => userSetKeys.has(k));
|
|
141
|
+
if (conflicts.length > 0) {
|
|
142
|
+
throw new Error(`Update would write the same secondary-index key twice: ` +
|
|
143
|
+
`[${conflicts.join(', ')}] is recomputed from the index template AND ` +
|
|
144
|
+
`set explicitly via .set(). Either remove the explicit .set() and let ` +
|
|
145
|
+
`the recomputation handle it, or include all template variables in ` +
|
|
146
|
+
`.set() so you take full control.`);
|
|
147
|
+
}
|
|
123
148
|
for (const [indexName, resolved] of Object.entries(idxActions)) {
|
|
124
149
|
const valueName = getUniqueValueName(indexName);
|
|
125
150
|
actionsToProcess.set.push({
|
|
@@ -50,9 +50,15 @@ export declare const applyPostDefaults: <M extends ModelDefinition>(model: M, va
|
|
|
50
50
|
timestamps?: boolean;
|
|
51
51
|
}) => InferModel<M>;
|
|
52
52
|
/**
|
|
53
|
-
* Removes internal DynamoDB keys from an item or array of items
|
|
54
|
-
*
|
|
55
|
-
*
|
|
53
|
+
* Removes internal DynamoDB keys from an item or array of items.
|
|
54
|
+
*
|
|
55
|
+
* Recurses into nested plain objects and arrays so an `_type` / `PK` / `SK`
|
|
56
|
+
* field embedded inside an attribute value (e.g. a denormalized snapshot
|
|
57
|
+
* of another entity) is also stripped. Class instances such as `Date` are
|
|
58
|
+
* passed through untouched — recursing into them would lose their type.
|
|
59
|
+
*
|
|
60
|
+
* @param data - Single item, array of items, or any nested value from DynamoDB
|
|
61
|
+
* @returns Data with internal keys removed at every level
|
|
56
62
|
*/
|
|
57
63
|
export declare const stripInternalKeys: <T>(data: T | T[] | undefined) => T | T[] | undefined;
|
|
58
64
|
//# sourceMappingURL=model-utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-utils.d.ts","sourceRoot":"","sources":["../../src/utils/model-utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAiB,eAAe,EAAE,MAAM,cAAc,CAAC;AAG1E;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAAI,UAAU,MAAM,KAAG,MAAM,EAG5D,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAI,UAAU,MAAM,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAG,MAoB7E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,eAAe,EACnD,OAAO,CAAC,EACR,OAAO,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC1B,OAAM,KAAK,GAAG,OAAO,GAAG,MAAc,KACrC,MAAM,CAAC,MAAM,EAAE,MAAM,CAWvB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;CACnE,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,GAAI,CAAC,SAAS,eAAe,EAC3D,OAAO,CAAC,EACR,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5B,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAC3B,mBAwBF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,SAAS,eAAe,EACzD,OAAO,CAAC,EACR,eAAe,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAClC,UAAU;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,KACrD,UAAU,CAAC,CAAC,CAiCd,CAAC;AAOF
|
|
1
|
+
{"version":3,"file":"model-utils.d.ts","sourceRoot":"","sources":["../../src/utils/model-utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAiB,eAAe,EAAE,MAAM,cAAc,CAAC;AAG1E;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAAI,UAAU,MAAM,KAAG,MAAM,EAG5D,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAI,UAAU,MAAM,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAG,MAoB7E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,eAAe,EACnD,OAAO,CAAC,EACR,OAAO,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC1B,OAAM,KAAK,GAAG,OAAO,GAAG,MAAc,KACrC,MAAM,CAAC,MAAM,EAAE,MAAM,CAWvB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;CACnE,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,GAAI,CAAC,SAAS,eAAe,EAC3D,OAAO,CAAC,EACR,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5B,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAC3B,mBAwBF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,SAAS,eAAe,EACzD,OAAO,CAAC,EACR,eAAe,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAClC,UAAU;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,KACrD,UAAU,CAAC,CAAC,CAiCd,CAAC;AAOF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,SAAS,KAAG,CAAC,GAAG,CAAC,EAAE,GAAG,SAqB1E,CAAC"}
|
|
@@ -125,9 +125,15 @@ exports.applyPostDefaults = applyPostDefaults;
|
|
|
125
125
|
*/
|
|
126
126
|
const INTERNAL_KEYS = ['PK', 'SK', '_type'];
|
|
127
127
|
/**
|
|
128
|
-
* Removes internal DynamoDB keys from an item or array of items
|
|
129
|
-
*
|
|
130
|
-
*
|
|
128
|
+
* Removes internal DynamoDB keys from an item or array of items.
|
|
129
|
+
*
|
|
130
|
+
* Recurses into nested plain objects and arrays so an `_type` / `PK` / `SK`
|
|
131
|
+
* field embedded inside an attribute value (e.g. a denormalized snapshot
|
|
132
|
+
* of another entity) is also stripped. Class instances such as `Date` are
|
|
133
|
+
* passed through untouched — recursing into them would lose their type.
|
|
134
|
+
*
|
|
135
|
+
* @param data - Single item, array of items, or any nested value from DynamoDB
|
|
136
|
+
* @returns Data with internal keys removed at every level
|
|
131
137
|
*/
|
|
132
138
|
const stripInternalKeys = (data) => {
|
|
133
139
|
if (data === undefined || data === null) {
|
|
@@ -136,12 +142,14 @@ const stripInternalKeys = (data) => {
|
|
|
136
142
|
if (Array.isArray(data)) {
|
|
137
143
|
return data.map((item) => (0, exports.stripInternalKeys)(item));
|
|
138
144
|
}
|
|
139
|
-
|
|
145
|
+
// Only recurse into plain objects; preserve Date, Buffer, Set, Map,
|
|
146
|
+
// and anything else with a non-Object prototype.
|
|
147
|
+
if (typeof data === 'object' && Object.getPrototypeOf(data) === Object.prototype) {
|
|
140
148
|
const cleaned = {};
|
|
141
149
|
for (const [key, value] of Object.entries(data)) {
|
|
142
|
-
if (
|
|
143
|
-
|
|
144
|
-
|
|
150
|
+
if (INTERNAL_KEYS.includes(key))
|
|
151
|
+
continue;
|
|
152
|
+
cleaned[key] = (0, exports.stripInternalKeys)(value);
|
|
145
153
|
}
|
|
146
154
|
return cleaned;
|
|
147
155
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ftschopp/dynatable-core",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.4",
|
|
4
4
|
"description": "Core library for DynamoDB single table design",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./package.json": "./package.json"
|
|
13
|
+
},
|
|
7
14
|
"files": [
|
|
8
15
|
"dist",
|
|
9
16
|
"README.md",
|
|
@@ -15,6 +22,7 @@
|
|
|
15
22
|
"scripts": {
|
|
16
23
|
"build": "tsc && tsc-alias",
|
|
17
24
|
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
|
|
25
|
+
"lint": "eslint src",
|
|
18
26
|
"test": "jest",
|
|
19
27
|
"prepublishOnly": "yarn build"
|
|
20
28
|
},
|