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