@karmaniverous/entity-manager 5.0.9 → 6.0.0

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.
Files changed (84) hide show
  1. package/README.md +15 -438
  2. package/dist/cjs/_virtual/_commonjsHelpers.js +7 -0
  3. package/dist/cjs/_virtual/lz-string.js +5 -0
  4. package/dist/cjs/node_modules/@karmaniverous/entity-tools/dist/mjs/Nil.js +14 -0
  5. package/dist/cjs/node_modules/@karmaniverous/entity-tools/dist/mjs/defaultTranscodes.js +94 -0
  6. package/dist/cjs/node_modules/@karmaniverous/entity-tools/dist/mjs/sort.js +52 -0
  7. package/dist/cjs/node_modules/lz-string/libs/lz-string.js +517 -0
  8. package/dist/cjs/node_modules/radash/dist/esm/array.js +90 -0
  9. package/dist/cjs/node_modules/radash/dist/esm/async.js +62 -0
  10. package/dist/cjs/node_modules/radash/dist/esm/object.js +25 -0
  11. package/dist/cjs/node_modules/radash/dist/esm/typed.js +35 -0
  12. package/dist/cjs/node_modules/string-hash/index.js +23 -0
  13. package/dist/cjs/node_modules/tslib/tslib.es6.js +39 -0
  14. package/dist/cjs/node_modules/zod/lib/index.js +4342 -0
  15. package/dist/cjs/src/EntityManager.js +93 -0
  16. package/dist/cjs/src/ParsedConfig.js +227 -0
  17. package/dist/cjs/src/addKeys.js +47 -0
  18. package/dist/cjs/src/decodeEntityElement.js +37 -0
  19. package/dist/cjs/src/decodeGeneratedProperty.js +48 -0
  20. package/dist/cjs/src/dehydrateIndexItem.js +52 -0
  21. package/dist/cjs/src/dehydratePageKeyMap.js +75 -0
  22. package/dist/cjs/src/encodeEntityElement.js +36 -0
  23. package/dist/cjs/src/encodeGeneratedProperty.js +44 -0
  24. package/dist/cjs/src/getHashKeySpace.js +41 -0
  25. package/dist/cjs/src/getShardBump.js +24 -0
  26. package/dist/cjs/src/index.js +7 -0
  27. package/dist/cjs/src/query.js +104 -0
  28. package/dist/cjs/src/rehydrateIndexItem.js +48 -0
  29. package/dist/cjs/src/rehydratePageKeyMap.js +66 -0
  30. package/dist/cjs/src/removeKeys.js +33 -0
  31. package/dist/cjs/src/unwrapIndex.js +40 -0
  32. package/dist/cjs/src/updateItemHashKey.js +55 -0
  33. package/dist/cjs/src/updateItemRangeKey.js +45 -0
  34. package/dist/cjs/src/validateEntityGeneratedProperty.js +29 -0
  35. package/dist/cjs/src/validateEntityIndexToken.js +22 -0
  36. package/dist/cjs/src/validateEntityToken.js +17 -0
  37. package/dist/index.d.cts +826 -0
  38. package/dist/index.d.mts +826 -0
  39. package/dist/index.d.ts +826 -0
  40. package/dist/mjs/_virtual/_commonjsHelpers.js +5 -0
  41. package/dist/mjs/_virtual/lz-string.js +3 -0
  42. package/dist/mjs/node_modules/@karmaniverous/entity-tools/dist/mjs/Nil.js +12 -0
  43. package/dist/mjs/node_modules/@karmaniverous/entity-tools/dist/mjs/defaultTranscodes.js +92 -0
  44. package/dist/mjs/node_modules/@karmaniverous/entity-tools/dist/mjs/sort.js +50 -0
  45. package/dist/mjs/node_modules/lz-string/libs/lz-string.js +515 -0
  46. package/dist/mjs/node_modules/radash/dist/esm/array.js +80 -0
  47. package/dist/mjs/node_modules/radash/dist/esm/async.js +58 -0
  48. package/dist/mjs/node_modules/radash/dist/esm/object.js +22 -0
  49. package/dist/mjs/node_modules/radash/dist/esm/typed.js +28 -0
  50. package/dist/mjs/node_modules/string-hash/index.js +21 -0
  51. package/dist/mjs/node_modules/tslib/tslib.es6.js +36 -0
  52. package/dist/mjs/node_modules/zod/lib/index.js +4233 -0
  53. package/dist/mjs/src/EntityManager.js +91 -0
  54. package/dist/mjs/src/ParsedConfig.js +225 -0
  55. package/dist/mjs/src/addKeys.js +45 -0
  56. package/dist/mjs/src/decodeEntityElement.js +35 -0
  57. package/dist/mjs/src/decodeGeneratedProperty.js +46 -0
  58. package/dist/mjs/src/dehydrateIndexItem.js +50 -0
  59. package/dist/mjs/src/dehydratePageKeyMap.js +73 -0
  60. package/dist/mjs/src/encodeEntityElement.js +34 -0
  61. package/dist/mjs/src/encodeGeneratedProperty.js +42 -0
  62. package/dist/mjs/src/getHashKeySpace.js +39 -0
  63. package/dist/mjs/src/getShardBump.js +22 -0
  64. package/dist/mjs/src/index.js +1 -0
  65. package/dist/mjs/src/query.js +102 -0
  66. package/dist/mjs/src/rehydrateIndexItem.js +46 -0
  67. package/dist/mjs/src/rehydratePageKeyMap.js +64 -0
  68. package/dist/mjs/src/removeKeys.js +31 -0
  69. package/dist/mjs/src/unwrapIndex.js +38 -0
  70. package/dist/mjs/src/updateItemHashKey.js +53 -0
  71. package/dist/mjs/src/updateItemRangeKey.js +43 -0
  72. package/dist/mjs/src/validateEntityGeneratedProperty.js +27 -0
  73. package/dist/mjs/src/validateEntityIndexToken.js +20 -0
  74. package/dist/mjs/src/validateEntityToken.js +15 -0
  75. package/package.json +107 -58
  76. package/.env +0 -1
  77. package/.github/FUNDING.yml +0 -3
  78. package/dist/default/lib/EntityManager/EntityManager.js +0 -423
  79. package/dist/default/lib/EntityManager/PrivateEntityManager.js +0 -421
  80. package/dist/default/lib/index.js +0 -12
  81. package/dist/package.json +0 -3
  82. package/lib/EntityManager/EntityManager.js +0 -396
  83. package/lib/EntityManager/PrivateEntityManager.js +0 -436
  84. package/lib/index.js +0 -1
