@atscript/typescript 0.1.2 → 0.1.3

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
@@ -234,7 +234,7 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
234
234
  this.writeln(" * Do not edit this file!");
235
235
  this.writeln(" */");
236
236
  this.writeln();
237
- this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TMetadataMap, Validator, TAtscriptAnnotatedTypeConstructor, TValidatorOptions } from \"@atscript/typescript/utils\"");
237
+ this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TAtscriptAnnotatedType, TMetadataMap, Validator, TValidatorOptions } from \"@atscript/typescript/utils\"");
238
238
  }
239
239
  post() {
240
240
  this.writeln("// prettier-ignore-end");
@@ -323,9 +323,10 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
323
323
  }
324
324
  if (asClass) {
325
325
  this.writeln("static __is_atscript_annotated_type: true");
326
- this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}>`);
326
+ this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}, ${asClass}>`);
327
327
  this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
328
- this.writeln(`static validator: <TT extends TAtscriptAnnotatedTypeConstructor = typeof ${asClass}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
328
+ this.writeln(`static validator: (opts?: Partial<TValidatorOptions>) => Validator<typeof ${asClass}>`);
329
+ if (resolveJsonSchemaMode(this.opts) === false) this.writeln("/** @deprecated JSON Schema support is disabled. Calling this method will throw a runtime error. To enable, set `jsonSchema: 'lazy'` or `jsonSchema: 'bundle'` in tsPlugin options, or add `@emit.jsonSchema` annotation to individual interfaces. */");
329
330
  this.writeln("static toJsonSchema: () => any");
330
331
  }
331
332
  this.pop();
@@ -340,6 +341,12 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
340
341
  if (struct?.entity === "structure") this.renderStructure(struct, node.id);
341
342
  else this.writeln("{}");
342
343
  this.writeln();
344
+ const nsPrefix = exported ? "export declare" : "declare";
345
+ this.write(`${nsPrefix} namespace ${node.id} `);
346
+ this.blockln("{}");
347
+ this.writeln(`type DataType = ${node.id}`);
348
+ this.popln();
349
+ this.writeln();
343
350
  }
344
351
  renderType(node) {
345
352
  this.writeln();
@@ -366,6 +373,11 @@ else this.writeln("{}");
366
373
  } else {
367
374
  this.write(exported ? "export declare " : "declare ");
368
375
  this.writeln(`class ${node.id} extends ${targetName} {}`);
376
+ const nsPrefix = exported ? "export declare" : "declare";
377
+ this.write(`${nsPrefix} namespace ${node.id} `);
378
+ this.blockln("{}");
379
+ this.writeln(`type DataType = ${node.id}`);
380
+ this.popln();
369
381
  this.writeln();
370
382
  }
371
383
  }
@@ -380,15 +392,17 @@ else this.writeln("{}");
380
392
  let realDef = inputDef;
381
393
  if ((0, __atscript_core.isRef)(inputDef)) realDef = this.doc.unwindType(inputDef.id, inputDef.chain)?.def || realDef;
382
394
  realDef = this.doc.mergeIntersection(realDef);
383
- if ((0, __atscript_core.isStructure)(realDef) || (0, __atscript_core.isInterface)(realDef)) typeDef = `TAtscriptTypeObject<keyof ${name}>`;
384
- else if ((0, __atscript_core.isGroup)(realDef)) typeDef = "TAtscriptTypeComplex";
385
- else if ((0, __atscript_core.isArray)(realDef)) typeDef = "TAtscriptTypeArray";
386
- else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef = "TAtscriptTypeFinal";
395
+ if ((0, __atscript_core.isStructure)(realDef) || (0, __atscript_core.isInterface)(realDef)) typeDef = `TAtscriptTypeObject<keyof ${name}, ${name}>`;
396
+ else if ((0, __atscript_core.isGroup)(realDef)) typeDef = `TAtscriptTypeComplex<${name}>`;
397
+ else if ((0, __atscript_core.isArray)(realDef)) typeDef = `TAtscriptTypeArray<${name}>`;
398
+ else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef = `TAtscriptTypeFinal<${name}>`;
387
399
  }
400
+ this.writeln(`type DataType = ${name}`);
388
401
  this.writeln(`const __is_atscript_annotated_type: true`);
389
402
  this.writeln(`const type: ${typeDef}`);
390
403
  this.writeln(`const metadata: TMetadataMap<AtscriptMetadata>`);
391
- this.writeln(`const validator: (opts?: Partial<TValidatorOptions>) => Validator<TAtscriptAnnotatedTypeConstructor, ${name}>`);
404
+ this.writeln(`const validator: (opts?: Partial<TValidatorOptions>) => Validator<TAtscriptAnnotatedType, ${name}>`);
405
+ if (resolveJsonSchemaMode(this.opts) === false) this.writeln("/** @deprecated JSON Schema support is disabled. Calling this method will throw a runtime error. To enable, set `jsonSchema: 'lazy'` or `jsonSchema: 'bundle'` in tsPlugin options, or add `@emit.jsonSchema` annotation to individual interfaces. */");
392
406
  this.writeln("const toJsonSchema: () => any");
393
407
  this.popln();
394
408
  }
@@ -430,6 +444,20 @@ function renderPrimitiveTypeDef(def) {
430
444
  }
431
445
  }
432
446
 
447
+ //#endregion
448
+ //#region packages/typescript/src/traverse.ts
449
+ function forAnnotatedType(def, handlers) {
450
+ switch (def.type.kind) {
451
+ case "": return handlers.final(def);
452
+ case "object": return handlers.object(def);
453
+ case "array": return handlers.array(def);
454
+ case "union": return handlers.union(def);
455
+ case "intersection": return handlers.intersection(def);
456
+ case "tuple": return handlers.tuple(def);
457
+ default: throw new Error(`Unknown type kind "${def.type.kind}"`);
458
+ }
459
+ }
460
+
433
461
  //#endregion
434
462
  //#region packages/typescript/src/validator.ts
435
463
  function _define_property$2(obj, key, value) {
@@ -475,7 +503,17 @@ var Validator = class {
475
503
  throw() {
476
504
  throw new ValidatorError(this.errors);
477
505
  }
478
- validate(value, safe) {
506
+ /**
507
+ * Validates a value against the type definition.
508
+ *
509
+ * Acts as a TypeScript type guard — when it returns `true`, the value
510
+ * is narrowed to `DataType`.
511
+ *
512
+ * @param value - The value to validate.
513
+ * @param safe - If `true`, returns `false` on failure instead of throwing.
514
+ * @returns `true` if the value matches the type definition.
515
+ * @throws {ValidatorError} When validation fails and `safe` is not `true`.
516
+ */ validate(value, safe) {
479
517
  this.push("");
480
518
  this.errors = [];
481
519
  this.stackErrors = [];
@@ -502,15 +540,14 @@ var Validator = class {
502
540
  return this.stackPath.slice(1).join(".");
503
541
  }
504
542
  validateAnnotatedType(def, value) {
505
- switch (def.type.kind) {
506
- case "object": return this.validateObject(def, value);
507
- case "union": return this.validateUnion(def, value);
508
- case "intersection": return this.validateIntersection(def, value);
509
- case "tuple": return this.validateTuple(def, value);
510
- case "array": return this.validateArray(def, value);
511
- case "": return this.validatePrimitive(def, value);
512
- default: throw new Error(`Unknown type "${def.type.kind}"`);
513
- }
543
+ return forAnnotatedType(def, {
544
+ final: (d) => this.validatePrimitive(d, value),
545
+ object: (d) => this.validateObject(d, value),
546
+ array: (d) => this.validateArray(d, value),
547
+ union: (d) => this.validateUnion(d, value),
548
+ intersection: (d) => this.validateIntersection(d, value),
549
+ tuple: (d) => this.validateTuple(d, value)
550
+ });
514
551
  }
515
552
  validateUnion(def, value) {
516
553
  let i = 0;
@@ -742,7 +779,7 @@ else {
742
779
  constructor(def, opts) {
743
780
  _define_property$2(this, "def", void 0);
744
781
  _define_property$2(this, "opts", void 0);
745
- _define_property$2(this, "errors", void 0);
782
+ /** Validation errors collected during the last {@link validate} call. */ _define_property$2(this, "errors", void 0);
746
783
  _define_property$2(this, "stackErrors", void 0);
747
784
  _define_property$2(this, "stackPath", void 0);
748
785
  this.def = def;
@@ -888,14 +925,12 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
888
925
  //#region packages/typescript/src/json-schema.ts
889
926
  function buildJsonSchema(type) {
890
927
  const build$1 = (def) => {
891
- const t = def.type;
892
928
  const meta = def.metadata;
893
- switch (t.kind) {
894
- case "object": {
895
- const obj = t;
929
+ return forAnnotatedType(def, {
930
+ object(d) {
896
931
  const properties = {};
897
932
  const required = [];
898
- for (const [key, val] of obj.props.entries()) {
933
+ for (const [key, val] of d.type.props.entries()) {
899
934
  properties[key] = build$1(val);
900
935
  if (!val.optional) required.push(key);
901
936
  }
@@ -905,41 +940,36 @@ function buildJsonSchema(type) {
905
940
  };
906
941
  if (required.length) schema.required = required;
907
942
  return schema;
908
- }
909
- case "array": {
910
- const arr = t;
943
+ },
944
+ array(d) {
911
945
  const schema = {
912
946
  type: "array",
913
- items: build$1(arr.of)
947
+ items: build$1(d.type.of)
914
948
  };
915
949
  const minLength = meta.get("expect.minLength");
916
950
  if (typeof minLength === "number") schema.minItems = minLength;
917
951
  const maxLength = meta.get("expect.maxLength");
918
952
  if (typeof maxLength === "number") schema.maxItems = maxLength;
919
953
  return schema;
920
- }
921
- case "union": {
922
- const grp = t;
923
- return { anyOf: grp.items.map(build$1) };
924
- }
925
- case "intersection": {
926
- const grp = t;
927
- return { allOf: grp.items.map(build$1) };
928
- }
929
- case "tuple": {
930
- const grp = t;
954
+ },
955
+ union(d) {
956
+ return { anyOf: d.type.items.map(build$1) };
957
+ },
958
+ intersection(d) {
959
+ return { allOf: d.type.items.map(build$1) };
960
+ },
961
+ tuple(d) {
931
962
  return {
932
963
  type: "array",
933
- items: grp.items.map(build$1),
964
+ items: d.type.items.map(build$1),
934
965
  additionalItems: false
935
966
  };
936
- }
937
- case "": {
938
- const fin = t;
967
+ },
968
+ final(d) {
939
969
  const schema = {};
940
- if (fin.value !== undefined) schema.const = fin.value;
941
- if (fin.designType && fin.designType !== "any") {
942
- schema.type = fin.designType === "undefined" ? "null" : fin.designType;
970
+ if (d.type.value !== undefined) schema.const = d.type.value;
971
+ if (d.type.designType && d.type.designType !== "any") {
972
+ schema.type = d.type.designType === "undefined" ? "null" : d.type.designType;
943
973
  if (schema.type === "number" && meta.get("expect.int")) schema.type = "integer";
944
974
  }
945
975
  if (schema.type === "string") {
@@ -959,8 +989,7 @@ else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern:
959
989
  }
960
990
  return schema;
961
991
  }
962
- default: return {};
963
- }
992
+ });
964
993
  };
965
994
  return build$1(type);
966
995
  }
@@ -982,7 +1011,7 @@ var JsRenderer = class extends BaseRenderer {
982
1011
  this.writeln("// prettier-ignore-start");
983
1012
  this.writeln("/* eslint-disable */");
984
1013
  const imports = ["defineAnnotatedType as $", "annotate as $a"];
985
- if (!this.opts?.preRenderJsonSchema) imports.push("buildJsonSchema as $$");
1014
+ if (resolveJsonSchemaMode(this.opts) === "lazy") imports.push("buildJsonSchema as $$");
986
1015
  this.writeln(`import { ${imports.join(", ")} } from "@atscript/typescript/utils"`);
987
1016
  }
988
1017
  buildAdHocMap(annotateNodes) {
@@ -1070,15 +1099,21 @@ else {
1070
1099
  this.writeln();
1071
1100
  }
1072
1101
  renderJsonSchemaMethod(node) {
1073
- if (this.opts?.preRenderJsonSchema) {
1102
+ const mode = resolveJsonSchemaMode(this.opts);
1103
+ const hasAnnotation = node.countAnnotations("emit.jsonSchema") > 0;
1104
+ if (hasAnnotation || mode === "bundle") {
1074
1105
  const schema = JSON.stringify(buildJsonSchema(this.toAnnotatedType(node)));
1075
1106
  this.writeln("static toJsonSchema() {");
1076
1107
  this.indent().writeln(`return ${schema}`).unindent();
1077
1108
  this.writeln("}");
1078
- } else {
1109
+ } else if (mode === "lazy") {
1079
1110
  this.writeln("static toJsonSchema() {");
1080
1111
  this.indent().writeln("return this._jsonSchema ?? (this._jsonSchema = $$(this))").unindent();
1081
1112
  this.writeln("}");
1113
+ } else {
1114
+ this.writeln("static toJsonSchema() {");
1115
+ this.indent().writeln("throw new Error(\"JSON Schema support is disabled. To enable, set `jsonSchema: 'lazy'` or `jsonSchema: 'bundle'` in tsPlugin options, or add @emit.jsonSchema annotation to individual interfaces.\")").unindent();
1116
+ this.writeln("}");
1082
1117
  }
1083
1118
  }
1084
1119
  toAnnotatedType(node) {
@@ -1521,6 +1556,10 @@ else {
1521
1556
 
1522
1557
  //#endregion
1523
1558
  //#region packages/typescript/src/plugin.ts
1559
+ function resolveJsonSchemaMode(opts) {
1560
+ if (opts?.jsonSchema !== undefined) return opts.jsonSchema;
1561
+ return false;
1562
+ }
1524
1563
  const tsPlugin = (opts) => {
1525
1564
  return {
1526
1565
  name: "typesccript",
package/dist/index.cjs CHANGED
@@ -231,7 +231,7 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
231
231
  this.writeln(" * Do not edit this file!");
232
232
  this.writeln(" */");
233
233
  this.writeln();
234
- this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TMetadataMap, Validator, TAtscriptAnnotatedTypeConstructor, TValidatorOptions } from \"@atscript/typescript/utils\"");
234
+ this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TAtscriptAnnotatedType, TMetadataMap, Validator, TValidatorOptions } from \"@atscript/typescript/utils\"");
235
235
  }
236
236
  post() {
237
237
  this.writeln("// prettier-ignore-end");
@@ -320,9 +320,10 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
320
320
  }
321
321
  if (asClass) {
322
322
  this.writeln("static __is_atscript_annotated_type: true");
323
- this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}>`);
323
+ this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}, ${asClass}>`);
324
324
  this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
325
- this.writeln(`static validator: <TT extends TAtscriptAnnotatedTypeConstructor = typeof ${asClass}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
325
+ this.writeln(`static validator: (opts?: Partial<TValidatorOptions>) => Validator<typeof ${asClass}>`);
326
+ if (resolveJsonSchemaMode(this.opts) === false) this.writeln("/** @deprecated JSON Schema support is disabled. Calling this method will throw a runtime error. To enable, set `jsonSchema: 'lazy'` or `jsonSchema: 'bundle'` in tsPlugin options, or add `@emit.jsonSchema` annotation to individual interfaces. */");
326
327
  this.writeln("static toJsonSchema: () => any");
