@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/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$4(obj, key, value) {
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$4(this, "lines", []);
124
- _define_property$4(this, "currentLine", "");
125
- _define_property$4(this, "indentLevel", 0);
126
- _define_property$4(this, "indentSize", 2);
127
- _define_property$4(this, "blockStack", []);
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$3(obj, key, value) {
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$3(this, "doc", void 0), _define_property$3(this, "_unused", void 0), this.doc = doc;
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$2(obj, key, value) {
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.hasDbTable(interfaceNode)) {
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 has the `@db.table` annotation.
437
+ * Checks whether an interface is a DB entity (`@db.table` or `@db.view`).
435
438
  *
436
- * NOTE: Only `@db.table` interfaces get the `__flat` static property.
437
- * This is intentionally hardcoded `__flat` exists solely to improve
438
- * type-safety for filter expressions and `$select`/`$sort` operations
439
- * in the DB layer. Non-DB interfaces have no use for dot-notation path types.
440
- */ hasDbTable(node) {
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
- const flatMap = (0, __atscript_core.flattenInterfaceNode)(this.doc, node);
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.write("static __flat: ");
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.pop();
495
+ if (opts?.trailingNewline) this.popln();
496
+ else this.pop();
476
497
  }
477
498
  /**
478
- * Renders the `static __pk` property — the primary key type for type-safe
479
- * `deleteOne`/`findById` signatures on `AtscriptDbTable`.
499
+ * Renders the `static __navProps` property — a map of navigation property names
500
+ * to their declared TypeScript types.
480
501
  *
481
- * - **Single PK** (one `@meta.id`) `static __pk: <scalar type>`
482
- * - **Compound PK** (multiple `@meta.id`) `static __pk: { field1: Type1; field2: Type2 }`
483
- * - **No PK** → no `__pk` emitted (unless unique indexes exist)
484
- * - **Unique indexes** (`@db.index.unique`) → appended as union members
485
- * - **Mongo collection** → always includes `string` (ObjectId) in the union;
486
- * if no `@meta.id` fields, `__pk` is just `string`
487
- */ renderPk(node) {
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$2(this, "opts", void 0), this.opts = opts;
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
- this.writeln(`$(${name ? `"", ${name}` : ""})`).indent().writeln(`.refTo(${ref.id}${chain})`);
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.type === "string" ? `"${escapeQuotes(an.args[i]?.text)}"` : an.args[i]?.text}${i === length - 1 ? "" : ", "} `;
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.type === "string" ? `"${escapeQuotes(an.args[0]?.text)}"` : an.args[0]?.text;
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
  }