@ftschopp/dynatable-core 1.5.0 → 1.5.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.
- package/CHANGELOG.md +14 -0
- package/dist/builders/query/create-query-builder.d.ts.map +1 -1
- package/dist/builders/query/create-query-builder.js +9 -1
- package/dist/builders/update/create-update-builder.d.ts.map +1 -1
- package/dist/builders/update/create-update-builder.js +84 -0
- package/dist/entity/create-entity-api.d.ts.map +1 -1
- package/dist/entity/create-entity-api.js +25 -8
- package/dist/entity/middleware/factories.d.ts +8 -2
- package/dist/entity/middleware/factories.d.ts.map +1 -1
- package/dist/entity/middleware/factories.js +9 -3
- package/dist/entity/types.d.ts +8 -0
- package/dist/entity/types.d.ts.map +1 -1
- package/dist/table.d.ts.map +1 -1
- package/dist/table.js +8 -0
- package/dist/utils/model-utils.d.ts +18 -2
- package/dist/utils/model-utils.d.ts.map +1 -1
- package/dist/utils/model-utils.js +40 -7
- package/dist/utils/zod-utils.d.ts.map +1 -1
- package/dist/utils/zod-utils.js +7 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## @ftschopp/dynatable-core [1.5.2](https://github.com/ftschopp/dynatable/compare/@ftschopp/dynatable-core@1.5.1...@ftschopp/dynatable-core@1.5.2) (2026-05-10)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **core:** guard primary-key template fields and non-set ops on GSI templates in update builder ([#42](https://github.com/ftschopp/dynatable/issues/42)) ([4487ff2](https://github.com/ftschopp/dynatable/commit/4487ff22ad507b37aa0e2e279a602a4813afddba))
|
|
7
|
+
|
|
8
|
+
## @ftschopp/dynatable-core [1.5.1](https://github.com/ftschopp/dynatable/compare/@ftschopp/dynatable-core@1.5.0...@ftschopp/dynatable-core@1.5.1) (2026-05-10)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **core:** close cleanInternalKeys gaps — derive from schema; cover put/query ([#41](https://github.com/ftschopp/dynatable/issues/41)) ([b429f36](https://github.com/ftschopp/dynatable/commit/b429f360ef70fec9612bc983b14903f2d1c1df91))
|
|
14
|
+
|
|
1
15
|
# @ftschopp/dynatable-core [1.5.0](https://github.com/ftschopp/dynatable/compare/@ftschopp/dynatable-core@1.4.4...@ftschopp/dynatable-core@1.5.0) (2026-05-09)
|
|
2
16
|
|
|
3
17
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-query-builder.d.ts","sourceRoot":"","sources":["../../../src/builders/query/create-query-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAM1D,OAAO,EAAE,YAAY,EAA8B,MAAM,SAAS,CAAC;AACnE,OAAO,EAAiB,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAElE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"create-query-builder.d.ts","sourceRoot":"","sources":["../../../src/builders/query/create-query-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAM1D,OAAO,EAAE,YAAY,EAA8B,MAAM,SAAS,CAAC;AACnE,OAAO,EAAiB,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAElE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAub7D;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EACtC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,cAAc,EACtB,KAAK,CAAC,EAAE,eAAe,EACvB,MAAM,CAAC,EAAE,cAAc,EACvB,UAAU,CAAC,EAAE,MAAM,GAClB,YAAY,CAAC,KAAK,CAAC,CA6BrB"}
|
|
@@ -99,7 +99,15 @@ function separateConditions(condition, model, indexName) {
|
|
|
99
99
|
}
|
|
100
100
|
return;
|
|
101
101
|
}
|
|
102
|
-
// It's a leaf condition - check if it's a key field
|
|
102
|
+
// It's a leaf condition - check if it's a key field. A negated leaf
|
|
103
|
+
// (`op.not(op.eq(attr.x, …))`) can never be a KeyConditionExpression
|
|
104
|
+
// — DynamoDB's KeyConditionExpression grammar has no NOT — and
|
|
105
|
+
// dropping the negation would silently change query semantics. Keep
|
|
106
|
+
// it as a filter so the negation is honored.
|
|
107
|
+
if (cond.isNegated) {
|
|
108
|
+
filterConditions.push(cond);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
103
111
|
// Extract field name from the expression (e.g., "#username" -> "username")
|
|
104
112
|
const fieldMatch = cond.expression.match(/#(\w+)/);
|
|
105
113
|
if (fieldMatch && fieldMatch[1]) {
|
|
@@ -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;AAyB7D;;;;;;;;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,CAwbtB"}
|
|
@@ -13,6 +13,18 @@ function extractAttrName(action) {
|
|
|
13
13
|
const m = action.expression.match(/^\s*#([A-Za-z0-9_]+)\s*=/);
|
|
14
14
|
return m?.[1];
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Pull the attribute name from any update action (set / remove / add / delete).
|
|
18
|
+
* Each action emitted by this builder carries exactly one entry in `names`
|
|
19
|
+
* mapping `#<attr>` → `<attr>`, so the value side of that map is the source
|
|
20
|
+
* of truth regardless of the surrounding expression syntax.
|
|
21
|
+
*/
|
|
22
|
+
function actionAttrName(action) {
|
|
23
|
+
if (!action.names)
|
|
24
|
+
return undefined;
|
|
25
|
+
const values = Object.values(action.names);
|
|
26
|
+
return values[0];
|
|
27
|
+
}
|
|
16
28
|
/**
|
|
17
29
|
* Creates an UpdateBuilder for an item key and table.
|
|
18
30
|
*
|
|
@@ -120,6 +132,68 @@ function createUpdateBuilder(tableName, key, client, prevConditions = [], update
|
|
|
120
132
|
// key vars + the .set() payload, we throw — recomputing from the
|
|
121
133
|
// existing item would require an extra read the builder won't do.
|
|
122
134
|
if (indexContext) {
|
|
135
|
+
// Collect the attribute name targeted by every update action,
|
|
136
|
+
// grouped by op. `setInputs` carries the .set() payload (already
|
|
137
|
+
// structured) — for remove/add/delete we read from the action's
|
|
138
|
+
// attribute-name map.
|
|
139
|
+
const setFields = Object.keys(setInputs);
|
|
140
|
+
const removeFields = updateActions.remove
|
|
141
|
+
.map(actionAttrName)
|
|
142
|
+
.filter((n) => !!n);
|
|
143
|
+
const addFields = updateActions.add
|
|
144
|
+
.map(actionAttrName)
|
|
145
|
+
.filter((n) => !!n);
|
|
146
|
+
const deleteFields = updateActions.delete
|
|
147
|
+
.map(actionAttrName)
|
|
148
|
+
.filter((n) => !!n);
|
|
149
|
+
// Guard 1: primary-key template fields are immutable in DynamoDB.
|
|
150
|
+
// The Key on the UpdateItem request fixes the row to operate on; if
|
|
151
|
+
// the user changes a field that participates in the PK/SK template,
|
|
152
|
+
// the row's PK doesn't move (DynamoDB doesn't allow that) but the
|
|
153
|
+
// attribute does — leaving an inconsistent row whose PK encodes the
|
|
154
|
+
// old value. Catch this on every op (.set / .remove / .add / .delete)
|
|
155
|
+
// before it leaves the process.
|
|
156
|
+
const primaryKeyTemplateVars = new Set();
|
|
157
|
+
for (const keyDef of Object.values(indexContext.model.key)) {
|
|
158
|
+
for (const v of (0, model_utils_1.extractTemplateVars)(keyDef.value)) {
|
|
159
|
+
primaryKeyTemplateVars.add(v);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const allTouched = [...setFields, ...removeFields, ...addFields, ...deleteFields];
|
|
163
|
+
const pkConflicts = [
|
|
164
|
+
...new Set(allTouched.filter((f) => primaryKeyTemplateVars.has(f))),
|
|
165
|
+
];
|
|
166
|
+
if (pkConflicts.length > 0) {
|
|
167
|
+
throw new Error(`Cannot update field(s) [${pkConflicts.join(', ')}] — they participate ` +
|
|
168
|
+
`in the primary key template, which is immutable in DynamoDB. To ` +
|
|
169
|
+
`"rename" a primary-key value, delete the old item and put a new one ` +
|
|
170
|
+
`(ideally inside a transactWrite for atomicity).`);
|
|
171
|
+
}
|
|
172
|
+
// Guard 2: .add() / .remove() / .delete() against a field used in a
|
|
173
|
+
// SECONDARY-index template. The recompute path needs an explicit new
|
|
174
|
+
// value to resolve the template — ADD increments without exposing
|
|
175
|
+
// the new value, REMOVE strips the field entirely, and DELETE
|
|
176
|
+
// mutates a Set without naming a scalar. Switching to .set(field,
|
|
177
|
+
// newValue) lets the recompute path handle it correctly.
|
|
178
|
+
if (indexContext.model.index) {
|
|
179
|
+
const indexTemplateVars = new Set();
|
|
180
|
+
for (const indexDef of Object.values(indexContext.model.index)) {
|
|
181
|
+
for (const v of (0, model_utils_1.extractTemplateVars)(indexDef.value)) {
|
|
182
|
+
indexTemplateVars.add(v);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const nonSetTouches = [...removeFields, ...addFields, ...deleteFields];
|
|
186
|
+
const gsiConflicts = [
|
|
187
|
+
...new Set(nonSetTouches.filter((f) => indexTemplateVars.has(f))),
|
|
188
|
+
];
|
|
189
|
+
if (gsiConflicts.length > 0) {
|
|
190
|
+
throw new Error(`Cannot use .add() / .remove() / .delete() on field(s) ` +
|
|
191
|
+
`[${gsiConflicts.join(', ')}] — they participate in a ` +
|
|
192
|
+
`secondary-index template, and the affected index key cannot be ` +
|
|
193
|
+
`recomputed without an explicit new value. Use .set(field, ` +
|
|
194
|
+
`newValue) instead so the index key is recomputed atomically.`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
123
197
|
const { actions: idxActions, missing } = (0, model_utils_1.computeIndexUpdates)(indexContext.model, indexContext.keyVars, setInputs);
|
|
124
198
|
if (missing.length > 0) {
|
|
125
199
|
const details = missing
|
|
@@ -196,6 +270,16 @@ function createUpdateBuilder(tableName, key, client, prevConditions = [], update
|
|
|
196
270
|
updateParts.push(`DELETE ${deleteExpressions.join(', ')}`);
|
|
197
271
|
}
|
|
198
272
|
const updateExpression = updateParts.join(' ');
|
|
273
|
+
// DynamoDB rejects an UpdateCommand without an UpdateExpression
|
|
274
|
+
// ("ValidationException: ExpressionAttributeNames must not be empty"
|
|
275
|
+
// or worse, "Member must not be null"). Throw a clearer error
|
|
276
|
+
// before the request leaves the process so the caller knows they
|
|
277
|
+
// forgot to call .set/.add/.remove/.delete.
|
|
278
|
+
if (!updateExpression) {
|
|
279
|
+
throw new Error('Update has no SET, REMOVE, ADD, or DELETE actions. Add at least one ' +
|
|
280
|
+
'before calling dbParams() / execute(). To check for existence without ' +
|
|
281
|
+
'modifying anything, use a get() instead.');
|
|
282
|
+
}
|
|
199
283
|
// Build ConditionExpression from conditions
|
|
200
284
|
let conditionExpression = '';
|
|
201
285
|
let conditionNames = {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-entity-api.d.ts","sourceRoot":"","sources":["../../src/entity/create-entity-api.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AActF,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAMtD;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,GAAI,KAAK,SAAS,eAAe,EAC3D,WAAW,MAAM,EACjB,WAAW,MAAM,EACjB,OAAO,KAAK,EACZ,QAAQ,cAAc,EACtB,UAAS,gBAAqB,KAC7B,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"create-entity-api.d.ts","sourceRoot":"","sources":["../../src/entity/create-entity-api.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AActF,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAMtD;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,GAAI,KAAK,SAAS,eAAe,EAC3D,WAAW,MAAM,EACjB,WAAW,MAAM,EACjB,OAAO,KAAK,EACZ,QAAQ,cAAc,EACtB,UAAS,gBAAqB,KAC7B,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,CA4NtE,CAAC"}
|
|
@@ -18,9 +18,10 @@ const key_validation_1 = require("./validation/key-validation");
|
|
|
18
18
|
* @returns EntityAPI with get, put, query, scan, update, delete, batchGet, and batchWrite methods
|
|
19
19
|
*/
|
|
20
20
|
const createEntityAPI = (tableName, modelName, model, client, options = {}) => {
|
|
21
|
-
const { logger, timestamps = false, cleanInternalKeys = false } = options;
|
|
21
|
+
const { logger, timestamps = false, cleanInternalKeys = false, internalKeys } = options;
|
|
22
22
|
// Build a Zod schema from the model
|
|
23
23
|
const zodSchema = (0, zod_utils_1.modelToZod)(model);
|
|
24
|
+
const cleanKeysMiddleware = (0, factories_1.createCleanKeysMiddleware)(cleanInternalKeys, internalKeys);
|
|
24
25
|
return {
|
|
25
26
|
get(key) {
|
|
26
27
|
// Validate key fields
|
|
@@ -28,7 +29,7 @@ const createEntityAPI = (tableName, modelName, model, client, options = {}) => {
|
|
|
28
29
|
// Resolve any key defaults or computed keys
|
|
29
30
|
const fullKey = (0, model_utils_1.resolveKeys)(model, key);
|
|
30
31
|
const builder = (0, builders_1.createGetBuilder)(tableName, fullKey, client, undefined, logger);
|
|
31
|
-
return (0, with_middleware_1.withMiddleware)(builder,
|
|
32
|
+
return (0, with_middleware_1.withMiddleware)(builder, cleanKeysMiddleware);
|
|
32
33
|
},
|
|
33
34
|
put(item) {
|
|
34
35
|
// Validate full input data
|
|
@@ -46,13 +47,29 @@ const createEntityAPI = (tableName, modelName, model, client, options = {}) => {
|
|
|
46
47
|
...fullKey,
|
|
47
48
|
_type: modelName, // Add entity type identifier
|
|
48
49
|
};
|
|
49
|
-
|
|
50
|
+
const builder = (0, builders_1.createPutBuilder)(tableName, fullItem, client, [], false, 'NONE', false, logger);
|
|
51
|
+
return (0, with_middleware_1.withMiddleware)(builder, cleanKeysMiddleware);
|
|
50
52
|
},
|
|
51
53
|
query() {
|
|
52
54
|
// Auto-filter by entity type so query() only returns items for this
|
|
53
55
|
// entity. Without this, queries on shared GSIs return items from other
|
|
54
56
|
// entities that share the same partition key.
|
|
55
|
-
|
|
57
|
+
const builder = (0, builders_1.createQueryBuilder)(tableName, client, model, logger, modelName);
|
|
58
|
+
// QueryBuilder exposes only `where()` — the executable surface lives
|
|
59
|
+
// on the QueryExecutor it returns. Wrap that executor's `execute()`
|
|
60
|
+
// so cleanInternalKeys is honored on the common direct path.
|
|
61
|
+
//
|
|
62
|
+
// KNOWN LIMITATION: chained executor calls (`.limit()`, `.useIndex()`,
|
|
63
|
+
// etc.) recreate a fresh executor without the wrap, and
|
|
64
|
+
// `executeWithPagination()` / `iterate()` are not wrapped at all.
|
|
65
|
+
// This matches the pre-existing behavior of scan/update/delete and is
|
|
66
|
+
// tracked as a follow-up — pushing cleanup down into the builders
|
|
67
|
+
// themselves is the proper fix.
|
|
68
|
+
return {
|
|
69
|
+
where(fn) {
|
|
70
|
+
return (0, with_middleware_1.withMiddleware)(builder.where(fn), cleanKeysMiddleware);
|
|
71
|
+
},
|
|
72
|
+
};
|
|
56
73
|
},
|
|
57
74
|
scan() {
|
|
58
75
|
// Auto-filter by entity type so scan only returns items for this entity
|
|
@@ -62,7 +79,7 @@ const createEntityAPI = (tableName, modelName, model, client, options = {}) => {
|
|
|
62
79
|
values: { ':_type': modelName },
|
|
63
80
|
};
|
|
64
81
|
const builder = (0, builders_1.createScanBuilder)(tableName, client, [typeFilter], [], undefined, false, undefined, undefined, undefined, logger);
|
|
65
|
-
return (0, with_middleware_1.withMiddleware)(builder,
|
|
82
|
+
return (0, with_middleware_1.withMiddleware)(builder, cleanKeysMiddleware);
|
|
66
83
|
},
|
|
67
84
|
update(key) {
|
|
68
85
|
// Validate key fields
|
|
@@ -73,7 +90,7 @@ const createEntityAPI = (tableName, modelName, model, client, options = {}) => {
|
|
|
73
90
|
// Carry the original template-vars-bearing key (e.g. { username: 'jane' })
|
|
74
91
|
// so the builder can recompute index keys when .set() touches their template fields.
|
|
75
92
|
{ model, keyVars: key });
|
|
76
|
-
return (0, with_middleware_1.withMiddleware)(builder,
|
|
93
|
+
return (0, with_middleware_1.withMiddleware)(builder, cleanKeysMiddleware);
|
|
77
94
|
},
|
|
78
95
|
delete(key) {
|
|
79
96
|
// Validate key fields
|
|
@@ -81,7 +98,7 @@ const createEntityAPI = (tableName, modelName, model, client, options = {}) => {
|
|
|
81
98
|
// Resolve any key defaults or computed keys
|
|
82
99
|
const fullKey = (0, model_utils_1.resolveKeys)(model, key);
|
|
83
100
|
const builder = (0, builders_1.createDeleteBuilder)(tableName, fullKey, client, [], 'NONE', logger);
|
|
84
|
-
return (0, with_middleware_1.withMiddleware)(builder,
|
|
101
|
+
return (0, with_middleware_1.withMiddleware)(builder, cleanKeysMiddleware);
|
|
85
102
|
},
|
|
86
103
|
batchGet(keys) {
|
|
87
104
|
// Process all keys and validate them
|
|
@@ -98,7 +115,7 @@ const createEntityAPI = (tableName, modelName, model, client, options = {}) => {
|
|
|
98
115
|
},
|
|
99
116
|
};
|
|
100
117
|
const builder = (0, builders_1.createBatchGetBuilder)(requestItems, client, undefined, logger);
|
|
101
|
-
return (0, with_middleware_1.withMiddleware)(builder,
|
|
118
|
+
return (0, with_middleware_1.withMiddleware)(builder, cleanKeysMiddleware);
|
|
102
119
|
},
|
|
103
120
|
batchWrite(items) {
|
|
104
121
|
// Validate and process all items
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { ExecutionMiddleware } from './types';
|
|
2
2
|
/**
|
|
3
|
-
* Creates middleware configuration for cleaning internal keys
|
|
3
|
+
* Creates middleware configuration for cleaning internal keys.
|
|
4
|
+
*
|
|
5
|
+
* @param shouldClean - When false, returns a no-op middleware.
|
|
6
|
+
* @param internalKeys - Optional schema-derived list of column names to
|
|
7
|
+
* strip. Falls back to the default `['PK', 'SK', '_type']` when omitted,
|
|
8
|
+
* which is only correct for schemas that follow the conventional naming
|
|
9
|
+
* AND have no secondary indexes whose columns leak into returned items.
|
|
4
10
|
*/
|
|
5
|
-
export declare function createCleanKeysMiddleware(shouldClean: boolean): ExecutionMiddleware;
|
|
11
|
+
export declare function createCleanKeysMiddleware(shouldClean: boolean, internalKeys?: readonly string[]): ExecutionMiddleware;
|
|
6
12
|
//# sourceMappingURL=factories.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factories.d.ts","sourceRoot":"","sources":["../../../src/entity/middleware/factories.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAE9C
|
|
1
|
+
{"version":3,"file":"factories.d.ts","sourceRoot":"","sources":["../../../src/entity/middleware/factories.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAE9C;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,OAAO,EACpB,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,GAC/B,mBAAmB,CAQrB"}
|
|
@@ -3,13 +3,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.createCleanKeysMiddleware = createCleanKeysMiddleware;
|
|
4
4
|
const model_utils_1 = require("../../utils/model-utils");
|
|
5
5
|
/**
|
|
6
|
-
* Creates middleware configuration for cleaning internal keys
|
|
6
|
+
* Creates middleware configuration for cleaning internal keys.
|
|
7
|
+
*
|
|
8
|
+
* @param shouldClean - When false, returns a no-op middleware.
|
|
9
|
+
* @param internalKeys - Optional schema-derived list of column names to
|
|
10
|
+
* strip. Falls back to the default `['PK', 'SK', '_type']` when omitted,
|
|
11
|
+
* which is only correct for schemas that follow the conventional naming
|
|
12
|
+
* AND have no secondary indexes whose columns leak into returned items.
|
|
7
13
|
*/
|
|
8
|
-
function createCleanKeysMiddleware(shouldClean) {
|
|
14
|
+
function createCleanKeysMiddleware(shouldClean, internalKeys) {
|
|
9
15
|
if (!shouldClean) {
|
|
10
16
|
return {};
|
|
11
17
|
}
|
|
12
18
|
return {
|
|
13
|
-
after: (result) => (0, model_utils_1.stripInternalKeys)(result),
|
|
19
|
+
after: (result) => (0, model_utils_1.stripInternalKeys)(result, internalKeys),
|
|
14
20
|
};
|
|
15
21
|
}
|
package/dist/entity/types.d.ts
CHANGED
|
@@ -7,6 +7,14 @@ export type EntityAPIOptions = {
|
|
|
7
7
|
logger?: DynamoDBLogger;
|
|
8
8
|
timestamps?: boolean;
|
|
9
9
|
cleanInternalKeys?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Schema-derived list of column names that `cleanInternalKeys` should
|
|
12
|
+
* strip from returned items. When omitted, falls back to the default
|
|
13
|
+
* `['PK', 'SK', '_type']` — which misses GSI/LSI columns and any
|
|
14
|
+
* primary key not literally named `PK` / `SK`. The Table constructor
|
|
15
|
+
* always populates this from `schema.indexes`.
|
|
16
|
+
*/
|
|
17
|
+
internalKeys?: readonly string[];
|
|
10
18
|
};
|
|
11
19
|
/**
|
|
12
20
|
* Entity API interface for a model
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/entity/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EACL,UAAU,EACV,UAAU,EACV,YAAY,EACZ,aAAa,EACb,aAAa,EACb,WAAW,EACX,eAAe,EACf,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iBAAiB,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/entity/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EACL,UAAU,EACV,UAAU,EACV,YAAY,EACZ,aAAa,EACb,aAAa,EACb,WAAW,EACX,eAAe,EACf,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,IAAI;IAC9C;;;;OAIG;IACH,GAAG,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAEpD;;;;OAIG;IACH,GAAG,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,UAAU,CAAC,KAAK,CAAC,CAAC;IAExC;;;OAGG;IACH,KAAK,EAAE,MAAM,YAAY,CAAC,KAAK,CAAC,CAAC;IAEjC;;;OAGG;IACH,IAAI,EAAE,MAAM,WAAW,CAAC,KAAK,CAAC,CAAC;IAE/B;;;;OAIG;IACH,MAAM,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,aAAa,CAAC,KAAK,CAAC,CAAC;IAEhD;;;;OAIG;IACH,MAAM,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,aAAa,CAAC,KAAK,CAAC,CAAC;IAEhD;;;;OAIG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,eAAe,CAAC,KAAK,CAAC,CAAC;IAEvD;;;;OAIG;IACH,UAAU,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,iBAAiB,CAAC;CACnD,CAAC"}
|
package/dist/table.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../src/table.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,gBAAgB,EACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAmB,SAAS,EAAE,MAAM,UAAU,CAAC;AACtD,OAAO,EAA8B,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAC7F,OAAO,EAA4B,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AACvF,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../src/table.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,gBAAgB,EACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAmB,SAAS,EAAE,MAAM,UAAU,CAAC;AACtD,OAAO,EAA8B,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAC7F,OAAO,EAA4B,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AACvF,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAGzD;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,gBAAgB,IAAI;IACpD,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,MAAM,EAAE,cAAc,CAAC;IACvB,uDAAuD;IACvD,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,wDAAwD;IACxD,MAAM,EAAE,CAAC,CAAC;CACX,CAAC;AAEF;;;GAGG;AACH,KAAK,QAAQ,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAE,GAAG,EAAE,CAAC;AAEjD;;GAEG;AACH,KAAK,SAAS,CAAC,CAAC,SAAS,gBAAgB,IAAI;KAC1C,CAAC,IAAI,MAAM,CAAC,CAAC,QAAQ,CAAC,GAAG,SAAS,CACjC,QAAQ,CAAC,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EACpC,QAAQ,CAAC,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EACpC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CACxC;CACF,CAAC;AAEF;;;;;GAKG;AACH,qBAAa,KAAK,CAAC,CAAC,SAAS,gBAAgB;IAC3C,4BAA4B;IAC5B,SAAgB,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IAEvC,sBAAsB;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;gBAE5B,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IA+BlC;;;;;;;;;;;;;OAaG;IACH,aAAa,IAAI,oBAAoB;IAIrC;;;;;;;;;;;;;OAaG;IACH,WAAW,IAAI,kBAAkB;CAGlC"}
|
package/dist/table.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.Table = void 0;
|
|
|
4
4
|
const entity_1 = require("./entity");
|
|
5
5
|
const transact_write_1 = require("./builders/transact-write");
|
|
6
6
|
const transact_get_1 = require("./builders/transact-get");
|
|
7
|
+
const model_utils_1 = require("./utils/model-utils");
|
|
7
8
|
/**
|
|
8
9
|
* Represents a typed DynamoDB Table with entity APIs
|
|
9
10
|
*
|
|
@@ -18,6 +19,12 @@ class Table {
|
|
|
18
19
|
constructor(config) {
|
|
19
20
|
const { client, schema, logger, name: tableName } = config;
|
|
20
21
|
this.client = client;
|
|
22
|
+
// Derive the full list of internal column names (primary + every GSI/LSI
|
|
23
|
+
// hash & sort key, plus the `_type` discriminator) once, from the schema
|
|
24
|
+
// itself. This way `cleanInternalKeys` strips GSI columns like
|
|
25
|
+
// `GSI1PK`/`GSI1SK` that the hardcoded fallback misses, and also works
|
|
26
|
+
// for schemas that use non-conventional primary key names.
|
|
27
|
+
const internalKeys = (0, model_utils_1.collectInternalKeyColumns)(schema.indexes);
|
|
21
28
|
const rawEntities = {};
|
|
22
29
|
for (const modelName in schema.models) {
|
|
23
30
|
const model = schema.models[modelName];
|
|
@@ -28,6 +35,7 @@ class Table {
|
|
|
28
35
|
logger,
|
|
29
36
|
timestamps: schema.params?.timestamps ?? false,
|
|
30
37
|
cleanInternalKeys: schema.params?.cleanInternalKeys ?? false,
|
|
38
|
+
internalKeys,
|
|
31
39
|
});
|
|
32
40
|
}
|
|
33
41
|
this.entities = rawEntities;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { InferModel, ModelDefinition } from '../core/types';
|
|
1
|
+
import { IndexesDefinition, InferModel, ModelDefinition } from '../core/types';
|
|
2
2
|
/**
|
|
3
3
|
* Extracts all variable names from a template string
|
|
4
4
|
* @param template - Template string with ${variable} syntax
|
|
@@ -49,6 +49,18 @@ export declare const applyPostDefaults: <M extends ModelDefinition>(model: M, va
|
|
|
49
49
|
isUpdate?: boolean;
|
|
50
50
|
timestamps?: boolean;
|
|
51
51
|
}) => InferModel<M>;
|
|
52
|
+
/**
|
|
53
|
+
* Derives the list of column names that should be stripped from returned
|
|
54
|
+
* items when `cleanInternalKeys` is enabled, based on the schema's index
|
|
55
|
+
* configuration. Returns the union of every index's hash + sort columns
|
|
56
|
+
* plus the `_type` discriminator.
|
|
57
|
+
*
|
|
58
|
+
* Examples:
|
|
59
|
+
* - `{ primary: { hash: 'PK', sort: 'SK' }, gsi1: { hash: 'GSI1PK', sort: 'GSI1SK' } }`
|
|
60
|
+
* → `['PK', 'SK', 'GSI1PK', 'GSI1SK', '_type']`
|
|
61
|
+
* - `{ primary: { hash: 'pk', sort: 'sk' } }` → `['pk', 'sk', '_type']`
|
|
62
|
+
*/
|
|
63
|
+
export declare const collectInternalKeyColumns: (indexes: IndexesDefinition | undefined) => string[];
|
|
52
64
|
/**
|
|
53
65
|
* Removes internal DynamoDB keys from an item or array of items.
|
|
54
66
|
*
|
|
@@ -58,7 +70,11 @@ export declare const applyPostDefaults: <M extends ModelDefinition>(model: M, va
|
|
|
58
70
|
* passed through untouched — recursing into them would lose their type.
|
|
59
71
|
*
|
|
60
72
|
* @param data - Single item, array of items, or any nested value from DynamoDB
|
|
73
|
+
* @param internalKeys - Column names to strip. Defaults to `['PK', 'SK', '_type']`
|
|
74
|
+
* for backwards compatibility; consumers operating on a real schema should
|
|
75
|
+
* derive the list with {@link collectInternalKeyColumns} so GSI/LSI keys
|
|
76
|
+
* and custom-named primary keys are also removed.
|
|
61
77
|
* @returns Data with internal keys removed at every level
|
|
62
78
|
*/
|
|
63
|
-
export declare const stripInternalKeys: <T>(data: T | T[] | undefined) => T | T[] | undefined;
|
|
79
|
+
export declare const stripInternalKeys: <T>(data: T | T[] | undefined, internalKeys?: readonly string[]) => T | T[] | undefined;
|
|
64
80
|
//# 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;
|
|
1
|
+
{"version":3,"file":"model-utils.d.ts","sourceRoot":"","sources":["../../src/utils/model-utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAiB,eAAe,EAAE,MAAM,cAAc,CAAC;AAG7F;;;;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;AAWF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,GACpC,SAAS,iBAAiB,GAAG,SAAS,KACrC,MAAM,EAUR,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,EACjC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,SAAS,EACzB,eAAc,SAAS,MAAM,EAA0B,KACtD,CAAC,GAAG,CAAC,EAAE,GAAG,SAqBZ,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.stripInternalKeys = exports.applyPostDefaults = exports.computeIndexUpdates = exports.resolveKeys = exports.resolveTemplate = exports.extractTemplateVars = void 0;
|
|
4
|
+
exports.stripInternalKeys = exports.collectInternalKeyColumns = exports.applyPostDefaults = exports.computeIndexUpdates = exports.resolveKeys = exports.resolveTemplate = exports.extractTemplateVars = void 0;
|
|
5
5
|
const ulid_1 = require("ulid");
|
|
6
6
|
/**
|
|
7
7
|
* Extracts all variable names from a template string
|
|
@@ -121,9 +121,38 @@ const applyPostDefaults = (model, validatedItem, options) => {
|
|
|
121
121
|
};
|
|
122
122
|
exports.applyPostDefaults = applyPostDefaults;
|
|
123
123
|
/**
|
|
124
|
-
*
|
|
124
|
+
* Default fallback list used when the caller doesn't pass a schema-derived
|
|
125
|
+
* set. Covers only the conventional primary-key column names (PK, SK) and
|
|
126
|
+
* the entity-type discriminator. Real consumers should pass the
|
|
127
|
+
* schema-derived list returned by {@link collectInternalKeyColumns} so that
|
|
128
|
+
* GSI/LSI columns and custom-named primary keys are also stripped.
|
|
125
129
|
*/
|
|
126
|
-
const
|
|
130
|
+
const DEFAULT_INTERNAL_KEYS = ['PK', 'SK', '_type'];
|
|
131
|
+
/**
|
|
132
|
+
* Derives the list of column names that should be stripped from returned
|
|
133
|
+
* items when `cleanInternalKeys` is enabled, based on the schema's index
|
|
134
|
+
* configuration. Returns the union of every index's hash + sort columns
|
|
135
|
+
* plus the `_type` discriminator.
|
|
136
|
+
*
|
|
137
|
+
* Examples:
|
|
138
|
+
* - `{ primary: { hash: 'PK', sort: 'SK' }, gsi1: { hash: 'GSI1PK', sort: 'GSI1SK' } }`
|
|
139
|
+
* → `['PK', 'SK', 'GSI1PK', 'GSI1SK', '_type']`
|
|
140
|
+
* - `{ primary: { hash: 'pk', sort: 'sk' } }` → `['pk', 'sk', '_type']`
|
|
141
|
+
*/
|
|
142
|
+
const collectInternalKeyColumns = (indexes) => {
|
|
143
|
+
const cols = new Set();
|
|
144
|
+
if (indexes) {
|
|
145
|
+
for (const idx of Object.values(indexes)) {
|
|
146
|
+
if (idx?.hash)
|
|
147
|
+
cols.add(idx.hash);
|
|
148
|
+
if (idx?.sort)
|
|
149
|
+
cols.add(idx.sort);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
cols.add('_type');
|
|
153
|
+
return [...cols];
|
|
154
|
+
};
|
|
155
|
+
exports.collectInternalKeyColumns = collectInternalKeyColumns;
|
|
127
156
|
/**
|
|
128
157
|
* Removes internal DynamoDB keys from an item or array of items.
|
|
129
158
|
*
|
|
@@ -133,23 +162,27 @@ const INTERNAL_KEYS = ['PK', 'SK', '_type'];
|
|
|
133
162
|
* passed through untouched — recursing into them would lose their type.
|
|
134
163
|
*
|
|
135
164
|
* @param data - Single item, array of items, or any nested value from DynamoDB
|
|
165
|
+
* @param internalKeys - Column names to strip. Defaults to `['PK', 'SK', '_type']`
|
|
166
|
+
* for backwards compatibility; consumers operating on a real schema should
|
|
167
|
+
* derive the list with {@link collectInternalKeyColumns} so GSI/LSI keys
|
|
168
|
+
* and custom-named primary keys are also removed.
|
|
136
169
|
* @returns Data with internal keys removed at every level
|
|
137
170
|
*/
|
|
138
|
-
const stripInternalKeys = (data) => {
|
|
171
|
+
const stripInternalKeys = (data, internalKeys = DEFAULT_INTERNAL_KEYS) => {
|
|
139
172
|
if (data === undefined || data === null) {
|
|
140
173
|
return data;
|
|
141
174
|
}
|
|
142
175
|
if (Array.isArray(data)) {
|
|
143
|
-
return data.map((item) => (0, exports.stripInternalKeys)(item));
|
|
176
|
+
return data.map((item) => (0, exports.stripInternalKeys)(item, internalKeys));
|
|
144
177
|
}
|
|
145
178
|
// Only recurse into plain objects; preserve Date, Buffer, Set, Map,
|
|
146
179
|
// and anything else with a non-Object prototype.
|
|
147
180
|
if (typeof data === 'object' && Object.getPrototypeOf(data) === Object.prototype) {
|
|
148
181
|
const cleaned = {};
|
|
149
182
|
for (const [key, value] of Object.entries(data)) {
|
|
150
|
-
if (
|
|
183
|
+
if (internalKeys.includes(key))
|
|
151
184
|
continue;
|
|
152
|
-
cleaned[key] = (0, exports.stripInternalKeys)(value);
|
|
185
|
+
cleaned[key] = (0, exports.stripInternalKeys)(value, internalKeys);
|
|
153
186
|
}
|
|
154
187
|
return cleaned;
|
|
155
188
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zod-utils.d.ts","sourceRoot":"","sources":["../../src/utils/zod-utils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAK,SAAS,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,SAAS,GAAI,MAAM,mBAAmB,KAAG,
|
|
1
|
+
{"version":3,"file":"zod-utils.d.ts","sourceRoot":"","sources":["../../src/utils/zod-utils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAK,SAAS,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,SAAS,GAAI,MAAM,mBAAmB,KAAG,OAoCrD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,eAAe,KAAG,SAAS,CAAC,GAAG,CAMhE,CAAC"}
|
package/dist/utils/zod-utils.js
CHANGED
|
@@ -35,9 +35,13 @@ const typeToZod = (attr) => {
|
|
|
35
35
|
? zod_1.z.number()
|
|
36
36
|
: attr.type === Boolean
|
|
37
37
|
? zod_1.z.boolean()
|
|
38
|
-
:
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
: // applyPostDefaults stores Date values as ISO strings (e.g.
|
|
39
|
+
// createdAt/updatedAt). Use coerce so reading back a stored
|
|
40
|
+
// Date attribute through zod accepts both the ISO string we
|
|
41
|
+
// wrote and a native Date the caller might pass on input.
|
|
42
|
+
attr.type === Date
|
|
43
|
+
? zod_1.z.coerce.date()
|
|
44
|
+
: zod_1.z.unknown();
|
|
41
45
|
}
|
|
42
46
|
const isGenerated = 'generate' in attr;
|
|
43
47
|
return attr.required && !isGenerated ? zodType : zodType.optional();
|