@@ -0,0 +1,93 @@
1
+ 'use strict';
2
+
3
+ var tslib_es6 = require('../node_modules/tslib/tslib.es6.js');
4
+ var addKeys = require('./addKeys.js');
5
+ var ParsedConfig = require('./ParsedConfig.js');
6
+ var query = require('./query.js');
7
+ var removeKeys = require('./removeKeys.js');
8
+
9
+ var _EntityManager_config;
10
+ /**
11
+ * The EntityManager class applies a configuration-driven sharded data model &
12
+ * query strategy to NoSql data.
13
+ *
14
+ * @category Entity Manager
15
+ */
16
+ class EntityManager {
17
+ /**
18
+ * Create an EntityManager instance.
19
+ *
20
+ * @param config - EntityManager {@link Config | `Config`} object.
21
+ */
22
+ constructor(config) {
23
+ _EntityManager_config.set(this, void 0);
24
+ tslib_es6.__classPrivateFieldSet(this, _EntityManager_config, ParsedConfig.configSchema.parse(config), "f");
25
+ }
26
+ /**
27
+ * Get the current EntityManager {@link Config | `Config`} object.
28
+ *
29
+ * @returns Current {@link Config | `Config`} object.
30
+ */
31
+ get config() {
32
+ return tslib_es6.__classPrivateFieldGet(this, _EntityManager_config, "f");
33
+ }
34
+ /**
35
+ * Set the current EntityManager {@link Config | `Config`} object.
36
+ *
37
+ * @param value - {@link Config | `Config`} object.
38
+ */
39
+ set config(value) {
40
+ tslib_es6.__classPrivateFieldSet(this, _EntityManager_config, ParsedConfig.configSchema.parse(value), "f");
41
+ }
42
+ /**
43
+ * Update generated properties, hash key, and range key on an {@link ItemMap | `ItemMap`} object. Mutates `item`.
44
+ *
45
+ * @param item - {@link ItemMap | `ItemMap`} object.
46
+ * @param entityToken - {@link ConfigKeys.entities | `this.config.entities`} key.
47
+ * @param overwrite - Overwrite existing properties (default `false`).
48
+ *
49
+ * @returns Mutated `item` with updated properties.
50
+ *
51
+ * @throws `Error` if `entityToken` is invalid.
52
+ */
53
+ addKeys(item, entityToken, overwrite = false) {
54
+ return addKeys.addKeys(this, item, entityToken, overwrite);
55
+ }
56
+ /**
57
+ * Strips generated properties, hash key, and range key from an {@link ItemMap | `ItemMap`} object. Mutates `item`.
58
+ *
59
+ * @param item - {@link ItemMap | `ItemMap`} object.
60
+ * @param entityToken - {@link ConfigKeys.entities | `this.config.entities`} key.
61
+ *
62
+ * @returns Mutated `item` without generated properties, hash key or range key.
63
+ *
64
+ * @throws `Error` if `entityToken` is invalid.
65
+ */
66
+ removeKeys(item, entityToken) {
67
+ return removeKeys.removeKeys(this, item, entityToken);
68
+ }
69
+ /**
70
+ * Query a database entity across shards in a provider-generic fashion.
71
+ *
72
+ * @remarks
73
+ * The provided {@link ShardQueryFunction | `ShardQueryFunction`} performs the actual query of individual data pages on individual shards. This function is presumed to express provider-specific query logic, including any necessary indexing or search constraints.
74
+ *
75
+ * Individual shard query results will be combined, deduped by {@link ConfigEntity.uniqueProperty} property value, and sorted by {@link QueryOptions.sortOrder | `sortOrder`}.
76
+ *
77
+ * In queries on sharded data, expect the leading and trailing edges of returned data pages to interleave somewhat with preceding & following pages.
78
+ *
79
+ * Unsharded query results should sort & page as expected.
80
+ *
81
+ * @param options - {@link QueryOptions | `QueryOptions`} object.
82
+ *
83
+ * @returns {@link QueryResult} object.
84
+ *
85
+ * @throws Error if {@link QueryOptions.pageKeyMap | `pageKeyMap`} keys do not match {@link QueryOptions.queryMap | `queryMap`} keys.
86
+ */
87
+ async query(options) {
88
+ return await query.query(this, options);
89
+ }
90
+ }
91
+ _EntityManager_config = new WeakMap();
92
+
93
+ exports.EntityManager = EntityManager;
@@ -0,0 +1,227 @@
1
+ 'use strict';
2
+
3
+ var defaultTranscodes = require('../node_modules/@karmaniverous/entity-tools/dist/mjs/defaultTranscodes.js');
4
+ var index = require('../node_modules/zod/lib/index.js');
5
+ var array = require('../node_modules/radash/dist/esm/array.js');
6
+
7
+ const defaultShardBump = { timestamp: 0, charBits: 1, chars: 0 };
8
+ const validateArrayUnique = (arr, ctx, identity = (item) => item, path = []) => {
9
+ const counts = array.counting(arr, identity);
10
+ for (const [element, count] of Object.entries(counts)) {
11
+ if (count > 1)
12
+ ctx.addIssue({
13
+ code: index.default.ZodIssueCode.custom,
14
+ message: `duplicate array element '${element}'`,
15
+ path,
16
+ });
17
+ }
18
+ };
19
+ const validateKeyExclusive = (key, label, ref, ctx) => {
20
+ if (ref.includes(key))
21
+ ctx.addIssue({
22
+ code: index.default.ZodIssueCode.custom,
23
+ message: `${label} '${key}' is not exclusive`,
24
+ });
25
+ };
26
+ const configSchema = index.default
27
+ .object({
28
+ entities: index.default
29
+ .record(index.default
30
+ .object({
31
+ defaultLimit: index.default
32
+ .number()
33
+ .int()
34
+ .positive()
35
+ .safe()
36
+ .optional()
37
+ .default(10),
38
+ defaultPageSize: index.default
39
+ .number()
40
+ .int()
41
+ .positive()
42
+ .safe()
43
+ .optional()
44
+ .default(10),
45
+ generated: index.default
46
+ .record(index.default
47
+ .object({
48
+ atomic: index.default.boolean().optional().default(false),
49
+ elements: index.default
50
+ .array(index.default.string().min(1))
51
+ .nonempty()
52
+ .superRefine(validateArrayUnique),
53
+ sharded: index.default.boolean().optional().default(false),
54
+ })
55
+ .optional())
56
+ .optional()
57
+ .default({}),
58
+ elementTypes: index.default.record(index.default.string()).optional().default({}),
59
+ indexes: index.default
60
+ .record(index.default
61
+ .array(index.default.string().min(1))
62
+ .nonempty()
63
+ .superRefine(validateArrayUnique))
64
+ .optional()
65
+ .default({}),
66
+ shardBumps: index.default
67
+ .array(index.default
68
+ .object({
69
+ timestamp: index.default.number().nonnegative().safe(),
70
+ charBits: index.default.number().int().min(1).max(5),
71
+ chars: index.default.number().int().min(0).max(40),
72
+ })
73
+ .strict())
74
+ .optional()
75
+ .default([defaultShardBump])
76
+ // validate shardBump uniqueness by timestamp.
77
+ .superRefine((val, ctx) => {
78
+ validateArrayUnique(val, ctx, ({ timestamp }) => timestamp, [
79
+ 'timestamp',
80
+ ]);
81
+ })
82
+ .transform((val) => {
83
+ // sort shardBumps by timestamp.
84
+ let sorted = array.sort(val, ({ timestamp }) => timestamp);
85
+ // prepend defaultShardBump if missing zero-timestamp bump.
86
+ if (sorted[0].timestamp !== 0) {
87
+ sorted = [defaultShardBump, ...sorted];
88
+ }
89
+ return sorted;
90
+ })
91
+ // validate shardBump chars mootonically increase with timestamp.
92
+ .superRefine((val, ctx) => {
93
+ if (val.length > 1) {
94
+ for (let i = 1; i < val.length; i++)
95
+ if (val[i].chars <= val[i - 1].chars)
96
+ ctx.addIssue({
97
+ code: index.default.ZodIssueCode.custom,
98
+ message: `shardBump chars do not monotonically increase at timestamp ${val[i].timestamp.toString()}`,
99
+ path: [i],
100
+ });
101
+ }
102
+ }),
103
+ timestampProperty: index.default.string().min(1),
104
+ uniqueProperty: index.default.string().min(1),
105
+ })
106
+ .strict()
107
+ .superRefine((data, ctx) => {
108
+ const generatedKeys = Object.keys(data.generated);
109
+ // validate timestampProperty is not a generated key.
110
+ validateKeyExclusive(data.timestampProperty, 'timestampProperty', generatedKeys, ctx);
111
+ // validate uniqueProperty is not a generated key.
112
+ validateKeyExclusive(data.uniqueProperty, 'uniqueProperty', generatedKeys, ctx);
113
+ }))
114
+ .optional()
115
+ .default({}),
116
+ generatedKeyDelimiter: index.default.string().regex(/\W+/).optional().default('|'),
117
+ generatedValueDelimiter: index.default.string().regex(/\W+/).optional().default('#'),
118
+ shardKeyDelimiter: index.default.string().regex(/\W+/).optional().default('!'),
119
+ hashKey: index.default.string().optional().default('hashKey'),
120
+ rangeKey: index.default.string().optional().default('rangeKey'),
121
+ throttle: index.default.number().int().positive().safe().optional().default(10),
122
+ transcodes: index.default
123
+ .record(index.default
124
+ .object({
125
+ encode: index.default.function().args(index.default.any()).returns(index.default.string()),
126
+ decode: index.default.function().args(index.default.string()).returns(index.default.any()),
127
+ })
128
+ .strict())
129
+ .optional()
130
+ .default(defaultTranscodes.defaultTranscodes),
131
+ })
132
+ .strict()
133
+ .superRefine((data, ctx) => {
134
+ // validate no generated key delimiter collision
135
+ if (data.generatedKeyDelimiter.includes(data.generatedValueDelimiter))
136
+ ctx.addIssue({
137
+ code: index.default.ZodIssueCode.custom,
138
+ message: 'generatedKeyDelimiter contains generatedValueDelimiter',
139
+ path: ['generatedKeyDelimiter'],
140
+ });
141
+ if (data.generatedKeyDelimiter.includes(data.shardKeyDelimiter))
142
+ ctx.addIssue({
143
+ code: index.default.ZodIssueCode.custom,
144
+ message: 'generatedKeyDelimiter contains shardKeyDelimiter',
145
+ path: ['generatedKeyDelimiter'],
146
+ });
147
+ // validate no generated value delimiter collision
148
+ if (data.generatedValueDelimiter.includes(data.generatedKeyDelimiter))
149
+ ctx.addIssue({
150
+ code: index.default.ZodIssueCode.custom,
151
+ message: 'generatedValueDelimiter contains generatedKeyDelimiter',
152
+ path: ['generatedValueDelimiter'],
153
+ });
154
+ if (data.generatedValueDelimiter.includes(data.shardKeyDelimiter))
155
+ ctx.addIssue({
156
+ code: index.default.ZodIssueCode.custom,
157
+ message: 'generatedValueDelimiter contains shardKeyDelimiter',
158
+ path: ['generatedValueDelimiter'],
159
+ });
160
+ // validate no shard key delimiter collision
161
+ if (data.shardKeyDelimiter.includes(data.generatedKeyDelimiter))
162
+ ctx.addIssue({
163
+ code: index.default.ZodIssueCode.custom,
164
+ message: 'shardKeyDelimiter contains generatedKeyDelimiter',
165
+ path: ['shardKeyDelimiter'],
166
+ });
167
+ if (data.shardKeyDelimiter.includes(data.generatedValueDelimiter))
168
+ ctx.addIssue({
169
+ code: index.default.ZodIssueCode.custom,
170
+ message: 'shardKeyDelimiter contains generatedValueDelimiter',
171
+ path: ['shardKeyDelimiter'],
172
+ });
173
+ const reservedKeys = Object.values(data.entities).reduce((reserved, { generated, timestampProperty, uniqueProperty }) => new Set([
174
+ ...reserved,
175
+ ...Object.keys(generated),
176
+ timestampProperty,
177
+ uniqueProperty,
178
+ ]), new Set());
179
+ // validate hashKey is not a reserved key.
180
+ validateKeyExclusive(data.hashKey, 'hashKey', [...reservedKeys, data.rangeKey], ctx);
181
+ // validate rangeKey is not a reserved key.
182
+ validateKeyExclusive(data.rangeKey, 'rangeKey', [...reservedKeys, data.hashKey], ctx);
183
+ // validate entities
184
+ const transcodes = Object.keys(data.transcodes);
185
+ for (const [entityToken, entity] of Object.entries(data.entities)) {
186
+ // validate all entity generated element type values are transcode keys.
187
+ for (const [element, generatedElementType] of Object.entries(entity.elementTypes))
188
+ if (!transcodes.includes(generatedElementType))
189
+ ctx.addIssue({
190
+ code: index.default.ZodIssueCode.invalid_enum_value,
191
+ options: transcodes,
192
+ path: ['entities', entityToken, 'elementTypes', element],
193
+ received: generatedElementType,
194
+ });
195
+ // validate all entity generated property elements have a corresponding entity element type.
196
+ const typedElements = Object.keys(entity.elementTypes);
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: index.default.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 all ungenerated entity index components have a corresponding entity element type.
213
+ const generatedProperties = Object.keys(entity.generated);
214
+ for (const [indexKey, index$1] of Object.entries(entity.indexes))
215
+ for (const component of index$1)
216
+ if (![data.hashKey, data.rangeKey, ...generatedProperties].includes(component) &&
217
+ !typedElements.includes(component))
218
+ ctx.addIssue({
219
+ code: index.default.ZodIssueCode.invalid_enum_value,
220
+ options: typedElements,
221
+ path: ['entities', entityToken, 'indexes', indexKey],
222
+ received: component,
223
+ });
224
+ }
225
+ });
226
+
227
+ exports.configSchema = configSchema;
@@ -0,0 +1,47 @@
1
+ 'use strict';
2
+
3
+ var Nil = require('../node_modules/@karmaniverous/entity-tools/dist/mjs/Nil.js');
4
+ var encodeGeneratedProperty = require('./encodeGeneratedProperty.js');
5
+ var updateItemHashKey = require('./updateItemHashKey.js');
6
+ var updateItemRangeKey = require('./updateItemRangeKey.js');
7
+ var validateEntityToken = require('./validateEntityToken.js');
8
+
9
+ /**
10
+ * Update generated properties, hash key, and range key on an {@link ItemMap | `ItemMap`} object. Mutates `item`.
11
+ *
12
+ * @param entityManager - {@link EntityManager | `EntityManager`} instance.
13
+ * @param item - {@link ItemMap | `ItemMap`} object.
14
+ * @param entityToken - {@link ConfigKeys.entities | `this.config.entities`} key.
15
+ * @param overwrite - Overwrite existing properties (default `false`).
16
+ *
17
+ * @returns Mutated `item` with updated properties.
18
+ *
19
+ * @throws `Error` if `entityToken` is invalid.
20
+ */
21
+ function addKeys(entityManager, item, entityToken, overwrite = false) {
22
+ try {
23
+ // Validate params.
24
+ validateEntityToken.validateEntityToken(entityManager, entityToken);
25
+ // Update hash key.
26
+ updateItemHashKey.updateItemHashKey(entityManager, item, entityToken, overwrite);
27
+ // Update range key.
28
+ updateItemRangeKey.updateItemRangeKey(entityManager, item, entityToken, overwrite);
29
+ // Update generated properties.
30
+ for (const property in entityManager.config.entities[entityToken]
31
+ .generated) {
32
+ if (overwrite || Nil.isNil(item[property])) {
33
+ const encoded = encodeGeneratedProperty.encodeGeneratedProperty(entityManager, item, entityToken, property);
34
+ if (encoded)
35
+ Object.assign(item, { [property]: encoded });
36
+ else
37
+ delete item[property];
38
+ }
39
+ }
40
+ return item;
41
+ }
42
+ catch (error) {
43
+ throw error;
44
+ }
45
+ }
46
+
47
+ exports.addKeys = addKeys;
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+
3
+ var validateEntityToken = require('./validateEntityToken.js');
4
+
5
+ /**
6
+ * Decode an {@link Entity | `Entity`} generated property element or ungenerated index component using the associated {@link Transcodes | Transcodes} `encode` function.
7
+ *
8
+ * Returns all `undefined` values.
9
+ *
10
+ * If `element` is the {@link Config.hashKey | `hashKey`} or {@link Config.rangeKey | `rangeKey`}, returns the value as-is.
11
+ *
12
+ * @param entityManager - {@link EntityManager | `EntityManager`} instance.
13
+ * @param item - Partial {@link ItemMap | `ItemMap`} object.
14
+ * @param entityToken - {@link ConfigKeys.entities | `entityManager.config.entities`} key.
15
+ * @param element - The {@link Entity | `Entity`} generated property element or ungenerated index component to encode.
16
+ *
17
+ * @returns Decoded value.
18
+ *
19
+ * @throws `Error` if `entityToken` is invalid.
20
+ */
21
+ function decodeEntityElement(entityManager, value, entityToken, element) {
22
+ try {
23
+ validateEntityToken.validateEntityToken(entityManager, entityToken);
24
+ const { entities, hashKey, rangeKey, transcodes } = entityManager.config;
25
+ if (!value)
26
+ return;
27
+ if ([hashKey, rangeKey].includes(element))
28
+ return value;
29
+ const decoded = transcodes[entities[entityToken].elementTypes[element]].decode(value);
30
+ return decoded;
31
+ }
32
+ catch (error) {
33
+ throw error;
34
+ }
35
+ }
36
+
37
+ exports.decodeEntityElement = decodeEntityElement;
@@ -0,0 +1,48 @@
1
+ 'use strict';
2
+
3
+ var decodeEntityElement = require('./decodeEntityElement.js');
4
+ var validateEntityToken = require('./validateEntityToken.js');
5
+ var array = require('../node_modules/radash/dist/esm/array.js');
6
+
7
+ /**
8
+ * Decode a generated property value. Returns a partial ItemMap.
9
+ *
10
+ * @param entityManager - {@link EntityManager | `EntityManager`} instance.
11
+ * @param encoded - Encoded generated property value.
12
+ * @param entityToken - `entityManager.config.entities` key.
13
+ *
14
+ * @returns Partial {@link ItemMap | `ItemMap`} object with updated properties decoded from `encoded`.
15
+ *
16
+ * @throws `Error` if `entityToken` is invalid.
17
+ */
18
+ function decodeGeneratedProperty(entityManager, encoded, entityToken) {
19
+ try {
20
+ const { generatedKeyDelimiter, generatedValueDelimiter, hashKey, shardKeyDelimiter, } = entityManager.config;
21
+ // Validate params.
22
+ validateEntityToken.validateEntityToken(entityManager, entityToken);
23
+ // Handle degenerate case.
24
+ if (!encoded)
25
+ return {};
26
+ // Split encoded into keys.
27
+ const keys = encoded.split(generatedKeyDelimiter);
28
+ // Initiate result with hashKey if sharded.
29
+ const decoded = keys[0].includes(shardKeyDelimiter)
30
+ ? { [hashKey]: keys.shift() }
31
+ : {};
32
+ // Split keys into values & validate.
33
+ const values = keys.map((key) => {
34
+ const pair = key.split(generatedValueDelimiter);
35
+ if (pair.length !== 2)
36
+ throw new Error(`invalid generated property value '${key}'`);
37
+ return pair;
38
+ });
39
+ // Assign decoded properties.
40
+ Object.assign(decoded, array.objectify(values, ([key]) => key, ([key, value]) => decodeEntityElement.decodeEntityElement(entityManager, value, entityToken, key)));
41
+ return decoded;
42
+ }
43
+ catch (error) {
44
+ throw error;
45
+ }
46
+ }
47
+
48
+ exports.decodeGeneratedProperty = decodeGeneratedProperty;
@@ -0,0 +1,52 @@
1
+ 'use strict';
2
+
3
+ var encodeEntityElement = require('./encodeEntityElement.js');
4
+ var unwrapIndex = require('./unwrapIndex.js');
5
+ var validateEntityIndexToken = require('./validateEntityIndexToken.js');
6
+
7
+ /**
8
+ * Condense a partial {@link ItemMap | `ItemMap`} object into a delimited string representing the deduped, sorted, ungenerated component elements of an {@link ConfigEntity.indexes | Entity index}.
9
+ *
10
+ * @remarks
11
+ * Reverses {@link EntityManager.rehydrateIndexItem | `rehydrateIndexItem`}.
12
+ *
13
+ * To create the output value, entityManager method:
14
+ *
15
+ * * Unwraps `index` components into deduped, sorted, ungenerated elements.
16
+ * * Joins `item` element values with {@link Config.generatedKeyDelimiter | `entityManager.config.generatedKeyDelimiter`}.
17
+ *
18
+ * `item` must be populated with all required index component elements!
19
+ *
20
+ * @param entityManager - {@link EntityManager | `EntityManager`} instance.
21
+ * @param item - Partial {@link ItemMap | `ItemMap`} object.
22
+ * @param entityToken - {@link ConfigKeys.entities | `entityManager.config.entities`} key.
23
+ * @param indexToken - {@link ConfigEntity.indexes | `entityManager.config.entities.<entityToken>.indexes`} key.
24
+ * @param omit - Array of index components to omit from the output value.
25
+ *
26
+ * @returns Dehydrated index value.
27
+ *
28
+ * @throws `Error` if `entityToken` is invalid.
29
+ * @throws `Error` if `indexToken` is invalid.
30
+ */
31
+ function dehydrateIndexItem(entityManager, item, entityToken, indexToken, omit = []) {
32
+ try {
33
+ const { generatedKeyDelimiter } = entityManager.config;
34
+ // Validate params.
35
+ validateEntityIndexToken.validateEntityIndexToken(entityManager, entityToken, indexToken);
36
+ // Handle degenerate case.
37
+ if (!item)
38
+ return '';
39
+ // Unwrap index elements.
40
+ const elements = unwrapIndex.unwrapIndex(entityManager, entityToken, indexToken).filter((element) => !omit.includes(element));
41
+ // Join index element values.
42
+ const dehydrated = elements
43
+ .map((element) => encodeEntityElement.encodeEntityElement(entityManager, item, entityToken, element))
44
+ .join(generatedKeyDelimiter);
45
+ return dehydrated;
46
+ }
47
+ catch (error) {
48
+ throw error;
49
+ }
50
+ }
51
+
52
+ exports.dehydrateIndexItem = dehydrateIndexItem;
@@ -0,0 +1,75 @@
1
+ 'use strict';
2
+
3
+ var decodeGeneratedProperty = require('./decodeGeneratedProperty.js');
4
+ var dehydrateIndexItem = require('./dehydrateIndexItem.js');
5
+ var validateEntityIndexToken = require('./validateEntityIndexToken.js');
6
+ var validateEntityToken = require('./validateEntityToken.js');
7
+
8
+ /**
9
+ * Dehydrate a {@link PageKeyMap | `PageKeyMap`} object into an array of dehydrated page keys.
10
+ *
11
+ * Reverses {@link EntityManager.rehydratePageKeyMap | `rehydratePageKeyMap`}.
12
+ *
13
+ * @param entityManager - {@link EntityManager | `EntityManager`} instance.
14
+ * @param pageKeyMap - {@link PageKeyMap | `PageKeyMap`} object to dehydrate.
15
+ * @param entityToken - {@link ConfigKeys.entities | `entityManager.config.entities`} key.
16
+ *
17
+ * @returns Array of dehydrated page keys.
18
+ *
19
+ * @throws `Error` if `entityToken` is invalid.
20
+ * @throws `Error` if any `pageKeyMap` key is an invalid indexToken is invalid {@link ConfigEntity.indexes | `entityManager.config.entities.<entityToken>.indexes`} key.
21
+ *
22
+ * @remarks
23
+ * In the returned array, an empty string member indicates the corresponding page key is `undefined`.
24
+ *
25
+ * An empty returned array indicates all page keys are `undefined`.
26
+ */
27
+ function dehydratePageKeyMap(entityManager, pageKeyMap, entityToken) {
28
+ try {
29
+ // Validate params.
30
+ validateEntityToken.validateEntityToken(entityManager, entityToken);
31
+ // Shortcut empty pageKeyMap.
32
+ if (!Object.keys(pageKeyMap).length) {
33
+ const dehydrated = [];
34
+ return dehydrated;
35
+ }
36
+ // Extract, sort & validate indexs.
37
+ const indexes = Object.keys(pageKeyMap).sort();
38
+ indexes.map((index) => validateEntityIndexToken.validateEntityIndexToken(entityManager, entityToken, index));
39
+ // Extract & sort hash keys.
40
+ const hashKeys = Object.keys(pageKeyMap[indexes[0]]);
41
+ // Dehydrate page keys.
42
+ let dehydrated = [];
43
+ for (const index of indexes) {
44
+ for (const hashKey of hashKeys) {
45
+ // Undefineed pageKey.
46
+ if (!pageKeyMap[index][hashKey]) {
47
+ dehydrated.push('');
48
+ continue;
49
+ }
50
+ // Compose item from page key
51
+ const item = Object.entries(pageKeyMap[index][hashKey]).reduce((item, [property, value]) => {
52
+ if (property in entityManager.config.entities[entityToken].generated ||
53
+ property === entityManager.config.rangeKey)
54
+ Object.assign(item, decodeGeneratedProperty.decodeGeneratedProperty(entityManager, value, entityToken));
55
+ else
56
+ Object.assign(item, { [property]: value });
57
+ return item;
58
+ }, {});
59
+ // Dehydrate index from item.
60
+ dehydrated.push(dehydrateIndexItem.dehydrateIndexItem(entityManager, item, entityToken, index, [
61
+ entityManager.config.hashKey,
62
+ ]));
63
+ }
64
+ }
65
+ // Replace with empty array if all pageKeys are empty strings.
66
+ if (dehydrated.every((pageKey) => pageKey === ''))
67
+ dehydrated = [];
68
+ return dehydrated;
69
+ }
70
+ catch (error) {
71
+ throw error;
72
+ }
73
+ }
74
+
75
+ exports.dehydratePageKeyMap = dehydratePageKeyMap;
@@ -0,0 +1,36 @@
1
+ 'use strict';
2
+
3
+ var validateEntityToken = require('./validateEntityToken.js');
4
+
5
+ /**
6
+ * Encode an {@link Entity | `Entity`} generated property element or ungenerated index component using the associated {@link Transcodes | Transcodes} `encode` function.
7
+ *
8
+ * Returns all `undefined` values.
9
+ *
10
+ * If `element` is the {@link Config.hashKey | `hashKey`} or {@link Config.rangeKey | `rangeKey`}, returns the value as-is.
11
+ *
12
+ * @param entityManager - {@link EntityManager | `EntityManager`} instance.
13
+ * @param item - Partial {@link ItemMap | `ItemMap`} object.
14
+ * @param entityToken - {@link ConfigKeys.entities | `entityManager.config.entities`} key.
15
+ * @param element - The {@link Entity | `Entity`} generated property element or ungenerated index component to encode.
16
+ *
17
+ * @returns Encoded value.
18
+ *
19
+ * @throws `Error` if `entityToken` is invalid.
20
+ */
21
+ function encodeEntityElement(entityManager, item, entityToken, element) {
22
+ try {
23
+ validateEntityToken.validateEntityToken(entityManager, entityToken);
24
+ const { entities, hashKey, rangeKey, transcodes } = entityManager.config;
25
+ const value = item[element];
26
+ if (value === undefined || [hashKey, rangeKey].includes(element))
27
+ return value;
28
+ const encoded = transcodes[entities[entityToken].elementTypes[element]].encode(item[element]) || undefined;
29
+ return encoded;
30
+ }
31
+ catch (error) {
32
+ throw error;
33
+ }
34
+ }
35
+
36
+ exports.encodeEntityElement = encodeEntityElement;
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ var Nil = require('../node_modules/@karmaniverous/entity-tools/dist/mjs/Nil.js');
4
+ var validateEntityGeneratedProperty = require('./validateEntityGeneratedProperty.js');
5
+
6
+ /**
7
+ * Encode a generated property value. Returns a string or undefined if atomicity requirement not met.
8
+ *
9
+ * @param entityManager - {@link EntityManager | `EntityManager`} instance.
10
+ * @param item - Partial {@link ItemMap | `ItemMap`} object.
11
+ * @param entityToken - `entityManager.config.entities` key.
12
+ * @param property - {@link ConfigEntityGenerated | `entityManager.config.entities.<entityToken>.generated`} key.
13
+ *
14
+ * @returns Encoded generated property value.
15
+ *
16
+ * @throws `Error` if `entityToken` is invalid.
17
+ * @throws `Error` if `property` is invalid.
18
+ */
19
+ function encodeGeneratedProperty(entityManager, item, entityToken, property) {
20
+ try {
21
+ // Validate params.
22
+ validateEntityGeneratedProperty.validateEntityGeneratedProperty(entityManager, entityToken, property);
23
+ const { atomic, elements, sharded } = entityManager.config.entities[entityToken].generated[property];
24
+ // Map elements to [element, value] pairs.
25
+ const elementMap = elements.map((element) => [
26
+ element,
27
+ item[element],
28
+ ]);
29
+ // Validate atomicity requirement.
30
+ if (atomic && elementMap.some(([, value]) => Nil.isNil(value)))
31
+ return;
32
+ // Encode property value.
33
+ const encoded = [
34
+ ...(sharded ? [item[entityManager.config.hashKey]] : []),
35
+ ...elementMap.map(([element, value]) => [element, (value ?? '').toString()].join(entityManager.config.generatedValueDelimiter)),
36
+ ].join(entityManager.config.generatedKeyDelimiter);
37
+ return encoded;
38
+ }
39
+ catch (error) {
40
+ throw error;
41
+ }
42
+ }
43
+
44
+ exports.encodeGeneratedProperty = encodeGeneratedProperty;