@atscript/typescript 0.0.16 → 0.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -204,7 +204,7 @@ function wrapProp(name) {
204
204
  return name;
205
205
  }
206
206
  function escapeQuotes(str) {
207
- return str.replace(/"/g, "\\\"");
207
+ return str.replace(/"/g, "\\\"").replace(/\\/g, "\\\\");
208
208
  }
209
209
 
210
210
  //#endregion
@@ -219,7 +219,7 @@ var TypeRenderer = class extends BaseRenderer {
219
219
  this.writeln(" * Do not edit this file!");
220
220
  this.writeln(" */");
221
221
  this.writeln();
222
- this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TMetadataMap, Validator } from \"@atscript/typescript\"");
222
+ this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TMetadataMap, Validator, TAtscriptAnnotatedTypeConstructor, TValidatorOptions } from \"@atscript/typescript\"");
223
223
  }
224
224
  post() {
225
225
  this.writeln("// prettier-ignore-end");
@@ -271,17 +271,41 @@ var TypeRenderer = class extends BaseRenderer {
271
271
  }
272
272
  renderStructure(struct, asClass) {
273
273
  this.blockln("{}");
274
+ const patterns = [];
275
+ let hasProp = false;
274
276
  for (const prop of Array.from(struct.props.values())) {
277
+ if (prop.token("identifier")?.pattern) {
278
+ patterns.push(prop);
279
+ continue;
280
+ }
281
+ hasProp = true;
275
282
  const optional = !!prop.token("optional");
276
283
  this.write(wrapProp(prop.id), optional ? "?" : "", ": ");
277
284
  this.renderTypeDef(prop.getDefinition());
278
285
  this.writeln();
279
286
  }
287
+ if (patterns.length) {
288
+ this.write(`[key: string]: `);
289
+ if (hasProp) this.writeln("any");
290
+ else if (patterns.length === 1) {
291
+ this.renderTypeDef(patterns[0].getDefinition());
292
+ this.writeln();
293
+ } else {
294
+ this.indent();
295
+ for (const prop of patterns) {
296
+ this.writeln();
297
+ this.write("| ");
298
+ this.renderTypeDef(prop.getDefinition());
299
+ }
300
+ this.unindent();
301
+ this.writeln();
302
+ }
303
+ }
280
304
  if (asClass) {
281
- this.writeln("static __is_anscript_annotated_type: true");
305
+ this.writeln("static __is_atscript_annotated_type: true");
282
306
  this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}>`);
283
307
  this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
284
- this.writeln(`static validator: () => Validator<${asClass}>`);
308
+ this.writeln(`static validator: <TT extends TAtscriptAnnotatedTypeConstructor = ${asClass}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
285
309
  }
286
310
  this.pop();
287
311
  }
@@ -320,10 +344,10 @@ else if ((0, __atscript_core.isGroup)(realDef)) typeDef = "TAtscriptTypeComplex"
320
344
  else if ((0, __atscript_core.isArray)(realDef)) typeDef = "TAtscriptTypeArray";
321
345
  else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef = "TAtscriptTypeFinal";
322
346
  }
323
- this.writeln(`const __is_anscript_annotated_type: true`);
347
+ this.writeln(`const __is_atscript_annotated_type: true`);
324
348
  this.writeln(`const type: ${typeDef}`);
325
349
  this.writeln(`const metadata: TMetadataMap<AtscriptMetadata>`);