327
328
  }
328
329
  this.pop();
@@ -337,6 +338,12 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
337
338
  if (struct?.entity === "structure") this.renderStructure(struct, node.id);
338
339
  else this.writeln("{}");
339
340
  this.writeln();
341
+ const nsPrefix = exported ? "export declare" : "declare";
342
+ this.write(`${nsPrefix} namespace ${node.id} `);
343
+ this.blockln("{}");
344
+ this.writeln(`type DataType = ${node.id}`);
345
+ this.popln();
346
+ this.writeln();
340
347
  }
341
348
  renderType(node) {
342
349
  this.writeln();
@@ -363,6 +370,11 @@ else this.writeln("{}");
363
370
  } else {
364
371
  this.write(exported ? "export declare " : "declare ");
365
372
  this.writeln(`class ${node.id} extends ${targetName} {}`);
373
+ const nsPrefix = exported ? "export declare" : "declare";
374
+ this.write(`${nsPrefix} namespace ${node.id} `);
375
+ this.blockln("{}");
376
+ this.writeln(`type DataType = ${node.id}`);
377
+ this.popln();
366
378
  this.writeln();
367
379
  }
368
380
  }
@@ -377,15 +389,17 @@ else this.writeln("{}");
377
389
  let realDef = inputDef;
378
390
  if ((0, __atscript_core.isRef)(inputDef)) realDef = this.doc.unwindType(inputDef.id, inputDef.chain)?.def || realDef;
