@atscript/typescript 0.1.87 → 0.1.89
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/cli.cjs +427 -389
- package/dist/index.cjs +4 -5
- package/dist/index.mjs +1 -2
- package/dist/{json-schema-3mti4G4J.cjs → json-schema-DXEiCfe1.cjs} +206 -97
- package/dist/{json-schema-B64qclVa.mjs → json-schema-DycaD0Rm.mjs} +207 -98
- package/dist/{plugin-CYzZm3rk.mjs → plugin-BOoquMkD.mjs} +117 -152
- package/dist/{plugin-CZ5g5D9I.cjs → plugin-DMBkdYCf.cjs} +180 -211
- package/dist/test-utils.cjs +16 -9
- package/dist/test-utils.mjs +9 -3
- package/dist/utils.cjs +176 -106
- package/dist/utils.mjs +153 -83
- package/package.json +2 -2
package/dist/utils.mjs
CHANGED
|
@@ -1,24 +1,52 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as forAnnotatedType, c as createAnnotatedTypeNode, d as isAnnotatedTypeOfPrimitive, f as isPhantomType, i as mergeJsonSchemas, l as defineAnnotatedType, m as ValidatorError, n as detectDiscriminator, o as annotate, p as Validator, r as fromJsonSchema, s as cloneRefProp, t as buildJsonSchema, u as isAnnotatedType } from "./json-schema-DycaD0Rm.mjs";
|
|
2
2
|
|
|
3
3
|
//#region packages/typescript/src/runtime/coerce.ts
|
|
4
4
|
/**
|
|
5
5
|
* Internal sentinel returned when a value cannot be coerced to a type node.
|
|
6
6
|
* Drives union branch selection without allocating per-node result objects.
|
|
7
7
|
*/ const NO_MATCH = Symbol("atscript.coerce.no-match");
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Coerces string-transport input (route params, query strings) toward the
|
|
10
|
+
* scalar shapes an annotated type expects.
|
|
11
|
+
*
|
|
12
|
+
* Pure and non-throwing: when a value can't be coerced it is returned
|
|
13
|
+
* untouched, leaving the {@link Validator} to report the proper error.
|
|
14
|
+
* Coercion converts representation only — constraint checks (`@expect.int`,
|
|
15
|
+
* `@expect.min`, patterns) remain the validator's job.
|
|
16
|
+
*
|
|
17
|
+
* Rules:
|
|
18
|
+
* - Strings targeting `number` are trimmed and parsed via `Number()`; only
|
|
19
|
+
* finite results are accepted.
|
|
20
|
+
* - Strings targeting `boolean` accept `"true"/"1"` and `"false"/"0"`.
|
|
21
|
+
* - Unions try each branch in declared order; the first successful parse wins.
|
|
22
|
+
* - Objects recurse into props when the input is a plain object (covers
|
|
23
|
+
* `@Query()` DTOs where every field arrives as a string).
|
|
24
|
+
* - Arrays and tuples coerce items; intersections apply each member in order.
|
|
25
|
+
* - `string` / `decimal` targets never change string input.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* coerceForType(KafkaOffset, '42') // → 42
|
|
30
|
+
* coerceForType(KafkaOffset, 'abc') // → 'abc' (validator reports the error)
|
|
31
|
+
* ```
|
|
32
|
+
*/ function coerceForType(def, value) {
|
|
9
33
|
const result = coerce(def, value);
|
|
10
34
|
return result === NO_MATCH ? value : result;
|
|
11
35
|
}
|
|
12
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Coerces a single scalar value toward a design type, using the same rules
|
|
38
|
+
* as {@link coerceForType}. Returns the input untouched when it can't coerce.
|
|
39
|
+
* Useful when only a design type is known (e.g. `design:paramtypes` fallbacks).
|
|
40
|
+
*/ function coerceScalar(designType, value) {
|
|
13
41
|
const result = scalar(designType, value);
|
|
14
42
|
return result === NO_MATCH ? value : result;
|
|
15
43
|
}
|
|
16
44
|
/** Returns the coerced value, or NO_MATCH when the value can't fit this node. */ function coerce(def, value) {
|
|
17
|
-
if (value ===
|
|
45
|
+
if (value === void 0 || value === null) return def.optional === true ? value : NO_MATCH;
|
|
18
46
|
return forAnnotatedType(def, {
|
|
19
47
|
final: (d) => {
|
|
20
48
|
const result = scalar(d.type.designType, value);
|
|
21
|
-
if (result !== NO_MATCH && d.type.value !==
|
|
49
|
+
if (result !== NO_MATCH && d.type.value !== void 0 && result !== d.type.value) return NO_MATCH;
|
|
22
50
|
return result;
|
|
23
51
|
},
|
|
24
52
|
phantom: () => value,
|
|
@@ -27,7 +55,7 @@ function coerceScalar(designType, value) {
|
|
|
27
55
|
return coerceProps(d, value);
|
|
28
56
|
},
|
|
29
57
|
array: (d) => Array.isArray(value) ? coerceItems(value, d.type.of) : NO_MATCH,
|
|
30
|
-
tuple: (d) => Array.isArray(value) && value.length === d.type.items.length ? coerceItems(value,
|
|
58
|
+
tuple: (d) => Array.isArray(value) && value.length === d.type.items.length ? coerceItems(value, void 0, d.type.items) : NO_MATCH,
|
|
31
59
|
union: (d) => {
|
|
32
60
|
for (const item of d.type.items) {
|
|
33
61
|
const result = coerce(item, value);
|
|
@@ -50,7 +78,7 @@ function scalar(designType, value) {
|
|
|
50
78
|
switch (designType) {
|
|
51
79
|
case "string":
|
|
52
80
|
case "decimal": return typeof value === "string" ? value : NO_MATCH;
|
|
53
|
-
case "number":
|
|
81
|
+
case "number":
|
|
54
82
|
if (typeof value === "number") return value;
|
|
55
83
|
if (typeof value === "string") {
|
|
56
84
|
const trimmed = value.trim();
|
|
@@ -60,13 +88,11 @@ function scalar(designType, value) {
|
|
|
60
88
|
}
|
|
61
89
|
}
|
|
62
90
|
return NO_MATCH;
|
|
63
|
-
|
|
64
|
-
case "boolean": {
|
|
91
|
+
case "boolean":
|
|
65
92
|
if (typeof value === "boolean") return value;
|
|
66
93
|
if (value === "true" || value === "1") return true;
|
|
67
94
|
if (value === "false" || value === "0") return false;
|
|
68
95
|
return NO_MATCH;
|
|
69
|
-
}
|
|
70
96
|
case "object": return typeof value === "object" ? value : NO_MATCH;
|
|
71
97
|
case "any":
|
|
72
98
|
case "phantom": return value;
|
|
@@ -95,7 +121,7 @@ function scalar(designType, value) {
|
|
|
95
121
|
if (result !== NO_MATCH) coerced = result;
|
|
96
122
|
}
|
|
97
123
|
if (out) out[key] = coerced;
|
|
98
|
-
else if (coerced !== input) {
|
|
124
|
+
else if (coerced !== input) {
|
|
99
125
|
out = {};
|
|
100
126
|
for (let j = 0; j < i; j++) out[keys[j]] = value[keys[j]];
|
|
101
127
|
out[key] = coerced;
|
|
@@ -113,7 +139,7 @@ else if (coerced !== input) {
|
|
|
113
139
|
const result = coerce(of ?? items[i], input);
|
|
114
140
|
const coerced = result === NO_MATCH ? input : result;
|
|
115
141
|
if (out) out.push(coerced);
|
|
116
|
-
else if (coerced !== input) {
|
|
142
|
+
else if (coerced !== input) {
|
|
117
143
|
out = value.slice(0, i);
|
|
118
144
|
out.push(coerced);
|
|
119
145
|
}
|
|
@@ -132,33 +158,32 @@ function isPlainObject(value) {
|
|
|
132
158
|
* Attempts to resolve a value from the mode for the given annotated type.
|
|
133
159
|
* Returns `undefined` when no value is available (or parse/validation fails).
|
|
134
160
|
*/ function resolveValue(prop, path, mode) {
|
|
135
|
-
if (!mode || mode === "empty") return
|
|
161
|
+
if (!mode || mode === "empty") return;
|
|
136
162
|
let raw;
|
|
137
163
|
if (typeof mode === "function") {
|
|
138
164
|
raw = mode(prop, path);
|
|
139
|
-
if (raw ===
|
|
165
|
+
if (raw === void 0) return;
|
|
140
166
|
if (prop.validator({ unknownProps: "ignore" }).validate(raw, true)) return { value: raw };
|
|
141
|
-
return
|
|
167
|
+
return;
|
|
142
168
|
}
|
|
143
169
|
if (mode === "db") {
|
|
144
170
|
const dbValue = prop.metadata.get("db.default");
|
|
145
|
-
if (dbValue !==
|
|
146
|
-
const parsed
|
|
147
|
-
if (parsed
|
|
148
|
-
return
|
|
171
|
+
if (dbValue !== void 0) {
|
|
172
|
+
const parsed = parseRawValue(dbValue, prop);
|
|
173
|
+
if (parsed !== void 0 && prop.validator({ unknownProps: "ignore" }).validate(parsed, true)) return { value: parsed };
|
|
174
|
+
return;
|
|
149
175
|
}
|
|
150
176
|
if (prop.metadata.has("db.default.increment")) return { value: "increment" };
|
|
151
177
|
if (prop.metadata.has("db.default.uuid")) return { value: "uuid" };
|
|
152
178
|
if (prop.metadata.has("db.default.now")) return { value: "now" };
|
|
153
|
-
return
|
|
179
|
+
return;
|
|
154
180
|
}
|
|
155
181
|
const metaKey = mode === "default" ? "meta.default" : "meta.example";
|
|
156
182
|
const rawStr = prop.metadata.get(metaKey);
|
|
157
|
-
if (rawStr ===
|
|
183
|
+
if (rawStr === void 0) return;
|
|
158
184
|
const parsed = parseRawValue(rawStr, prop);
|
|
159
|
-
if (parsed ===
|
|
185
|
+
if (parsed === void 0) return;
|
|
160
186
|
if (prop.validator({ unknownProps: "ignore" }).validate(parsed, true)) return { value: parsed };
|
|
161
|
-
return undefined;
|
|
162
187
|
}
|
|
163
188
|
/**
|
|
164
189
|
* Parses a raw annotation string into a JS value.
|
|
@@ -168,28 +193,45 @@ function isPlainObject(value) {
|
|
|
168
193
|
try {
|
|
169
194
|
return JSON.parse(raw);
|
|
170
195
|
} catch {
|
|
171
|
-
return
|
|
196
|
+
return;
|
|
172
197
|
}
|
|
173
198
|
}
|
|
174
199
|
/** Returns the structural default for a final (primitive/literal) type. */ function finalDefault(def) {
|
|
175
|
-
if (def.type.value !==
|
|
200
|
+
if (def.type.value !== void 0) return def.type.value;
|
|
176
201
|
switch (def.type.designType) {
|
|
177
202
|
case "string": return "";
|
|
178
203
|
case "number": return 0;
|
|
179
204
|
case "boolean": return false;
|
|
180
|
-
case "undefined": return
|
|
205
|
+
case "undefined": return;
|
|
181
206
|
case "null": return null;
|
|
182
|
-
default: return
|
|
207
|
+
default: return;
|
|
183
208
|
}
|
|
184
209
|
}
|
|
185
|
-
|
|
210
|
+
/**
|
|
211
|
+
* Creates a data object from an ATScript annotated type definition.
|
|
212
|
+
*
|
|
213
|
+
* Supports five modes:
|
|
214
|
+
* - `'empty'` — structural defaults only; optional props omitted
|
|
215
|
+
* - `'default'` — uses `@meta.default` annotations; optional props omitted unless annotated
|
|
216
|
+
* - `'example'` — uses `@meta.example` annotations; optional props always included; arrays get one sample item
|
|
217
|
+
* - `'db'` — uses `@db.default` (parsed) or `@db.default.increment/uuid/now` (fn name string); optional props omitted unless annotated
|
|
218
|
+
* - `function` — custom resolver; optional props omitted unless resolver returns a value
|
|
219
|
+
*
|
|
220
|
+
* When a `@meta.default` / `@meta.example` value is set on a complex type (object, array)
|
|
221
|
+
* and passes full validation, the entire subtree is replaced — no recursion into inner props.
|
|
222
|
+
* If validation fails, the annotation is ignored and structural defaults are built from inner props.
|
|
223
|
+
*
|
|
224
|
+
* @param type - The ATScript annotated type to create data from.
|
|
225
|
+
* @param opts - Options controlling value resolution mode.
|
|
226
|
+
* @returns A value conforming to the type's shape.
|
|
227
|
+
*/ function createDataFromAnnotatedType(type, opts) {
|
|
186
228
|
return build(type, "", opts?.mode);
|
|
187
229
|
}
|
|
188
230
|
function build(def, path, mode) {
|
|
189
231
|
const resolved = resolveValue(def, path, mode);
|
|
190
|
-
if (resolved !==
|
|
232
|
+
if (resolved !== void 0) return resolved.value;
|
|
191
233
|
return forAnnotatedType(def, {
|
|
192
|
-
phantom: () =>
|
|
234
|
+
phantom: () => void 0,
|
|
193
235
|
final: (d) => finalDefault(d),
|
|
194
236
|
object: (d) => {
|
|
195
237
|
const data = {};
|
|
@@ -198,9 +240,9 @@ function build(def, path, mode) {
|
|
|
198
240
|
const childPath = path ? `${path}.${key}` : key;
|
|
199
241
|
if (prop.optional) {
|
|
200
242
|
if (mode === "example") data[key] = build(prop, childPath, mode);
|
|
201
|
-
else {
|
|
243
|
+
else {
|
|
202
244
|
const childResolved = resolveValue(prop, childPath, mode);
|
|
203
|
-
if (childResolved !==
|
|
245
|
+
if (childResolved !== void 0) data[key] = childResolved.value;
|
|
204
246
|
}
|
|
205
247
|
continue;
|
|
206
248
|
}
|
|
@@ -211,25 +253,28 @@ else {
|
|
|
211
253
|
array: (d) => {
|
|
212
254
|
if (mode === "example") {
|
|
213
255
|
const item = build(d.type.of, `${path}.0`, mode);
|
|
214
|
-
return item !==
|
|
256
|
+
return item !== void 0 ? [item] : [];
|
|
215
257
|
}
|
|
216
258
|
return [];
|
|
217
259
|
},
|
|
218
260
|
tuple: (d) => d.type.items.map((item, i) => build(item, `${path}.${i}`, mode)),
|
|
219
261
|
union: (d) => {
|
|
220
262
|
const first = d.type.items[0];
|
|
221
|
-
return first ? build(first, path, mode) :
|
|
263
|
+
return first ? build(first, path, mode) : void 0;
|
|
222
264
|
},
|
|
223
265
|
intersection: (d) => {
|
|
224
266
|
const first = d.type.items[0];
|
|
225
|
-
return first ? build(first, path, mode) :
|
|
267
|
+
return first ? build(first, path, mode) : void 0;
|
|
226
268
|
}
|
|
227
269
|
});
|
|
228
270
|
}
|
|
229
271
|
|
|
230
272
|
//#endregion
|
|
231
273
|
//#region packages/typescript/src/runtime/throw-disabled.ts
|
|
232
|
-
|
|
274
|
+
/**
|
|
275
|
+
* Throws a runtime error indicating that a feature is disabled.
|
|
276
|
+
* Used by generated JS files to avoid duplicating the error message string.
|
|
277
|
+
*/ function throwFeatureDisabled(feature, option, annotation) {
|
|
233
278
|
throw new Error(`${feature} support is disabled. To enable, set \`${option}: 'lazy'\` or \`${option}: 'bundle'\` in tsPlugin options, or add @${annotation} annotation to individual interfaces.`);
|
|
234
279
|
}
|
|
235
280
|
|
|
@@ -251,7 +296,7 @@ function throwFeatureDisabled(feature, option, annotation) {
|
|
|
251
296
|
* @param type - The root annotated type (must be an object type).
|
|
252
297
|
* @param options - Optional hooks for domain-specific processing.
|
|
253
298
|
* @returns A map of dot-separated field paths to their annotated types.
|
|
254
|
-
*/ const EMPTY_ANCESTORS = new Set();
|
|
299
|
+
*/ const EMPTY_ANCESTORS = /* @__PURE__ */ new Set();
|
|
255
300
|
function extendAncestors(ancestors, id) {
|
|
256
301
|
if (!id || ancestors.has(id)) return ancestors;
|
|
257
302
|
const next = new Set(ancestors);
|
|
@@ -259,14 +304,14 @@ function extendAncestors(ancestors, id) {
|
|
|
259
304
|
return next;
|
|
260
305
|
}
|
|
261
306
|
function flattenAnnotatedType(type, options) {
|
|
262
|
-
const flatMap = new Map();
|
|
307
|
+
const flatMap = /* @__PURE__ */ new Map();
|
|
263
308
|
const skipPhantom = !!options?.excludePhantomTypes;
|
|
264
309
|
function addFieldToFlatMap(name, def) {
|
|
265
310
|
const existing = flatMap.get(name);
|
|
266
311
|
if (existing) {
|
|
267
312
|
const flatUnion = defineAnnotatedType("union").copyMetadata(existing.metadata).copyMetadata(def.metadata);
|
|
268
313
|
if (existing.__flat_union) existing.type.items.forEach((item) => flatUnion.item(item));
|
|
269
|
-
else flatUnion.item(existing);
|
|
314
|
+
else flatUnion.item(existing);
|
|
270
315
|
flatUnion.item(def);
|
|
271
316
|
const unionType = flatUnion.$type;
|
|
272
317
|
unionType.__flat_union = true;
|
|
@@ -292,14 +337,12 @@ else flatUnion.item(existing);
|
|
|
292
337
|
}
|
|
293
338
|
case "union":
|
|
294
339
|
case "intersection":
|
|
295
|
-
case "tuple":
|
|
340
|
+
case "tuple":
|
|
296
341
|
for (const item of def.type.items) flattenArray(item, name, childAncestors);
|
|
297
342
|
break;
|
|
298
|
-
|
|
299
|
-
case "array": {
|
|
343
|
+
case "array":
|
|
300
344
|
flattenArray(def.type.of, name, childAncestors);
|
|
301
345
|
break;
|
|
302
|
-
}
|
|
303
346
|
default:
|
|
304
347
|
}
|
|
305
348
|
}
|
|
@@ -322,14 +365,13 @@ else flatUnion.item(existing);
|
|
|
322
365
|
childAncestors = extendAncestors(ancestors, typeId);
|
|
323
366
|
}
|
|
324
367
|
switch (kind) {
|
|
325
|
-
case "object":
|
|
368
|
+
case "object":
|
|
326
369
|
addFieldToFlatMap(prefix || "", def);
|
|
327
370
|
for (const [key, value] of def.type.props.entries()) {
|
|
328
371
|
if (skipPhantom && isPhantomType(value)) continue;
|
|
329
372
|
flattenType(value, prefix ? `${prefix}.${key}` : key, inComplexTypeOrArray, childAncestors);
|
|
330
373
|
}
|
|
331
374
|
break;
|
|
332
|
-
}
|
|
333
375
|
case "array": {
|
|
334
376
|
let typeArray = def;
|
|
335
377
|
if (!inComplexTypeOrArray) {
|
|
@@ -343,7 +385,7 @@ else flatUnion.item(existing);
|
|
|
343
385
|
}
|
|
344
386
|
case "intersection":
|
|
345
387
|
case "tuple":
|
|
346
|
-
case "union":
|
|
388
|
+
case "union":
|
|
347
389
|
for (const item of def.type.items) flattenType(item, prefix, true, childAncestors);
|
|
348
390
|
addFieldToFlatMap(prefix || "", def);
|
|
349
391
|
if (def.optional) {
|
|
@@ -351,11 +393,9 @@ else flatUnion.item(existing);
|
|
|
351
393
|
if (entry) entry.optional = def.optional;
|
|
352
394
|
}
|
|
353
395
|
break;
|
|
354
|
-
|
|
355
|
-
default: {
|
|
396
|
+
default:
|
|
356
397
|
addFieldToFlatMap(prefix || "", def);
|
|
357
398
|
break;
|
|
358
|
-
}
|
|
359
399
|
}
|
|
360
400
|
if (prefix) options?.onField?.(prefix, def, def.metadata);
|
|
361
401
|
}
|
|
@@ -365,10 +405,26 @@ else flatUnion.item(existing);
|
|
|
365
405
|
|
|
366
406
|
//#endregion
|
|
367
407
|
//#region packages/typescript/src/runtime/serialize.ts
|
|
368
|
-
const SERIALIZE_VERSION = 2;
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
408
|
+
/** Current serialization format version. Bumped on breaking changes to the serialized shape. */ const SERIALIZE_VERSION = 2;
|
|
409
|
+
/**
|
|
410
|
+
* Converts a runtime {@link TAtscriptAnnotatedType} into a plain JSON-safe object.
|
|
411
|
+
*
|
|
412
|
+
* The result can be stored, transmitted over the network, and later
|
|
413
|
+
* restored with {@link deserializeAnnotatedType}.
|
|
414
|
+
*
|
|
415
|
+
* @example
|
|
416
|
+
* ```ts
|
|
417
|
+
* import { serializeAnnotatedType } from '@atscript/typescript'
|
|
418
|
+
*
|
|
419
|
+
* const json = serializeAnnotatedType(MyInterface)
|
|
420
|
+
* // json is a plain object safe for JSON.stringify
|
|
421
|
+
* ```
|
|
422
|
+
*
|
|
423
|
+
* @param type - The annotated type to serialize.
|
|
424
|
+
* @param options - Optional filtering/transformation for annotations.
|
|
425
|
+
* @returns A versioned, JSON-safe representation of the type.
|
|
426
|
+
*/ function serializeAnnotatedType(type, options) {
|
|
427
|
+
const result = serializeNode(type, [], options, /* @__PURE__ */ new Set());
|
|
372
428
|
result.$v = SERIALIZE_VERSION;
|
|
373
429
|
return result;
|
|
374
430
|
}
|
|
@@ -423,23 +479,22 @@ function serializeTypeDef(def, path, options, visited) {
|
|
|
423
479
|
designType: d.type.designType,
|
|
424
480
|
tags: Array.from(d.type.tags)
|
|
425
481
|
};
|
|
426
|
-
if (d.type.value !==
|
|
482
|
+
if (d.type.value !== void 0) result.value = d.type.value;
|
|
427
483
|
return result;
|
|
428
484
|
},
|
|
429
485
|
object(d) {
|
|
430
486
|
const props = {};
|
|
431
487
|
for (const [key, val] of d.type.props.entries()) props[key] = serializeNode(val, [...path, key], options, visited);
|
|
432
|
-
const propsPatterns = d.type.propsPatterns.map((pp) => ({
|
|
433
|
-
pattern: {
|
|
434
|
-
source: pp.pattern.source,
|
|
435
|
-
flags: pp.pattern.flags
|
|
436
|
-
},
|
|
437
|
-
def: serializeNode(pp.def, path, options, visited)
|
|
438
|
-
}));
|
|
439
488
|
return {
|
|
440
489
|
kind: "object",
|
|
441
490
|
props,
|
|
442
|
-
propsPatterns
|
|
491
|
+
propsPatterns: d.type.propsPatterns.map((pp) => ({
|
|
492
|
+
pattern: {
|
|
493
|
+
source: pp.pattern.source,
|
|
494
|
+
flags: pp.pattern.flags
|
|
495
|
+
},
|
|
496
|
+
def: serializeNode(pp.def, path, options, visited)
|
|
497
|
+
})),
|
|
443
498
|
tags: Array.from(d.type.tags)
|
|
444
499
|
};
|
|
445
500
|
},
|
|
@@ -475,7 +530,7 @@ function serializeTypeDef(def, path, options, visited) {
|
|
|
475
530
|
}
|
|
476
531
|
function serializeMetadata(metadata, path, kind, options) {
|
|
477
532
|
const result = {};
|
|
478
|
-
const ignoreSet = options?.ignoreAnnotations ? new Set(options.ignoreAnnotations) :
|
|
533
|
+
const ignoreSet = options?.ignoreAnnotations ? new Set(options.ignoreAnnotations) : void 0;
|
|
479
534
|
for (const [key, value] of metadata.entries()) {
|
|
480
535
|
if (ignoreSet?.has(key)) continue;
|
|
481
536
|
if (options?.processAnnotation) {
|
|
@@ -485,7 +540,7 @@ function serializeMetadata(metadata, path, kind, options) {
|
|
|
485
540
|
path,
|
|
486
541
|
kind
|
|
487
542
|
});
|
|
488
|
-
if (processed ===
|
|
543
|
+
if (processed === void 0 || processed === null) continue;
|
|
489
544
|
result[processed.key] = processed.value;
|
|
490
545
|
continue;
|
|
491
546
|
}
|
|
@@ -493,17 +548,33 @@ function serializeMetadata(metadata, path, kind, options) {
|
|
|
493
548
|
}
|
|
494
549
|
return result;
|
|
495
550
|
}
|
|
496
|
-
|
|
551
|
+
/**
|
|
552
|
+
* Restores a runtime {@link TAtscriptAnnotatedType} from its serialized form.
|
|
553
|
+
*
|
|
554
|
+
* The returned object is fully functional — it has a working `.validator()` method
|
|
555
|
+
* and can be used with {@link buildJsonSchema} or the {@link Validator} directly.
|
|
556
|
+
*
|
|
557
|
+
* @example
|
|
558
|
+
* ```ts
|
|
559
|
+
* import { deserializeAnnotatedType } from '@atscript/typescript'
|
|
560
|
+
*
|
|
561
|
+
* const type = deserializeAnnotatedType(json)
|
|
562
|
+
* type.validator().validate(someValue) // works
|
|
563
|
+
* ```
|
|
564
|
+
*
|
|
565
|
+
* @param data - A serialized type produced by {@link serializeAnnotatedType}.
|
|
566
|
+
* @returns A live annotated type with validator support.
|
|
567
|
+
* @throws If the serialized version doesn't match {@link SERIALIZE_VERSION}.
|
|
568
|
+
*/ function deserializeAnnotatedType(data) {
|
|
497
569
|
if (data.$v !== SERIALIZE_VERSION) throw new Error(`Unsupported serialized type version: ${data.$v} (expected ${SERIALIZE_VERSION})`);
|
|
498
|
-
|
|
499
|
-
return deserializeNode(data, resolved);
|
|
570
|
+
return deserializeNode(data, /* @__PURE__ */ new Map());
|
|
500
571
|
}
|
|
501
572
|
function emptyObjectTypeDef() {
|
|
502
573
|
return {
|
|
503
574
|
kind: "object",
|
|
504
|
-
props: new Map(),
|
|
575
|
+
props: /* @__PURE__ */ new Map(),
|
|
505
576
|
propsPatterns: [],
|
|
506
|
-
tags: new Set()
|
|
577
|
+
tags: /* @__PURE__ */ new Set()
|
|
507
578
|
};
|
|
508
579
|
}
|
|
509
580
|
function toMetadataMap(record) {
|
|
@@ -512,7 +583,7 @@ function toMetadataMap(record) {
|
|
|
512
583
|
function deserializeNode(data, resolved) {
|
|
513
584
|
if (data.type.kind === "$ref") {
|
|
514
585
|
const refId = data.type.id;
|
|
515
|
-
return resolved.get(refId) || createAnnotatedTypeNode(emptyObjectTypeDef(), new Map(), { id: refId });
|
|
586
|
+
return resolved.get(refId) || createAnnotatedTypeNode(emptyObjectTypeDef(), /* @__PURE__ */ new Map(), { id: refId });
|
|
516
587
|
}
|
|
517
588
|
const metadata = toMetadataMap(data.metadata);
|
|
518
589
|
let result;
|
|
@@ -520,7 +591,7 @@ function deserializeNode(data, resolved) {
|
|
|
520
591
|
const existing = resolved.get(data.id);
|
|
521
592
|
if (existing) return existing;
|
|
522
593
|
result = createAnnotatedTypeNode(emptyObjectTypeDef(), metadata, {
|
|
523
|
-
optional: data.optional ||
|
|
594
|
+
optional: data.optional || void 0,
|
|
524
595
|
id: data.id
|
|
525
596
|
});
|
|
526
597
|
resolved.set(data.id, result);
|
|
@@ -536,7 +607,7 @@ function deserializeNode(data, resolved) {
|
|
|
536
607
|
field: data.ref.field
|
|
537
608
|
};
|
|
538
609
|
} else {
|
|
539
|
-
const sentinel = createAnnotatedTypeNode(emptyObjectTypeDef(), toMetadataMap(refTargetData.metadata), { id: refTargetData.id ||
|
|
610
|
+
const sentinel = createAnnotatedTypeNode(emptyObjectTypeDef(), toMetadataMap(refTargetData.metadata), { id: refTargetData.id || void 0 });
|
|
540
611
|
ref = {
|
|
541
612
|
type: () => sentinel,
|
|
542
613
|
field: data.ref.field
|
|
@@ -549,13 +620,13 @@ function deserializeNode(data, resolved) {
|
|
|
549
620
|
return result;
|
|
550
621
|
}
|
|
551
622
|
return createAnnotatedTypeNode(type, metadata, {
|
|
552
|
-
optional: data.optional ||
|
|
553
|
-
id: data.id ||
|
|
623
|
+
optional: data.optional || void 0,
|
|
624
|
+
id: data.id || void 0,
|
|
554
625
|
ref
|
|
555
626
|
});
|
|
556
627
|
}
|
|
557
628
|
function deserializeTypeDef(t, resolved) {
|
|
558
|
-
const tags = "tags" in t ? new Set(t.tags) : new Set();
|
|
629
|
+
const tags = "tags" in t ? new Set(t.tags) : /* @__PURE__ */ new Set();
|
|
559
630
|
switch (t.kind) {
|
|
560
631
|
case "": {
|
|
561
632
|
const result = {
|
|
@@ -563,20 +634,19 @@ function deserializeTypeDef(t, resolved) {
|
|
|
563
634
|
designType: t.designType,
|
|
564
635
|
tags
|
|
565
636
|
};
|
|
566
|
-
if (t.value !==
|
|
637
|
+
if (t.value !== void 0) result.value = t.value;
|
|
567
638
|
return result;
|
|
568
639
|
}
|
|
569
640
|
case "object": {
|
|
570
|
-
const props = new Map();
|
|
641
|
+
const props = /* @__PURE__ */ new Map();
|
|
571
642
|
for (const [key, val] of Object.entries(t.props)) props.set(key, deserializeNode(val, resolved));
|
|
572
|
-
const propsPatterns = t.propsPatterns.map((pp) => ({
|
|
573
|
-
pattern: new RegExp(pp.pattern.source, pp.pattern.flags),
|
|
574
|
-
def: deserializeNode(pp.def, resolved)
|
|
575
|
-
}));
|
|
576
643
|
return {
|
|
577
644
|
kind: "object",
|
|
578
645
|
props,
|
|
579
|
-
propsPatterns
|
|
646
|
+
propsPatterns: t.propsPatterns.map((pp) => ({
|
|
647
|
+
pattern: new RegExp(pp.pattern.source, pp.pattern.flags),
|
|
648
|
+
def: deserializeNode(pp.def, resolved)
|
|
649
|
+
})),
|
|
580
650
|
tags
|
|
581
651
|
};
|
|
582
652
|
}
|
|
@@ -597,7 +667,7 @@ function deserializeTypeDef(t, resolved) {
|
|
|
597
667
|
if (existing) return existing.type;
|
|
598
668
|
return {
|
|
599
669
|
kind: "object",
|
|
600
|
-
props: new Map(),
|
|
670
|
+
props: /* @__PURE__ */ new Map(),
|
|
601
671
|
propsPatterns: [],
|
|
602
672
|
tags
|
|
603
673
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/typescript",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.89",
|
|
4
4
|
"description": "Atscript: typescript-gen support.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"annotations",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
75
75
|
"@atscript/db": "^0.1.97",
|
|
76
|
-
"@atscript/core": "^0.1.
|
|
76
|
+
"@atscript/core": "^0.1.89"
|
|
77
77
|
},
|
|
78
78
|
"peerDependenciesMeta": {
|
|
79
79
|
"@atscript/db": {
|