@karmaniverous/entity-manager 6.7.5 → 6.8.1
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/README.md +1 -1
- package/dist/cjs/BaseEntityClient.js +1 -1
- package/dist/cjs/BaseQueryBuilder.js +5 -1
- package/dist/cjs/EntityManager.js +18 -9
- package/dist/cjs/ParsedConfig.js +143 -113
- package/dist/cjs/addKeys.js +6 -6
- package/dist/{mjs/decodeEntityElement.js → cjs/decodeElement.js} +10 -12
- package/dist/cjs/decodeGeneratedProperty.js +6 -10
- package/dist/cjs/dehydrateIndexItem.js +12 -11
- package/dist/cjs/dehydratePageKeyMap.js +10 -7
- package/dist/{mjs/encodeEntityElement.js → cjs/encodeElement.js} +8 -10
- package/dist/cjs/encodeGeneratedProperty.js +14 -18
- package/dist/cjs/getHashKeySpace.js +6 -6
- package/dist/cjs/getIndexComponents.js +6 -8
- package/dist/cjs/getShardBump.js +1 -1
- package/dist/cjs/index.js +0 -2
- package/dist/cjs/query.js +1 -1
- package/dist/cjs/rehydrateIndexItem.js +12 -10
- package/dist/cjs/rehydratePageKeyMap.js +18 -11
- package/dist/cjs/removeKeys.js +3 -2
- package/dist/cjs/unwrapIndex.js +25 -11
- package/dist/cjs/updateItemHashKey.js +5 -3
- package/dist/cjs/updateItemRangeKey.js +6 -4
- package/dist/cjs/validateGeneratedProperty.js +23 -0
- package/dist/cjs/validateIndexToken.js +16 -0
- package/dist/cjs/validateTranscodedProperty.js +16 -0
- package/dist/index.d.ts +265 -538
- package/dist/mjs/BaseEntityClient.js +1 -1
- package/dist/mjs/BaseQueryBuilder.js +5 -1
- package/dist/mjs/EntityManager.js +18 -9
- package/dist/mjs/ParsedConfig.js +143 -113
- package/dist/mjs/addKeys.js +6 -6
- package/dist/{cjs/decodeEntityElement.js → mjs/decodeElement.js} +8 -14
- package/dist/mjs/decodeGeneratedProperty.js +6 -10
- package/dist/mjs/dehydrateIndexItem.js +12 -11
- package/dist/mjs/dehydratePageKeyMap.js +10 -7
- package/dist/{cjs/encodeEntityElement.js → mjs/encodeElement.js} +7 -13
- package/dist/mjs/encodeGeneratedProperty.js +14 -18
- package/dist/mjs/getHashKeySpace.js +6 -6
- package/dist/mjs/getIndexComponents.js +6 -8
- package/dist/mjs/getShardBump.js +1 -1
- package/dist/mjs/index.js +0 -1
- package/dist/mjs/query.js +1 -1
- package/dist/mjs/rehydrateIndexItem.js +12 -10
- package/dist/mjs/rehydratePageKeyMap.js +18 -11
- package/dist/mjs/removeKeys.js +3 -2
- package/dist/mjs/unwrapIndex.js +26 -12
- package/dist/mjs/updateItemHashKey.js +5 -3
- package/dist/mjs/updateItemRangeKey.js +6 -4
- package/dist/mjs/validateGeneratedProperty.js +21 -0
- package/dist/mjs/validateIndexToken.js +14 -0
- package/dist/mjs/validateTranscodedProperty.js +14 -0
- package/package.json +6 -6
- package/dist/cjs/conditionalize.js +0 -25
- package/dist/cjs/validateEntityGeneratedProperty.js +0 -29
- package/dist/cjs/validateEntityIndexToken.js +0 -22
- package/dist/mjs/conditionalize.js +0 -23
- package/dist/mjs/validateEntityGeneratedProperty.js +0 -27
- package/dist/mjs/validateEntityIndexToken.js +0 -20
|
@@ -7,7 +7,7 @@ class BaseEntityClient {
|
|
|
7
7
|
/**
|
|
8
8
|
* DynamoDB EntityClient constructor.
|
|
9
9
|
*
|
|
10
|
-
* @param options - {@link
|
|
10
|
+
* @param options - {@link BaseEntityClientOptions | `BaseEntityClientOptions`} object.
|
|
11
11
|
*/
|
|
12
12
|
constructor(options) {
|
|
13
13
|
const { batchProcessOptions = {}, logger = console } = options;
|
|
@@ -3,7 +3,11 @@ import { mapValues } from 'radash';
|
|
|
3
3
|
/**
|
|
4
4
|
* Abstract base class supporting a fluent API for building a {@link ShardQueryMap | `ShardQueryMap`} using a database client.
|
|
5
5
|
*
|
|
6
|
-
* @
|
|
6
|
+
* @typeParam C - {@link ConfigMap | `ConfigMap`} that defines an {@link Config | `EntityManager configuration`}'s {@link EntityMap | `EntityMap`}, key properties, and {@link TranscodeMap | `TranscodeMap`}. If omitted, defaults to {@link BaseConfigMap | `BaseConfigMap`}.
|
|
7
|
+
* @typeParam EntityClient - {@link BaseEntityClient | `BaseEntityClient`} derived class instance.
|
|
8
|
+
* @typeParam IndexParams - Database platform-specific, index-specific query parameters.
|
|
9
|
+
*
|
|
10
|
+
* @category QueryBuilder
|
|
7
11
|
*/
|
|
8
12
|
class BaseQueryBuilder {
|
|
9
13
|
/** BaseQueryBuilder constructor. */
|
|
@@ -9,7 +9,12 @@ var _EntityManager_config;
|
|
|
9
9
|
* The EntityManager class applies a configuration-driven sharded data model &
|
|
10
10
|
* query strategy to NoSql data.
|
|
11
11
|
*
|
|
12
|
-
* @
|
|
12
|
+
* @typeParam C - {@link ConfigMap | `ConfigMap`} that defines the configuration's {@link EntityMap | `EntityMap`}, key properties, and {@link TranscodeMap | `TranscodeMap`}. If omitted, defaults to {@link BaseConfigMap | `BaseConfigMap`}.
|
|
13
|
+
*
|
|
14
|
+
* @remarks
|
|
15
|
+
* While the {@link EntityManager.query | `query`} method is `public`, normally it should not be called directly. The `query` method is used by a platform-specific {@link BaseQueryBuilder.query | `QueryBuilder.query`} method to provide a fluent query API.
|
|
16
|
+
*
|
|
17
|
+
* @category EntityManager
|
|
13
18
|
*/
|
|
14
19
|
class EntityManager {
|
|
15
20
|
/**
|
|
@@ -40,10 +45,10 @@ class EntityManager {
|
|
|
40
45
|
__classPrivateFieldSet(this, _EntityManager_config, configSchema.parse(value), "f");
|
|
41
46
|
}
|
|
42
47
|
/**
|
|
43
|
-
* Update generated properties, hash key, and range key on an {@link
|
|
48
|
+
* Update generated properties, hash key, and range key on an {@link EntityItem | `EntityItem`} object.
|
|
44
49
|
*
|
|
45
|
-
* @param entityToken - {@link
|
|
46
|
-
* @param item - {@link
|
|
50
|
+
* @param entityToken - {@link Config | `Config`} `entities` key.
|
|
51
|
+
* @param item - {@link EntityItem | `EntityItem`} object.
|
|
47
52
|
* @param overwrite - Overwrite existing properties (default `false`).
|
|
48
53
|
*
|
|
49
54
|
* @returns Shallow clone of `item` with updated properties.
|
|
@@ -54,10 +59,10 @@ class EntityManager {
|
|
|
54
59
|
return addKeys(this, entityToken, item, overwrite);
|
|
55
60
|
}
|
|
56
61
|
/**
|
|
57
|
-
* Strips generated properties, hash key, and range key from an {@link
|
|
62
|
+
* Strips generated properties, hash key, and range key from an {@link EntityItem | `EntityItem`} object.
|
|
58
63
|
*
|
|
59
|
-
* @param entityToken - {@link
|
|
60
|
-
* @param item - {@link
|
|
64
|
+
* @param entityToken - {@link Config | `Config`} `entities` key.
|
|
65
|
+
* @param item - {@link EntityItem | `EntityItem`} object.
|
|
61
66
|
*
|
|
62
67
|
* @returns Shallow clone of `item` without generated properties, hash key or range key.
|
|
63
68
|
*
|
|
@@ -72,17 +77,21 @@ class EntityManager {
|
|
|
72
77
|
* @remarks
|
|
73
78
|
* The provided `shardQueryMap` performs the actual query of individual data pages on individual index/shard combinations.
|
|
74
79
|
*
|
|
75
|
-
* Individual shard query results will be combined, deduped by {@link
|
|
80
|
+
* Individual shard query results will be combined, deduped by {@link Config | `Config`} `uniqueProperty` value, and sorted by {@link QueryOptions.sortOrder | `sortOrder`}.
|
|
76
81
|
*
|
|
77
82
|
* In queries on sharded data, expect the leading and trailing edges of returned data pages to interleave somewhat with preceding & following pages.
|
|
78
83
|
*
|
|
79
84
|
* Unsharded query results should sort & page as expected.
|
|
80
85
|
*
|
|
86
|
+
* **Normally this method should not be called directly!** It is used by a platform-specific {@link BaseQueryBuilder.query | `QueryBuilder.query`} method to provide a fluent query API.
|
|
87
|
+
*
|
|
81
88
|
* @param options - {@link QueryOptions | `QueryOptions`} object.
|
|
82
89
|
*
|
|
83
90
|
* @returns {@link QueryResult} object.
|
|
84
91
|
*
|
|
85
|
-
* @throws Error if {@link QueryOptions.
|
|
92
|
+
* @throws Error if `options` {@link QueryOptions.pageKeyMap | `pageKeyMap`} `pageKeyMap` keys do not match {@link QueryOptions.shardQueryMap | `shardQueryMap`} keys.
|
|
93
|
+
*
|
|
94
|
+
* @protected
|
|
86
95
|
*/
|
|
87
96
|
async query(options) {
|
|
88
97
|
return await query(this, options);
|
package/dist/mjs/ParsedConfig.js
CHANGED
|
@@ -9,16 +9,18 @@ const validateArrayUnique = (arr, ctx, identity = (item) => item, path = []) =>
|
|
|
9
9
|
if (count > 1)
|
|
10
10
|
ctx.addIssue({
|
|
11
11
|
code: z.ZodIssueCode.custom,
|
|
12
|
-
message: `duplicate array element
|
|
12
|
+
message: `duplicate array element`,
|
|
13
|
+
params: { element },
|
|
13
14
|
path,
|
|
14
15
|
});
|
|
15
16
|
}
|
|
16
17
|
};
|
|
17
|
-
const
|
|
18
|
-
|
|
18
|
+
const validateKeysExclusive = (keys, label, ref, ctx) => {
|
|
19
|
+
const intersection = keys.filter((key) => ref.includes(key));
|
|
20
|
+
if (intersection.length)
|
|
19
21
|
ctx.addIssue({
|
|
20
22
|
code: z.ZodIssueCode.custom,
|
|
21
|
-
message: `${label}
|
|
23
|
+
message: `${label} key collision: ${intersection.toString()}`,
|
|
22
24
|
});
|
|
23
25
|
};
|
|
24
26
|
const componentArray = z
|
|
@@ -44,25 +46,6 @@ const configSchema = z
|
|
|
44
46
|
.safe()
|
|
45
47
|
.optional()
|
|
46
48
|
.default(10),
|
|
47
|
-
generated: z
|
|
48
|
-
.record(z
|
|
49
|
-
.object({
|
|
50
|
-
atomic: z.boolean().optional().default(false),
|
|
51
|
-
elements: componentArray,
|
|
52
|
-
sharded: z.boolean().optional().default(false),
|
|
53
|
-
})
|
|
54
|
-
.optional())
|
|
55
|
-
.optional()
|
|
56
|
-
.default({}),
|
|
57
|
-
elementTranscodes: z.record(z.string()).optional().default({}),
|
|
58
|
-
indexes: z
|
|
59
|
-
.record(z.object({
|
|
60
|
-
hashKey: z.string().min(1),
|
|
61
|
-
rangeKey: z.string().min(1),
|
|
62
|
-
projections: componentArray.optional(),
|
|
63
|
-
}))
|
|
64
|
-
.optional()
|
|
65
|
-
.default({}),
|
|
66
49
|
shardBumps: z
|
|
67
50
|
.array(z
|
|
68
51
|
.object({
|
|
@@ -103,21 +86,30 @@ const configSchema = z
|
|
|
103
86
|
timestampProperty: z.string().min(1),
|
|
104
87
|
uniqueProperty: z.string().min(1),
|
|
105
88
|
})
|
|
106
|
-
.strict()
|
|
107
|
-
.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
89
|
+
.strict())
|
|
90
|
+
.optional()
|
|
91
|
+
.default({}),
|
|
92
|
+
generatedProperties: z
|
|
93
|
+
.object({
|
|
94
|
+
sharded: z.record(componentArray).optional().default({}),
|
|
95
|
+
unsharded: z.record(componentArray).optional().default({}),
|
|
96
|
+
})
|
|
97
|
+
.optional()
|
|
98
|
+
.default({ sharded: {}, unsharded: {} }),
|
|
99
|
+
hashKey: z.string(),
|
|
100
|
+
indexes: z
|
|
101
|
+
.record(z.object({
|
|
102
|
+
hashKey: z.string().min(1),
|
|
103
|
+
rangeKey: z.string().min(1),
|
|
104
|
+
projections: componentArray.optional(),
|
|
113
105
|
}))
|
|
114
106
|
.optional()
|
|
115
107
|
.default({}),
|
|
116
108
|
generatedKeyDelimiter: z.string().regex(/\W+/).optional().default('|'),
|
|
117
109
|
generatedValueDelimiter: z.string().regex(/\W+/).optional().default('#'),
|
|
110
|
+
propertyTranscodes: z.record(z.string()).optional().default({}),
|
|
111
|
+
rangeKey: z.string(),
|
|
118
112
|
shardKeyDelimiter: z.string().regex(/\W+/).optional().default('!'),
|
|
119
|
-
hashKey: z.string().optional().default('hashKey'),
|
|
120
|
-
rangeKey: z.string().optional().default('rangeKey'),
|
|
121
113
|
throttle: z.number().int().positive().safe().optional().default(10),
|
|
122
114
|
transcodes: z
|
|
123
115
|
.record(z
|
|
@@ -136,12 +128,20 @@ const configSchema = z
|
|
|
136
128
|
ctx.addIssue({
|
|
137
129
|
code: z.ZodIssueCode.custom,
|
|
138
130
|
message: 'generatedKeyDelimiter contains generatedValueDelimiter',
|
|
131
|
+
params: {
|
|
132
|
+
generatedKeyDelimiter: data.generatedKeyDelimiter,
|
|
133
|
+
generatedValueDelimiter: data.generatedValueDelimiter,
|
|
134
|
+
},
|
|
139
135
|
path: ['generatedKeyDelimiter'],
|
|
140
136
|
});
|
|
141
137
|
if (data.generatedKeyDelimiter.includes(data.shardKeyDelimiter))
|
|
142
138
|
ctx.addIssue({
|
|
143
139
|
code: z.ZodIssueCode.custom,
|
|
144
140
|
message: 'generatedKeyDelimiter contains shardKeyDelimiter',
|
|
141
|
+
params: {
|
|
142
|
+
generatedKeyDelimiter: data.generatedKeyDelimiter,
|
|
143
|
+
shardKeyDelimiter: data.shardKeyDelimiter,
|
|
144
|
+
},
|
|
145
145
|
path: ['generatedKeyDelimiter'],
|
|
146
146
|
});
|
|
147
147
|
// validate no generated value delimiter collision
|
|
@@ -149,12 +149,20 @@ const configSchema = z
|
|
|
149
149
|
ctx.addIssue({
|
|
150
150
|
code: z.ZodIssueCode.custom,
|
|
151
151
|
message: 'generatedValueDelimiter contains generatedKeyDelimiter',
|
|
152
|
+
params: {
|
|
153
|
+
generatedValueDelimiter: data.generatedValueDelimiter,
|
|
154
|
+
generatedKeyDelimiter: data.generatedKeyDelimiter,
|
|
155
|
+
},
|
|
152
156
|
path: ['generatedValueDelimiter'],
|
|
153
157
|
});
|
|
154
158
|
if (data.generatedValueDelimiter.includes(data.shardKeyDelimiter))
|
|
155
159
|
ctx.addIssue({
|
|
156
160
|
code: z.ZodIssueCode.custom,
|
|
157
161
|
message: 'generatedValueDelimiter contains shardKeyDelimiter',
|
|
162
|
+
params: {
|
|
163
|
+
generatedValueDelimiter: data.generatedValueDelimiter,
|
|
164
|
+
shardKeyDelimiter: data.shardKeyDelimiter,
|
|
165
|
+
},
|
|
158
166
|
path: ['generatedValueDelimiter'],
|
|
159
167
|
});
|
|
160
168
|
// validate no shard key delimiter collision
|
|
@@ -162,103 +170,125 @@ const configSchema = z
|
|
|
162
170
|
ctx.addIssue({
|
|
163
171
|
code: z.ZodIssueCode.custom,
|
|
164
172
|
message: 'shardKeyDelimiter contains generatedKeyDelimiter',
|
|
173
|
+
params: {
|
|
174
|
+
generatedKeyDelimiter: data.generatedKeyDelimiter,
|
|
175
|
+
shardKeyDelimiter: data.shardKeyDelimiter,
|
|
176
|
+
},
|
|
165
177
|
path: ['shardKeyDelimiter'],
|
|
166
178
|
});
|
|
167
179
|
if (data.shardKeyDelimiter.includes(data.generatedValueDelimiter))
|
|
168
180
|
ctx.addIssue({
|
|
169
181
|
code: z.ZodIssueCode.custom,
|
|
170
182
|
message: 'shardKeyDelimiter contains generatedValueDelimiter',
|
|
183
|
+
params: {
|
|
184
|
+
generatedValueDelimiter: data.generatedValueDelimiter,
|
|
185
|
+
shardKeyDelimiter: data.shardKeyDelimiter,
|
|
186
|
+
},
|
|
171
187
|
path: ['shardKeyDelimiter'],
|
|
172
188
|
});
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
]
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
189
|
+
// get reserved keys
|
|
190
|
+
const shardedKeys = Object.keys(data.generatedProperties.sharded);
|
|
191
|
+
const unshardedKeys = Object.keys(data.generatedProperties.unsharded);
|
|
192
|
+
const transcodedProperties = Object.keys(data.propertyTranscodes);
|
|
193
|
+
// validate hashKey exclusive.
|
|
194
|
+
validateKeysExclusive([data.hashKey], 'hashKey', [
|
|
195
|
+
data.rangeKey,
|
|
196
|
+
...shardedKeys,
|
|
197
|
+
...unshardedKeys,
|
|
198
|
+
...transcodedProperties,
|
|
199
|
+
], ctx);
|
|
200
|
+
// validate rangeKey exclusive.
|
|
201
|
+
validateKeysExclusive([data.rangeKey], 'rangeKey', [...shardedKeys, ...unshardedKeys, ...transcodedProperties], ctx);
|
|
202
|
+
// validate shardedKeys exclusive.
|
|
203
|
+
validateKeysExclusive(shardedKeys, 'shardedKeys', [...unshardedKeys, ...transcodedProperties], ctx);
|
|
204
|
+
// validate unshardedKeys exclusive.
|
|
205
|
+
validateKeysExclusive(unshardedKeys, 'unshardedKeys', transcodedProperties, ctx);
|
|
206
|
+
// validate all propertyTranscode values are transcode keys.
|
|
184
207
|
const transcodes = Object.keys(data.transcodes);
|
|
185
|
-
for (const [
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
208
|
+
for (const [property, transcode] of Object.entries(data.propertyTranscodes))
|
|
209
|
+
if (!transcodes.includes(transcode))
|
|
210
|
+
ctx.addIssue({
|
|
211
|
+
code: z.ZodIssueCode.invalid_enum_value,
|
|
212
|
+
options: transcodes,
|
|
213
|
+
path: ['propertyTranscodes', property],
|
|
214
|
+
received: transcode,
|
|
215
|
+
});
|
|
216
|
+
// Validate all sharded property elements are transcoded properties.
|
|
217
|
+
for (const [property, elements] of Object.entries(data.generatedProperties.sharded))
|
|
218
|
+
for (const element of elements)
|
|
219
|
+
if (!transcodedProperties.includes(element))
|
|
189
220
|
ctx.addIssue({
|
|
190
221
|
code: z.ZodIssueCode.invalid_enum_value,
|
|
191
|
-
options:
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
});
|
|
195
|
-
// validate all entity generated property elements have a corresponding entity element type.
|
|
196
|
-
const typedElements = Object.keys(entity.elementTranscodes);
|
|
197
|
-
for (const [generatedKey, generated] of Object.entries(entity.generated))
|
|
198
|
-
for (const element of generated?.elements ?? [])
|
|
199
|
-
if (!typedElements.includes(element))
|
|
200
|
-
ctx.addIssue({
|
|
201
|
-
code: z.ZodIssueCode.invalid_enum_value,
|
|
202
|
-
options: typedElements,
|
|
203
|
-
path: [
|
|
204
|
-
'entities',
|
|
205
|
-
entityToken,
|
|
206
|
-
'generated',
|
|
207
|
-
generatedKey,
|
|
208
|
-
'elements',
|
|
209
|
-
],
|
|
210
|
-
received: element,
|
|
211
|
-
});
|
|
212
|
-
// validate indexes.
|
|
213
|
-
const generatedProperties = Object.keys(entity.generated);
|
|
214
|
-
for (const [indexKey, { hashKey, rangeKey, projections },] of Object.entries(entity.indexes)) {
|
|
215
|
-
// validate index hash key is sharded
|
|
216
|
-
if (hashKey !== data.hashKey && !entity.generated[hashKey]?.sharded)
|
|
217
|
-
ctx.addIssue({
|
|
218
|
-
code: z.ZodIssueCode.custom,
|
|
219
|
-
message: 'index hash key is not sharded',
|
|
220
|
-
path: ['entities', entityToken, 'indexes', indexKey, 'hashKey'],
|
|
222
|
+
options: transcodedProperties,
|
|
223
|
+
received: element,
|
|
224
|
+
path: ['generatedProperties', 'sharded', property],
|
|
221
225
|
});
|
|
222
|
-
|
|
223
|
-
|
|
226
|
+
// Validate all unsharded property elements are transcoded properties.
|
|
227
|
+
for (const [property, elements] of Object.entries(data.generatedProperties.unsharded))
|
|
228
|
+
for (const element of elements)
|
|
229
|
+
if (!transcodedProperties.includes(element))
|
|
224
230
|
ctx.addIssue({
|
|
225
|
-
code: z.ZodIssueCode.
|
|
226
|
-
|
|
227
|
-
|
|
231
|
+
code: z.ZodIssueCode.invalid_enum_value,
|
|
232
|
+
options: transcodedProperties,
|
|
233
|
+
received: element,
|
|
234
|
+
path: ['generatedProperties', 'unsharded', property],
|
|
228
235
|
});
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
236
|
+
// Validate indexes.
|
|
237
|
+
for (const [indexKey, { hashKey, rangeKey, projections }] of Object.entries(data.indexes)) {
|
|
238
|
+
// Validate hash key is sharded.
|
|
239
|
+
if (![data.hashKey, ...shardedKeys].includes(hashKey)) {
|
|
240
|
+
ctx.addIssue({
|
|
241
|
+
code: z.ZodIssueCode.invalid_enum_value,
|
|
242
|
+
options: [data.hashKey, ...shardedKeys],
|
|
243
|
+
path: ['indexes', indexKey, 'hashKey'],
|
|
244
|
+
received: hashKey,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
// Validate range key is unsharded or transcodable.
|
|
248
|
+
if (![data.rangeKey, ...unshardedKeys, ...transcodedProperties].includes(rangeKey)) {
|
|
249
|
+
ctx.addIssue({
|
|
250
|
+
code: z.ZodIssueCode.invalid_enum_value,
|
|
251
|
+
options: [data.rangeKey, ...unshardedKeys],
|
|
252
|
+
path: ['indexes', indexKey, 'rangeKey'],
|
|
253
|
+
received: rangeKey,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
// Validate no index projections are keys.
|
|
257
|
+
if (projections)
|
|
258
|
+
for (const projection of projections)
|
|
259
|
+
if ([
|
|
260
|
+
data.hashKey,
|
|
261
|
+
data.rangeKey,
|
|
262
|
+
hashKey,
|
|
263
|
+
rangeKey,
|
|
264
|
+
...shardedKeys,
|
|
265
|
+
...unshardedKeys,
|
|
266
|
+
].includes(projection))
|
|
233
267
|
ctx.addIssue({
|
|
234
|
-
code: z.ZodIssueCode.
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
entityToken,
|
|
239
|
-
'indexes',
|
|
240
|
-
indexKey,
|
|
241
|
-
'components',
|
|
242
|
-
],
|
|
243
|
-
received: component,
|
|
268
|
+
code: z.ZodIssueCode.custom,
|
|
269
|
+
message: 'index projection is a key',
|
|
270
|
+
params: { projection },
|
|
271
|
+
path: ['indexes', indexKey, 'projections'],
|
|
244
272
|
});
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
273
|
+
}
|
|
274
|
+
// validate entities
|
|
275
|
+
for (const [entityToken, { timestampProperty, uniqueProperty },] of Object.entries(data.entities)) {
|
|
276
|
+
// validate timestampProperty is a transcoded property.
|
|
277
|
+
if (!transcodedProperties.includes(timestampProperty))
|
|
278
|
+
ctx.addIssue({
|
|
279
|
+
code: z.ZodIssueCode.invalid_enum_value,
|
|
280
|
+
options: transcodedProperties,
|
|
281
|
+
path: ['entities', entityToken, 'timestampProperty'],
|
|
282
|
+
received: timestampProperty,
|
|
283
|
+
});
|
|
284
|
+
// validate uniqueProperty is a transcoded property.
|
|
285
|
+
if (!transcodedProperties.includes(uniqueProperty))
|
|
286
|
+
ctx.addIssue({
|
|
287
|
+
code: z.ZodIssueCode.invalid_enum_value,
|
|
288
|
+
options: transcodedProperties,
|
|
289
|
+
path: ['entities', entityToken, 'uniqueProperty'],
|
|
290
|
+
received: uniqueProperty,
|
|
291
|
+
});
|
|
262
292
|
}
|
|
263
293
|
});
|
|
264
294
|
|
package/dist/mjs/addKeys.js
CHANGED
|
@@ -5,11 +5,11 @@ import { updateItemRangeKey } from './updateItemRangeKey.js';
|
|
|
5
5
|
import { validateEntityToken } from './validateEntityToken.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* Update generated properties, hash key, and range key on an {@link
|
|
8
|
+
* Update generated properties, hash key, and range key on an {@link EntityItem | `EntityItem`} object.
|
|
9
9
|
*
|
|
10
10
|
* @param entityManager - {@link EntityManager | `EntityManager`} instance.
|
|
11
|
-
* @param entityToken - {@link
|
|
12
|
-
* @param item - {@link
|
|
11
|
+
* @param entityToken - {@link ConfigEntity.uniqueProperty | `this.config.entities`} key.
|
|
12
|
+
* @param item - {@link EntityItem | `EntityItem`} object.
|
|
13
13
|
* @param overwrite - Overwrite existing properties (default `false`).
|
|
14
14
|
*
|
|
15
15
|
* @returns Shallow clone of `item` with updated properties.
|
|
@@ -25,10 +25,10 @@ function addKeys(entityManager, entityToken, item, overwrite = false) {
|
|
|
25
25
|
// Update range key.
|
|
26
26
|
newItem = updateItemRangeKey(entityManager, entityToken, newItem, overwrite);
|
|
27
27
|
// Update generated properties.
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
const { sharded, unsharded } = entityManager.config.generatedProperties;
|
|
29
|
+
for (const property in { ...sharded, ...unsharded }) {
|
|
30
30
|
if (overwrite || isNil(item[property])) {
|
|
31
|
-
const encoded = encodeGeneratedProperty(entityManager,
|
|
31
|
+
const encoded = encodeGeneratedProperty(entityManager, property, newItem);
|
|
32
32
|
if (encoded)
|
|
33
33
|
Object.assign(newItem, { [property]: encoded });
|
|
34
34
|
else
|
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var validateEntityToken = require('./validateEntityToken.js');
|
|
1
|
+
import { validateTranscodedProperty } from './validateTranscodedProperty.js';
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
|
-
* Decode an {@link
|
|
4
|
+
* Decode an {@link EntityItem | `EntityItem`} generated property element or ungenerated index component using the associated {@link Transcodes | Transcodes} `encode` function.
|
|
7
5
|
*
|
|
8
6
|
* Returns all `undefined` values.
|
|
9
7
|
*
|
|
10
8
|
* If `element` is the {@link Config.hashKey | `hashKey`} or {@link Config.rangeKey | `rangeKey`}, returns the value as-is.
|
|
11
9
|
*
|
|
12
10
|
* @param entityManager - {@link EntityManager | `EntityManager`} instance.
|
|
13
|
-
* @param entityToken - {@link ConfigKeys.entities | `entityManager.config.entities`} key.
|
|
14
11
|
* @param element - The {@link Entity | `Entity`} generated property element or ungenerated index component to encode.
|
|
15
12
|
* @param value - Encoded entity element.
|
|
16
13
|
*
|
|
@@ -18,17 +15,15 @@ var validateEntityToken = require('./validateEntityToken.js');
|
|
|
18
15
|
*
|
|
19
16
|
* @throws `Error` if `entityToken` is invalid.
|
|
20
17
|
*/
|
|
21
|
-
function
|
|
18
|
+
function decodeElement(entityManager, element, value) {
|
|
22
19
|
try {
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
// Validate params.
|
|
21
|
+
validateTranscodedProperty(entityManager, element);
|
|
25
22
|
if (!value)
|
|
26
23
|
return;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const decoded = transcodes[entities[entityToken].elementTranscodes[element]].decode(value);
|
|
24
|
+
const { propertyTranscodes, transcodes } = entityManager.config;
|
|
25
|
+
const decoded = transcodes[propertyTranscodes[element]].decode(value);
|
|
30
26
|
entityManager.logger.debug('decoded entity element', {
|
|
31
|
-
entityToken,
|
|
32
27
|
element,
|
|
33
28
|
value,
|
|
34
29
|
decoded,
|
|
@@ -38,7 +33,6 @@ function decodeEntityElement(entityManager, entityToken, element, value) {
|
|
|
38
33
|
catch (error) {
|
|
39
34
|
if (error instanceof Error)
|
|
40
35
|
entityManager.logger.error(error.message, {
|
|
41
|
-
entityToken,
|
|
42
36
|
element,
|
|
43
37
|
value,
|
|
44
38
|
});
|
|
@@ -46,4 +40,4 @@ function decodeEntityElement(entityManager, entityToken, element, value) {
|
|
|
46
40
|
}
|
|
47
41
|
}
|
|
48
42
|
|
|
49
|
-
|
|
43
|
+
export { decodeElement };
|
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
import { objectify } from 'radash';
|
|
2
|
-
import {
|
|
3
|
-
import { validateEntityToken } from './validateEntityToken.js';
|
|
2
|
+
import { decodeElement } from './decodeElement.js';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
|
-
* Decode a generated property value. Returns
|
|
5
|
+
* Decode a generated property value. Returns an {@link EntityItem | `EntityItem`}.
|
|
7
6
|
*
|
|
8
7
|
* @param entityManager - {@link EntityManager | `EntityManager`} instance.
|
|
9
8
|
* @param entityToken - `entityManager.config.entities` key.
|
|
10
9
|
* @param encoded - Encoded generated property value.
|
|
11
10
|
*
|
|
12
|
-
* @returns
|
|
11
|
+
* @returns {@link EntityItem | `EntityItem`} object with updated properties decoded from `encoded`.
|
|
13
12
|
*
|
|
14
13
|
* @throws `Error` if `entityToken` is invalid.
|
|
15
14
|
*/
|
|
16
|
-
function decodeGeneratedProperty(entityManager,
|
|
15
|
+
function decodeGeneratedProperty(entityManager, encoded) {
|
|
17
16
|
try {
|
|
18
17
|
const { generatedKeyDelimiter, generatedValueDelimiter, hashKey, shardKeyDelimiter, } = entityManager.config;
|
|
19
|
-
// Validate params.
|
|
20
|
-
validateEntityToken(entityManager, entityToken);
|
|
21
18
|
// Handle degenerate case.
|
|
22
19
|
if (!encoded)
|
|
23
20
|
return {};
|
|
@@ -35,9 +32,8 @@ function decodeGeneratedProperty(entityManager, entityToken, encoded) {
|
|
|
35
32
|
return pair;
|
|
36
33
|
});
|
|
37
34
|
// Assign decoded properties.
|
|
38
|
-
Object.assign(decoded, objectify(values, ([key]) => key, ([key, value]) =>
|
|
35
|
+
Object.assign(decoded, objectify(values, ([key]) => key, ([key, value]) => decodeElement(entityManager, key, value)));
|
|
39
36
|
entityManager.logger.debug('decoded generated property', {
|
|
40
|
-
entityToken,
|
|
41
37
|
encoded,
|
|
42
38
|
decoded,
|
|
43
39
|
});
|
|
@@ -45,7 +41,7 @@ function decodeGeneratedProperty(entityManager, entityToken, encoded) {
|
|
|
45
41
|
}
|
|
46
42
|
catch (error) {
|
|
47
43
|
if (error instanceof Error)
|
|
48
|
-
entityManager.logger.error(error.message, {
|
|
44
|
+
entityManager.logger.error(error.message, { encoded });
|
|
49
45
|
throw error;
|
|
50
46
|
}
|
|
51
47
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { encodeElement } from './encodeElement.js';
|
|
2
2
|
import { unwrapIndex } from './unwrapIndex.js';
|
|
3
|
-
import {
|
|
3
|
+
import { validateEntityToken } from './validateEntityToken.js';
|
|
4
|
+
import { validateIndexToken } from './validateIndexToken.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
|
-
* Condense
|
|
7
|
+
* Condense an {@link EntityItem | `EntityItem`} into a delimited string representing the deduped, sorted, ungenerated component elements of an {@link Config.indexes | index}, leaving out those of the index hash key.
|
|
7
8
|
*
|
|
8
9
|
* @remarks
|
|
9
10
|
* Reverses {@link EntityManager.rehydrateIndexItem | `rehydrateIndexItem`}.
|
|
@@ -16,31 +17,31 @@ import { validateEntityIndexToken } from './validateEntityIndexToken.js';
|
|
|
16
17
|
* `item` must be populated with all required index component elements!
|
|
17
18
|
*
|
|
18
19
|
* @param entityManager - {@link EntityManager | `EntityManager`} instance.
|
|
19
|
-
* @param entityToken - {@link
|
|
20
|
-
* @param indexToken - {@link ConfigEntity.indexes | `entityManager.config.
|
|
21
|
-
* @param item -
|
|
20
|
+
* @param entityToken - {@link Config.entities | `entityManager.config.entities`} key.
|
|
21
|
+
* @param indexToken - {@link ConfigEntity.indexes | `entityManager.config.indexes`} key.
|
|
22
|
+
* @param item - {@link EntityItem | `EntityItem`} object.
|
|
22
23
|
*
|
|
23
24
|
* @returns Dehydrated index value.
|
|
24
25
|
*
|
|
25
|
-
* @throws `Error` if `entityToken` is invalid.
|
|
26
26
|
* @throws `Error` if `indexToken` is invalid.
|
|
27
27
|
*/
|
|
28
28
|
function dehydrateIndexItem(entityManager, entityToken, indexToken, item) {
|
|
29
29
|
try {
|
|
30
|
-
const { generatedKeyDelimiter } = entityManager.config;
|
|
31
30
|
// Validate params.
|
|
32
|
-
|
|
31
|
+
validateEntityToken(entityManager, entityToken);
|
|
32
|
+
validateIndexToken(entityManager, indexToken);
|
|
33
33
|
// Handle degenerate case.
|
|
34
34
|
if (!item)
|
|
35
35
|
return '';
|
|
36
36
|
// Unwrap index elements.
|
|
37
|
-
const { hashKey } = entityManager.config.
|
|
37
|
+
const { hashKey } = entityManager.config.indexes[indexToken];
|
|
38
38
|
const elements = unwrapIndex(entityManager, entityToken, indexToken, [
|
|
39
39
|
hashKey,
|
|
40
40
|
]);
|
|
41
41
|
// Join index element values.
|
|
42
|
+
const { generatedKeyDelimiter } = entityManager.config;
|
|
42
43
|
const dehydrated = elements
|
|
43
|
-
.map((element) =>
|
|
44
|
+
.map((element) => encodeElement(entityManager, element, item))
|
|
44
45
|
.join(generatedKeyDelimiter);
|
|
45
46
|
entityManager.logger.debug('dehydrated index', {
|
|
46
47
|
item,
|