@atscript/typescript 0.1.33 → 0.1.35
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/LICENSE +1 -1
- package/dist/cli.cjs +607 -126
- package/dist/index.cjs +171 -778
- package/dist/index.mjs +163 -770
- package/dist/json-schema-0UUPoHud.mjs +952 -0
- package/dist/json-schema-S5-XAOrR.cjs +1030 -0
- package/dist/utils.cjs +80 -945
- package/dist/utils.d.ts +64 -6
- package/dist/utils.mjs +58 -923
- package/package.json +11 -6
package/dist/index.cjs
CHANGED
|
@@ -23,11 +23,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
}) : target, mod));
|
|
24
24
|
|
|
25
25
|
//#endregion
|
|
26
|
+
const require_json_schema = require('./json-schema-S5-XAOrR.cjs');
|
|
26
27
|
const path = __toESM(require("path"));
|
|
27
28
|
const __atscript_core = __toESM(require("@atscript/core"));
|
|
28
29
|
|
|
29
30
|
//#region packages/typescript/src/codegen/code-printer.ts
|
|
30
|
-
function _define_property$
|
|
31
|
+
function _define_property$3(obj, key, value) {
|
|
31
32
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
32
33
|
value,
|
|
33
34
|
enumerable: true,
|
|
@@ -120,17 +121,17 @@ else this.write(closing);
|
|
|
120
121
|
return " ".repeat(this.indentLevel * this.indentSize);
|
|
121
122
|
}
|
|
122
123
|
constructor() {
|
|
123
|
-
_define_property$
|
|
124
|
-
_define_property$
|
|
125
|
-
_define_property$
|
|
126
|
-
_define_property$
|
|
127
|
-
_define_property$
|
|
124
|
+
_define_property$3(this, "lines", []);
|
|
125
|
+
_define_property$3(this, "currentLine", "");
|
|
126
|
+
_define_property$3(this, "indentLevel", 0);
|
|
127
|
+
_define_property$3(this, "indentSize", 2);
|
|
128
|
+
_define_property$3(this, "blockStack", []);
|
|
128
129
|
}
|
|
129
130
|
};
|
|
130
131
|
|
|
131
132
|
//#endregion
|
|
132
133
|
//#region packages/typescript/src/codegen/base-renderer.ts
|
|
133
|
-
function _define_property$
|
|
134
|
+
function _define_property$2(obj, key, value) {
|
|
134
135
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
135
136
|
value,
|
|
136
137
|
enumerable: true,
|
|
@@ -197,7 +198,7 @@ var BaseRenderer = class extends CodePrinter {
|
|
|
197
198
|
}
|
|
198
199
|
}
|
|
199
200
|
constructor(doc) {
|
|
200
|
-
super(), _define_property$
|
|
201
|
+
super(), _define_property$2(this, "doc", void 0), _define_property$2(this, "_unused", void 0), this.doc = doc;
|
|
201
202
|
}
|
|
202
203
|
};
|
|
203
204
|
|
|
@@ -214,7 +215,7 @@ function escapeQuotes(str) {
|
|
|
214
215
|
|
|
215
216
|
//#endregion
|
|
216
217
|
//#region packages/typescript/src/codegen/type-renderer.ts
|
|
217
|
-
function _define_property$
|
|
218
|
+
function _define_property$1(obj, key, value) {
|
|
218
219
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
219
220
|
value,
|
|
220
221
|
enumerable: true,
|
|
@@ -339,8 +340,10 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
339
340
|
this.writeln("static toJsonSchema: () => any");
|
|
340
341
|
if (!this.opts?.exampleData) this.writeln("/** @deprecated Example Data support is disabled. To enable, set `exampleData: true` in tsPlugin options. */");
|
|
341
342
|
this.writeln("static toExampleData?: () => any");
|
|
342
|
-
if (interfaceNode && this.
|
|
343
|
+
if (interfaceNode && this.hasDbEntity(interfaceNode)) {
|
|
343
344
|
this.renderFlat(interfaceNode);
|
|
345
|
+
this.renderOwnProps(interfaceNode);
|
|
346
|
+
this.renderNavProps(interfaceNode);
|
|
344
347
|
this.renderPk(interfaceNode);
|
|
345
348
|
}
|
|
346
349
|
}
|
|
@@ -431,14 +434,13 @@ else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef = `TAtscriptTypeFina
|
|
|
431
434
|
this.popln();
|
|
432
435
|
}
|
|
433
436
|
/**
|
|
434
|
-
* Checks whether an interface
|
|
437
|
+
* Checks whether an interface is a DB entity (`@db.table` or `@db.view`).
|
|
435
438
|
*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
*
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
return !!node.annotations?.some((a) => a.name === "db.table");
|
|
439
|
+
* Only DB entities get `__flat`, `__pk`, `__ownProps`, and `__navProps` static properties.
|
|
440
|
+
* These exist solely to improve type-safety for filter expressions, `$select`/`$sort`,
|
|
441
|
+
* and `$with` operations in the DB layer.
|
|
442
|
+
*/ hasDbEntity(node) {
|
|
443
|
+
return !!node.annotations?.some((a) => a.name === "db.table" || a.name === "db.view" || a.name === "db.view.for");
|
|
442
444
|
}
|
|
443
445
|
/**
|
|
444
446
|
* Renders the `static __flat` property — a map of all dot-notation paths
|
|
@@ -455,9 +457,27 @@ else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef = `TAtscriptTypeFina
|
|
|
455
457
|
* not individually queryable)
|
|
456
458
|
* - **Leaf fields** → their original TypeScript type
|
|
457
459
|
*/ renderFlat(node) {
|
|
458
|
-
|
|
460
|
+
this.renderFlatMap("__flat", (0, __atscript_core.flattenInterfaceNode)(this.doc, node));
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Renders the `static __ownProps` property — table-owned fields only (no nav props).
|
|
464
|
+
*/ renderOwnProps(node) {
|
|
465
|
+
this.renderFlatMap("__ownProps", (0, __atscript_core.flattenInterfaceNode)(this.doc, node, { skipNavProps: true }), {
|
|
466
|
+
leadingNewline: true,
|
|
467
|
+
trailingNewline: true
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Shared renderer for flat-map static properties (`__flat`, `__ownProps`).
|
|
472
|
+
*
|
|
473
|
+
* Special rendering rules:
|
|
474
|
+
* - **Intermediate paths** (structures, arrays of structures) → `never`
|
|
475
|
+
* - **`@db.json` fields** → `string` (stored as serialized JSON in DB)
|
|
476
|
+
* - **Leaf fields** → their original TypeScript type
|
|
477
|
+
*/ renderFlatMap(propName, flatMap, opts) {
|
|
459
478
|
if (flatMap.size === 0) return;
|
|
460
|
-
this.
|
|
479
|
+
if (opts?.leadingNewline) this.writeln();
|
|
480
|
+
this.write(`static ${propName}: `);
|
|
461
481
|
this.blockln("{}");
|
|
462
482
|
for (const [path$2, descriptor] of flatMap) {
|
|
463
483
|
this.write(`"${escapeQuotes(path$2)}"`);
|
|
@@ -472,19 +492,46 @@ else {
|
|
|
472
492
|
renderedDef.split("\n").forEach((l) => this.writeln(l));
|
|
473
493
|
}
|
|
474
494
|
}
|
|
475
|
-
this.
|
|
495
|
+
if (opts?.trailingNewline) this.popln();
|
|
496
|
+
else this.pop();
|
|
476
497
|
}
|
|
477
498
|
/**
|
|
478
|
-
* Renders the `static
|
|
479
|
-
*
|
|
499
|
+
* Renders the `static __navProps` property — a map of navigation property names
|
|
500
|
+
* to their declared TypeScript types.
|
|
480
501
|
*
|
|
481
|
-
* -
|
|
482
|
-
*
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
502
|
+
* This enables type-safe `$with` in queries: `name` is constrained to known
|
|
503
|
+
* navigation property keys, and nested filter/controls are typed to the target entity.
|
|
504
|
+
*/ renderNavProps(node) {
|
|
505
|
+
let struct;
|
|
506
|
+
if (node.hasExtends) struct = this.doc.resolveInterfaceExtends(node);
|
|
507
|
+
if (!struct) struct = node.getDefinition();
|
|
508
|
+
if (!struct || !(0, __atscript_core.isStructure)(struct)) return;
|
|
509
|
+
const structNode = struct;
|
|
510
|
+
const navProps = [];
|
|
511
|
+
for (const [name, prop] of structNode.props) {
|
|
512
|
+
if (prop.token("identifier")?.pattern) continue;
|
|
513
|
+
if ((0, __atscript_core.hasNavPropAnnotation)(prop)) navProps.push({
|
|
514
|
+
name,
|
|
515
|
+
prop
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
if (navProps.length === 0) return;
|
|
519
|
+
this.writeln();
|
|
520
|
+
this.write("static __navProps: ");
|
|
521
|
+
this.blockln("{}");
|
|
522
|
+
for (const { name, prop } of navProps) {
|
|
523
|
+
this.write(`"${escapeQuotes(name)}"`);
|
|
524
|
+
if (prop.token("optional")) this.write("?");
|
|
525
|
+
this.write(": ");
|
|
526
|
+
const propDef = prop.getDefinition();
|
|
527
|
+
if (propDef) {
|
|
528
|
+
const renderedDef = this.renderTypeDefString(propDef);
|
|
529
|
+
renderedDef.split("\n").forEach((l) => this.writeln(l));
|
|
530
|
+
} else this.writeln("unknown");
|
|
531
|
+
}
|
|
532
|
+
this.popln();
|
|
533
|
+
}
|
|
534
|
+
renderPk(node) {
|
|
488
535
|
const isMongoCollection = !!node.annotations?.some((a) => a.name === "db.mongo.collection");
|
|
489
536
|
let struct;
|
|
490
537
|
if (node.hasExtends) struct = this.doc.resolveInterfaceExtends(node);
|
|
@@ -584,7 +631,7 @@ else if (pkProps.length === 1) {
|
|
|
584
631
|
this.writeln(` */`);
|
|
585
632
|
}
|
|
586
633
|
constructor(doc, opts) {
|
|
587
|
-
super(doc), _define_property$
|
|
634
|
+
super(doc), _define_property$1(this, "opts", void 0), this.opts = opts;
|
|
588
635
|
}
|
|
589
636
|
};
|
|
590
637
|
function renderPrimitiveTypeDef(def) {
|
|
@@ -604,737 +651,6 @@ function renderPrimitiveTypeDef(def) {
|
|
|
604
651
|
}
|
|
605
652
|
}
|
|
606
653
|
|
|
607
|
-
//#endregion
|
|
608
|
-
//#region packages/typescript/src/validator.ts
|
|
609
|
-
function _define_property$1(obj, key, value) {
|
|
610
|
-
if (key in obj) Object.defineProperty(obj, key, {
|
|
611
|
-
value,
|
|
612
|
-
enumerable: true,
|
|
613
|
-
configurable: true,
|
|
614
|
-
writable: true
|
|
615
|
-
});
|
|
616
|
-
else obj[key] = value;
|
|
617
|
-
return obj;
|
|
618
|
-
}
|
|
619
|
-
const regexCache = new Map();
|
|
620
|
-
var Validator = class {
|
|
621
|
-
isLimitExceeded() {
|
|
622
|
-
if (this.stackErrors.length > 0) {
|
|
623
|
-
const top = this.stackErrors[this.stackErrors.length - 1];
|
|
624
|
-
return top !== null && top.length >= this.opts.errorLimit;
|
|
625
|
-
}
|
|
626
|
-
return this.errors.length >= this.opts.errorLimit;
|
|
627
|
-
}
|
|
628
|
-
push(name) {
|
|
629
|
-
this.stackPath.push(name);
|
|
630
|
-
this.stackErrors.push(null);
|
|
631
|
-
this.cachedPath = this.stackPath.length <= 1 ? "" : this.stackPath[1] + (this.stackPath.length > 2 ? "." + this.stackPath.slice(2).join(".") : "");
|
|
632
|
-
}
|
|
633
|
-
pop(saveErrors) {
|
|
634
|
-
this.stackPath.pop();
|
|
635
|
-
const popped = this.stackErrors.pop();
|
|
636
|
-
if (saveErrors && popped !== null && popped !== undefined && popped.length > 0) for (const err of popped) this.error(err.message, err.path, err.details);
|
|
637
|
-
this.cachedPath = this.stackPath.length <= 1 ? "" : this.stackPath[1] + (this.stackPath.length > 2 ? "." + this.stackPath.slice(2).join(".") : "");
|
|
638
|
-
return popped;
|
|
639
|
-
}
|
|
640
|
-
clear() {
|
|
641
|
-
this.stackErrors[this.stackErrors.length - 1] = null;
|
|
642
|
-
}
|
|
643
|
-
error(message, path$2, details) {
|
|
644
|
-
let errors = this.stackErrors[this.stackErrors.length - 1];
|
|
645
|
-
if (!errors) if (this.stackErrors.length > 0) {
|
|
646
|
-
errors = [];
|
|
647
|
-
this.stackErrors[this.stackErrors.length - 1] = errors;
|
|
648
|
-
} else errors = this.errors;
|
|
649
|
-
const error = {
|
|
650
|
-
path: path$2 || this.cachedPath,
|
|
651
|
-
message
|
|
652
|
-
};
|
|
653
|
-
if (details?.length) error.details = details;
|
|
654
|
-
errors.push(error);
|
|
655
|
-
}
|
|
656
|
-
throw() {
|
|
657
|
-
throw new ValidatorError(this.errors);
|
|
658
|
-
}
|
|
659
|
-
/**
|
|
660
|
-
* Validates a value against the type definition.
|
|
661
|
-
*
|
|
662
|
-
* Acts as a TypeScript type guard — when it returns `true`, the value
|
|
663
|
-
* is narrowed to `DataType`.
|
|
664
|
-
*
|
|
665
|
-
* @param value - The value to validate.
|
|
666
|
-
* @param safe - If `true`, returns `false` on failure instead of throwing.
|
|
667
|
-
* @returns `true` if the value matches the type definition.
|
|
668
|
-
* @throws {ValidatorError} When validation fails and `safe` is not `true`.
|
|
669
|
-
*/ validate(value, safe, context) {
|
|
670
|
-
this.errors = [];
|
|
671
|
-
this.stackErrors = [];
|
|
672
|
-
this.stackPath = [""];
|
|
673
|
-
this.cachedPath = "";
|
|
674
|
-
this.context = context;
|
|
675
|
-
const passed = this.validateSafe(this.def, value);
|
|
676
|
-
this.pop(!passed);
|
|
677
|
-
this.context = undefined;
|
|
678
|
-
if (!passed) {
|
|
679
|
-
if (safe) return false;
|
|
680
|
-
this.throw();
|
|
681
|
-
}
|
|
682
|
-
return true;
|
|
683
|
-
}
|
|
684
|
-
validateSafe(def, value) {
|
|
685
|
-
if (this.isLimitExceeded()) return false;
|
|
686
|
-
if (!isAnnotatedType(def)) throw new Error("Can not validate not-annotated type");
|
|
687
|
-
if (typeof this.opts.replace === "function") def = this.opts.replace(def, this.cachedPath);
|
|
688
|
-
if (def.optional && value === undefined) return true;
|
|
689
|
-
for (const plugin of this.opts.plugins) {
|
|
690
|
-
const result = plugin(this, def, value);
|
|
691
|
-
if (result === false || result === true) return result;
|
|
692
|
-
}
|
|
693
|
-
return this.validateAnnotatedType(def, value);
|
|
694
|
-
}
|
|
695
|
-
get path() {
|
|
696
|
-
return this.cachedPath;
|
|
697
|
-
}
|
|
698
|
-
validateAnnotatedType(def, value) {
|
|
699
|
-
switch (def.type.kind) {
|
|
700
|
-
case "": {
|
|
701
|
-
if (def.type.designType === "phantom") return true;
|
|
702
|
-
return this.validatePrimitive(def, value);
|
|
703
|
-
}
|
|
704
|
-
case "object": return this.validateObject(def, value);
|
|
705
|
-
case "array": return this.validateArray(def, value);
|
|
706
|
-
case "union": return this.validateUnion(def, value);
|
|
707
|
-
case "intersection": return this.validateIntersection(def, value);
|
|
708
|
-
case "tuple": return this.validateTuple(def, value);
|
|
709
|
-
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
validateUnion(def, value) {
|
|
713
|
-
let i = 0;
|
|
714
|
-
const popped = [];
|
|
715
|
-
for (const item of def.type.items) {
|
|
716
|
-
this.push(`[${item.type.kind || item.type.designType}(${i})]`);
|
|
717
|
-
if (this.validateSafe(item, value)) {
|
|
718
|
-
this.pop(false);
|
|
719
|
-
return true;
|
|
720
|
-
}
|
|
721
|
-
const errors = this.pop(false);
|
|
722
|
-
if (errors) popped.push(...errors);
|
|
723
|
-
i++;
|
|
724
|
-
}
|
|
725
|
-
this.clear();
|
|
726
|
-
const expected = def.type.items.map((item, i$1) => `[${item.type.kind || item.type.designType}(${i$1})]`).join(", ");
|
|
727
|
-
this.error(`Value does not match any of the allowed types: ${expected}`, undefined, popped);
|
|
728
|
-
return false;
|
|
729
|
-
}
|
|
730
|
-
validateIntersection(def, value) {
|
|
731
|
-
for (const item of def.type.items) if (!this.validateSafe(item, value)) return false;
|
|
732
|
-
return true;
|
|
733
|
-
}
|
|
734
|
-
validateTuple(def, value) {
|
|
735
|
-
if (!Array.isArray(value) || value.length !== def.type.items.length) {
|
|
736
|
-
this.error(`Expected array of length ${def.type.items.length}`);
|
|
737
|
-
return false;
|
|
738
|
-
}
|
|
739
|
-
let i = 0;
|
|
740
|
-
for (const item of def.type.items) {
|
|
741
|
-
this.push(String(i));
|
|
742
|
-
if (!this.validateSafe(item, value[i])) {
|
|
743
|
-
this.pop(true);
|
|
744
|
-
return false;
|
|
745
|
-
}
|
|
746
|
-
this.pop(false);
|
|
747
|
-
i++;
|
|
748
|
-
}
|
|
749
|
-
return true;
|
|
750
|
-
}
|
|
751
|
-
validateArray(def, value) {
|
|
752
|
-
if (!Array.isArray(value)) {
|
|
753
|
-
this.error("Expected array");
|
|
754
|
-
return false;
|
|
755
|
-
}
|
|
756
|
-
const minLength = def.metadata.get("expect.minLength");
|
|
757
|
-
if (minLength) {
|
|
758
|
-
const length = typeof minLength === "number" ? minLength : minLength.length;
|
|
759
|
-
if (value.length < length) {
|
|
760
|
-
const message = typeof minLength === "object" && minLength.message ? minLength.message : `Expected minimum length of ${length} items, got ${value.length} items`;
|
|
761
|
-
this.error(message);
|
|
762
|
-
return false;
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
const maxLength = def.metadata.get("expect.maxLength");
|
|
766
|
-
if (maxLength) {
|
|
767
|
-
const length = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
768
|
-
if (value.length > length) {
|
|
769
|
-
const message = typeof maxLength === "object" && maxLength.message ? maxLength.message : `Expected maximum length of ${length} items, got ${value.length} items`;
|
|
770
|
-
this.error(message);
|
|
771
|
-
return false;
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
const uniqueItems = def.metadata.get("expect.array.uniqueItems");
|
|
775
|
-
if (uniqueItems) {
|
|
776
|
-
const separator = "▼↩";
|
|
777
|
-
const seen = new Set();
|
|
778
|
-
const keyProps = new Set();
|
|
779
|
-
if (def.type.of.type.kind === "object") {
|
|
780
|
-
for (const [key, val] of def.type.of.type.props.entries()) if (val.metadata.get("expect.array.key")) keyProps.add(key);
|
|
781
|
-
}
|
|
782
|
-
for (let idx = 0; idx < value.length; idx++) {
|
|
783
|
-
const item = value[idx];
|
|
784
|
-
let key;
|
|
785
|
-
if (keyProps.size > 0) {
|
|
786
|
-
key = "";
|
|
787
|
-
for (const prop of keyProps) key += JSON.stringify(item[prop]) + separator;
|
|
788
|
-
} else key = JSON.stringify(item);
|
|
789
|
-
if (seen.has(key)) {
|
|
790
|
-
this.push(String(idx));
|
|
791
|
-
this.error(uniqueItems.message || "Duplicate items are not allowed");
|
|
792
|
-
this.pop(true);
|
|
793
|
-
return false;
|
|
794
|
-
}
|
|
795
|
-
seen.add(key);
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
let i = 0;
|
|
799
|
-
let passed = true;
|
|
800
|
-
for (const item of value) {
|
|
801
|
-
this.push(String(i));
|
|
802
|
-
if (!this.validateSafe(def.type.of, item)) {
|
|
803
|
-
passed = false;
|
|
804
|
-
this.pop(true);
|
|
805
|
-
if (this.isLimitExceeded()) return false;
|
|
806
|
-
} else this.pop(false);
|
|
807
|
-
i++;
|
|
808
|
-
}
|
|
809
|
-
return passed;
|
|
810
|
-
}
|
|
811
|
-
validateObject(def, value) {
|
|
812
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
813
|
-
this.error("Expected object");
|
|
814
|
-
return false;
|
|
815
|
-
}
|
|
816
|
-
let passed = true;
|
|
817
|
-
const valueKeys = new Set(Object.keys(value));
|
|
818
|
-
const typeKeys = new Set();
|
|
819
|
-
let skipList;
|
|
820
|
-
if (this.opts.skipList) {
|
|
821
|
-
const path$2 = this.stackPath.length > 1 ? `${this.cachedPath}.` : "";
|
|
822
|
-
for (const item of this.opts.skipList) if (item.startsWith(path$2)) {
|
|
823
|
-
const key = item.slice(path$2.length);
|
|
824
|
-
if (!skipList) skipList = new Set();
|
|
825
|
-
skipList.add(key);
|
|
826
|
-
valueKeys.delete(key);
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
let partialFunctionMatched = false;
|
|
830
|
-
if (typeof this.opts.partial === "function") partialFunctionMatched = this.opts.partial(def, this.cachedPath);
|
|
831
|
-
for (const [key, item] of def.type.props.entries()) {
|
|
832
|
-
if (skipList && skipList.has(key) || isPhantomType(item)) continue;
|
|
833
|
-
typeKeys.add(key);
|
|
834
|
-
if (value[key] === undefined) {
|
|
835
|
-
if (partialFunctionMatched || this.opts.partial === "deep" || this.opts.partial === true && this.stackPath.length <= 1) continue;
|
|
836
|
-
}
|
|
837
|
-
this.push(key);
|
|
838
|
-
if (this.validateSafe(item, value[key])) this.pop(false);
|
|
839
|
-
else {
|
|
840
|
-
passed = false;
|
|
841
|
-
this.pop(true);
|
|
842
|
-
if (this.isLimitExceeded()) return false;
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
for (const key of valueKeys)
|
|
846
|
-
/** matched patterns for unknown keys */ if (!typeKeys.has(key)) {
|
|
847
|
-
const matched = [];
|
|
848
|
-
for (const { pattern, def: propDef } of def.type.propsPatterns) if (pattern.test(key)) matched.push({
|
|
849
|
-
pattern,
|
|
850
|
-
def: propDef
|
|
851
|
-
});
|
|
852
|
-
if (matched.length > 0) {
|
|
853
|
-
this.push(key);
|
|
854
|
-
let keyPassed = false;
|
|
855
|
-
for (const { def: propDef } of matched) {
|
|
856
|
-
if (this.validateSafe(propDef, value[key])) {
|
|
857
|
-
keyPassed = true;
|
|
858
|
-
break;
|
|
859
|
-
}
|
|
860
|
-
this.clear();
|
|
861
|
-
}
|
|
862
|
-
if (!keyPassed) {
|
|
863
|
-
this.validateSafe(matched[0].def, value[key]);
|
|
864
|
-
this.pop(true);
|
|
865
|
-
passed = false;
|
|
866
|
-
if (this.isLimitExceeded()) return false;
|
|
867
|
-
} else this.pop(false);
|
|
868
|
-
} else if (this.opts.unknownProps !== "ignore") {
|
|
869
|
-
if (this.opts.unknownProps === "error") {
|
|
870
|
-
this.push(key);
|
|
871
|
-
this.error(`Unexpected property`);
|
|
872
|
-
this.pop(true);
|
|
873
|
-
if (this.isLimitExceeded()) return false;
|
|
874
|
-
passed = false;
|
|
875
|
-
} else if (this.opts.unknownProps === "strip") delete value[key];
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
return passed;
|
|
879
|
-
}
|
|
880
|
-
validatePrimitive(def, value) {
|
|
881
|
-
if (def.type.value !== undefined) {
|
|
882
|
-
if (value !== def.type.value) {
|
|
883
|
-
this.error(`Expected ${def.type.value}, got ${value}`);
|
|
884
|
-
return false;
|
|
885
|
-
}
|
|
886
|
-
return true;
|
|
887
|
-
}
|
|
888
|
-
const typeOfValue = Array.isArray(value) ? "array" : typeof value;
|
|
889
|
-
switch (def.type.designType) {
|
|
890
|
-
case "never": {
|
|
891
|
-
this.error(`This type is impossible, must be an internal problem`);
|
|
892
|
-
return false;
|
|
893
|
-
}
|
|
894
|
-
case "any": return true;
|
|
895
|
-
case "string": {
|
|
896
|
-
if (typeOfValue !== def.type.designType) {
|
|
897
|
-
this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
|
|
898
|
-
return false;
|
|
899
|
-
}
|
|
900
|
-
return this.validateString(def, value);
|
|
901
|
-
}
|
|
902
|
-
case "number": {
|
|
903
|
-
if (typeOfValue !== def.type.designType) {
|
|
904
|
-
this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
|
|
905
|
-
return false;
|
|
906
|
-
}
|
|
907
|
-
return this.validateNumber(def, value);
|
|
908
|
-
}
|
|
909
|
-
case "boolean": {
|
|
910
|
-
if (typeOfValue !== def.type.designType) {
|
|
911
|
-
this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
|
|
912
|
-
return false;
|
|
913
|
-
}
|
|
914
|
-
return this.validateBoolean(def, value);
|
|
915
|
-
}
|
|
916
|
-
case "undefined": {
|
|
917
|
-
if (value !== undefined) {
|
|
918
|
-
this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
|
|
919
|
-
return false;
|
|
920
|
-
}
|
|
921
|
-
return true;
|
|
922
|
-
}
|
|
923
|
-
case "null": {
|
|
924
|
-
if (value !== null) {
|
|
925
|
-
this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
|
|
926
|
-
return false;
|
|
927
|
-
}
|
|
928
|
-
return true;
|
|
929
|
-
}
|
|
930
|
-
default: throw new Error(`Unknown type "${def.type.designType}"`);
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
validateString(def, value) {
|
|
934
|
-
const filled = def.metadata.get("meta.required");
|
|
935
|
-
if (filled) {
|
|
936
|
-
if (value.trim().length === 0) {
|
|
937
|
-
const message = typeof filled === "object" && filled.message ? filled.message : `Must not be empty`;
|
|
938
|
-
this.error(message);
|
|
939
|
-
return false;
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
const minLength = def.metadata.get("expect.minLength");
|
|
943
|
-
if (minLength) {
|
|
944
|
-
const length = typeof minLength === "number" ? minLength : minLength.length;
|
|
945
|
-
if (value.length < length) {
|
|
946
|
-
const message = typeof minLength === "object" && minLength.message ? minLength.message : `Expected minimum length of ${length} characters, got ${value.length} characters`;
|
|
947
|
-
this.error(message);
|
|
948
|
-
return false;
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
const maxLength = def.metadata.get("expect.maxLength");
|
|
952
|
-
if (maxLength) {
|
|
953
|
-
const length = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
954
|
-
if (value.length > length) {
|
|
955
|
-
const message = typeof maxLength === "object" && maxLength.message ? maxLength.message : `Expected maximum length of ${length} characters, got ${value.length} characters`;
|
|
956
|
-
this.error(message);
|
|
957
|
-
return false;
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
const patterns = def.metadata.get("expect.pattern");
|
|
961
|
-
for (const { pattern, flags, message } of patterns || []) {
|
|
962
|
-
if (!pattern) continue;
|
|
963
|
-
const cacheKey = `${pattern}//${flags || ""}`;
|
|
964
|
-
let regex = regexCache.get(cacheKey);
|
|
965
|
-
if (!regex) {
|
|
966
|
-
regex = new RegExp(pattern, flags);
|
|
967
|
-
regexCache.set(cacheKey, regex);
|
|
968
|
-
}
|
|
969
|
-
if (!regex.test(value)) {
|
|
970
|
-
this.error(message || `Value is expected to match pattern "${pattern}"`);
|
|
971
|
-
return false;
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
return true;
|
|
975
|
-
}
|
|
976
|
-
validateNumber(def, value) {
|
|
977
|
-
const int = def.metadata.get("expect.int");
|
|
978
|
-
if (int && value % 1 !== 0) {
|
|
979
|
-
const message = typeof int === "object" && int.message ? int.message : `Expected integer, got ${value}`;
|
|
980
|
-
this.error(message);
|
|
981
|
-
return false;
|
|
982
|
-
}
|
|
983
|
-
const min = def.metadata.get("expect.min");
|
|
984
|
-
if (min) {
|
|
985
|
-
const minValue = typeof min === "number" ? min : min.minValue;
|
|
986
|
-
if (value < minValue) {
|
|
987
|
-
const message = typeof min === "object" && min.message ? min.message : `Expected minimum ${minValue}, got ${value}`;
|
|
988
|
-
this.error(message);
|
|
989
|
-
return false;
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
const max = def.metadata.get("expect.max");
|
|
993
|
-
if (max) {
|
|
994
|
-
const maxValue = typeof max === "number" ? max : max.maxValue;
|
|
995
|
-
if (value > maxValue) {
|
|
996
|
-
const message = typeof max === "object" && max.message ? max.message : `Expected maximum ${maxValue}, got ${value}`;
|
|
997
|
-
this.error(message);
|
|
998
|
-
return false;
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
return true;
|
|
1002
|
-
}
|
|
1003
|
-
validateBoolean(def, value) {
|
|
1004
|
-
const filled = def.metadata.get("meta.required");
|
|
1005
|
-
if (filled) {
|
|
1006
|
-
if (value !== true) {
|
|
1007
|
-
const message = typeof filled === "object" && filled.message ? filled.message : `Must be checked`;
|
|
1008
|
-
this.error(message);
|
|
1009
|
-
return false;
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
return true;
|
|
1013
|
-
}
|
|
1014
|
-
constructor(def, opts) {
|
|
1015
|
-
_define_property$1(this, "def", void 0);
|
|
1016
|
-
_define_property$1(this, "opts", void 0);
|
|
1017
|
-
/** Validation errors collected during the last {@link validate} call. */ _define_property$1(this, "errors", void 0);
|
|
1018
|
-
_define_property$1(this, "stackErrors", void 0);
|
|
1019
|
-
_define_property$1(this, "stackPath", void 0);
|
|
1020
|
-
_define_property$1(this, "cachedPath", void 0);
|
|
1021
|
-
_define_property$1(this, "context", void 0);
|
|
1022
|
-
this.def = def;
|
|
1023
|
-
this.errors = [];
|
|
1024
|
-
this.stackErrors = [];
|
|
1025
|
-
this.stackPath = [];
|
|
1026
|
-
this.cachedPath = "";
|
|
1027
|
-
this.opts = {
|
|
1028
|
-
partial: false,
|
|
1029
|
-
unknownProps: "error",
|
|
1030
|
-
errorLimit: 10,
|
|
1031
|
-
...opts,
|
|
1032
|
-
plugins: opts?.plugins || []
|
|
1033
|
-
};
|
|
1034
|
-
}
|
|
1035
|
-
};
|
|
1036
|
-
var ValidatorError = class extends Error {
|
|
1037
|
-
constructor(errors) {
|
|
1038
|
-
super(`${errors[0].path ? errors[0].path + ": " : ""}${errors[0].message}`), _define_property$1(this, "errors", void 0), _define_property$1(this, "name", void 0), this.errors = errors, this.name = "Validation Error";
|
|
1039
|
-
}
|
|
1040
|
-
};
|
|
1041
|
-
|
|
1042
|
-
//#endregion
|
|
1043
|
-
//#region packages/typescript/src/annotated-type.ts
|
|
1044
|
-
const COMPLEX_KINDS = new Set([
|
|
1045
|
-
"union",
|
|
1046
|
-
"intersection",
|
|
1047
|
-
"tuple"
|
|
1048
|
-
]);
|
|
1049
|
-
/** Shared validator method reused by all annotated type nodes. */ function validatorMethod(opts) {
|
|
1050
|
-
return new Validator(this, opts);
|
|
1051
|
-
}
|
|
1052
|
-
function createAnnotatedTypeNode(type, metadata, opts) {
|
|
1053
|
-
return {
|
|
1054
|
-
__is_atscript_annotated_type: true,
|
|
1055
|
-
type,
|
|
1056
|
-
metadata,
|
|
1057
|
-
validator: validatorMethod,
|
|
1058
|
-
id: opts?.id,
|
|
1059
|
-
optional: opts?.optional
|
|
1060
|
-
};
|
|
1061
|
-
}
|
|
1062
|
-
function isAnnotatedType(type) {
|
|
1063
|
-
return type && type.__is_atscript_annotated_type;
|
|
1064
|
-
}
|
|
1065
|
-
function annotate(metadata, key, value, asArray) {
|
|
1066
|
-
if (!metadata) return;
|
|
1067
|
-
if (asArray) if (metadata.has(key)) {
|
|
1068
|
-
const a = metadata.get(key);
|
|
1069
|
-
if (Array.isArray(a)) a.push(value);
|
|
1070
|
-
else metadata.set(key, [a, value]);
|
|
1071
|
-
} else metadata.set(key, [value]);
|
|
1072
|
-
else metadata.set(key, value);
|
|
1073
|
-
}
|
|
1074
|
-
function defineAnnotatedType(_kind, base) {
|
|
1075
|
-
const kind = _kind || "";
|
|
1076
|
-
const type = base?.type || {};
|
|
1077
|
-
type.kind = kind;
|
|
1078
|
-
if (COMPLEX_KINDS.has(kind)) type.items = [];
|
|
1079
|
-
if (kind === "object") {
|
|
1080
|
-
type.props = new Map();
|
|
1081
|
-
type.propsPatterns = [];
|
|
1082
|
-
}
|
|
1083
|
-
type.tags = new Set();
|
|
1084
|
-
const metadata = base?.metadata || new Map();
|
|
1085
|
-
const payload = {
|
|
1086
|
-
__is_atscript_annotated_type: true,
|
|
1087
|
-
metadata,
|
|
1088
|
-
type,
|
|
1089
|
-
validator: validatorMethod
|
|
1090
|
-
};
|
|
1091
|
-
base = base ? Object.assign(base, payload) : payload;
|
|
1092
|
-
const handle = {
|
|
1093
|
-
$type: base,
|
|
1094
|
-
$def: type,
|
|
1095
|
-
$metadata: metadata,
|
|
1096
|
-
tags(...tags) {
|
|
1097
|
-
for (const tag of tags) this.$def.tags.add(tag);
|
|
1098
|
-
return this;
|
|
1099
|
-
},
|
|
1100
|
-
designType(value) {
|
|
1101
|
-
this.$def.designType = value;
|
|
1102
|
-
return this;
|
|
1103
|
-
},
|
|
1104
|
-
value(value) {
|
|
1105
|
-
this.$def.value = value;
|
|
1106
|
-
return this;
|
|
1107
|
-
},
|
|
1108
|
-
of(value) {
|
|
1109
|
-
this.$def.of = value;
|
|
1110
|
-
return this;
|
|
1111
|
-
},
|
|
1112
|
-
item(value) {
|
|
1113
|
-
this.$def.items.push(value);
|
|
1114
|
-
return this;
|
|
1115
|
-
},
|
|
1116
|
-
prop(name, value) {
|
|
1117
|
-
this.$def.props.set(name, value);
|
|
1118
|
-
return this;
|
|
1119
|
-
},
|
|
1120
|
-
propPattern(pattern, def) {
|
|
1121
|
-
this.$def.propsPatterns.push({
|
|
1122
|
-
pattern,
|
|
1123
|
-
def
|
|
1124
|
-
});
|
|
1125
|
-
return this;
|
|
1126
|
-
},
|
|
1127
|
-
optional(value = true) {
|
|
1128
|
-
this.$type.optional = value;
|
|
1129
|
-
return this;
|
|
1130
|
-
},
|
|
1131
|
-
copyMetadata(fromMetadata, ignore) {
|
|
1132
|
-
for (const [key, value] of fromMetadata.entries()) if (!ignore || !ignore.has(key)) this.$metadata.set(key, value);
|
|
1133
|
-
return this;
|
|
1134
|
-
},
|
|
1135
|
-
refTo(type$1, chain) {
|
|
1136
|
-
if (!isAnnotatedType(type$1)) throw new Error(`${type$1} is not annotated type`);
|
|
1137
|
-
let newBase = type$1;
|
|
1138
|
-
const typeName = type$1.name || "Unknown";
|
|
1139
|
-
if (chain) for (let i = 0; i < chain.length; i++) {
|
|
1140
|
-
const c = chain[i];
|
|
1141
|
-
if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
|
|
1142
|
-
else {
|
|
1143
|
-
const keys = chain.slice(0, i + 1).map((k) => `["${k}"]`).join("");
|
|
1144
|
-
throw new Error(`Can't find prop ${typeName}${keys}`);
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1147
|
-
this.$type = createAnnotatedTypeNode(newBase.type, metadata, { id: newBase.id });
|
|
1148
|
-
return this;
|
|
1149
|
-
},
|
|
1150
|
-
annotate(key, value, asArray) {
|
|
1151
|
-
annotate(this.$metadata, key, value, asArray);
|
|
1152
|
-
return this;
|
|
1153
|
-
},
|
|
1154
|
-
id(value) {
|
|
1155
|
-
this.$type.id = value;
|
|
1156
|
-
return this;
|
|
1157
|
-
}
|
|
1158
|
-
};
|
|
1159
|
-
return handle;
|
|
1160
|
-
}
|
|
1161
|
-
function isPhantomType(def) {
|
|
1162
|
-
return def.type.kind === "" && def.type.designType === "phantom";
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
//#endregion
|
|
1166
|
-
//#region packages/typescript/src/traverse.ts
|
|
1167
|
-
function forAnnotatedType(def, handlers) {
|
|
1168
|
-
switch (def.type.kind) {
|
|
1169
|
-
case "": {
|
|
1170
|
-
const typed = def;
|
|
1171
|
-
if (handlers.phantom && typed.type.designType === "phantom") return handlers.phantom(typed);
|
|
1172
|
-
return handlers.final(typed);
|
|
1173
|
-
}
|
|
1174
|
-
case "object": return handlers.object(def);
|
|
1175
|
-
case "array": return handlers.array(def);
|
|
1176
|
-
case "union": return handlers.union(def);
|
|
1177
|
-
case "intersection": return handlers.intersection(def);
|
|
1178
|
-
case "tuple": return handlers.tuple(def);
|
|
1179
|
-
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
1180
|
-
}
|
|
1181
|
-
}
|
|
1182
|
-
|
|
1183
|
-
//#endregion
|
|
1184
|
-
//#region packages/typescript/src/json-schema.ts
|
|
1185
|
-
/**
|
|
1186
|
-
* Detects a discriminator property across union items.
|
|
1187
|
-
*
|
|
1188
|
-
* Scans all items for object-typed members that share a common property
|
|
1189
|
-
* with distinct const/literal values. If exactly one such property exists,
|
|
1190
|
-
* it is returned as the discriminator.
|
|
1191
|
-
*/ function detectDiscriminator(items) {
|
|
1192
|
-
if (items.length < 2) return null;
|
|
1193
|
-
for (const item of items) if (item.type.kind !== "object") return null;
|
|
1194
|
-
const firstObj = items[0].type;
|
|
1195
|
-
const candidates = [];
|
|
1196
|
-
for (const [propName, propType] of firstObj.props.entries()) if (propType.type.kind === "" && propType.type.value !== undefined) candidates.push(propName);
|
|
1197
|
-
let result = null;
|
|
1198
|
-
for (const candidate of candidates) {
|
|
1199
|
-
const values = new Set();
|
|
1200
|
-
const indexMapping = {};
|
|
1201
|
-
let valid = true;
|
|
1202
|
-
for (let i = 0; i < items.length; i++) {
|
|
1203
|
-
const obj = items[i].type;
|
|
1204
|
-
const prop = obj.props.get(candidate);
|
|
1205
|
-
if (!prop || prop.type.kind !== "" || prop.type.value === undefined) {
|
|
1206
|
-
valid = false;
|
|
1207
|
-
break;
|
|
1208
|
-
}
|
|
1209
|
-
const val = prop.type.value;
|
|
1210
|
-
if (values.has(val)) {
|
|
1211
|
-
valid = false;
|
|
1212
|
-
break;
|
|
1213
|
-
}
|
|
1214
|
-
values.add(val);
|
|
1215
|
-
indexMapping[String(val)] = i;
|
|
1216
|
-
}
|
|
1217
|
-
if (valid) {
|
|
1218
|
-
if (result) return null;
|
|
1219
|
-
result = {
|
|
1220
|
-
propertyName: candidate,
|
|
1221
|
-
indexMapping
|
|
1222
|
-
};
|
|
1223
|
-
}
|
|
1224
|
-
}
|
|
1225
|
-
return result;
|
|
1226
|
-
}
|
|
1227
|
-
function buildJsonSchema(type) {
|
|
1228
|
-
const defs = {};
|
|
1229
|
-
let hasDefs = false;
|
|
1230
|
-
const buildObject = (d) => {
|
|
1231
|
-
const properties = {};
|
|
1232
|
-
const required = [];
|
|
1233
|
-
for (const [key, val] of d.type.props.entries()) {
|
|
1234
|
-
if (isPhantomType(val)) continue;
|
|
1235
|
-
properties[key] = build(val);
|
|
1236
|
-
if (!val.optional) required.push(key);
|
|
1237
|
-
}
|
|
1238
|
-
const schema$1 = {
|
|
1239
|
-
type: "object",
|
|
1240
|
-
properties
|
|
1241
|
-
};
|
|
1242
|
-
if (required.length > 0) schema$1.required = required;
|
|
1243
|
-
return schema$1;
|
|
1244
|
-
};
|
|
1245
|
-
const build = (def) => {
|
|
1246
|
-
if (def.id && def.type.kind === "object" && def !== type) {
|
|
1247
|
-
const name = def.id;
|
|
1248
|
-
if (!defs[name]) {
|
|
1249
|
-
hasDefs = true;
|
|
1250
|
-
defs[name] = {};
|
|
1251
|
-
defs[name] = buildObject(def);
|
|
1252
|
-
}
|
|
1253
|
-
return { $ref: `#/$defs/${name}` };
|
|
1254
|
-
}
|
|
1255
|
-
const meta = def.metadata;
|
|
1256
|
-
return forAnnotatedType(def, {
|
|
1257
|
-
phantom() {
|
|
1258
|
-
return {};
|
|
1259
|
-
},
|
|
1260
|
-
object(d) {
|
|
1261
|
-
return buildObject(d);
|
|
1262
|
-
},
|
|
1263
|
-
array(d) {
|
|
1264
|
-
const schema$1 = {
|
|
1265
|
-
type: "array",
|
|
1266
|
-
items: build(d.type.of)
|
|
1267
|
-
};
|
|
1268
|
-
const minLength = meta.get("expect.minLength");
|
|
1269
|
-
if (minLength) schema$1.minItems = typeof minLength === "number" ? minLength : minLength.length;
|
|
1270
|
-
const maxLength = meta.get("expect.maxLength");
|
|
1271
|
-
if (maxLength) schema$1.maxItems = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
1272
|
-
return schema$1;
|
|
1273
|
-
},
|
|
1274
|
-
union(d) {
|
|
1275
|
-
const disc = detectDiscriminator(d.type.items);
|
|
1276
|
-
if (disc) {
|
|
1277
|
-
const oneOf = d.type.items.map(build);
|
|
1278
|
-
const mapping = {};
|
|
1279
|
-
for (const [val, idx] of Object.entries(disc.indexMapping)) {
|
|
1280
|
-
const item = d.type.items[idx];
|
|
1281
|
-
mapping[val] = item.id && defs[item.id] ? `#/$defs/${item.id}` : `#/oneOf/${idx}`;
|
|
1282
|
-
}
|
|
1283
|
-
return {
|
|
1284
|
-
oneOf,
|
|
1285
|
-
discriminator: {
|
|
1286
|
-
propertyName: disc.propertyName,
|
|
1287
|
-
mapping
|
|
1288
|
-
}
|
|
1289
|
-
};
|
|
1290
|
-
}
|
|
1291
|
-
return { anyOf: d.type.items.map(build) };
|
|
1292
|
-
},
|
|
1293
|
-
intersection(d) {
|
|
1294
|
-
return { allOf: d.type.items.map(build) };
|
|
1295
|
-
},
|
|
1296
|
-
tuple(d) {
|
|
1297
|
-
return {
|
|
1298
|
-
type: "array",
|
|
1299
|
-
items: d.type.items.map(build),
|
|
1300
|
-
additionalItems: false
|
|
1301
|
-
};
|
|
1302
|
-
},
|
|
1303
|
-
final(d) {
|
|
1304
|
-
const schema$1 = {};
|
|
1305
|
-
if (d.type.value !== undefined) schema$1.const = d.type.value;
|
|
1306
|
-
if (d.type.designType && d.type.designType !== "any") {
|
|
1307
|
-
schema$1.type = d.type.designType === "undefined" ? "null" : d.type.designType;
|
|
1308
|
-
if (schema$1.type === "number" && meta.get("expect.int")) schema$1.type = "integer";
|
|
1309
|
-
}
|
|
1310
|
-
if (schema$1.type === "string") {
|
|
1311
|
-
if (meta.get("meta.required")) schema$1.minLength = 1;
|
|
1312
|
-
const minLength = meta.get("expect.minLength");
|
|
1313
|
-
if (minLength) schema$1.minLength = typeof minLength === "number" ? minLength : minLength.length;
|
|
1314
|
-
const maxLength = meta.get("expect.maxLength");
|
|
1315
|
-
if (maxLength) schema$1.maxLength = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
1316
|
-
const patterns = meta.get("expect.pattern");
|
|
1317
|
-
if (patterns?.length) if (patterns.length === 1) schema$1.pattern = patterns[0].pattern;
|
|
1318
|
-
else schema$1.allOf = (schema$1.allOf || []).concat(patterns.map((p) => ({ pattern: p.pattern })));
|
|
1319
|
-
}
|
|
1320
|
-
if (schema$1.type === "number" || schema$1.type === "integer") {
|
|
1321
|
-
const min = meta.get("expect.min");
|
|
1322
|
-
if (min) schema$1.minimum = typeof min === "number" ? min : min.minValue;
|
|
1323
|
-
const max = meta.get("expect.max");
|
|
1324
|
-
if (max) schema$1.maximum = typeof max === "number" ? max : max.maxValue;
|
|
1325
|
-
}
|
|
1326
|
-
return schema$1;
|
|
1327
|
-
}
|
|
1328
|
-
});
|
|
1329
|
-
};
|
|
1330
|
-
const schema = build(type);
|
|
1331
|
-
if (hasDefs) return {
|
|
1332
|
-
...schema,
|
|
1333
|
-
$defs: defs
|
|
1334
|
-
};
|
|
1335
|
-
return schema;
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
654
|
//#endregion
|
|
1339
655
|
//#region packages/typescript/src/codegen/js-renderer.ts
|
|
1340
656
|
function _define_property(obj, key, value) {
|
|
@@ -1347,6 +663,19 @@ function _define_property(obj, key, value) {
|
|
|
1347
663
|
else obj[key] = value;
|
|
1348
664
|
return obj;
|
|
1349
665
|
}
|
|
666
|
+
const QUERY_OP_MAP = {
|
|
667
|
+
"=": "$eq",
|
|
668
|
+
"!=": "$ne",
|
|
669
|
+
">": "$gt",
|
|
670
|
+
">=": "$gte",
|
|
671
|
+
"<": "$lt",
|
|
672
|
+
"<=": "$lte",
|
|
673
|
+
"in": "$in",
|
|
674
|
+
"not in": "$nin",
|
|
675
|
+
"matches": "$regex",
|
|
676
|
+
"exists": "$exists",
|
|
677
|
+
"not exists": "$exists"
|
|
678
|
+
};
|
|
1350
679
|
var JsRenderer = class extends BaseRenderer {
|
|
1351
680
|
pre() {
|
|
1352
681
|
this.writeln("// prettier-ignore-start");
|
|
@@ -1467,7 +796,7 @@ else def = def.getDefinition() || def;
|
|
|
1467
796
|
const mode = resolveJsonSchemaMode(this.opts);
|
|
1468
797
|
const hasAnnotation = node.countAnnotations("emit.jsonSchema") > 0;
|
|
1469
798
|
if (hasAnnotation || mode === "bundle") {
|
|
1470
|
-
const schema = JSON.stringify(buildJsonSchema(this.toAnnotatedType(node)));
|
|
799
|
+
const schema = JSON.stringify(require_json_schema.buildJsonSchema(this.toAnnotatedType(node)));
|
|
1471
800
|
this.writeln("static toJsonSchema() {");
|
|
1472
801
|
this.indent().writeln(`return ${schema}`).unindent();
|
|
1473
802
|
this.writeln("}");
|
|
@@ -1492,7 +821,7 @@ else def = def.getDefinition() || def;
|
|
|
1492
821
|
return this.toAnnotatedHandle(node).$type;
|
|
1493
822
|
}
|
|
1494
823
|
toAnnotatedHandle(node, skipAnnotations = false) {
|
|
1495
|
-
if (!node) return defineAnnotatedType();
|
|
824
|
+
if (!node) return require_json_schema.defineAnnotatedType();
|
|
1496
825
|
switch (node.entity) {
|
|
1497
826
|
case "interface":
|
|
1498
827
|
case "type": {
|
|
@@ -1524,14 +853,14 @@ else def = def.getDefinition() || def;
|
|
|
1524
853
|
}
|
|
1525
854
|
case "primitive": {
|
|
1526
855
|
const prim = node;
|
|
1527
|
-
const handle = defineAnnotatedType();
|
|
856
|
+
const handle = require_json_schema.defineAnnotatedType();
|
|
1528
857
|
handle.designType(prim.id === "never" ? "never" : prim.config.type);
|
|
1529
858
|
if (!skipAnnotations) this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
|
|
1530
859
|
return handle;
|
|
1531
860
|
}
|
|
1532
861
|
case "const": {
|
|
1533
862
|
const c = node;
|
|
1534
|
-
const handle = defineAnnotatedType();
|
|
863
|
+
const handle = require_json_schema.defineAnnotatedType();
|
|
1535
864
|
const t = c.token("identifier")?.type;
|
|
1536
865
|
handle.designType(t === "number" ? "number" : "string");
|
|
1537
866
|
handle.value(t === "number" ? Number(c.id) : c.id);
|
|
@@ -1539,7 +868,7 @@ else def = def.getDefinition() || def;
|
|
|
1539
868
|
}
|
|
1540
869
|
case "structure": {
|
|
1541
870
|
const struct = node;
|
|
1542
|
-
const handle = defineAnnotatedType("object");
|
|
871
|
+
const handle = require_json_schema.defineAnnotatedType("object");
|
|
1543
872
|
for (const prop of Array.from(struct.props.values())) {
|
|
1544
873
|
const propHandle = this.toAnnotatedHandle(prop);
|
|
1545
874
|
const pattern = prop.token("identifier")?.pattern;
|
|
@@ -1551,24 +880,24 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1551
880
|
case "group": {
|
|
1552
881
|
const group = node;
|
|
1553
882
|
const kind = group.op === "|" ? "union" : "intersection";
|
|
1554
|
-
const handle = defineAnnotatedType(kind);
|
|
883
|
+
const handle = require_json_schema.defineAnnotatedType(kind);
|
|
1555
884
|
for (const item of group.unwrap()) handle.item(this.toAnnotatedHandle(item).$type);
|
|
1556
885
|
return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
|
|
1557
886
|
}
|
|
1558
887
|
case "tuple": {
|
|
1559
888
|
const group = node;
|
|
1560
|
-
const handle = defineAnnotatedType("tuple");
|
|
889
|
+
const handle = require_json_schema.defineAnnotatedType("tuple");
|
|
1561
890
|
for (const item of group.unwrap()) handle.item(this.toAnnotatedHandle(item).$type);
|
|
1562
891
|
return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
|
|
1563
892
|
}
|
|
1564
893
|
case "array": {
|
|
1565
894
|
const arr = node;
|
|
1566
|
-
const handle = defineAnnotatedType("array");
|
|
895
|
+
const handle = require_json_schema.defineAnnotatedType("array");
|
|
1567
896
|
handle.of(this.toAnnotatedHandle(arr.getDefinition()).$type);
|
|
1568
897
|
return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
|
|
1569
898
|
}
|
|
1570
899
|
default: {
|
|
1571
|
-
const handle = defineAnnotatedType();
|
|
900
|
+
const handle = require_json_schema.defineAnnotatedType();
|
|
1572
901
|
return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
|
|
1573
902
|
}
|
|
1574
903
|
}
|
|
@@ -1629,8 +958,8 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1629
958
|
const ref = node;
|
|
1630
959
|
const decl = this.doc.unwindType(ref.id, ref.chain)?.def;
|
|
1631
960
|
if ((0, __atscript_core.isPrimitive)(decl)) {
|
|
1632
|
-
const ownerDecl = this.doc.getDeclarationOwnerNode(ref.id);
|
|
1633
|
-
if (!ownerDecl?.node || ownerDecl.node.entity !== "type" && ownerDecl.node.entity !== "interface") {
|
|
961
|
+
const ownerDecl$1 = this.doc.getDeclarationOwnerNode(ref.id);
|
|
962
|
+
if (!ownerDecl$1?.node || ownerDecl$1.node.entity !== "type" && ownerDecl$1.node.entity !== "interface") {
|
|
1634
963
|
this.annotateType(decl, name);
|
|
1635
964
|
return this;
|
|
1636
965
|
}
|
|
@@ -1644,9 +973,11 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1644
973
|
}
|
|
1645
974
|
}
|
|
1646
975
|
const chain = ref.hasChain ? `, [${ref.chain.map((c) => `"${escapeQuotes(c.text)}"`).join(", ")}]` : "";
|
|
1647
|
-
|
|
976
|
+
const ownerDecl = this.doc.getDeclarationOwnerNode(ref.id);
|
|
977
|
+
const isImported = ownerDecl ? ownerDecl.doc !== this.doc : false;
|
|
978
|
+
const refExpr = isImported ? `() => ${ref.id}` : ref.id;
|
|
979
|
+
this.writeln(`$(${name ? `"", ${name}` : ""})`).indent().writeln(`.refTo(${refExpr}${chain})`);
|
|
1648
980
|
if (!ref.hasChain) {
|
|
1649
|
-
const ownerDecl = this.doc.getDeclarationOwnerNode(ref.id);
|
|
1650
981
|
if (ownerDecl?.node) {
|
|
1651
982
|
const typeAnnotations = ownerDecl.doc.evalAnnotationsForNode(ownerDecl.node);
|
|
1652
983
|
typeAnnotations?.forEach((an) => {
|
|
@@ -1840,6 +1171,68 @@ else if (resolved && (0, __atscript_core.isPrimitive)(resolved)) {
|
|
|
1840
1171
|
if (multiple) this.writeln(`.annotate("${escapeQuotes(an.name)}", ${value}, true)`);
|
|
1841
1172
|
else this.writeln(`.annotate("${escapeQuotes(an.name)}", ${value})`);
|
|
1842
1173
|
}
|
|
1174
|
+
emitRefValue(text) {
|
|
1175
|
+
const dotIdx = text.indexOf(".");
|
|
1176
|
+
if (dotIdx === -1) return `() => ${text}`;
|
|
1177
|
+
const typeName = text.slice(0, dotIdx);
|
|
1178
|
+
const field = text.slice(dotIdx + 1);
|
|
1179
|
+
return `{ type: () => ${typeName}, field: "${escapeQuotes(field)}" }`;
|
|
1180
|
+
}
|
|
1181
|
+
emitArgValue(aSpec, argToken) {
|
|
1182
|
+
if (aSpec.type === "ref") return this.emitRefValue(argToken.text);
|
|
1183
|
+
if (aSpec.type === "query" && argToken.queryNode) return this.emitQueryTree(argToken.queryNode);
|
|
1184
|
+
return aSpec.type === "string" ? `"${escapeQuotes(argToken.text)}"` : argToken.text;
|
|
1185
|
+
}
|
|
1186
|
+
emitQueryTree(queryNode) {
|
|
1187
|
+
return this.emitQueryExpr(queryNode.expression);
|
|
1188
|
+
}
|
|
1189
|
+
emitQueryExpr(node) {
|
|
1190
|
+
if ((0, __atscript_core.isQueryLogical)(node)) return this.emitQueryLogical(node);
|
|
1191
|
+
return this.emitQueryComparison(node);
|
|
1192
|
+
}
|
|
1193
|
+
emitQueryLogical(node) {
|
|
1194
|
+
if (node.operator === "not") return `{ "$not": ${this.emitQueryExpr(node.operands[0])} }`;
|
|
1195
|
+
const key = node.operator === "and" ? "$and" : "$or";
|
|
1196
|
+
const items = node.operands.map((op) => this.emitQueryExpr(op)).join(", ");
|
|
1197
|
+
return `{ "${key}": [${items}] }`;
|
|
1198
|
+
}
|
|
1199
|
+
emitQueryComparison(node) {
|
|
1200
|
+
const left = this.emitQueryFieldRef(node.left);
|
|
1201
|
+
const mappedOp = QUERY_OP_MAP[node.operator] || node.operator;
|
|
1202
|
+
const parts = [`left: ${left}`, `op: "${mappedOp}"`];
|
|
1203
|
+
if (node.right) {
|
|
1204
|
+
if ("fieldRef" in node.right && node.right.fieldRef) parts.push(`right: ${this.emitQueryFieldRef(node.right)}`);
|
|
1205
|
+
else if ("values" in node.right && node.right.values) {
|
|
1206
|
+
const values = node.right.values.map((v) => this.emitQueryLiteral(v)).join(", ");
|
|
1207
|
+
parts.push(`right: [${values}]`);
|
|
1208
|
+
} else if ("valueToken" in node.right) parts.push(`right: ${this.emitQueryLiteral(node.right)}`);
|
|
1209
|
+
} else if (node.operator === "exists") parts.push("right: true");
|
|
1210
|
+
else if (node.operator === "not exists") parts.push("right: false");
|
|
1211
|
+
return `{ ${parts.join(", ")} }`;
|
|
1212
|
+
}
|
|
1213
|
+
emitQueryFieldRef(node) {
|
|
1214
|
+
const parts = [];
|
|
1215
|
+
if (node.typeRef) parts.push(`type: () => ${node.typeRef.text}`);
|
|
1216
|
+
parts.push(`field: "${escapeQuotes(node.fieldRef.text)}"`);
|
|
1217
|
+
return `{ ${parts.join(", ")} }`;
|
|
1218
|
+
}
|
|
1219
|
+
emitQueryLiteral(node) {
|
|
1220
|
+
const token = node.valueToken;
|
|
1221
|
+
switch (token.type) {
|
|
1222
|
+
case "text": return `"${escapeQuotes(token.text)}"`;
|
|
1223
|
+
case "number": return token.text;
|
|
1224
|
+
case "regexp": {
|
|
1225
|
+
const match = /^\/(.*)\/[a-z]*$/.exec(token.text);
|
|
1226
|
+
return `"${escapeQuotes(match ? match[1] : token.text)}"`;
|
|
1227
|
+
}
|
|
1228
|
+
case "identifier": {
|
|
1229
|
+
if (token.text === "true" || token.text === "false") return token.text;
|
|
1230
|
+
if (token.text === "null" || token.text === "undefined") return "null";
|
|
1231
|
+
return token.text;
|
|
1232
|
+
}
|
|
1233
|
+
default: return token.text;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1843
1236
|
computeAnnotationValue(node, an) {
|
|
1844
1237
|
const spec = this.doc.resolveAnnotation(an.name);
|
|
1845
1238
|
let targetValue = "true";
|
|
@@ -1851,13 +1244,13 @@ else this.writeln(`.annotate("${escapeQuotes(an.name)}", ${value})`);
|
|
|
1851
1244
|
targetValue = "{ ";
|
|
1852
1245
|
let i = 0;
|
|
1853
1246
|
for (const aSpec of spec.arguments) {
|
|
1854
|
-
if (an.args[i]) targetValue += `${wrapProp(aSpec.name)}: ${aSpec
|
|
1247
|
+
if (an.args[i]) targetValue += `${wrapProp(aSpec.name)}: ${this.emitArgValue(aSpec, an.args[i])}${i === length - 1 ? "" : ", "} `;
|
|
1855
1248
|
i++;
|
|
1856
1249
|
}
|
|
1857
1250
|
targetValue += "}";
|
|
1858
1251
|
} else {
|
|
1859
1252
|
const aSpec = spec.arguments[0];
|
|
1860
|
-
if (an.args[0]) targetValue = aSpec
|
|
1253
|
+
if (an.args[0]) targetValue = this.emitArgValue(aSpec, an.args[0]);
|
|
1861
1254
|
else targetValue = "true";
|
|
1862
1255
|
}
|
|
1863
1256
|
} else {
|
|
@@ -2028,8 +1421,8 @@ const tsPlugin = (opts) => {
|
|
|
2028
1421
|
for (const [key, val] of Object.entries(annotations)) {
|
|
2029
1422
|
const multiple = val.multiple;
|
|
2030
1423
|
const typeLine = Array.from(val.types).map((t) => {
|
|
2031
|
-
if (t.type === "object") return `{ ${Object.entries(t.props).map(([k, v]) => `${wrapProp(k)}${v.optional ? "?" : ""}: ${v.type}`).join(", ")} }`;
|
|
2032
|
-
else return t.optional ? `${t.type} | true` : t.type;
|
|
1424
|
+
if (t.type === "object" && "props" in t) return `{ ${Object.entries(t.props).map(([k, v]) => `${wrapProp(k)}${v.optional ? "?" : ""}: ${v.type}`).join(", ")} }`;
|
|
1425
|
+
else return "optional" in t && t.optional ? `${t.type} | true` : t.type;
|
|
2033
1426
|
}).join(" | ");
|
|
2034
1427
|
rendered.push(`${wrapProp(key)}: ${multiple ? "(" : ""}${typeLine}${multiple ? ")[]" : ""}`);
|
|
2035
1428
|
}
|