@reharik/smart-enum 0.4.5 → 0.4.6
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/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +3 -3
- package/dist/core.d.ts +3 -3
- package/dist/core.js +214 -10
- package/dist/core.js.map +1 -1
- package/dist/database.cjs.map +1 -1
- package/dist/database.d.cts +2 -2
- package/dist/database.d.ts +2 -2
- package/dist/database.js +389 -12
- package/dist/database.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +605 -26
- package/dist/index.js.map +1 -1
- package/dist/{transformation-mQpinVn6.d.cts → transformation-BRPSNA-C.d.cts} +3 -1
- package/dist/{transformation-mQpinVn6.d.ts → transformation-BRPSNA-C.d.ts} +3 -1
- package/dist/transport.cjs.map +1 -1
- package/dist/transport.d.cts +2 -2
- package/dist/transport.d.ts +2 -2
- package/dist/transport.js +336 -14
- package/dist/transport.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-62LMAE3X.js +0 -230
- package/dist/chunk-62LMAE3X.js.map +0 -1
- package/dist/chunk-F65KCMGE.js +0 -54
- package/dist/chunk-F65KCMGE.js.map +0 -1
- package/dist/chunk-QNY5W4SX.js +0 -73
- package/dist/chunk-QNY5W4SX.js.map +0 -1
- package/dist/chunk-WNCJN5E6.js +0 -300
- package/dist/chunk-WNCJN5E6.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,29 +1,608 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
1
|
+
// src/types.ts
|
|
2
|
+
var SMART_ENUM_ITEM = Symbol("smart-enum-item");
|
|
3
|
+
var SMART_ENUM_ID = Symbol("smart-enum-id");
|
|
4
|
+
var SMART_ENUM = Symbol("smart-enum");
|
|
5
|
+
|
|
6
|
+
// src/utilities/typeGuards.ts
|
|
7
|
+
var isSmartEnumItem = (x) => {
|
|
8
|
+
return !!x && typeof x === "object" && Reflect.get(x, SMART_ENUM_ITEM) === true;
|
|
9
|
+
};
|
|
10
|
+
var isSmartEnum = (x) => {
|
|
11
|
+
return !!x && typeof x === "object" && Reflect.get(x, SMART_ENUM) === true;
|
|
12
|
+
};
|
|
13
|
+
var isSerializedSmartEnumItem = (x) => {
|
|
14
|
+
return !!x && typeof x === "object" && Reflect.has(x, "__smart_enum_type") && Reflect.has(x, "value");
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// src/utilities/enumItemsEqual.ts
|
|
18
|
+
var enumItemsEqual = (a, b) => {
|
|
19
|
+
if (!isSmartEnumItem(a) || !isSmartEnumItem(b)) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
return Reflect.get(a, SMART_ENUM_ID) === Reflect.get(b, SMART_ENUM_ID) && a.value === b.value;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// src/extensionMethods.ts
|
|
26
|
+
var addExtensionMethods = (enumItems) => {
|
|
27
|
+
const findBy = (field, target) => enumItems.find((item) => item[field] === target);
|
|
28
|
+
const requireBy = (field, target, label) => {
|
|
29
|
+
const item = findBy(field, target);
|
|
30
|
+
if (!item) {
|
|
31
|
+
throw new Error(`No enum ${label} found for '${String(target)}'`);
|
|
32
|
+
}
|
|
33
|
+
return item;
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
fromValue: (value) => requireBy("value", value, "value"),
|
|
37
|
+
tryFromValue: (value) => value ? findBy("value", value) : void 0,
|
|
38
|
+
fromKey: (key) => requireBy("key", key, "key"),
|
|
39
|
+
tryFromKey: (key) => key ? findBy("key", key) : void 0,
|
|
40
|
+
equals: enumItemsEqual,
|
|
41
|
+
items: () => [...enumItems],
|
|
42
|
+
values: () => enumItems.map((item) => item.value),
|
|
43
|
+
keys: () => enumItems.map((item) => item.key)
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// src/utilities/getSubsetByProp.ts
|
|
48
|
+
var RESERVED_ENUM_MEMBER_KEYS = /* @__PURE__ */ new Set([
|
|
49
|
+
"fromValue",
|
|
50
|
+
"tryFromValue",
|
|
51
|
+
"fromKey",
|
|
52
|
+
"tryFromKey",
|
|
53
|
+
"items",
|
|
54
|
+
"values",
|
|
55
|
+
"keys"
|
|
56
|
+
]);
|
|
57
|
+
var getSubsetByProp = (enumInstance, prop, value) => {
|
|
58
|
+
const matching = [];
|
|
59
|
+
const memberEntries = {};
|
|
60
|
+
for (const key of Reflect.ownKeys(enumInstance)) {
|
|
61
|
+
if (typeof key !== "string" || RESERVED_ENUM_MEMBER_KEYS.has(key)) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const entry = enumInstance[key];
|
|
65
|
+
if (!isSmartEnumItem(entry)) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const item = entry;
|
|
69
|
+
if (Object.is(item[prop], value)) {
|
|
70
|
+
memberEntries[key] = item;
|
|
71
|
+
matching.push(item);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const methods = addExtensionMethods(
|
|
75
|
+
matching
|
|
76
|
+
);
|
|
77
|
+
return {
|
|
78
|
+
...memberEntries,
|
|
79
|
+
...methods
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
var subsetByProp = (prop) => (enumInstance, value) => (
|
|
83
|
+
// `getSubsetByProp` widens `prop` in its return; re-assert to keep curried literal `P` for Pick/Extract.
|
|
84
|
+
getSubsetByProp(
|
|
85
|
+
enumInstance,
|
|
86
|
+
prop,
|
|
87
|
+
value
|
|
88
|
+
)
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// src/enumerations.ts
|
|
92
|
+
import { capitalCase, constantCase } from "case-anything";
|
|
93
|
+
|
|
94
|
+
// src/utilities/serializationMode.ts
|
|
95
|
+
var globalDefault;
|
|
96
|
+
var setDefaultSerializationMode = (mode) => {
|
|
97
|
+
globalDefault = mode;
|
|
98
|
+
};
|
|
99
|
+
var resetDefaultSerializationMode = () => {
|
|
100
|
+
globalDefault = void 0;
|
|
101
|
+
};
|
|
102
|
+
var resolveSerializationMode = (perEnum) => perEnum ?? globalDefault ?? "wrapped";
|
|
103
|
+
|
|
104
|
+
// src/enumerations.ts
|
|
105
|
+
function normalizeInput(input) {
|
|
106
|
+
if (Array.isArray(input)) {
|
|
107
|
+
return Object.fromEntries(
|
|
108
|
+
input.map((k) => [k, {}])
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
return input;
|
|
112
|
+
}
|
|
113
|
+
var finalizeEnumItem = (item, enumType, enumInstanceId, serializeAs) => {
|
|
114
|
+
Object.defineProperty(item, SMART_ENUM_ITEM, {
|
|
115
|
+
value: true,
|
|
116
|
+
enumerable: false
|
|
117
|
+
});
|
|
118
|
+
Object.defineProperty(item, SMART_ENUM_ID, {
|
|
119
|
+
value: enumInstanceId,
|
|
120
|
+
enumerable: false
|
|
121
|
+
});
|
|
122
|
+
Object.defineProperty(item, "__smart_enum_brand", {
|
|
123
|
+
value: true,
|
|
124
|
+
enumerable: false
|
|
125
|
+
});
|
|
126
|
+
Object.defineProperty(item, "__smart_enum_type", {
|
|
127
|
+
value: enumType,
|
|
128
|
+
enumerable: false
|
|
129
|
+
});
|
|
130
|
+
Object.defineProperty(item, "toJSON", {
|
|
131
|
+
value: () => {
|
|
132
|
+
const mode = resolveSerializationMode(serializeAs);
|
|
133
|
+
if (mode === "value") {
|
|
134
|
+
return item.value;
|
|
135
|
+
}
|
|
136
|
+
return { __smart_enum_type: enumType, value: item.value };
|
|
137
|
+
},
|
|
138
|
+
enumerable: false
|
|
139
|
+
});
|
|
140
|
+
Object.defineProperty(item, "toPostgres", {
|
|
141
|
+
value: () => item.value,
|
|
142
|
+
enumerable: false
|
|
143
|
+
});
|
|
144
|
+
Object.defineProperty(item, "equals", {
|
|
145
|
+
value: (other) => enumItemsEqual(item, other),
|
|
146
|
+
enumerable: false
|
|
147
|
+
});
|
|
148
|
+
return item;
|
|
149
|
+
};
|
|
150
|
+
var formatProperties = (k, formatters) => formatters.reduce(
|
|
151
|
+
(acc, formatter) => {
|
|
152
|
+
acc[formatter.key] = formatter.format(k);
|
|
153
|
+
return acc;
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
value: constantCase(k),
|
|
157
|
+
display: capitalCase(k)
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
function buildEnumFromObject(enumType, input, propertyAutoFormatters, serializeAs) {
|
|
161
|
+
const formattersWithDefaults = [
|
|
162
|
+
{ key: "value", format: constantCase },
|
|
163
|
+
{ key: "display", format: capitalCase },
|
|
164
|
+
...propertyAutoFormatters ?? []
|
|
165
|
+
];
|
|
166
|
+
const rawEnumItems = {};
|
|
167
|
+
const enumInstanceId = Symbol("smart-enum-instance");
|
|
168
|
+
let index = 0;
|
|
169
|
+
for (const key in input) {
|
|
170
|
+
if (Object.prototype.hasOwnProperty.call(input, key)) {
|
|
171
|
+
const typedKey = key;
|
|
172
|
+
const value = input[typedKey];
|
|
173
|
+
const enumItemBase = {
|
|
174
|
+
index,
|
|
175
|
+
key: typedKey,
|
|
176
|
+
...formatProperties(typedKey, formattersWithDefaults),
|
|
177
|
+
...value
|
|
178
|
+
};
|
|
179
|
+
const enumItem = finalizeEnumItem(
|
|
180
|
+
enumItemBase,
|
|
181
|
+
enumType,
|
|
182
|
+
enumInstanceId,
|
|
183
|
+
serializeAs
|
|
184
|
+
);
|
|
185
|
+
Object.freeze(enumItem);
|
|
186
|
+
rawEnumItems[typedKey] = enumItem;
|
|
187
|
+
index++;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const extensionMethods = addExtensionMethods(
|
|
191
|
+
Object.values(rawEnumItems)
|
|
192
|
+
);
|
|
193
|
+
const enumObject = {
|
|
194
|
+
...rawEnumItems,
|
|
195
|
+
...extensionMethods
|
|
196
|
+
};
|
|
197
|
+
Object.defineProperty(enumObject, SMART_ENUM, {
|
|
198
|
+
value: true,
|
|
199
|
+
enumerable: false
|
|
200
|
+
});
|
|
201
|
+
Object.freeze(enumObject);
|
|
202
|
+
return enumObject;
|
|
203
|
+
}
|
|
204
|
+
function enumeration(enumType, props) {
|
|
205
|
+
const normalized = normalizeInput(props.input);
|
|
206
|
+
return buildEnumFromObject(
|
|
207
|
+
enumType,
|
|
208
|
+
normalized,
|
|
209
|
+
props.propertyAutoFormatters,
|
|
210
|
+
props.serializeAs
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// src/utilities/logger.ts
|
|
215
|
+
var consoleLogger = {
|
|
216
|
+
debug(message, ...args) {
|
|
217
|
+
console.debug(`[@reharik/smart-enum:debug] ${message}`, ...args);
|
|
218
|
+
},
|
|
219
|
+
info(message, ...args) {
|
|
220
|
+
console.info(`[@reharik/smart-enum:info] ${message}`, ...args);
|
|
221
|
+
},
|
|
222
|
+
warn(message, ...args) {
|
|
223
|
+
console.warn(`[@reharik/smart-enum:warn] ${message}`, ...args);
|
|
224
|
+
},
|
|
225
|
+
error(message, ...args) {
|
|
226
|
+
console.error(`[@reharik/smart-enum:error] ${message}`, ...args);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
var globalLogger = consoleLogger;
|
|
230
|
+
function setLogger(logger) {
|
|
231
|
+
globalLogger = logger;
|
|
232
|
+
}
|
|
233
|
+
function getLogger() {
|
|
234
|
+
return globalLogger;
|
|
235
|
+
}
|
|
236
|
+
function debug(message, ...args) {
|
|
237
|
+
globalLogger.debug(message, ...args);
|
|
238
|
+
}
|
|
239
|
+
function info(message, ...args) {
|
|
240
|
+
globalLogger.info(message, ...args);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// src/utilities/transformation.ts
|
|
244
|
+
var isPlainObject = (x) => typeof x === "object" && x !== null && Object.getPrototypeOf(x) === Object.prototype;
|
|
245
|
+
function serializeSmartEnums(input) {
|
|
246
|
+
const seen = /* @__PURE__ */ new WeakMap();
|
|
247
|
+
const walk = (v) => {
|
|
248
|
+
if (isSmartEnumItem(v)) {
|
|
249
|
+
return {
|
|
250
|
+
__smart_enum_type: v.__smart_enum_type,
|
|
251
|
+
value: v.value
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
if (typeof v === "object" && v !== null && seen.has(v)) {
|
|
255
|
+
return seen.get(v);
|
|
256
|
+
}
|
|
257
|
+
if (Array.isArray(v)) {
|
|
258
|
+
const arr = [];
|
|
259
|
+
seen.set(v, arr);
|
|
260
|
+
for (const item of v) {
|
|
261
|
+
arr.push(walk(item));
|
|
262
|
+
}
|
|
263
|
+
return arr;
|
|
264
|
+
}
|
|
265
|
+
if (isPlainObject(v)) {
|
|
266
|
+
const out = {};
|
|
267
|
+
seen.set(v, out);
|
|
268
|
+
for (const [k, val] of Object.entries(v)) {
|
|
269
|
+
out[k] = walk(val);
|
|
270
|
+
}
|
|
271
|
+
return out;
|
|
272
|
+
}
|
|
273
|
+
return v;
|
|
274
|
+
};
|
|
275
|
+
return walk(input);
|
|
276
|
+
}
|
|
277
|
+
function reviveSmartEnums(input, registry) {
|
|
278
|
+
const seen = /* @__PURE__ */ new WeakMap();
|
|
279
|
+
const walk = (v) => {
|
|
280
|
+
if (isSerializedSmartEnumItem(v)) {
|
|
281
|
+
debug(`Found serialized smartEnum: ${v.__smart_enum_type}`);
|
|
282
|
+
const enumInstance = registry[v.__smart_enum_type];
|
|
283
|
+
if (enumInstance) {
|
|
284
|
+
debug(`Found enumInstance in registry: ${v.__smart_enum_type}`);
|
|
285
|
+
const enumItem = enumInstance.tryFromValue(v.value);
|
|
286
|
+
if (enumItem) {
|
|
287
|
+
debug(`Revived enumItem using value: ${v.value}`);
|
|
288
|
+
return enumItem;
|
|
289
|
+
}
|
|
290
|
+
const key = v.value.toLowerCase();
|
|
291
|
+
const enumItemFromKey = enumInstance.tryFromKey(key);
|
|
292
|
+
if (enumItemFromKey) {
|
|
293
|
+
debug(`Revived enumItem using key: ${key}`);
|
|
294
|
+
return enumItemFromKey;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return v;
|
|
298
|
+
}
|
|
299
|
+
if (typeof v === "object" && v !== null && seen.has(v)) {
|
|
300
|
+
return seen.get(v);
|
|
301
|
+
}
|
|
302
|
+
if (Array.isArray(v)) {
|
|
303
|
+
const arr = [];
|
|
304
|
+
seen.set(v, arr);
|
|
305
|
+
for (const item of v) arr.push(walk(item));
|
|
306
|
+
return arr;
|
|
307
|
+
}
|
|
308
|
+
if (isPlainObject(v)) {
|
|
309
|
+
const out = {};
|
|
310
|
+
seen.set(v, out);
|
|
311
|
+
for (const [k, val] of Object.entries(v)) {
|
|
312
|
+
out[k] = walk(val);
|
|
313
|
+
}
|
|
314
|
+
return out;
|
|
315
|
+
}
|
|
316
|
+
return v;
|
|
317
|
+
};
|
|
318
|
+
return walk(input);
|
|
319
|
+
}
|
|
320
|
+
var reviveEnumField = (value, smartEnum, strict = false) => {
|
|
321
|
+
if (typeof value !== "string") return void 0;
|
|
322
|
+
const revived = smartEnum.tryFromValue(value);
|
|
323
|
+
if (revived !== void 0) return revived;
|
|
324
|
+
if (strict) {
|
|
325
|
+
throw new Error(`Unknown enum value: ${JSON.stringify(value)}`);
|
|
326
|
+
}
|
|
327
|
+
return void 0;
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
// src/utilities/transport/transportRegistry.ts
|
|
331
|
+
var createLevelFilteredLogger = (logger, level) => {
|
|
332
|
+
const levels = {
|
|
333
|
+
debug: 0,
|
|
334
|
+
info: 1,
|
|
335
|
+
warn: 2,
|
|
336
|
+
error: 3
|
|
337
|
+
};
|
|
338
|
+
const currentLevel = levels[level];
|
|
339
|
+
return {
|
|
340
|
+
debug: (message, ...args) => {
|
|
341
|
+
if (currentLevel <= levels.debug) {
|
|
342
|
+
logger.debug(message, ...args);
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
info: (message, ...args) => {
|
|
346
|
+
if (currentLevel <= levels.info) {
|
|
347
|
+
logger.info(message, ...args);
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
warn: (message, ...args) => {
|
|
351
|
+
if (currentLevel <= levels.warn) {
|
|
352
|
+
logger.warn(message, ...args);
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
error: (message, ...args) => {
|
|
356
|
+
if (currentLevel <= levels.error) {
|
|
357
|
+
logger.error(message, ...args);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
};
|
|
362
|
+
var globalEnumRegistry;
|
|
363
|
+
var initializeSmartEnumMappings = (config) => {
|
|
364
|
+
globalEnumRegistry = config.enumRegistry;
|
|
365
|
+
const logLevel = config.logLevel ?? "error";
|
|
366
|
+
const logger = config.logger ?? getLogger();
|
|
367
|
+
setLogger(createLevelFilteredLogger(logger, logLevel));
|
|
368
|
+
info("Initialized smart enum mappings", {
|
|
369
|
+
enumCount: Object.keys(config.enumRegistry).length,
|
|
370
|
+
enumTypes: Object.keys(config.enumRegistry),
|
|
371
|
+
logLevel
|
|
372
|
+
});
|
|
373
|
+
};
|
|
374
|
+
var getGlobalEnumRegistry = () => globalEnumRegistry;
|
|
375
|
+
|
|
376
|
+
// src/utilities/transport/reviveAfterTransport.ts
|
|
377
|
+
var reviveAfterTransport = (payload) => {
|
|
378
|
+
const registry = getGlobalEnumRegistry();
|
|
379
|
+
if (!registry) {
|
|
380
|
+
return payload;
|
|
381
|
+
}
|
|
382
|
+
return reviveSmartEnums(payload, registry);
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
// src/utilities/transport/serializeForTransport.ts
|
|
386
|
+
var serializeForTransport = (payload) => serializeSmartEnums(payload);
|
|
387
|
+
|
|
388
|
+
// src/db/prepareForDatabase.ts
|
|
389
|
+
var isPlainObject2 = (x) => typeof x === "object" && x !== null && Object.getPrototypeOf(x) === Object.prototype;
|
|
390
|
+
var prepareForDatabase = (payload) => {
|
|
391
|
+
const seen = /* @__PURE__ */ new WeakMap();
|
|
392
|
+
const walk = (v) => {
|
|
393
|
+
if (isSmartEnumItem(v)) {
|
|
394
|
+
return v.value;
|
|
395
|
+
}
|
|
396
|
+
if (typeof v === "object" && v !== null && seen.has(v)) {
|
|
397
|
+
return seen.get(v);
|
|
398
|
+
}
|
|
399
|
+
if (Array.isArray(v)) {
|
|
400
|
+
const arr = [];
|
|
401
|
+
seen.set(v, arr);
|
|
402
|
+
for (const item of v) {
|
|
403
|
+
arr.push(walk(item));
|
|
404
|
+
}
|
|
405
|
+
return arr;
|
|
406
|
+
}
|
|
407
|
+
if (isPlainObject2(v)) {
|
|
408
|
+
const out = {};
|
|
409
|
+
seen.set(v, out);
|
|
410
|
+
for (const [k, val] of Object.entries(v)) {
|
|
411
|
+
out[k] = walk(val);
|
|
412
|
+
}
|
|
413
|
+
return out;
|
|
414
|
+
}
|
|
415
|
+
return v;
|
|
416
|
+
};
|
|
417
|
+
return walk(payload);
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
// src/db/enumRevivalError.ts
|
|
421
|
+
var EnumRevivalError = class extends Error {
|
|
422
|
+
constructor(message, path, value) {
|
|
423
|
+
super(message);
|
|
424
|
+
this.path = path;
|
|
425
|
+
this.value = value;
|
|
426
|
+
this.name = "EnumRevivalError";
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
// src/db/reviveRowFromDatabase.ts
|
|
431
|
+
var reviveRowFromDatabase = (row, options) => {
|
|
432
|
+
const { fieldEnumMapping, strict = false } = options;
|
|
433
|
+
const out = { ...row };
|
|
434
|
+
for (const [field, smartEnum] of Object.entries(fieldEnumMapping)) {
|
|
435
|
+
if (!Object.hasOwn(out, field)) {
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
const raw = out[field];
|
|
439
|
+
if (typeof raw === "string") {
|
|
440
|
+
const revived = smartEnum.tryFromValue(raw);
|
|
441
|
+
if (revived !== void 0) {
|
|
442
|
+
out[field] = revived;
|
|
443
|
+
} else if (strict) {
|
|
444
|
+
throw new EnumRevivalError(
|
|
445
|
+
`Cannot revive field ${JSON.stringify(field)}: unknown enum value ${JSON.stringify(raw)}`,
|
|
446
|
+
field,
|
|
447
|
+
raw
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
if (Array.isArray(raw)) {
|
|
453
|
+
const revivedItems = [];
|
|
454
|
+
for (const item of raw) {
|
|
455
|
+
if (typeof item !== "string") {
|
|
456
|
+
revivedItems.push(item);
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
const revived = smartEnum.tryFromValue(item);
|
|
460
|
+
if (revived !== void 0) {
|
|
461
|
+
revivedItems.push(revived);
|
|
462
|
+
} else if (strict) {
|
|
463
|
+
throw new EnumRevivalError(
|
|
464
|
+
`Cannot revive field ${JSON.stringify(field)}: unknown enum value ${JSON.stringify(item)} in array`,
|
|
465
|
+
field,
|
|
466
|
+
item
|
|
467
|
+
);
|
|
468
|
+
} else {
|
|
469
|
+
revivedItems.push(item);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
out[field] = revivedItems;
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return out;
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
// src/db/revivePayloadFromDatabase.ts
|
|
480
|
+
var isObjectRecord = (x) => typeof x === "object" && x !== null && !Array.isArray(x);
|
|
481
|
+
var parsePath = (pathStr) => {
|
|
482
|
+
const tokens = pathStr.split(".").filter(Boolean);
|
|
483
|
+
const segs = [];
|
|
484
|
+
for (const token of tokens) {
|
|
485
|
+
if (token.endsWith("[]")) {
|
|
486
|
+
const name = token.slice(0, -2);
|
|
487
|
+
if (name.length === 0) {
|
|
488
|
+
throw new Error(
|
|
489
|
+
`Invalid enum revival path "${pathStr}": empty key before []`
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
segs.push({ type: "prop", name }, { type: "arrayEach" });
|
|
493
|
+
} else {
|
|
494
|
+
segs.push({ type: "prop", name: token });
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
const last = segs.at(-1);
|
|
498
|
+
if (!last || last.type !== "prop") {
|
|
499
|
+
throw new Error(
|
|
500
|
+
`Invalid enum revival path "${pathStr}": must end with a property name (not [])`
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
return segs;
|
|
504
|
+
};
|
|
505
|
+
var reviveLeaf = (host, key, smartEnum, strict, pathLabel) => {
|
|
506
|
+
const raw = host[key];
|
|
507
|
+
if (typeof raw === "string") {
|
|
508
|
+
const revived = smartEnum.tryFromValue(raw);
|
|
509
|
+
if (revived !== void 0) {
|
|
510
|
+
host[key] = revived;
|
|
511
|
+
} else if (strict) {
|
|
512
|
+
throw new EnumRevivalError(
|
|
513
|
+
`Cannot revive path "${pathLabel}": unknown enum value ${JSON.stringify(raw)}`,
|
|
514
|
+
pathLabel,
|
|
515
|
+
raw
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
if (Array.isArray(raw)) {
|
|
521
|
+
const revivedItems = [];
|
|
522
|
+
for (const item of raw) {
|
|
523
|
+
if (typeof item !== "string") {
|
|
524
|
+
revivedItems.push(item);
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
const revived = smartEnum.tryFromValue(item);
|
|
528
|
+
if (revived !== void 0) {
|
|
529
|
+
revivedItems.push(revived);
|
|
530
|
+
} else if (strict) {
|
|
531
|
+
throw new EnumRevivalError(
|
|
532
|
+
`Cannot revive path "${pathLabel}": unknown enum value ${JSON.stringify(item)} in array`,
|
|
533
|
+
pathLabel,
|
|
534
|
+
item
|
|
535
|
+
);
|
|
536
|
+
} else {
|
|
537
|
+
revivedItems.push(item);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
host[key] = revivedItems;
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
var walkPath = (value, segs, segIndex, smartEnum, strict, pathLabel) => {
|
|
545
|
+
const seg = segs[segIndex];
|
|
546
|
+
if (seg === void 0) {
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
const isLast = segIndex === segs.length - 1;
|
|
550
|
+
if (isLast) {
|
|
551
|
+
if (seg.type !== "prop") {
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
if (!isObjectRecord(value)) {
|
|
555
|
+
if (strict) {
|
|
556
|
+
throw new EnumRevivalError(
|
|
557
|
+
`Cannot revive path "${pathLabel}": expected object at parent of "${seg.name}"`,
|
|
558
|
+
pathLabel,
|
|
559
|
+
value
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
reviveLeaf(value, seg.name, smartEnum, strict, pathLabel);
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
if (seg.type === "prop") {
|
|
568
|
+
if (!isObjectRecord(value)) {
|
|
569
|
+
if (strict) {
|
|
570
|
+
throw new EnumRevivalError(
|
|
571
|
+
`Cannot revive path "${pathLabel}": expected object at "${seg.name}"`,
|
|
572
|
+
pathLabel,
|
|
573
|
+
value
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
walkPath(value[seg.name], segs, segIndex + 1, smartEnum, strict, pathLabel);
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
if (seg.type === "arrayEach") {
|
|
582
|
+
if (!Array.isArray(value)) {
|
|
583
|
+
if (strict) {
|
|
584
|
+
throw new EnumRevivalError(
|
|
585
|
+
`Cannot revive path "${pathLabel}": expected array before nested path`,
|
|
586
|
+
pathLabel,
|
|
587
|
+
value
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
for (const el of value) {
|
|
593
|
+
walkPath(el, segs, segIndex + 1, smartEnum, strict, pathLabel);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
var revivePayloadFromDatabase = (payload, options) => {
|
|
598
|
+
const { pathEnumMapping, strict = false } = options;
|
|
599
|
+
const root = structuredClone(payload);
|
|
600
|
+
for (const [pathStr, smartEnum] of Object.entries(pathEnumMapping)) {
|
|
601
|
+
const segs = parsePath(pathStr);
|
|
602
|
+
walkPath(root, segs, 0, smartEnum, strict, pathStr);
|
|
603
|
+
}
|
|
604
|
+
return root;
|
|
605
|
+
};
|
|
27
606
|
export {
|
|
28
607
|
EnumRevivalError,
|
|
29
608
|
enumeration,
|