379
391
  realDef = this.doc.mergeIntersection(realDef);
380
- if ((0, __atscript_core.isStructure)(realDef) || (0, __atscript_core.isInterface)(realDef)) typeDef = `TAtscriptTypeObject<keyof ${name}>`;
381
- else if ((0, __atscript_core.isGroup)(realDef)) typeDef = "TAtscriptTypeComplex";
382
- else if ((0, __atscript_core.isArray)(realDef)) typeDef = "TAtscriptTypeArray";
383
- else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef = "TAtscriptTypeFinal";
392
+ if ((0, __atscript_core.isStructure)(realDef) || (0, __atscript_core.isInterface)(realDef)) typeDef = `TAtscriptTypeObject<keyof ${name}, ${name}>`;
393
+ else if ((0, __atscript_core.isGroup)(realDef)) typeDef = `TAtscriptTypeComplex<${name}>`;
394
+ else if ((0, __atscript_core.isArray)(realDef)) typeDef = `TAtscriptTypeArray<${name}>`;
395
+ else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef = `TAtscriptTypeFinal<${name}>`;
384
396
  }
397
+ this.writeln(`type DataType = ${name}`);
385
398
  this.writeln(`const __is_atscript_annotated_type: true`);
386
399
  this.writeln(`const type: ${typeDef}`);