326
- this.writeln(`const validator: () => Validator<${node.id}>`);
350
+ this.writeln(`const validator: <TT extends TAtscriptAnnotatedTypeConstructor = ${node.id}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
327
351
  this.popln();
328
352
  }
329
353
  renderJsDoc(node) {
@@ -382,7 +406,7 @@ var JsRenderer = class extends BaseRenderer {
382
406
  this.write(exported ? "export " : "");
383
407
  this.write(`class ${node.id} `);
384
408
  this.blockln("{}");
385
- this.writeln("static __is_anscript_annotated_type = true");
409
+ this.writeln("static __is_atscript_annotated_type = true");
386
410
  this.writeln("static type = {}");
387
411
  this.writeln("static metadata = new Map()");
388
412
  this.popln();
@@ -395,7 +419,7 @@ var JsRenderer = class extends BaseRenderer {
395
419
  this.write(exported ? "export " : "");
396
420
  this.write(`class ${node.id} `);
397
421
  this.blockln("{}");
398
- this.writeln("static __is_anscript_annotated_type = true");
422
+ this.writeln("static __is_atscript_annotated_type = true");
399
423
  this.writeln("static type = {}");
400
424
  this.writeln("static metadata = new Map()");
401
425
  this.popln();
@@ -508,6 +532,17 @@ var JsRenderer = class extends BaseRenderer {
508
532
  this.unindent();
509
533
  this.write(`)`);
510
534
  }
535
+ for (const [key, propDef] of Object.entries(def.propsPatterns)) {
536
+ const optional = typeof propDef === "object" && propDef.optional;
537
+ this.writeln(`.propPattern(`);
538
+ this.indent();
539
+ this.writeln(`${key},`);
540
+ this.renderPrimitiveDef(propDef);
541
+ if (optional) this.writeln(".optional()");
542
+ this.writeln(".$type");
543
+ this.unindent();
544
+ this.write(`)`);
545
+ }
511
546
  this.unindent();
512
547
  return;
513
548
  default: return this.writeln(`$(${d()}).designType("any")`);
@@ -516,10 +551,17 @@ var JsRenderer = class extends BaseRenderer {
516
551
  defineObject(node) {
517
552
  const props = Array.from(node.props.values());
518
553
  for (const prop of props) {
554
+ const pattern = prop.token("identifier")?.pattern;
519
555
  const optional = !!prop.token("optional");
520
- this.writeln(`.prop(`);
521
- this.indent();
522
- this.writeln(`"${escapeQuotes(prop.id)}",`);
556
+ if (pattern) {
557
+ this.writeln(`.propPattern(`);
558
+ this.indent();
559
+ this.writeln(`/${pattern.source}/${pattern.flags},`);
560
+ } else {
561
+ this.writeln(`.prop(`);
562
+ this.indent();
563
+ this.writeln(`"${escapeQuotes(prop.id)}",`);
564
+ }
523
565
  this.annotateType(prop.getDefinition());
524
566
  this.indent().defineMetadata(prop).unindent();
525
567
  if (optional) this.writeln(" .optional()");
package/dist/index.cjs CHANGED
@@ -202,7 +202,7 @@ function wrapProp(name) {
202
202
  return name;
203
203
  }
204
204
  function escapeQuotes(str) {
205
- return str.replace(/"/g, "\\\"");
205
+ return str.replace(/"/g, "\\\"").replace(/\\/g, "\\\\");
206
206
  }
207
207
 
208
208
  //#endregion
@@ -217,7 +217,7 @@ var TypeRenderer = class extends BaseRenderer {
217
217
  this.writeln(" * Do not edit this file!");
218
218
  this.writeln(" */");
219
219
  this.writeln();
220
- this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TMetadataMap, Validator } from \"@atscript/typescript\"");
220
+ this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TMetadataMap, Validator, TAtscriptAnnotatedTypeConstructor, TValidatorOptions } from \"@atscript/typescript\"");
221
221
  }
222
222
  post() {
223
223
  this.writeln("// prettier-ignore-end");
@@ -269,17 +269,41 @@ var TypeRenderer = class extends BaseRenderer {
269
269
  }
270
270
  renderStructure(struct, asClass) {
271
271
  this.blockln("{}");
272
+ const patterns = [];
273
+ let hasProp = false;
272
274
  for (const prop of Array.from(struct.props.values())) {
275
+ if (prop.token("identifier")?.pattern) {
276
+ patterns.push(prop);
277
+ continue;
278
+ }
279
+ hasProp = true;
273
280
  const optional = !!prop.token("optional");
274
281
  this.write(wrapProp(prop.id), optional ? "?" : "", ": ");
275
282
  this.renderTypeDef(prop.getDefinition());
276
283
  this.writeln();
277
284
  }
285
+ if (patterns.length) {
286
+ this.write(`[key: string]: `);
287
+ if (hasProp) this.writeln("any");
288
+ else if (patterns.length === 1) {
289
+ this.renderTypeDef(patterns[0].getDefinition());
290
+ this.writeln();
291
+ } else {
292
+ this.indent();
293
+ for (const prop of patterns) {
294
+ this.writeln();
295
+ this.write("| ");
296
+ this.renderTypeDef(prop.getDefinition());
297
+ }
298
+ this.unindent();
299
+ this.writeln();
300
+ }
301
+ }
278
302
  if (asClass) {
279
- this.writeln("static __is_anscript_annotated_type: true");
303
+ this.writeln("static __is_atscript_annotated_type: true");
280
304
  this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}>`);
281
305
  this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
282
- this.writeln(`static validator: () => Validator<${asClass}>`);
306
+ this.writeln(`static validator: <TT extends TAtscriptAnnotatedTypeConstructor = ${asClass}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
283
307
  }
284
308
  this.pop();
285
309
  }
@@ -318,10 +342,10 @@ else if ((0, __atscript_core.isGroup)(realDef)) typeDef = "TAtscriptTypeComplex"
318
342
  else if ((0, __atscript_core.isArray)(realDef)) typeDef = "TAtscriptTypeArray";
319
343
  else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef = "TAtscriptTypeFinal";
320
344
  }
321
- this.writeln(`const __is_anscript_annotated_type: true`);
345
+ this.writeln(`const __is_atscript_annotated_type: true`);
322
346
  this.writeln(`const type: ${typeDef}`);
323
347
  this.writeln(`const metadata: TMetadataMap<AtscriptMetadata>`);
324
- this.writeln(`const validator: () => Validator<${node.id}>`);
348
+ this.writeln(`const validator: <TT extends TAtscriptAnnotatedTypeConstructor = ${node.id}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
325
349
  this.popln();
326
350
  }
327
351
  renderJsDoc(node) {
@@ -380,7 +404,7 @@ var JsRenderer = class extends BaseRenderer {
380
404
  this.write(exported ? "export " : "");
381
405
  this.write(`class ${node.id} `);
382
406
  this.blockln("{}");
383
- this.writeln("static __is_anscript_annotated_type = true");
407
+ this.writeln("static __is_atscript_annotated_type = true");
384
408
  this.writeln("static type = {}");
385
409
  this.writeln("static metadata = new Map()");
386
410
  this.popln();
@@ -393,7 +417,7 @@ var JsRenderer = class extends BaseRenderer {
393
417
  this.write(exported ? "export " : "");
394
418
  this.write(`class ${node.id} `);
395
419
  this.blockln("{}");
396
- this.writeln("static __is_anscript_annotated_type = true");
420
+ this.writeln("static __is_atscript_annotated_type = true");
397
421
  this.writeln("static type = {}");
398
422
  this.writeln("static metadata = new Map()");
399
423
  this.popln();
@@ -506,6 +530,17 @@ var JsRenderer = class extends BaseRenderer {
506
530
  this.unindent();
507
531
  this.write(`)`);
508
532
  }
533
+ for (const [key, propDef] of Object.entries(def.propsPatterns)) {
534
+ const optional = typeof propDef === "object" && propDef.optional;
535
+ this.writeln(`.propPattern(`);
536
+ this.indent();
537
+ this.writeln(`${key},`);
538
+ this.renderPrimitiveDef(propDef);
539
+ if (optional) this.writeln(".optional()");
540
+ this.writeln(".$type");
541
+ this.unindent();
542
+ this.write(`)`);
543
+ }
509
544
  this.unindent();
510
545
  return;
511
546
  default: return this.writeln(`$(${d()}).designType("any")`);
@@ -514,10 +549,17 @@ var JsRenderer = class extends BaseRenderer {
514
549
  defineObject(node) {
515
550
  const props = Array.from(node.props.values());
516
551
  for (const prop of props) {
552
+ const pattern = prop.token("identifier")?.pattern;
517
553
  const optional = !!prop.token("optional");
518
- this.writeln(`.prop(`);
519
- this.indent();
520
- this.writeln(`"${escapeQuotes(prop.id)}",`);
554
+ if (pattern) {
555
+ this.writeln(`.propPattern(`);
556
+ this.indent();
557
+ this.writeln(`/${pattern.source}/${pattern.flags},`);
558
+ } else {
559
+ this.writeln(`.prop(`);
560
+ this.indent();
561
+ this.writeln(`"${escapeQuotes(prop.id)}",`);
562
+ }
521
563
  this.annotateType(prop.getDefinition());
522
564
  this.indent().defineMetadata(prop).unindent();
523
565
  if (optional) this.writeln(" .optional()");
@@ -653,7 +695,7 @@ var Validator = class {
653
695
  error(message, path$2, details) {
654
696
  const errors = this.stackErrors[this.stackErrors.length - 1] || this.errors;
655
697
  const error = {
656
- path: path$2 || this.stackPath.join(".").slice(1),
698
+ path: path$2 || this.path,
657
699
  message
658
700
  };
659
701
  if (details?.length) error.details = details;
@@ -666,7 +708,7 @@ var Validator = class {
666
708
  this.push("");
667
709
  this.errors = [];
668
710
  this.stackErrors = [];
669
- const passed = this._validate(this.def, value);
711
+ const passed = this.validateSafe(this.def, value);
670
712
  this.pop(!passed);
671
713
  if (!passed) {
672
714
  if (safe) return false;
@@ -674,10 +716,21 @@ var Validator = class {
674
716
  }
675
717
  return true;
676
718
  }
677
- _validate(def, value) {
719
+ validateSafe(def, value) {
678
720
  if (this.isLimitExceeded()) return false;
679
721
  if (!isAnnotatedType(def)) throw new Error("Can not validate not-annotated type");
722
+ if (typeof this.opts.replace === "function") def = this.opts.replace(def, this.path);
680
723
  if (def.optional && value === undefined) return true;
724
+ for (const plugin of this.opts.plugins) {
725
+ const result = plugin(this, def, value);
726
+ if (result === false || result === true) return result;
727
+ }
728
+ return this.validateAnnotatedType(def, value);
729
+ }
730
+ get path() {
731
+ return this.stackPath.slice(1).join(".");
732
+ }
733
+ validateAnnotatedType(def, value) {
681
734
  switch (def.type.kind) {
682
735
  case "object": return this.validateObject(def, value);
683
736
  case "union": return this.validateUnion(def, value);
@@ -693,7 +746,7 @@ var Validator = class {
693
746
  const popped = [];
694
747
  for (const item of def.type.items) {
695
748
  this.push(`[${item.type.kind || item.type.designType}(${i})]`);
696
- if (this._validate(item, value)) {
749
+ if (this.validateSafe(item, value)) {
697
750
  this.pop(false);
698
751
  return true;
699
752
  }
@@ -707,7 +760,7 @@ var Validator = class {
707
760
  return false;
708
761
  }
709
762
  validateIntersection(def, value) {
710
- for (const item of def.type.items) if (!this._validate(item, value)) return false;
763
+ for (const item of def.type.items) if (!this.validateSafe(item, value)) return false;
711
764
  return true;
712
765
  }
713
766
  validateTuple(def, value) {
@@ -718,7 +771,7 @@ var Validator = class {
718
771
  let i = 0;
719
772
  for (const item of def.type.items) {
720
773
  this.push(`[${i}]`);
721
- if (!this._validate(item, value[i])) {
774
+ if (!this.validateSafe(item, value[i])) {
722
775
  this.pop(true);
723
776
  return false;
724
777
  }
@@ -746,7 +799,7 @@ var Validator = class {
746
799
  let passed = true;
747
800
  for (const item of value) {
748
801
  this.push(`[${i}]`);
749
- if (!this._validate(def.type.of, item)) {
802
+ if (!this.validateSafe(def.type.of, item)) {
750
803
  passed = false;
751
804
  this.pop(true);
752
805
  if (this.isLimitExceeded()) return false;
@@ -763,21 +816,55 @@ var Validator = class {
763
816
  let passed = true;
764
817
  const valueKeys = new Set(Object.keys(value));
765
818
  const typeKeys = new Set();
819
+ const skipList = new Set();
820
+ if (this.opts.skipList) {
821
+ const path$2 = this.stackPath.length > 1 ? this.path + "." : "";
822
+ this.opts.skipList.forEach((item) => {
823
+ if (item.startsWith(path$2)) {
824
+ const key = item.slice(path$2.length);
825
+ skipList.add(key);
826
+ valueKeys.delete(key);
827
+ }
828
+ });
829
+ }
830
+ let partialFunctionMatched = false;
831
+ if (typeof this.opts.partial === "function") partialFunctionMatched = this.opts.partial(def, this.path);
766
832
  for (const [key, item] of def.type.props.entries()) {
833
+ if (skipList.has(key)) continue;
767
834
  typeKeys.add(key);
768
835
  if (value[key] === undefined) {
769
- if (this.opts.partial === "deep" || this.opts.partial === true && this.stackPath.length <= 1) continue;
836
+ if (partialFunctionMatched || this.opts.partial === "deep" || this.opts.partial === true && this.stackPath.length <= 1) continue;
770
837
  }
771
838
  this.push(key);
772
- if (this._validate(item, value[key])) this.pop(false);
839
+ if (this.validateSafe(item, value[key])) this.pop(false);
773
840
  else {
774
841
  passed = false;
775
842
  this.pop(true);
776
843
  if (this.isLimitExceeded()) return false;
777
844
  }
778
845
  }
779
- for (const key of valueKeys) if (this.opts.unknwonProps !== "ignore") {
780
- if (!typeKeys.has(key)) {
846
+ for (const key of valueKeys)
847
+ /** matched patterns for unknown keys */ if (!typeKeys.has(key)) {
848
+ const matched = [];
849
+ for (const { pattern, def: propDef } of def.type.propsPatterns) if (pattern.test(key)) matched.push({
850
+ pattern,
851
+ def: propDef
852
+ });
853
+ if (matched.length) {
854
+ let keyPassed = false;
855
+ for (const { def: def$1 } of matched) if (this.validateSafe(def$1, value[key])) {
856
+ this.pop(false);
857
+ keyPassed = true;
858
+ break;
859
+ }
860
+ if (!keyPassed) {
861
+ this.push(key);
862
+ this.validateSafe(matched[0].def, value[key]);
863
+ this.pop(true);
864
+ passed = false;
865
+ if (this.isLimitExceeded()) return false;
866
+ }
867
+ } else if (this.opts.unknwonProps !== "ignore") {
781
868
  if (this.opts.unknwonProps === "error") {
782
869
  this.push(key);
783
870
  this.error(`Unexpected property`);
@@ -895,7 +982,8 @@ else {
895
982
  partial: false,
896
983
  unknwonProps: "error",
897
984
  errorLimit: 10,
898
- ...opts
985
+ ...opts,
986
+ plugins: opts?.plugins || []
899
987
  };
900
988
  }
901
989
  };
@@ -908,7 +996,7 @@ var ValidatorError = class extends Error {
908
996
  //#endregion
909
997
  //#region packages/typescript/src/annotated-type.ts
910
998
  function isAnnotatedType(type) {
911
- return type && type.__is_anscript_annotated_type;
999
+ return type && type.__is_atscript_annotated_type;
912
1000
  }
913
1001
  function defineAnnotatedType(_kind, base) {
914
1002
  const kind = _kind || "";
@@ -919,11 +1007,14 @@ function defineAnnotatedType(_kind, base) {
919
1007
  "intersection",
920
1008
  "tuple"
921
1009
  ].includes(kind)) type.items = [];
922
- if (kind === "object") type.props = new Map();
1010
+ if (kind === "object") {
1011
+ type.props = new Map();
1012
+ type.propsPatterns = [];
1013
+ }
923
1014
  type.tags = new Set();
924
1015
  const metadata = base?.metadata || new Map();
925
1016
  if (base) Object.assign(base, {
926
- __is_anscript_annotated_type: true,
1017
+ __is_atscript_annotated_type: true,
927
1018
  metadata,
928
1019
  type,
929
1020
  validator(opts) {
@@ -931,7 +1022,7 @@ function defineAnnotatedType(_kind, base) {
931
1022
  }
932
1023
  });
933
1024
  else base = {
934
- __is_anscript_annotated_type: true,
1025
+ __is_atscript_annotated_type: true,
935
1026
  metadata,
936
1027
  type,
937
1028
  validator(opts) {
@@ -967,8 +1058,19 @@ else base = {
967
1058
  this.$def.props.set(name, value);
968
1059
  return this;
969
1060
  },
970
- optional() {
971
- this.$type.optional = true;
1061
+ propPattern(pattern, def) {
1062
+ this.$def.propsPatterns.push({
1063
+ pattern,
1064
+ def
1065
+ });
1066
+ return this;
1067
+ },
1068
+ optional(value = true) {
1069
+ this.$type.optional = value;
1070
+ return this;
1071
+ },
1072
+ copyMetadata(fromMetadata, ignore) {
1073
+ for (const [key, value] of fromMetadata.entries()) if (!ignore || !ignore.has(key)) this.$metadata.set(key, value);
972
1074
  return this;
973
1075
  },
974
1076
  refTo(type$1, chain) {
@@ -978,13 +1080,13 @@ else base = {
978
1080
  let keys = "";
979
1081
  for (const c of chain || []) {
980
1082
  keys += `["${c}"]`;
981
- if (newBase.type.kind === "object") newBase = newBase.type.props.get(c);
1083
+ if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
982
1084
  else throw new Error(`Can't find prop ${typeName}${keys}`);
983
1085
  }
984
1086
  if (!newBase && keys) throw new Error(`Can't find prop ${typeName}${keys}`);
985
1087
  else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
986
1088
  this.$type = {
987
- __is_anscript_annotated_type: true,
1089
+ __is_atscript_annotated_type: true,
988
1090
  type: newBase.type,
989
1091
  metadata,
990
1092
  validator(opts) {
@@ -1006,6 +1108,19 @@ else this.$metadata.set(key, value);
1006
1108
  };
1007
1109
  return handle;
1008
1110
  }
1111
+ function isAnnotatedTypeOfPrimitive(t) {
1112
+ if (["array", "object"].includes(t.type.kind)) return false;
1113
+ if (!t.type.kind) return true;
1114
+ if ([
1115
+ "union",
1116
+ "tuple",
1117
+ "intersection"
1118
+ ].includes(t.type.kind)) {
1119
+ for (const item of t.type.items) if (!isAnnotatedTypeOfPrimitive(item)) return false;
1120
+ return true;
1121
+ }
1122
+ return false;
1123
+ }
1009
1124
 
1010
1125
  //#endregion
1011
1126
  //#region packages/typescript/src/index.ts
@@ -1016,4 +1131,5 @@ exports.Validator = Validator
1016
1131
  exports.ValidatorError = ValidatorError
1017
1132
  exports.default = src_default
1018
1133
  exports.defineAnnotatedType = defineAnnotatedType
1019
- exports.isAnnotatedType = isAnnotatedType
1134
+ exports.isAnnotatedType = isAnnotatedType
1135
+ exports.isAnnotatedTypeOfPrimitive = isAnnotatedTypeOfPrimitive
package/dist/index.d.ts CHANGED
@@ -7,15 +7,25 @@ interface TError {
7
7
  message: string;
8
8
  details?: TError[];
9
9
  }
10
+ type TValidatorPlugin = (ctx: TValidatorPluginContext, def: TAtscriptAnnotatedType, value: any) => boolean | undefined;
10
11
  interface TValidatorOptions {
11
- partial: boolean | 'deep';
12
+ partial: boolean | 'deep' | ((type: TAtscriptAnnotatedType<TAtscriptTypeObject>, path: string) => boolean);
13
+ replace?: (type: TAtscriptAnnotatedType, path: string) => TAtscriptAnnotatedType;
14
+ plugins: TValidatorPlugin[];
12
15
  unknwonProps: 'strip' | 'ignore' | 'error';
13
16
  errorLimit: number;
17
+ skipList?: Set<string>;
18
+ }
19
+ interface TValidatorPluginContext {
20
+ opts: Validator<any>['opts'];
21
+ validateAnnotatedType: Validator<any>['validateAnnotatedType'];
22
+ error: Validator<any>['error'];
23
+ path: Validator<any>['path'];
14
24
  }
15
25
  declare class Validator<T extends TAtscriptAnnotatedTypeConstructor> {
16
- protected readonly def: T;
26
+ protected readonly def: T | TAtscriptAnnotatedType<any>;
17
27
  protected opts: TValidatorOptions;
18
- constructor(def: T, opts?: Partial<TValidatorOptions>);
28
+ constructor(def: T | TAtscriptAnnotatedType<any>, opts?: Partial<TValidatorOptions>);
19
29
  errors: TError[];
20
30
  protected stackErrors: TError[][];
21
31
  protected stackPath: string[];
@@ -25,8 +35,10 @@ declare class Validator<T extends TAtscriptAnnotatedTypeConstructor> {
25
35
  protected clear(): void;
26
36
  protected error(message: string, path?: string, details?: TError[]): void;
27
37
  protected throw(): void;
28
- validate<TT = InstanceType<T>>(value: any, safe?: boolean): value is TT;
29
- protected _validate(def: TAtscriptAnnotatedType, value: any): boolean;
38
+ validate<TT = T>(value: any, safe?: boolean): value is TT;
39
+ protected validateSafe(def: TAtscriptAnnotatedType, value: any): boolean;
40
+ protected get path(): string;
41
+ protected validateAnnotatedType(def: TAtscriptAnnotatedType, value: any): boolean;
30
42
  protected validateUnion(def: TAtscriptAnnotatedType<TAtscriptTypeComplex>, value: any): boolean;
31
43
  protected validateIntersection(def: TAtscriptAnnotatedType<TAtscriptTypeComplex>, value: any): boolean;
32
44
  protected validateTuple(def: TAtscriptAnnotatedType<TAtscriptTypeComplex>, value: any): boolean;
@@ -55,45 +67,67 @@ interface TAtscriptTypeArray {
55
67
  interface TAtscriptTypeObject<K extends string = string> {
56
68
  kind: 'object';
57
69
  props: Map<K, TAtscriptAnnotatedType>;
70
+ propsPatterns: {
71
+ pattern: RegExp;
72
+ def: TAtscriptAnnotatedType;
73
+ }[];
58
74
  tags: Set<AtscriptPrimitiveTags>;
59
75
  }
60
76
  interface TAtscriptTypeFinal {
61
77
  kind: '';
78
+ /**
79
+ * design type
80
+ */
62
81
  designType: 'string' | 'number' | 'boolean' | 'undefined' | 'null' | 'object' | 'any' | 'never';
82
+ /**
83
+ * value for literals
84
+ */
63
85
  value?: string | number | boolean;
64
86
  tags: Set<AtscriptPrimitiveTags>;
65
87
  }
66
88
  type TAtscriptTypeDef = TAtscriptTypeComplex | TAtscriptTypeFinal | TAtscriptTypeArray | TAtscriptTypeObject<string>;
67
89
  interface TAtscriptAnnotatedType<T = TAtscriptTypeDef> {
68
- __is_anscript_annotated_type: true;
90
+ __is_atscript_annotated_type: true;
69
91
  type: T;
70
- validator: (opts?: TValidatorOptions) => Validator<any>;
92
+ validator: <TT extends TAtscriptAnnotatedTypeConstructor>(opts?: Partial<TValidatorOptions>) => Validator<TT>;
71
93
  metadata: TMetadataMap<AtscriptMetadata>;
72
94
  optional?: boolean;
73
95
  }
74
96
  type TAtscriptAnnotatedTypeConstructor = TAtscriptAnnotatedType & (new (...args: any[]) => any);
97
+ /**
98
+ * Type Guard to check if a type is atscript-annotated
99
+ */
75
100
  declare function isAnnotatedType(type: any): type is TAtscriptAnnotatedType;
76
101
  type TKind = '' | 'array' | 'object' | 'union' | 'intersection' | 'tuple';
77
- declare function defineAnnotatedType(_kind?: TKind, base?: any): {
78
- $type: any;
79
- $def: {
80
- kind: TKind;
81
- } & Omit<TAtscriptTypeComplex, "kind"> & Omit<TAtscriptTypeFinal, "kind"> & Omit<TAtscriptTypeArray, "kind"> & Omit<TAtscriptTypeObject<string>, "kind">;
82
- $metadata: Map<string, unknown>;
83
- _existingObject: TAtscriptAnnotatedType | undefined;
84
- tags(...tags: string[]): any;
85
- designType(value: TAtscriptTypeFinal["designType"]): any;
86
- value(value: string | number | boolean): any;
87
- of(value: TAtscriptAnnotatedType): any;
88
- item(value: TAtscriptAnnotatedType): any;
89
- prop(name: string, value: TAtscriptAnnotatedType): any;
90
- optional(): any;
91
- refTo(type: any, chain?: string[]): any;
92
- annotate(key: string, value: any, asArray?: boolean): any;
93
- };
102
+ declare function defineAnnotatedType(_kind?: TKind, base?: any): TAnnotatedTypeHandle;
103
+ /**
104
+ * Atscript Metadata Map with typed setters/getters
105
+ */
94
106
  interface TMetadataMap<O extends object> extends Map<keyof O, O[keyof O]> {
95
107
  get<K extends keyof O>(key: K): O[K] | undefined;
96
108
  set<K extends keyof O>(key: K, value: O[K]): this;
97
109
  }
110
+ interface TAnnotatedTypeHandle {
111
+ $type: TAtscriptAnnotatedType;
112
+ $def: {
113
+ kind: TKind;
114
+ } & Omit<TAtscriptTypeComplex, 'kind'> & Omit<TAtscriptTypeFinal, 'kind'> & Omit<TAtscriptTypeArray, 'kind'> & Omit<TAtscriptTypeObject<string>, 'kind'>;
115
+ $metadata: TMetadataMap<AtscriptMetadata>;
116
+ _existingObject: TAtscriptAnnotatedType | undefined;
117
+ tags(...tags: string[]): TAnnotatedTypeHandle;
118
+ designType(value: TAtscriptTypeFinal['designType']): TAnnotatedTypeHandle;
119
+ value(value: string | number | boolean): TAnnotatedTypeHandle;
120
+ of(value: TAtscriptAnnotatedType): TAnnotatedTypeHandle;
121
+ item(value: TAtscriptAnnotatedType): TAnnotatedTypeHandle;
122
+ prop(name: string, value: TAtscriptAnnotatedType): TAnnotatedTypeHandle;
123
+ propPattern(pattern: RegExp, value: TAtscriptAnnotatedType): TAnnotatedTypeHandle;
124
+ optional(value?: boolean): TAnnotatedTypeHandle;
125
+ copyMetadata(fromMetadata: TMetadataMap<AtscriptMetadata>): TAnnotatedTypeHandle;
126
+ refTo(type: TAtscriptAnnotatedType & {
127
+ name?: string;
128
+ }, chain?: string[]): TAnnotatedTypeHandle;
129
+ annotate(key: keyof AtscriptMetadata, value: any, asArray?: boolean): TAnnotatedTypeHandle;
130
+ }
131
+ declare function isAnnotatedTypeOfPrimitive(t: TAtscriptAnnotatedType): boolean;
98
132
 
99
- export { type TAtscriptAnnotatedType, type TAtscriptAnnotatedTypeConstructor, type TAtscriptTypeArray, type TAtscriptTypeComplex, type TAtscriptTypeDef, type TAtscriptTypeFinal, type TAtscriptTypeObject, type TMetadataMap, type TValidatorOptions, Validator, ValidatorError, tsPlugin as default, defineAnnotatedType, isAnnotatedType };
133
+ export { type TAnnotatedTypeHandle, type TAtscriptAnnotatedType, type TAtscriptAnnotatedTypeConstructor, type TAtscriptTypeArray, type TAtscriptTypeComplex, type TAtscriptTypeDef, type TAtscriptTypeFinal, type TAtscriptTypeObject, type TMetadataMap, type TValidatorOptions, type TValidatorPlugin, type TValidatorPluginContext, Validator, ValidatorError, tsPlugin as default, defineAnnotatedType, isAnnotatedType, isAnnotatedTypeOfPrimitive };
package/dist/index.mjs CHANGED
@@ -177,7 +177,7 @@ function wrapProp(name) {
177
177
  return name;
178
178
  }
179
179
  function escapeQuotes(str) {
180
- return str.replace(/"/g, "\\\"");
180
+ return str.replace(/"/g, "\\\"").replace(/\\/g, "\\\\");
181
181
  }
182
182
 
183
183
  //#endregion
@@ -192,7 +192,7 @@ var TypeRenderer = class extends BaseRenderer {
192
192
  this.writeln(" * Do not edit this file!");
193
193
  this.writeln(" */");
194
194
  this.writeln();
195
- this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TMetadataMap, Validator } from \"@atscript/typescript\"");
195
+ this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TMetadataMap, Validator, TAtscriptAnnotatedTypeConstructor, TValidatorOptions } from \"@atscript/typescript\"");
196
196
  }
197
197
  post() {
198
198
  this.writeln("// prettier-ignore-end");
@@ -244,17 +244,41 @@ var TypeRenderer = class extends BaseRenderer {
244
244
  }
245
245
  renderStructure(struct, asClass) {
246
246
  this.blockln("{}");
247
+ const patterns = [];
248
+ let hasProp = false;
247
249
  for (const prop of Array.from(struct.props.values())) {
250
+ if (prop.token("identifier")?.pattern) {
251
+ patterns.push(prop);
252
+ continue;
253
+ }
254
+ hasProp = true;
248
255
  const optional = !!prop.token("optional");
249
256
  this.write(wrapProp(prop.id), optional ? "?" : "", ": ");
250
257
  this.renderTypeDef(prop.getDefinition());
251
258
  this.writeln();
252
259
  }
260
+ if (patterns.length) {
261
+ this.write(`[key: string]: `);
262
+ if (hasProp) this.writeln("any");
263
+ else if (patterns.length === 1) {
264
+ this.renderTypeDef(patterns[0].getDefinition());
265
+ this.writeln();
266
+ } else {
267
+ this.indent();
268
+ for (const prop of patterns) {
269
+ this.writeln();
270
+ this.write("| ");
271
+ this.renderTypeDef(prop.getDefinition());
272
+ }
273
+ this.unindent();
274
+ this.writeln();
275
+ }
276
+ }
253
277
  if (asClass) {
254
- this.writeln("static __is_anscript_annotated_type: true");
278
+ this.writeln("static __is_atscript_annotated_type: true");
255
279
  this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}>`);
256
280
  this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
257
- this.writeln(`static validator: () => Validator<${asClass}>`);
281
+ this.writeln(`static validator: <TT extends TAtscriptAnnotatedTypeConstructor = ${asClass}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
258
282
  }
259
283
  this.pop();
260
284
  }
@@ -293,10 +317,10 @@ else if (isGroup(realDef)) typeDef = "TAtscriptTypeComplex";
293
317
  else if (isArray(realDef)) typeDef = "TAtscriptTypeArray";
294
318
  else if (isPrimitive(realDef)) typeDef = "TAtscriptTypeFinal";
295
319
  }
296
- this.writeln(`const __is_anscript_annotated_type: true`);
320
+ this.writeln(`const __is_atscript_annotated_type: true`);
297
321
  this.writeln(`const type: ${typeDef}`);
298
322
  this.writeln(`const metadata: TMetadataMap<AtscriptMetadata>`);
299
- this.writeln(`const validator: () => Validator<${node.id}>`);
323
+ this.writeln(`const validator: <TT extends TAtscriptAnnotatedTypeConstructor = ${node.id}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
300
324
  this.popln();
301
325
  }
302
326
  renderJsDoc(node) {
@@ -355,7 +379,7 @@ var JsRenderer = class extends BaseRenderer {
355
379
  this.write(exported ? "export " : "");
356
380
  this.write(`class ${node.id} `);
357
381
  this.blockln("{}");
358
- this.writeln("static __is_anscript_annotated_type = true");
382
+ this.writeln("static __is_atscript_annotated_type = true");
359
383
  this.writeln("static type = {}");
360
384
  this.writeln("static metadata = new Map()");
361
385
  this.popln();
@@ -368,7 +392,7 @@ var JsRenderer = class extends BaseRenderer {
368
392
  this.write(exported ? "export " : "");
369
393
  this.write(`class ${node.id} `);
370
394
  this.blockln("{}");
371
- this.writeln("static __is_anscript_annotated_type = true");
395
+ this.writeln("static __is_atscript_annotated_type = true");
372
396
  this.writeln("static type = {}");
373
397
  this.writeln("static metadata = new Map()");
374
398
  this.popln();
@@ -481,6 +505,17 @@ var JsRenderer = class extends BaseRenderer {
481
505
  this.unindent();
482
506
  this.write(`)`);
483
507
  }
508
+ for (const [key, propDef] of Object.entries(def.propsPatterns)) {
509
+ const optional = typeof propDef === "object" && propDef.optional;
510
+ this.writeln(`.propPattern(`);
511
+ this.indent();
512
+ this.writeln(`${key},`);
513
+ this.renderPrimitiveDef(propDef);
514
+ if (optional) this.writeln(".optional()");
515
+ this.writeln(".$type");
516
+ this.unindent();
517
+ this.write(`)`);
518
+ }
484
519
  this.unindent();
485
520
  return;
486
521
  default: return this.writeln(`$(${d()}).designType("any")`);
@@ -489,10 +524,17 @@ var JsRenderer = class extends BaseRenderer {
489
524
  defineObject(node) {
490
525
  const props = Array.from(node.props.values());
491
526
  for (const prop of props) {
527
+ const pattern = prop.token("identifier")?.pattern;
492
528
  const optional = !!prop.token("optional");
493
- this.writeln(`.prop(`);
494
- this.indent();
495
- this.writeln(`"${escapeQuotes(prop.id)}",`);
529
+ if (pattern) {
530
+ this.writeln(`.propPattern(`);
531
+ this.indent();
532
+ this.writeln(`/${pattern.source}/${pattern.flags},`);
533
+ } else {
534
+ this.writeln(`.prop(`);
535
+ this.indent();
536
+ this.writeln(`"${escapeQuotes(prop.id)}",`);
537
+ }
496
538
  this.annotateType(prop.getDefinition());
497
539
  this.indent().defineMetadata(prop).unindent();
498
540
  if (optional) this.writeln(" .optional()");
@@ -628,7 +670,7 @@ var Validator = class {
628
670
  error(message, path$1, details) {
629
671
  const errors = this.stackErrors[this.stackErrors.length - 1] || this.errors;
630
672
  const error = {
631
- path: path$1 || this.stackPath.join(".").slice(1),
673
+ path: path$1 || this.path,
632
674
  message
633
675
  };
634
676
  if (details?.length) error.details = details;
@@ -641,7 +683,7 @@ var Validator = class {
641
683
  this.push("");
642
684
  this.errors = [];
643
685
  this.stackErrors = [];
644
- const passed = this._validate(this.def, value);
686
+ const passed = this.validateSafe(this.def, value);
645
687
  this.pop(!passed);
646
688
  if (!passed) {
647
689
  if (safe) return false;
@@ -649,10 +691,21 @@ var Validator = class {
649
691
  }
650
692
  return true;
651
693
  }
652
- _validate(def, value) {
694
+ validateSafe(def, value) {
653
695
  if (this.isLimitExceeded()) return false;
654
696
  if (!isAnnotatedType(def)) throw new Error("Can not validate not-annotated type");
697
+ if (typeof this.opts.replace === "function") def = this.opts.replace(def, this.path);
655
698
  if (def.optional && value === undefined) return true;
699
+ for (const plugin of this.opts.plugins) {
700
+ const result = plugin(this, def, value);
701
+ if (result === false || result === true) return result;
702
+ }
703
+ return this.validateAnnotatedType(def, value);
704
+ }
705
+ get path() {
706
+ return this.stackPath.slice(1).join(".");
707
+ }
708
+ validateAnnotatedType(def, value) {
656
709
  switch (def.type.kind) {
657
710
  case "object": return this.validateObject(def, value);
658
711
  case "union": return this.validateUnion(def, value);
@@ -668,7 +721,7 @@ var Validator = class {
668
721
  const popped = [];
669
722
  for (const item of def.type.items) {
670
723
  this.push(`[${item.type.kind || item.type.designType}(${i})]`);
671
- if (this._validate(item, value)) {
724
+ if (this.validateSafe(item, value)) {
672
725
  this.pop(false);
673
726
  return true;
674
727
  }
@@ -682,7 +735,7 @@ var Validator = class {
682
735
  return false;
683
736
  }
684
737
  validateIntersection(def, value) {
685
- for (const item of def.type.items) if (!this._validate(item, value)) return false;
738
+ for (const item of def.type.items) if (!this.validateSafe(item, value)) return false;
686
739
  return true;
687
740
  }
688
741
  validateTuple(def, value) {
@@ -693,7 +746,7 @@ var Validator = class {
693
746
  let i = 0;
694
747
  for (const item of def.type.items) {
695
748
  this.push(`[${i}]`);
696
- if (!this._validate(item, value[i])) {
749
+ if (!this.validateSafe(item, value[i])) {
697
750
  this.pop(true);
698
751
  return false;
699
752
  }
@@ -721,7 +774,7 @@ var Validator = class {
721
774
  let passed = true;
722
775
  for (const item of value) {
723
776
  this.push(`[${i}]`);
724
- if (!this._validate(def.type.of, item)) {
777
+ if (!this.validateSafe(def.type.of, item)) {
725
778
  passed = false;
726
779
  this.pop(true);
727
780
  if (this.isLimitExceeded()) return false;
@@ -738,21 +791,55 @@ var Validator = class {
738
791
  let passed = true;
739
792
  const valueKeys = new Set(Object.keys(value));
740
793
  const typeKeys = new Set();
794
+ const skipList = new Set();
795
+ if (this.opts.skipList) {
796
+ const path$1 = this.stackPath.length > 1 ? this.path + "." : "";
797
+ this.opts.skipList.forEach((item) => {
798
+ if (item.startsWith(path$1)) {
799
+ const key = item.slice(path$1.length);
800
+ skipList.add(key);
801
+ valueKeys.delete(key);
802
+ }
803
+ });
804
+ }
805
+ let partialFunctionMatched = false;
806
+ if (typeof this.opts.partial === "function") partialFunctionMatched = this.opts.partial(def, this.path);
741
807
  for (const [key, item] of def.type.props.entries()) {
808
+ if (skipList.has(key)) continue;
742
809
  typeKeys.add(key);
743
810
  if (value[key] === undefined) {
744
- if (this.opts.partial === "deep" || this.opts.partial === true && this.stackPath.length <= 1) continue;
811
+ if (partialFunctionMatched || this.opts.partial === "deep" || this.opts.partial === true && this.stackPath.length <= 1) continue;
745
812
  }
746
813
  this.push(key);
747
- if (this._validate(item, value[key])) this.pop(false);
814
+ if (this.validateSafe(item, value[key])) this.pop(false);
748
815
  else {
749
816
  passed = false;
750
817
  this.pop(true);
751
818
  if (this.isLimitExceeded()) return false;
752
819
  }
753
820
  }
754
- for (const key of valueKeys) if (this.opts.unknwonProps !== "ignore") {
755
- if (!typeKeys.has(key)) {
821
+ for (const key of valueKeys)
822
+ /** matched patterns for unknown keys */ if (!typeKeys.has(key)) {
823
+ const matched = [];
824
+ for (const { pattern, def: propDef } of def.type.propsPatterns) if (pattern.test(key)) matched.push({
825
+ pattern,
826
+ def: propDef
827
+ });
828
+ if (matched.length) {
829
+ let keyPassed = false;
830
+ for (const { def: def$1 } of matched) if (this.validateSafe(def$1, value[key])) {
831
+ this.pop(false);
832
+ keyPassed = true;
833
+ break;
834
+ }
835
+ if (!keyPassed) {
836
+ this.push(key);
837
+ this.validateSafe(matched[0].def, value[key]);
838
+ this.pop(true);
839
+ passed = false;
840
+ if (this.isLimitExceeded()) return false;
841
+ }
842
+ } else if (this.opts.unknwonProps !== "ignore") {
756
843
  if (this.opts.unknwonProps === "error") {
757
844
  this.push(key);
758
845
  this.error(`Unexpected property`);
@@ -870,7 +957,8 @@ else {
870
957
  partial: false,
871
958
  unknwonProps: "error",
872
959
  errorLimit: 10,
873
- ...opts
960
+ ...opts,
961
+ plugins: opts?.plugins || []
874
962
  };
875
963
  }
876
964
  };
@@ -883,7 +971,7 @@ var ValidatorError = class extends Error {
883
971
  //#endregion
884
972
  //#region packages/typescript/src/annotated-type.ts
885
973
  function isAnnotatedType(type) {
886
- return type && type.__is_anscript_annotated_type;
974
+ return type && type.__is_atscript_annotated_type;
887
975
  }
888
976
  function defineAnnotatedType(_kind, base) {
889
977
  const kind = _kind || "";
@@ -894,11 +982,14 @@ function defineAnnotatedType(_kind, base) {
894
982
  "intersection",
895
983
  "tuple"
896
984
  ].includes(kind)) type.items = [];
897
- if (kind === "object") type.props = new Map();
985
+ if (kind === "object") {
986
+ type.props = new Map();
987
+ type.propsPatterns = [];
988
+ }
898
989
  type.tags = new Set();
899
990
  const metadata = base?.metadata || new Map();
900
991
  if (base) Object.assign(base, {
901
- __is_anscript_annotated_type: true,
992
+ __is_atscript_annotated_type: true,
902
993
  metadata,
903
994
  type,
904
995
  validator(opts) {
@@ -906,7 +997,7 @@ function defineAnnotatedType(_kind, base) {
906
997
  }
907
998
  });
908
999
  else base = {
909
- __is_anscript_annotated_type: true,
1000
+ __is_atscript_annotated_type: true,
910
1001
  metadata,
911
1002
  type,
912
1003
  validator(opts) {
@@ -942,8 +1033,19 @@ else base = {
942
1033
  this.$def.props.set(name, value);
943
1034
  return this;
944
1035
  },
945
- optional() {
946
- this.$type.optional = true;
1036
+ propPattern(pattern, def) {
1037
+ this.$def.propsPatterns.push({
1038
+ pattern,
1039
+ def
1040
+ });
1041
+ return this;
1042
+ },
1043
+ optional(value = true) {
1044
+ this.$type.optional = value;
1045
+ return this;
1046
+ },
1047
+ copyMetadata(fromMetadata, ignore) {
1048
+ for (const [key, value] of fromMetadata.entries()) if (!ignore || !ignore.has(key)) this.$metadata.set(key, value);
947
1049
  return this;
948
1050
  },
949
1051
  refTo(type$1, chain) {
@@ -953,13 +1055,13 @@ else base = {
953
1055
  let keys = "";
954
1056
  for (const c of chain || []) {
955
1057
  keys += `["${c}"]`;
956
- if (newBase.type.kind === "object") newBase = newBase.type.props.get(c);
1058
+ if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
957
1059
  else throw new Error(`Can't find prop ${typeName}${keys}`);
958
1060
  }
959
1061
  if (!newBase && keys) throw new Error(`Can't find prop ${typeName}${keys}`);
960
1062
  else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
961
1063
  this.$type = {
962
- __is_anscript_annotated_type: true,
1064
+ __is_atscript_annotated_type: true,
963
1065
  type: newBase.type,
964
1066
  metadata,
965
1067
  validator(opts) {
@@ -981,10 +1083,23 @@ else this.$metadata.set(key, value);
981
1083
  };
982
1084
  return handle;
983
1085
  }
1086
+ function isAnnotatedTypeOfPrimitive(t) {
1087
+ if (["array", "object"].includes(t.type.kind)) return false;
1088
+ if (!t.type.kind) return true;
1089
+ if ([
1090
+ "union",
1091
+ "tuple",
1092
+ "intersection"
1093
+ ].includes(t.type.kind)) {
1094
+ for (const item of t.type.items) if (!isAnnotatedTypeOfPrimitive(item)) return false;
1095
+ return true;
1096
+ }
1097
+ return false;
1098
+ }
984
1099
 
985
1100
  //#endregion
986
1101
  //#region packages/typescript/src/index.ts
987
1102
  var src_default = tsPlugin;
988
1103
 
989
1104
  //#endregion
990
- export { Validator, ValidatorError, src_default as default, defineAnnotatedType, isAnnotatedType };
1105
+ export { Validator, ValidatorError, src_default as default, defineAnnotatedType, isAnnotatedType, isAnnotatedTypeOfPrimitive };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/typescript",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "description": "Atscript: typescript-gen support.",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -46,13 +46,15 @@
46
46
  },
47
47
  "homepage": "https://github.com/moostjs/atscript/tree/main/packages/typescript#readme",
48
48
  "license": "ISC",
49
+ "peerDependencies": {
50
+ "@atscript/core": "^0.0.18"
51
+ },
49
52
  "dependencies": {
50
- "@moostjs/event-cli": "^0.5.26",
51
- "moost": "^0.5.26",
52
- "@atscript/core": "^0.0.16"
53
+ "@moostjs/event-cli": "^0.5.30",
54
+ "moost": "^0.5.30"
53
55
  },
54
56
  "devDependencies": {
55
- "vitest": "^3.0.0"
57
+ "vitest": "3.2.4"
56
58
  },
57
59
  "scripts": {
58
60
  "pub": "pnpm publish --access public",