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