387
400
  this.writeln(`const metadata: TMetadataMap<AtscriptMetadata>`);
388
- this.writeln(`const validator: (opts?: Partial<TValidatorOptions>) => Validator<TAtscriptAnnotatedTypeConstructor, ${name}>`);
401
+ this.writeln(`const validator: (opts?: Partial<TValidatorOptions>) => Validator<TAtscriptAnnotatedType, ${name}>`);
402
+ if (resolveJsonSchemaMode(this.opts) === false) this.writeln("/** @deprecated JSON Schema support is disabled. Calling this method will throw a runtime error. To enable, set `jsonSchema: 'lazy'` or `jsonSchema: 'bundle'` in tsPlugin options, or add `@emit.jsonSchema` annotation to individual interfaces. */");
389
403
  this.writeln("const toJsonSchema: () => any");
390
404
  this.popln();
391
405
  }
@@ -427,6 +441,20 @@ function renderPrimitiveTypeDef(def) {
427
441
  }
428
442
  }
429
443
 
444
+ //#endregion
445
+ //#region packages/typescript/src/traverse.ts
446
+ function forAnnotatedType(def, handlers) {
447
+ switch (def.type.kind) {
448
+ case "": return handlers.final(def);
449
+ case "object": return handlers.object(def);
450
+ case "array": return handlers.array(def);
451
+ case "union": return handlers.union(def);
452
+ case "intersection": return handlers.intersection(def);
453
+ case "tuple": return handlers.tuple(def);
454
+ default: throw new Error(`Unknown type kind "${def.type.kind}"`);
455
+ }
456
+ }
457
+
430
458
  //#endregion
