@atscript/typescript 0.1.31 → 0.1.33

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