@atscript/typescript 0.1.31 → 0.1.33
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 +301 -236
- package/dist/index.cjs +301 -236
- package/dist/index.mjs +301 -236
- package/dist/utils.cjs +160 -143
- package/dist/utils.d.ts +28 -8
- package/dist/utils.mjs +160 -144
- package/package.json +2 -2
- package/skills/atscript-typescript/annotations.md +2 -1
package/dist/utils.cjs
CHANGED
|
@@ -1,23 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
//#region packages/typescript/src/traverse.ts
|
|
4
|
-
function forAnnotatedType(def, handlers) {
|
|
5
|
-
switch (def.type.kind) {
|
|
6
|
-
case "": {
|
|
7
|
-
const typed = def;
|
|
8
|
-
if (handlers.phantom && typed.type.designType === "phantom") return handlers.phantom(typed);
|
|
9
|
-
return handlers.final(typed);
|
|
10
|
-
}
|
|
11
|
-
case "object": return handlers.object(def);
|
|
12
|
-
case "array": return handlers.array(def);
|
|
13
|
-
case "union": return handlers.union(def);
|
|
14
|
-
case "intersection": return handlers.intersection(def);
|
|
15
|
-
case "tuple": return handlers.tuple(def);
|
|
16
|
-
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
//#endregion
|
|
21
3
|
//#region packages/typescript/src/validator.ts
|
|
22
4
|
function _define_property(obj, key, value) {
|
|
23
5
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
@@ -32,28 +14,35 @@ else obj[key] = value;
|
|
|
32
14
|
const regexCache = new Map();
|
|
33
15
|
var Validator = class {
|
|
34
16
|
isLimitExceeded() {
|
|
35
|
-
if (this.stackErrors.length > 0)
|
|
17
|
+
if (this.stackErrors.length > 0) {
|
|
18
|
+
const top = this.stackErrors[this.stackErrors.length - 1];
|
|
19
|
+
return top !== null && top.length >= this.opts.errorLimit;
|
|
20
|
+
}
|
|
36
21
|
return this.errors.length >= this.opts.errorLimit;
|
|
37
22
|
}
|
|
38
23
|
push(name) {
|
|
39
24
|
this.stackPath.push(name);
|
|
40
|
-
this.stackErrors.push(
|
|
25
|
+
this.stackErrors.push(null);
|
|
26
|
+
this.cachedPath = this.stackPath.length <= 1 ? "" : this.stackPath[1] + (this.stackPath.length > 2 ? "." + this.stackPath.slice(2).join(".") : "");
|
|
41
27
|
}
|
|
42
28
|
pop(saveErrors) {
|
|
43
29
|
this.stackPath.pop();
|
|
44
30
|
const popped = this.stackErrors.pop();
|
|
45
|
-
if (saveErrors && popped
|
|
46
|
-
|
|
47
|
-
});
|
|
31
|
+
if (saveErrors && popped !== null && popped !== undefined && popped.length > 0) for (const err of popped) this.error(err.message, err.path, err.details);
|
|
32
|
+
this.cachedPath = this.stackPath.length <= 1 ? "" : this.stackPath[1] + (this.stackPath.length > 2 ? "." + this.stackPath.slice(2).join(".") : "");
|
|
48
33
|
return popped;
|
|
49
34
|
}
|
|
50
35
|
clear() {
|
|
51
|
-
this.stackErrors[this.stackErrors.length - 1] =
|
|
36
|
+
this.stackErrors[this.stackErrors.length - 1] = null;
|
|
52
37
|
}
|
|
53
38
|
error(message, path, details) {
|
|
54
|
-
|
|
39
|
+
let errors = this.stackErrors[this.stackErrors.length - 1];
|
|
40
|
+
if (!errors) if (this.stackErrors.length > 0) {
|
|
41
|
+
errors = [];
|
|
42
|
+
this.stackErrors[this.stackErrors.length - 1] = errors;
|
|
43
|
+
} else errors = this.errors;
|
|
55
44
|
const error = {
|
|
56
|
-
path: path || this.
|
|
45
|
+
path: path || this.cachedPath,
|
|
57
46
|
message
|
|
58
47
|
};
|
|
59
48
|
if (details?.length) error.details = details;
|
|
@@ -73,9 +62,10 @@ var Validator = class {
|
|
|
73
62
|
* @returns `true` if the value matches the type definition.
|
|
74
63
|
* @throws {ValidatorError} When validation fails and `safe` is not `true`.
|
|
75
64
|
*/ validate(value, safe, context) {
|
|
76
|
-
this.push("");
|
|
77
65
|
this.errors = [];
|
|
78
66
|
this.stackErrors = [];
|
|
67
|
+
this.stackPath = [""];
|
|
68
|
+
this.cachedPath = "";
|
|
79
69
|
this.context = context;
|
|
80
70
|
const passed = this.validateSafe(this.def, value);
|
|
81
71
|
this.pop(!passed);
|
|
@@ -89,7 +79,7 @@ var Validator = class {
|
|
|
89
79
|
validateSafe(def, value) {
|
|
90
80
|
if (this.isLimitExceeded()) return false;
|
|
91
81
|
if (!isAnnotatedType(def)) throw new Error("Can not validate not-annotated type");
|
|
92
|
-
if (typeof this.opts.replace === "function") def = this.opts.replace(def, this.
|
|
82
|
+
if (typeof this.opts.replace === "function") def = this.opts.replace(def, this.cachedPath);
|
|
93
83
|
if (def.optional && value === undefined) return true;
|
|
94
84
|
for (const plugin of this.opts.plugins) {
|
|
95
85
|
const result = plugin(this, def, value);
|
|
@@ -98,18 +88,21 @@ var Validator = class {
|
|
|
98
88
|
return this.validateAnnotatedType(def, value);
|
|
99
89
|
}
|
|
100
90
|
get path() {
|
|
101
|
-
return this.
|
|
91
|
+
return this.cachedPath;
|
|
102
92
|
}
|
|
103
93
|
validateAnnotatedType(def, value) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
94
|
+
switch (def.type.kind) {
|
|
95
|
+
case "": {
|
|
96
|
+
if (def.type.designType === "phantom") return true;
|
|
97
|
+
return this.validatePrimitive(def, value);
|
|
98
|
+
}
|
|
99
|
+
case "object": return this.validateObject(def, value);
|
|
100
|
+
case "array": return this.validateArray(def, value);
|
|
101
|
+
case "union": return this.validateUnion(def, value);
|
|
102
|
+
case "intersection": return this.validateIntersection(def, value);
|
|
103
|
+
case "tuple": return this.validateTuple(def, value);
|
|
104
|
+
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
105
|
+
}
|
|
113
106
|
}
|
|
114
107
|
validateUnion(def, value) {
|
|
115
108
|
let i = 0;
|
|
@@ -173,6 +166,30 @@ var Validator = class {
|
|
|
173
166
|
return false;
|
|
174
167
|
}
|
|
175
168
|
}
|
|
169
|
+
const uniqueItems = def.metadata.get("expect.array.uniqueItems");
|
|
170
|
+
if (uniqueItems) {
|
|
171
|
+
const separator = "▼↩";
|
|
172
|
+
const seen = new Set();
|
|
173
|
+
const keyProps = new Set();
|
|
174
|
+
if (def.type.of.type.kind === "object") {
|
|
175
|
+
for (const [key, val] of def.type.of.type.props.entries()) if (val.metadata.get("expect.array.key")) keyProps.add(key);
|
|
176
|
+
}
|
|
177
|
+
for (let idx = 0; idx < value.length; idx++) {
|
|
178
|
+
const item = value[idx];
|
|
179
|
+
let key;
|
|
180
|
+
if (keyProps.size > 0) {
|
|
181
|
+
key = "";
|
|
182
|
+
for (const prop of keyProps) key += JSON.stringify(item[prop]) + separator;
|
|
183
|
+
} else key = JSON.stringify(item);
|
|
184
|
+
if (seen.has(key)) {
|
|
185
|
+
this.push(String(idx));
|
|
186
|
+
this.error(uniqueItems.message || "Duplicate items are not allowed");
|
|
187
|
+
this.pop(true);
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
seen.add(key);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
176
193
|
let i = 0;
|
|
177
194
|
let passed = true;
|
|
178
195
|
for (const item of value) {
|
|
@@ -194,21 +211,20 @@ var Validator = class {
|
|
|
194
211
|
let passed = true;
|
|
195
212
|
const valueKeys = new Set(Object.keys(value));
|
|
196
213
|
const typeKeys = new Set();
|
|
197
|
-
|
|
214
|
+
let skipList;
|
|
198
215
|
if (this.opts.skipList) {
|
|
199
|
-
const path = this.stackPath.length > 1 ? `${this.
|
|
200
|
-
this.opts.skipList.
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
});
|
|
216
|
+
const path = this.stackPath.length > 1 ? `${this.cachedPath}.` : "";
|
|
217
|
+
for (const item of this.opts.skipList) if (item.startsWith(path)) {
|
|
218
|
+
const key = item.slice(path.length);
|
|
219
|
+
if (!skipList) skipList = new Set();
|
|
220
|
+
skipList.add(key);
|
|
221
|
+
valueKeys.delete(key);
|
|
222
|
+
}
|
|
207
223
|
}
|
|
208
224
|
let partialFunctionMatched = false;
|
|
209
|
-
if (typeof this.opts.partial === "function") partialFunctionMatched = this.opts.partial(def, this.
|
|
225
|
+
if (typeof this.opts.partial === "function") partialFunctionMatched = this.opts.partial(def, this.cachedPath);
|
|
210
226
|
for (const [key, item] of def.type.props.entries()) {
|
|
211
|
-
if (skipList.has(key) || isPhantomType(item)) continue;
|
|
227
|
+
if (skipList && skipList.has(key) || isPhantomType(item)) continue;
|
|
212
228
|
typeKeys.add(key);
|
|
213
229
|
if (value[key] === undefined) {
|
|
214
230
|
if (partialFunctionMatched || this.opts.partial === "deep" || this.opts.partial === true && this.stackPath.length <= 1) continue;
|
|
@@ -229,19 +245,21 @@ else {
|
|
|
229
245
|
def: propDef
|
|
230
246
|
});
|
|
231
247
|
if (matched.length > 0) {
|
|
248
|
+
this.push(key);
|
|
232
249
|
let keyPassed = false;
|
|
233
|
-
for (const { def:
|
|
234
|
-
this.
|
|
235
|
-
|
|
236
|
-
|
|
250
|
+
for (const { def: propDef } of matched) {
|
|
251
|
+
if (this.validateSafe(propDef, value[key])) {
|
|
252
|
+
keyPassed = true;
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
this.clear();
|
|
237
256
|
}
|
|
238
257
|
if (!keyPassed) {
|
|
239
|
-
this.push(key);
|
|
240
258
|
this.validateSafe(matched[0].def, value[key]);
|
|
241
259
|
this.pop(true);
|
|
242
260
|
passed = false;
|
|
243
261
|
if (this.isLimitExceeded()) return false;
|
|
244
|
-
}
|
|
262
|
+
} else this.pop(false);
|
|
245
263
|
} else if (this.opts.unknownProps !== "ignore") {
|
|
246
264
|
if (this.opts.unknownProps === "error") {
|
|
247
265
|
this.push(key);
|
|
@@ -394,11 +412,13 @@ else {
|
|
|
394
412
|
/** Validation errors collected during the last {@link validate} call. */ _define_property(this, "errors", void 0);
|
|
395
413
|
_define_property(this, "stackErrors", void 0);
|
|
396
414
|
_define_property(this, "stackPath", void 0);
|
|
415
|
+
_define_property(this, "cachedPath", void 0);
|
|
397
416
|
_define_property(this, "context", void 0);
|
|
398
417
|
this.def = def;
|
|
399
418
|
this.errors = [];
|
|
400
419
|
this.stackErrors = [];
|
|
401
420
|
this.stackPath = [];
|
|
421
|
+
this.cachedPath = "";
|
|
402
422
|
this.opts = {
|
|
403
423
|
partial: false,
|
|
404
424
|
unknownProps: "error",
|
|
@@ -416,6 +436,25 @@ var ValidatorError = class extends Error {
|
|
|
416
436
|
|
|
417
437
|
//#endregion
|
|
418
438
|
//#region packages/typescript/src/annotated-type.ts
|
|
439
|
+
const COMPLEX_KINDS = new Set([
|
|
440
|
+
"union",
|
|
441
|
+
"intersection",
|
|
442
|
+
"tuple"
|
|
443
|
+
]);
|
|
444
|
+
const NON_PRIMITIVE_KINDS = new Set(["array", "object"]);
|
|
445
|
+
/** Shared validator method reused by all annotated type nodes. */ function validatorMethod(opts) {
|
|
446
|
+
return new Validator(this, opts);
|
|
447
|
+
}
|
|
448
|
+
function createAnnotatedTypeNode(type, metadata, opts) {
|
|
449
|
+
return {
|
|
450
|
+
__is_atscript_annotated_type: true,
|
|
451
|
+
type,
|
|
452
|
+
metadata,
|
|
453
|
+
validator: validatorMethod,
|
|
454
|
+
id: opts?.id,
|
|
455
|
+
optional: opts?.optional
|
|
456
|
+
};
|
|
457
|
+
}
|
|
419
458
|
function isAnnotatedType(type) {
|
|
420
459
|
return type && type.__is_atscript_annotated_type;
|
|
421
460
|
}
|
|
@@ -434,32 +473,22 @@ function cloneRefProp(parentType, propName) {
|
|
|
434
473
|
const existing = objType.props.get(propName);
|
|
435
474
|
if (!existing) return;
|
|
436
475
|
const clonedType = cloneTypeDef(existing.type);
|
|
437
|
-
objType.props.set(propName, {
|
|
438
|
-
__is_atscript_annotated_type: true,
|
|
439
|
-
type: clonedType,
|
|
440
|
-
metadata: new Map(existing.metadata),
|
|
476
|
+
objType.props.set(propName, createAnnotatedTypeNode(clonedType, new Map(existing.metadata), {
|
|
441
477
|
id: existing.id,
|
|
442
|
-
optional: existing.optional
|
|
443
|
-
|
|
444
|
-
return new Validator(this, opts);
|
|
445
|
-
}
|
|
446
|
-
});
|
|
478
|
+
optional: existing.optional
|
|
479
|
+
}));
|
|
447
480
|
}
|
|
448
481
|
function cloneTypeDef(type) {
|
|
449
482
|
if (type.kind === "object") {
|
|
450
483
|
const obj = type;
|
|
484
|
+
const props = new Map();
|
|
485
|
+
for (const [k, v] of obj.props) props.set(k, createAnnotatedTypeNode(v.type, new Map(v.metadata), {
|
|
486
|
+
id: v.id,
|
|
487
|
+
optional: v.optional
|
|
488
|
+
}));
|
|
451
489
|
return {
|
|
452
490
|
kind: "object",
|
|
453
|
-
props
|
|
454
|
-
__is_atscript_annotated_type: true,
|
|
455
|
-
type: v.type,
|
|
456
|
-
metadata: new Map(v.metadata),
|
|
457
|
-
id: v.id,
|
|
458
|
-
optional: v.optional,
|
|
459
|
-
validator(opts) {
|
|
460
|
-
return new Validator(this, opts);
|
|
461
|
-
}
|
|
462
|
-
}])),
|
|
491
|
+
props,
|
|
463
492
|
propsPatterns: [...obj.propsPatterns],
|
|
464
493
|
tags: new Set(obj.tags)
|
|
465
494
|
};
|
|
@@ -489,38 +518,24 @@ function defineAnnotatedType(_kind, base) {
|
|
|
489
518
|
const kind = _kind || "";
|
|
490
519
|
const type = base?.type || {};
|
|
491
520
|
type.kind = kind;
|
|
492
|
-
if ([
|
|
493
|
-
"union",
|
|
494
|
-
"intersection",
|
|
495
|
-
"tuple"
|
|
496
|
-
].includes(kind)) type.items = [];
|
|
521
|
+
if (COMPLEX_KINDS.has(kind)) type.items = [];
|
|
497
522
|
if (kind === "object") {
|
|
498
523
|
type.props = new Map();
|
|
499
524
|
type.propsPatterns = [];
|
|
500
525
|
}
|
|
501
526
|
type.tags = new Set();
|
|
502
527
|
const metadata = base?.metadata || new Map();
|
|
503
|
-
|
|
528
|
+
const payload = {
|
|
504
529
|
__is_atscript_annotated_type: true,
|
|
505
530
|
metadata,
|
|
506
531
|
type,
|
|
507
|
-
validator
|
|
508
|
-
return new Validator(this, opts);
|
|
509
|
-
}
|
|
510
|
-
});
|
|
511
|
-
else base = {
|
|
512
|
-
__is_atscript_annotated_type: true,
|
|
513
|
-
metadata,
|
|
514
|
-
type,
|
|
515
|
-
validator(opts) {
|
|
516
|
-
return new Validator(this, opts);
|
|
517
|
-
}
|
|
532
|
+
validator: validatorMethod
|
|
518
533
|
};
|
|
534
|
+
base = base ? Object.assign(base, payload) : payload;
|
|
519
535
|
const handle = {
|
|
520
536
|
$type: base,
|
|
521
537
|
$def: type,
|
|
522
538
|
$metadata: metadata,
|
|
523
|
-
_existingObject: undefined,
|
|
524
539
|
tags(...tags) {
|
|
525
540
|
for (const tag of tags) this.$def.tags.add(tag);
|
|
526
541
|
return this;
|
|
@@ -561,27 +576,18 @@ else base = {
|
|
|
561
576
|
return this;
|
|
562
577
|
},
|
|
563
578
|
refTo(type$1, chain) {
|
|
579
|
+
if (!isAnnotatedType(type$1)) throw new Error(`${type$1} is not annotated type`);
|
|
564
580
|
let newBase = type$1;
|
|
565
581
|
const typeName = type$1.name || "Unknown";
|
|
566
|
-
if (
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
582
|
+
if (chain) for (let i = 0; i < chain.length; i++) {
|
|
583
|
+
const c = chain[i];
|
|
584
|
+
if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
|
|
585
|
+
else {
|
|
586
|
+
const keys = chain.slice(0, i + 1).map((k) => `["${k}"]`).join("");
|
|
587
|
+
throw new Error(`Can't find prop ${typeName}${keys}`);
|
|
572
588
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
this.$type = {
|
|
576
|
-
__is_atscript_annotated_type: true,
|
|
577
|
-
type: newBase.type,
|
|
578
|
-
metadata,
|
|
579
|
-
id: newBase.id,
|
|
580
|
-
validator(opts) {
|
|
581
|
-
return new Validator(this, opts);
|
|
582
|
-
}
|
|
583
|
-
};
|
|
584
|
-
} else throw new Error(`${type$1} is not annotated type`);
|
|
589
|
+
}
|
|
590
|
+
this.$type = createAnnotatedTypeNode(newBase.type, metadata, { id: newBase.id });
|
|
585
591
|
return this;
|
|
586
592
|
},
|
|
587
593
|
annotate(key, value, asArray) {
|
|
@@ -599,19 +605,33 @@ function isPhantomType(def) {
|
|
|
599
605
|
return def.type.kind === "" && def.type.designType === "phantom";
|
|
600
606
|
}
|
|
601
607
|
function isAnnotatedTypeOfPrimitive(t) {
|
|
602
|
-
if (
|
|
608
|
+
if (NON_PRIMITIVE_KINDS.has(t.type.kind)) return false;
|
|
603
609
|
if (!t.type.kind) return true;
|
|
604
|
-
if (
|
|
605
|
-
"union",
|
|
606
|
-
"tuple",
|
|
607
|
-
"intersection"
|
|
608
|
-
].includes(t.type.kind)) {
|
|
610
|
+
if (COMPLEX_KINDS.has(t.type.kind)) {
|
|
609
611
|
for (const item of t.type.items) if (!isAnnotatedTypeOfPrimitive(item)) return false;
|
|
610
612
|
return true;
|
|
611
613
|
}
|
|
612
614
|
return false;
|
|
613
615
|
}
|
|
614
616
|
|
|
617
|
+
//#endregion
|
|
618
|
+
//#region packages/typescript/src/traverse.ts
|
|
619
|
+
function forAnnotatedType(def, handlers) {
|
|
620
|
+
switch (def.type.kind) {
|
|
621
|
+
case "": {
|
|
622
|
+
const typed = def;
|
|
623
|
+
if (handlers.phantom && typed.type.designType === "phantom") return handlers.phantom(typed);
|
|
624
|
+
return handlers.final(typed);
|
|
625
|
+
}
|
|
626
|
+
case "object": return handlers.object(def);
|
|
627
|
+
case "array": return handlers.array(def);
|
|
628
|
+
case "union": return handlers.union(def);
|
|
629
|
+
case "intersection": return handlers.intersection(def);
|
|
630
|
+
case "tuple": return handlers.tuple(def);
|
|
631
|
+
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
615
635
|
//#endregion
|
|
616
636
|
//#region packages/typescript/src/json-schema.ts
|
|
617
637
|
/**
|
|
@@ -626,10 +646,10 @@ function isAnnotatedTypeOfPrimitive(t) {
|
|
|
626
646
|
const firstObj = items[0].type;
|
|
627
647
|
const candidates = [];
|
|
628
648
|
for (const [propName, propType] of firstObj.props.entries()) if (propType.type.kind === "" && propType.type.value !== undefined) candidates.push(propName);
|
|
629
|
-
|
|
649
|
+
let result = null;
|
|
630
650
|
for (const candidate of candidates) {
|
|
631
651
|
const values = new Set();
|
|
632
|
-
const
|
|
652
|
+
const indexMapping = {};
|
|
633
653
|
let valid = true;
|
|
634
654
|
for (let i = 0; i < items.length; i++) {
|
|
635
655
|
const obj = items[i].type;
|
|
@@ -644,19 +664,21 @@ function isAnnotatedTypeOfPrimitive(t) {
|
|
|
644
664
|
break;
|
|
645
665
|
}
|
|
646
666
|
values.add(val);
|
|
647
|
-
|
|
667
|
+
indexMapping[String(val)] = i;
|
|
668
|
+
}
|
|
669
|
+
if (valid) {
|
|
670
|
+
if (result) return null;
|
|
671
|
+
result = {
|
|
672
|
+
propertyName: candidate,
|
|
673
|
+
indexMapping
|
|
674
|
+
};
|
|
648
675
|
}
|
|
649
|
-
if (valid) validCandidates.push({
|
|
650
|
-
propertyName: candidate,
|
|
651
|
-
mapping
|
|
652
|
-
});
|
|
653
676
|
}
|
|
654
|
-
|
|
655
|
-
return null;
|
|
677
|
+
return result;
|
|
656
678
|
}
|
|
657
679
|
function buildJsonSchema(type) {
|
|
658
680
|
const defs = {};
|
|
659
|
-
let
|
|
681
|
+
let hasDefs = false;
|
|
660
682
|
const buildObject = (d) => {
|
|
661
683
|
const properties = {};
|
|
662
684
|
const required = [];
|
|
@@ -673,15 +695,15 @@ function buildJsonSchema(type) {
|
|
|
673
695
|
return schema$1;
|
|
674
696
|
};
|
|
675
697
|
const build$1 = (def) => {
|
|
676
|
-
if (def.id && def.type.kind === "object" &&
|
|
698
|
+
if (def.id && def.type.kind === "object" && def !== type) {
|
|
677
699
|
const name = def.id;
|
|
678
700
|
if (!defs[name]) {
|
|
701
|
+
hasDefs = true;
|
|
679
702
|
defs[name] = {};
|
|
680
703
|
defs[name] = buildObject(def);
|
|
681
704
|
}
|
|
682
705
|
return { $ref: `#/$defs/${name}` };
|
|
683
706
|
}
|
|
684
|
-
isRoot = false;
|
|
685
707
|
const meta = def.metadata;
|
|
686
708
|
return forAnnotatedType(def, {
|
|
687
709
|
phantom() {
|
|
@@ -706,11 +728,9 @@ function buildJsonSchema(type) {
|
|
|
706
728
|
if (disc) {
|
|
707
729
|
const oneOf = d.type.items.map(build$1);
|
|
708
730
|
const mapping = {};
|
|
709
|
-
for (const [val,
|
|
710
|
-
const idx = Number.parseInt(origPath.split("/").pop(), 10);
|
|
731
|
+
for (const [val, idx] of Object.entries(disc.indexMapping)) {
|
|
711
732
|
const item = d.type.items[idx];
|
|
712
|
-
|
|
713
|
-
else mapping[val] = origPath;
|
|
733
|
+
mapping[val] = item.id && defs[item.id] ? `#/$defs/${item.id}` : `#/oneOf/${idx}`;
|
|
714
734
|
}
|
|
715
735
|
return {
|
|
716
736
|
oneOf,
|
|
@@ -760,7 +780,7 @@ else schema$1.allOf = (schema$1.allOf || []).concat(patterns.map((p) => ({ patte
|
|
|
760
780
|
});
|
|
761
781
|
};
|
|
762
782
|
const schema = build$1(type);
|
|
763
|
-
if (
|
|
783
|
+
if (hasDefs) return {
|
|
764
784
|
...schema,
|
|
765
785
|
$defs: defs
|
|
766
786
|
};
|
|
@@ -775,6 +795,8 @@ function fromJsonSchema(schema) {
|
|
|
775
795
|
const refName = s.$ref.replace(/^#\/(\$defs|definitions)\//, "");
|
|
776
796
|
if (resolved.has(refName)) return resolved.get(refName);
|
|
777
797
|
if (defsSource[refName]) {
|
|
798
|
+
const placeholder = defineAnnotatedType().designType("any").$type;
|
|
799
|
+
resolved.set(refName, placeholder);
|
|
778
800
|
const type = convert(defsSource[refName]);
|
|
779
801
|
resolved.set(refName, type);
|
|
780
802
|
return type;
|
|
@@ -1185,16 +1207,10 @@ function deserializeAnnotatedType(data) {
|
|
|
1185
1207
|
function deserializeNode(data) {
|
|
1186
1208
|
const metadata = new Map(Object.entries(data.metadata));
|
|
1187
1209
|
const type = deserializeTypeDef(data.type);
|
|
1188
|
-
const result = {
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
validator(opts) {
|
|
1193
|
-
return new Validator(this, opts);
|
|
1194
|
-
}
|
|
1195
|
-
};
|
|
1196
|
-
if (data.optional) result.optional = true;
|
|
1197
|
-
if (data.id) result.id = data.id;
|
|
1210
|
+
const result = createAnnotatedTypeNode(type, metadata, {
|
|
1211
|
+
optional: data.optional || undefined,
|
|
1212
|
+
id: data.id || undefined
|
|
1213
|
+
});
|
|
1198
1214
|
return result;
|
|
1199
1215
|
}
|
|
1200
1216
|
function deserializeTypeDef(t) {
|
|
@@ -1246,6 +1262,7 @@ exports.ValidatorError = ValidatorError
|
|
|
1246
1262
|
exports.annotate = annotate
|
|
1247
1263
|
exports.buildJsonSchema = buildJsonSchema
|
|
1248
1264
|
exports.cloneRefProp = cloneRefProp
|
|
1265
|
+
exports.createAnnotatedTypeNode = createAnnotatedTypeNode
|
|
1249
1266
|
exports.createDataFromAnnotatedType = createDataFromAnnotatedType
|
|
1250
1267
|
exports.defineAnnotatedType = defineAnnotatedType
|
|
1251
1268
|
exports.deserializeAnnotatedType = deserializeAnnotatedType
|
package/dist/utils.d.ts
CHANGED
|
@@ -48,18 +48,19 @@ interface TValidatorPluginContext {
|
|
|
48
48
|
* @typeParam T - The annotated type definition.
|
|
49
49
|
* @typeParam DataType - The TypeScript type that `validate` narrows to (auto-inferred).
|
|
50
50
|
*/
|
|
51
|
-
declare class Validator<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType, DataType = TAtscriptDataType
|
|
51
|
+
declare class Validator<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType, DataType = TAtscriptDataType<T>> {
|
|
52
52
|
protected readonly def: T;
|
|
53
53
|
protected opts: TValidatorOptions;
|
|
54
54
|
constructor(def: T, opts?: Partial<TValidatorOptions>);
|
|
55
55
|
/** Validation errors collected during the last {@link validate} call. */
|
|
56
56
|
errors: TError[];
|
|
57
|
-
protected stackErrors: TError[]
|
|
57
|
+
protected stackErrors: Array<TError[] | null>;
|
|
58
58
|
protected stackPath: string[];
|
|
59
|
+
protected cachedPath: string;
|
|
59
60
|
protected context: unknown;
|
|
60
61
|
protected isLimitExceeded(): boolean;
|
|
61
62
|
protected push(name: string): void;
|
|
62
|
-
protected pop(saveErrors: boolean): TError[] | undefined;
|
|
63
|
+
protected pop(saveErrors: boolean): TError[] | null | undefined;
|
|
63
64
|
protected clear(): void;
|
|
64
65
|
protected error(message: string, path?: string, details?: TError[]): void;
|
|
65
66
|
protected throw(): void;
|
|
@@ -95,6 +96,14 @@ declare class ValidatorError extends Error {
|
|
|
95
96
|
constructor(errors: TError[]);
|
|
96
97
|
}
|
|
97
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Creates a minimal annotated type node from a type def and metadata map.
|
|
101
|
+
* Centralises the shape so callers never duplicate the boilerplate.
|
|
102
|
+
*/
|
|
103
|
+
declare function createAnnotatedTypeNode(type: TAtscriptTypeDef, metadata: TMetadataMap<AtscriptMetadata>, opts?: {
|
|
104
|
+
id?: string;
|
|
105
|
+
optional?: boolean;
|
|
106
|
+
}): TAtscriptAnnotatedType;
|
|
98
107
|
/** Type definition for union, intersection, or tuple types. */
|
|
99
108
|
interface TAtscriptTypeComplex<DataType = unknown> {
|
|
100
109
|
kind: 'union' | 'intersection' | 'tuple';
|
|
@@ -159,7 +168,7 @@ type InferDataType<T> = T extends {
|
|
|
159
168
|
* type Data = TAtscriptDataType<typeof MyInterface>
|
|
160
169
|
* ```
|
|
161
170
|
*/
|
|
162
|
-
type TAtscriptDataType
|
|
171
|
+
type TAtscriptDataType<T extends TAtscriptAnnotatedType = TAtscriptAnnotatedType> = T extends {
|
|
163
172
|
type: {
|
|
164
173
|
__dataType?: infer D;
|
|
165
174
|
};
|
|
@@ -234,7 +243,6 @@ interface TAnnotatedTypeHandle {
|
|
|
234
243
|
kind: TKind;
|
|
235
244
|
} & Omit<TAtscriptTypeComplex, 'kind'> & Omit<TAtscriptTypeFinal, 'kind'> & Omit<TAtscriptTypeArray, 'kind'> & Omit<TAtscriptTypeObject<string>, 'kind'>;
|
|
236
245
|
$metadata: TMetadataMap<AtscriptMetadata>;
|
|
237
|
-
_existingObject: TAtscriptAnnotatedType | undefined;
|
|
238
246
|
tags(...tags: string[]): TAnnotatedTypeHandle;
|
|
239
247
|
designType(value: TAtscriptTypeFinal['designType']): TAnnotatedTypeHandle;
|
|
240
248
|
value(value: string | number | boolean): TAnnotatedTypeHandle;
|
|
@@ -544,7 +552,19 @@ declare function deserializeAnnotatedType(data: TSerializedAnnotatedType): TAtsc
|
|
|
544
552
|
*/
|
|
545
553
|
type FlatOf<T> = T extends {
|
|
546
554
|
__flat: infer F;
|
|
547
|
-
} ? F : TAtscriptDataType<T
|
|
555
|
+
} ? F : T extends TAtscriptAnnotatedType ? TAtscriptDataType<T> : unknown;
|
|
556
|
+
/**
|
|
557
|
+
* Extracts the primary key type from an Atscript annotated type.
|
|
558
|
+
* If the type has a `__pk` static property (emitted for `@db.table` interfaces
|
|
559
|
+
* with `@meta.id` fields), returns that type. Otherwise falls back to `unknown`.
|
|
560
|
+
*
|
|
561
|
+
* - Single `@meta.id` field → scalar type (e.g., `string`, `number`)
|
|
562
|
+
* - Multiple `@meta.id` fields → object type (e.g., `{ userId: string; orderId: number }`)
|
|
563
|
+
* - No `@meta.id` fields → `unknown`
|
|
564
|
+
*/
|
|
565
|
+
type PrimaryKeyOf<T> = T extends {
|
|
566
|
+
__pk: infer PK;
|
|
567
|
+
} ? PK : unknown;
|
|
548
568
|
|
|
549
|
-
export { SERIALIZE_VERSION, Validator, ValidatorError, annotate, buildJsonSchema, cloneRefProp, createDataFromAnnotatedType, defineAnnotatedType, deserializeAnnotatedType, flattenAnnotatedType, forAnnotatedType, fromJsonSchema, isAnnotatedType, isAnnotatedTypeOfPrimitive, isPhantomType, mergeJsonSchemas, serializeAnnotatedType, throwFeatureDisabled };
|
|
550
|
-
export type { FlatOf, InferDataType, TAnnotatedTypeHandle, TAtscriptAnnotatedType, TAtscriptAnnotatedTypeConstructor, TAtscriptDataType
|
|
569
|
+
export { SERIALIZE_VERSION, Validator, ValidatorError, annotate, buildJsonSchema, cloneRefProp, createAnnotatedTypeNode, createDataFromAnnotatedType, defineAnnotatedType, deserializeAnnotatedType, flattenAnnotatedType, forAnnotatedType, fromJsonSchema, isAnnotatedType, isAnnotatedTypeOfPrimitive, isPhantomType, mergeJsonSchemas, serializeAnnotatedType, throwFeatureDisabled };
|
|
570
|
+
export type { FlatOf, InferDataType, PrimaryKeyOf, TAnnotatedTypeHandle, TAtscriptAnnotatedType, TAtscriptAnnotatedTypeConstructor, TAtscriptDataType, TAtscriptTypeArray, TAtscriptTypeComplex, TAtscriptTypeDef, TAtscriptTypeFinal, TAtscriptTypeObject, TCreateDataOptions, TFlattenOptions, TJsonSchema, TMetadataMap, TProcessAnnotationContext, TSerializeOptions, TSerializedAnnotatedType, TSerializedAnnotatedTypeInner, TSerializedTypeArray, TSerializedTypeComplex, TSerializedTypeDef, TSerializedTypeFinal, TSerializedTypeObject, TValidatorOptions, TValidatorPlugin, TValidatorPluginContext, TValueResolver };
|