431
459
  //#region packages/typescript/src/validator.ts
432
460
  function _define_property$1(obj, key, value) {
@@ -472,7 +500,17 @@ var Validator = class {
472
500
  throw() {
473
501
  throw new ValidatorError(this.errors);
474
502
  }
475
- validate(value, safe) {
503
+ /**
504
+ * Validates a value against the type definition.
505
+ *
506
+ * Acts as a TypeScript type guard — when it returns `true`, the value
507
+ * is narrowed to `DataType`.
508
+ *
509
+ * @param value - The value to validate.
510
+ * @param safe - If `true`, returns `false` on failure instead of throwing.
511
+ * @returns `true` if the value matches the type definition.
512
+ * @throws {ValidatorError} When validation fails and `safe` is not `true`.
513
+ */ validate(value, safe) {
476
514
  this.push("");
477
515
  this.errors = [];
478
516
  this.stackErrors = [];
@@ -499,15 +537,14 @@ var Validator = class {
499
537
  return this.stackPath.slice(1).join(".");
500
538
  }
501
539
  validateAnnotatedType(def, value) {
502
- switch (def.type.kind) {
503
- case "object": return this.validateObject(def, value);
504
- case "union": return this.validateUnion(def, value);
505
- case "intersection": return this.validateIntersection(def, value);
506
- case "tuple": return this.validateTuple(def, value);
507
- case "array": return this.validateArray(def, value);
508
- case "": return this.validatePrimitive(def, value);
509
- default: throw new Error(`Unknown type "${def.type.kind}"`);
510
- }
540
+ return forAnnotatedType(def, {
541
+ final: (d) => this.validatePrimitive(d, value),
542
+ object: (d) => this.validateObject(d, value),
543
+ array: (d) => this.validateArray(d, value),
544
+ union: (d) => this.validateUnion(d, value),
545
+ intersection: (d) => this.validateIntersection(d, value),
546
+ tuple: (d) => this.validateTuple(d, value)
547
+ });
511
548
  }
512
549
  validateUnion(def, value) {
513
550
  let i = 0;
@@ -739,7 +776,7 @@ else {
739
776
  constructor(def, opts) {
740
777
  _define_property$1(this, "def", void 0);
741
778
  _define_property$1(this, "opts", void 0);
742
- _define_property$1(this, "errors", void 0);
779
+ /** Validation errors collected during the last {@link validate} call. */ _define_property$1(this, "errors", void 0);
743
780
  _define_property$1(this, "stackErrors", void 0);
744
781
  _define_property$1(this, "stackPath", void 0);
745
782
  this.def = def;
@@ -885,14 +922,12 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
885
922
  //#region packages/typescript/src/json-schema.ts
886
923
  function buildJsonSchema(type) {
887
924
  const build = (def) => {
888
- const t = def.type;
889
925
  const meta = def.metadata;
890
- switch (t.kind) {
891
- case "object": {
892
- const obj = t;
926
+ return forAnnotatedType(def, {
927
+ object(d) {
893
928
  const properties = {};
894
929
  const required = [];
895
- for (const [key, val] of obj.props.entries()) {
930
+ for (const [key, val] of d.type.props.entries()) {
896
931
  properties[key] = build(val);
897
932
  if (!val.optional) required.push(key);
898
933
  }
@@ -902,41 +937,36 @@ function buildJsonSchema(type) {
902
937
  };
903
938
  if (required.length) schema.required = required;
904
939
  return schema;
905
- }
906
- case "array": {
907
- const arr = t;
940
+ },
941
+ array(d) {
908
942
  const schema = {
909
943
  type: "array",
910
- items: build(arr.of)
944
+ items: build(d.type.of)
911
945
  };
912
946
  const minLength = meta.get("expect.minLength");
913
947
  if (typeof minLength === "number") schema.minItems = minLength;
914
948
  const maxLength = meta.get("expect.maxLength");
915
949
  if (typeof maxLength === "number") schema.maxItems = maxLength;
916
950
  return schema;
917
- }
918
- case "union": {
919
- const grp = t;
920
- return { anyOf: grp.items.map(build) };
921
- }
922
- case "intersection": {
923
- const grp = t;
924
- return { allOf: grp.items.map(build) };
925
- }
926
- case "tuple": {
927
- const grp = t;
951
+ },
952
+ union(d) {
953
+ return { anyOf: d.type.items.map(build) };
954
+ },
955
+ intersection(d) {
956
+ return { allOf: d.type.items.map(build) };
957
+ },
958
+ tuple(d) {
928
959
  return {
929
960
  type: "array",
930
- items: grp.items.map(build),
961
+ items: d.type.items.map(build),
931
962
  additionalItems: false
932
963
  };
933
- }
934
- case "": {
935
- const fin = t;
964
+ },
965
+ final(d) {
936
966
  const schema = {};
937
- if (fin.value !== undefined) schema.const = fin.value;
938
- if (fin.designType && fin.designType !== "any") {
939
- schema.type = fin.designType === "undefined" ? "null" : fin.designType;
967
+ if (d.type.value !== undefined) schema.const = d.type.value;
968
+ if (d.type.designType && d.type.designType !== "any") {
969
+ schema.type = d.type.designType === "undefined" ? "null" : d.type.designType;
940
970
  if (schema.type === "number" && meta.get("expect.int")) schema.type = "integer";
941
971
  }
942
972
  if (schema.type === "string") {
@@ -956,8 +986,7 @@ else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern:
956
986
  }
957
987
  return schema;
958
988
  }
959
- default: return {};
960
- }
989
+ });
961
990
  };
962
991
  return build(type);
963
992
  }
@@ -979,7 +1008,7 @@ var JsRenderer = class extends BaseRenderer {
979
1008
  this.writeln("// prettier-ignore-start");
980
1009
  this.writeln("/* eslint-disable */");
981
1010
  const imports = ["defineAnnotatedType as $", "annotate as $a"];
982
- if (!this.opts?.preRenderJsonSchema) imports.push("buildJsonSchema as $$");
1011
+ if (resolveJsonSchemaMode(this.opts) === "lazy") imports.push("buildJsonSchema as $$");
983
1012
  this.writeln(`import { ${imports.join(", ")} } from "@atscript/typescript/utils"`);
984
1013
  }
985
1014
  buildAdHocMap(annotateNodes) {
@@ -1067,15 +1096,21 @@ else {
1067
1096
  this.writeln();
1068
1097
  }
1069
1098
  renderJsonSchemaMethod(node) {
1070
- if (this.opts?.preRenderJsonSchema) {
1099
+ const mode = resolveJsonSchemaMode(this.opts);
1100
+ const hasAnnotation = node.countAnnotations("emit.jsonSchema") > 0;
1101
+ if (hasAnnotation || mode === "bundle") {
1071
1102
  const schema = JSON.stringify(buildJsonSchema(this.toAnnotatedType(node)));
1072
1103
  this.writeln("static toJsonSchema() {");
1073
1104
  this.indent().writeln(`return ${schema}`).unindent();
1074
1105
  this.writeln("}");
1075
- } else {
1106
+ } else if (mode === "lazy") {
1076
1107
  this.writeln("static toJsonSchema() {");
1077
1108
  this.indent().writeln("return this._jsonSchema ?? (this._jsonSchema = $$(this))").unindent();
1078
1109
  this.writeln("}");
1110
+ } else {
1111
+ this.writeln("static toJsonSchema() {");
1112
+ this.indent().writeln("throw new Error(\"JSON Schema support is disabled. To enable, set `jsonSchema: 'lazy'` or `jsonSchema: 'bundle'` in tsPlugin options, or add @emit.jsonSchema annotation to individual interfaces.\")").unindent();
1113
+ this.writeln("}");
1079
1114
  }
1080
1115
  }
1081
1116
  toAnnotatedType(node) {
@@ -1518,6 +1553,10 @@ else {
1518
1553
 
1519
1554
  //#endregion
1520
1555
  //#region packages/typescript/src/plugin.ts
1556
+ function resolveJsonSchemaMode(opts) {
1557
+ if (opts?.jsonSchema !== undefined) return opts.jsonSchema;
1558
+ return false;
1559
+ }
1521
1560
  const tsPlugin = (opts) => {
1522
1561
  return {
1523
1562
  name: "typesccript",
package/dist/index.d.ts CHANGED
@@ -2,9 +2,15 @@ import { TAtscriptPlugin } from '@atscript/core';
2
2
 
3
3
  interface TTsPluginOptions {
4
4
  /**
5
- * Render JSON schemas at build-time
5
+ * JSON Schema support mode:
6
+ * - `false` — No support. `toJsonSchema()` throws at runtime. No `buildJsonSchema` import. *(default)*
7
+ * - `'lazy'` — Import `buildJsonSchema`, compute on demand, cache in type object.
8
+ * - `'bundle'` — Pre-compute at build time, embed static JSON in output. No import.
9
+ *
10
+ * Individual interfaces can override this with `@emit.jsonSchema` annotation
11
+ * to force build-time embedding regardless of this setting.
6
12
  */
7
- preRenderJsonSchema?: boolean;
13
+ jsonSchema?: false | 'lazy' | 'bundle';
8
14
  }
9
15
  declare const tsPlugin: (opts?: TTsPluginOptions) => TAtscriptPlugin;
10
16