@openpkg-ts/sdk 0.45.0 → 0.47.0

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +187 -98
  2. package/dist/index.js +783 -82
  3. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1848,9 +1848,13 @@ import ts4 from "typescript";
1848
1848
 
1849
1849
  // src/types/schema-builder.ts
1850
1850
  import ts3 from "typescript";
1851
+
1852
+ // src/schema/builtins.ts
1851
1853
  var BUILTIN_TYPE_SCHEMAS = {
1854
+ Array: { type: "array" },
1855
+ ReadonlyArray: { type: "array" },
1852
1856
  Date: { type: "string", format: "date-time" },
1853
- RegExp: { type: "object", description: "RegExp" },
1857
+ RegExp: { type: "object" },
1854
1858
  Error: { type: "object" },
1855
1859
  Promise: { type: "object" },
1856
1860
  Map: { type: "object" },
@@ -1860,6 +1864,7 @@ var BUILTIN_TYPE_SCHEMAS = {
1860
1864
  Function: { type: "object" },
1861
1865
  ArrayBuffer: { type: "string", format: "binary" },
1862
1866
  ArrayBufferLike: { type: "string", format: "binary" },
1867
+ SharedArrayBuffer: { type: "string", format: "binary" },
1863
1868
  DataView: { type: "string", format: "binary" },
1864
1869
  Uint8Array: { type: "string", format: "byte" },
1865
1870
  Uint16Array: { type: "string", format: "byte" },
@@ -1872,6 +1877,32 @@ var BUILTIN_TYPE_SCHEMAS = {
1872
1877
  BigInt64Array: { type: "string", format: "byte" },
1873
1878
  BigUint64Array: { type: "string", format: "byte" }
1874
1879
  };
1880
+
1881
+ // src/types/schema-builder.ts
1882
+ function escapeRegex(text) {
1883
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1884
+ }
1885
+ function buildTemplatePattern(type) {
1886
+ const slotPattern = (slot) => {
1887
+ if (slot.flags & ts3.TypeFlags.NumberLike)
1888
+ return "-?\\d+(?:\\.\\d+)?";
1889
+ if (slot.flags & ts3.TypeFlags.BigIntLike)
1890
+ return "-?\\d+";
1891
+ if (slot.flags & ts3.TypeFlags.BooleanLike)
1892
+ return "(?:true|false)";
1893
+ return ".*";
1894
+ };
1895
+ let pattern = `^${escapeRegex(type.texts[0] ?? "")}`;
1896
+ type.types.forEach((slot, i) => {
1897
+ pattern += slotPattern(slot) + escapeRegex(type.texts[i + 1] ?? "");
1898
+ });
1899
+ return `${pattern}$`;
1900
+ }
1901
+ function builtinSchema(name) {
1902
+ const schema = { ...BUILTIN_TYPE_SCHEMAS[name] ?? { type: "object" } };
1903
+ setSchemaExtension(schema, "x-ts-type", name);
1904
+ return schema;
1905
+ }
1875
1906
  function setSchemaExtension(schema, key, value) {
1876
1907
  schema[key] = value;
1877
1908
  }
@@ -2169,9 +2200,15 @@ function buildSchema(type, checker, ctx) {
2169
2200
  return ensureNonEmptySchema(schema, type, checker);
2170
2201
  }
2171
2202
  function buildMaxDepthSchema(type, checker) {
2203
+ if (type.flags & ts3.TypeFlags.TypeParameter && type.isThisType !== true) {
2204
+ return { "x-ts-type": checker.typeToString(type) };
2205
+ }
2172
2206
  const symbol = type.getSymbol() || type.aliasSymbol;
2173
2207
  if (symbol && !isAnonymous(type)) {
2174
2208
  const name = symbol.getName();
2209
+ if (BUILTIN_TYPES.has(name) || isBuiltinGeneric(name)) {
2210
+ return builtinSchema(name);
2211
+ }
2175
2212
  if (!name.startsWith("__") && !isPrimitiveName(name)) {
2176
2213
  return { $ref: `#/types/${name}` };
2177
2214
  }
@@ -2188,6 +2225,13 @@ function buildMaxDepthSchema(type, checker) {
2188
2225
  return { type: "null" };
2189
2226
  if (type.flags & ts3.TypeFlags.Void)
2190
2227
  return { type: "void" };
2228
+ if (type.flags & ts3.TypeFlags.TemplateLiteral) {
2229
+ return {
2230
+ type: "string",
2231
+ pattern: buildTemplatePattern(type),
2232
+ "x-ts-type": renderTypeText(type, checker)
2233
+ };
2234
+ }
2191
2235
  if (type.isUnion()) {
2192
2236
  const schemas = type.types.map((t) => buildMaxDepthSchema(t, checker));
2193
2237
  return { anyOf: schemas };
@@ -2209,7 +2253,11 @@ function buildSchemaInternal(type, checker, ctx) {
2209
2253
  }
2210
2254
  const symbol = type.getSymbol() || type.aliasSymbol;
2211
2255
  if (symbol && !isAnonymous(type)) {
2212
- return { $ref: `#/types/${symbol.getName()}` };
2256
+ const name = symbol.getName();
2257
+ if (BUILTIN_TYPES.has(name) || isBuiltinGeneric(name)) {
2258
+ return builtinSchema(name);
2259
+ }
2260
+ return { $ref: `#/types/${name}` };
2213
2261
  }
2214
2262
  return { type: checker.typeToString(type) };
2215
2263
  }
@@ -2250,6 +2298,9 @@ function buildSchemaInternal(type, checker, ctx) {
2250
2298
  };
2251
2299
  }
2252
2300
  }
2301
+ if (type.flags & ts3.TypeFlags.TypeParameter) {
2302
+ return { "x-ts-type": checker.typeToString(type) };
2303
+ }
2253
2304
  if (type.flags & ts3.TypeFlags.StringLiteral) {
2254
2305
  const literal = type.value;
2255
2306
  return { type: "string", enum: [literal] };
@@ -2273,6 +2324,16 @@ function buildSchemaInternal(type, checker, ctx) {
2273
2324
  return schema;
2274
2325
  }
2275
2326
  }
2327
+ if (type.flags & ts3.TypeFlags.TemplateLiteral) {
2328
+ return {
2329
+ type: "string",
2330
+ pattern: buildTemplatePattern(type),
2331
+ "x-ts-type": renderTypeText(type, checker)
2332
+ };
2333
+ }
2334
+ if (type.flags & ts3.TypeFlags.StringMapping) {
2335
+ return { type: "string", "x-ts-type": renderTypeText(type, checker) };
2336
+ }
2276
2337
  if (type.isUnion()) {
2277
2338
  const types = type.types;
2278
2339
  const allStringLiterals = types.every((t) => t.flags & ts3.TypeFlags.StringLiteral);
@@ -2285,12 +2346,23 @@ function buildSchemaInternal(type, checker, ctx) {
2285
2346
  const enumValues = types.map((t) => t.value);
2286
2347
  return { type: "number", enum: enumValues };
2287
2348
  }
2349
+ const allBooleanLiterals = types.every((t) => t.flags & ts3.TypeFlags.BooleanLiteral);
2350
+ if (allBooleanLiterals) {
2351
+ return { type: "boolean" };
2352
+ }
2353
+ const isBoolLiteral = (t) => !!(t.flags & ts3.TypeFlags.BooleanLiteral);
2354
+ let members = types;
2355
+ if (types.filter(isBoolLiteral).length === 2) {
2356
+ const firstBool = types.findIndex(isBoolLiteral);
2357
+ members = types.filter((t, i) => !isBoolLiteral(t) || i === firstBool);
2358
+ }
2359
+ const buildBranch = (t) => isBoolLiteral(t) ? { type: "boolean" } : buildSchema(t, checker, ctx);
2288
2360
  if (ctx) {
2289
2361
  return withDepth(ctx, () => ({
2290
- anyOf: types.map((t) => buildSchema(t, checker, ctx))
2362
+ anyOf: members.map(buildBranch)
2291
2363
  }));
2292
2364
  }
2293
- return { anyOf: types.map((t) => buildSchema(t, checker, ctx)) };
2365
+ return { anyOf: members.map(buildBranch) };
2294
2366
  }
2295
2367
  const isIntersectionType = type.isIntersection() || !!(type.flags & ts3.TypeFlags.Intersection);
2296
2368
  if (isIntersectionType && "types" in type) {
@@ -2311,7 +2383,7 @@ function buildSchemaInternal(type, checker, ctx) {
2311
2383
  }
2312
2384
  const typeString = checker.typeToString(type);
2313
2385
  if (typeString === "never[]" || typeString === "[]") {
2314
- return { type: "array", prefixedItems: [], minItems: 0, maxItems: 0 };
2386
+ return { type: "array", prefixItems: [], minItems: 0, maxItems: 0 };
2315
2387
  }
2316
2388
  const symbol = type.getSymbol() || type.aliasSymbol;
2317
2389
  if (symbol?.getName() === "Array" && isBuiltinSymbol(symbol)) {
@@ -2348,7 +2420,7 @@ function buildSchemaInternal(type, checker, ctx) {
2348
2420
  try {
2349
2421
  return {
2350
2422
  type: "array",
2351
- prefixedItems: elementTypes.map((t) => buildSchema(t, checker, ctx)),
2423
+ prefixItems: elementTypes.map((t) => buildSchema(t, checker, ctx)),
2352
2424
  minItems: elementTypes.length,
2353
2425
  maxItems: elementTypes.length
2354
2426
  };
@@ -2359,7 +2431,7 @@ function buildSchemaInternal(type, checker, ctx) {
2359
2431
  }
2360
2432
  return {
2361
2433
  type: "array",
2362
- prefixedItems: elementTypes.map((t) => buildSchema(t, checker, ctx)),
2434
+ prefixItems: elementTypes.map((t) => buildSchema(t, checker, ctx)),
2363
2435
  minItems: elementTypes.length,
2364
2436
  maxItems: elementTypes.length
2365
2437
  };
@@ -2370,9 +2442,16 @@ function buildSchemaInternal(type, checker, ctx) {
2370
2442
  const symbol2 = typeRef.target.getSymbol();
2371
2443
  const name = symbol2?.getName();
2372
2444
  if (name && BUILTIN_TYPES.has(name)) {
2373
- return { $ref: `#/types/${name}` };
2445
+ return builtinSchema(name);
2374
2446
  }
2375
- if (name && (isBuiltinGeneric(name) || !isAnonymous(typeRef.target))) {
2447
+ if (name && isBuiltinGeneric(name)) {
2448
+ const build = () => ({
2449
+ ...builtinSchema(name),
2450
+ typeArguments: typeArgs.map((t) => buildSchema(t, checker, ctx))
2451
+ });
2452
+ return ctx ? withDepth(ctx, build) : build();
2453
+ }
2454
+ if (name && !isAnonymous(typeRef.target)) {
2376
2455
  const packageOrigin = getTypeOrigin(typeRef.target, checker);
2377
2456
  if (ctx) {
2378
2457
  return withDepth(ctx, () => {
@@ -2401,7 +2480,7 @@ function buildSchemaInternal(type, checker, ctx) {
2401
2480
  if (aliasSymbol && aliasTypeArgs && aliasTypeArgs.length > 0) {
2402
2481
  const name = aliasSymbol.getName();
2403
2482
  if (BUILTIN_TYPES.has(name)) {
2404
- return { $ref: `#/types/${name}` };
2483
+ return builtinSchema(name);
2405
2484
  }
2406
2485
  if (RESOLVED_UTILITY_TYPES.has(name) && type.flags & ts3.TypeFlags.Object) {
2407
2486
  const props = type.getProperties();
@@ -2410,7 +2489,14 @@ function buildSchemaInternal(type, checker, ctx) {
2410
2489
  return buildObjectSchema(props, checker, ctx, type);
2411
2490
  }
2412
2491
  }
2413
- if (isBuiltinGeneric(name) || !name.startsWith("__")) {
2492
+ if (isBuiltinGeneric(name)) {
2493
+ const build = () => ({
2494
+ ...builtinSchema(name),
2495
+ typeArguments: aliasTypeArgs.map((t) => buildSchema(t, checker, ctx))
2496
+ });
2497
+ return ctx ? withDepth(ctx, build) : build();
2498
+ }
2499
+ if (!name.startsWith("__")) {
2414
2500
  const packageOrigin = getTypeOrigin(type, checker);
2415
2501
  if (ctx) {
2416
2502
  return withDepth(ctx, () => {
@@ -2446,7 +2532,10 @@ function buildSchemaInternal(type, checker, ctx) {
2446
2532
  return { type: name };
2447
2533
  }
2448
2534
  if (BUILTIN_TYPES.has(name)) {
2449
- return { $ref: `#/types/${name}` };
2535
+ return builtinSchema(name);
2536
+ }
2537
+ if (isBuiltinGeneric(name) && isBuiltinSymbol(symbol)) {
2538
+ return builtinSchema(name);
2450
2539
  }
2451
2540
  if (!name.startsWith("__")) {
2452
2541
  const packageOrigin = getTypeOrigin(type, checker);
@@ -2460,7 +2549,7 @@ function buildSchemaInternal(type, checker, ctx) {
2460
2549
  if (type.flags & ts3.TypeFlags.Object) {
2461
2550
  const objectType = type;
2462
2551
  const properties = type.getProperties();
2463
- if (properties.length > 0 || objectType.objectFlags & ts3.ObjectFlags.Anonymous) {
2552
+ if (properties.length > 0 || objectType.objectFlags & ts3.ObjectFlags.Anonymous || checker.getIndexInfosOfType(type).length > 0) {
2464
2553
  return buildObjectSchema(properties, checker, ctx, type);
2465
2554
  }
2466
2555
  }
@@ -2480,9 +2569,10 @@ function buildFunctionSchema(callSignatures, checker, ctx) {
2480
2569
  return [];
2481
2570
  const paramType = checker.getTypeOfSymbolAtLocation(param, decl);
2482
2571
  const isOptional = !!decl?.questionToken || !!decl?.initializer;
2572
+ const effectiveType = isOptional ? stripUndefinedFromType(paramType, checker) : paramType;
2483
2573
  return {
2484
2574
  name: param.getName(),
2485
- schema: buildSchema(paramType, checker, ctx),
2575
+ schema: buildSchema(effectiveType, checker, ctx),
2486
2576
  required: !isOptional
2487
2577
  };
2488
2578
  });
@@ -2503,6 +2593,8 @@ function buildFunctionSchema(callSignatures, checker, ctx) {
2503
2593
  }
2504
2594
  function buildObjectSchema(properties, checker, ctx, originalType) {
2505
2595
  const isArrayLikeType = originalType ? checker.isArrayType(originalType) || checker.isTupleType(originalType) || originalType.symbol?.getName() === "Array" && isBuiltinSymbol(originalType.symbol) : false;
2596
+ const isStringLikeType = !!(originalType && originalType.flags & ts3.TypeFlags.StringLike);
2597
+ const isNumberLikeType = !!(originalType && originalType.flags & ts3.TypeFlags.NumberLike);
2506
2598
  const buildProps = () => {
2507
2599
  const props = {};
2508
2600
  const required = [];
@@ -2513,7 +2605,15 @@ function buildObjectSchema(properties, checker, ctx, originalType) {
2513
2605
  if (isArrayLikeType && ARRAY_PROTOTYPE_METHODS.has(propName)) {
2514
2606
  continue;
2515
2607
  }
2516
- const propType = checker.getTypeOfSymbol(prop);
2608
+ if (isStringLikeType && STRING_PROTOTYPE_METHODS.has(propName)) {
2609
+ continue;
2610
+ }
2611
+ if (isNumberLikeType && NUMBER_PROTOTYPE_METHODS.has(propName)) {
2612
+ continue;
2613
+ }
2614
+ const isOptionalProp = !!(prop.flags & ts3.SymbolFlags.Optional);
2615
+ const rawPropType = checker.getTypeOfSymbol(prop);
2616
+ const propType = isOptionalProp ? stripUndefinedFromType(rawPropType, checker) : rawPropType;
2517
2617
  let propSchema = buildSchema(propType, checker, ctx);
2518
2618
  const docComment = prop.getDocumentationComment(checker);
2519
2619
  if (docComment.length > 0) {
@@ -2538,11 +2638,19 @@ function buildObjectSchema(properties, checker, ctx, originalType) {
2538
2638
  properties: props,
2539
2639
  ...required.length > 0 ? { required } : {}
2540
2640
  };
2541
- const stringIndex = originalType ? checker.getIndexInfosOfType(originalType).find((i) => i.keyType.flags & ts3.TypeFlags.String) : undefined;
2641
+ const indexInfos = originalType ? checker.getIndexInfosOfType(originalType) : [];
2642
+ const stringIndex = indexInfos.find((i) => i.keyType.flags & ts3.TypeFlags.String);
2542
2643
  if (stringIndex) {
2543
2644
  schema.additionalProperties = buildSchema(stringIndex.type, checker, ctx);
2544
2645
  }
2545
- if (Object.keys(props).length === 0 && originalType && !stringIndex) {
2646
+ const numberIndex = indexInfos.find((i) => i.keyType.flags & ts3.TypeFlags.Number);
2647
+ if (numberIndex) {
2648
+ schema.patternProperties = {
2649
+ "^\\d+$": buildSchema(numberIndex.type, checker, ctx)
2650
+ };
2651
+ setSchemaExtension(schema, "x-ts-index-key", "number");
2652
+ }
2653
+ if (Object.keys(props).length === 0 && originalType && !stringIndex && !numberIndex) {
2546
2654
  setSchemaExtension(schema, "x-ts-type", checker.typeToString(originalType));
2547
2655
  }
2548
2656
  return schema;
@@ -2701,6 +2809,9 @@ function extractParameters(signature, ctx) {
2701
2809
  if (description) {
2702
2810
  paramResult.description = description;
2703
2811
  }
2812
+ if (decl.initializer) {
2813
+ applyDefault(paramResult, decl.initializer);
2814
+ }
2704
2815
  result.push(paramResult);
2705
2816
  }
2706
2817
  }
@@ -2733,7 +2844,7 @@ function expandBindingPattern(paramDecl, paramType, jsdocTags, ctx) {
2733
2844
  param.description = description;
2734
2845
  }
2735
2846
  if (element.initializer) {
2736
- param.default = extractDefaultValue(element.initializer);
2847
+ applyDefault(param, element.initializer);
2737
2848
  }
2738
2849
  result.push(param);
2739
2850
  }
@@ -2781,23 +2892,37 @@ function inferParamAlias(jsdocTags) {
2781
2892
  counts.set(p, (counts.get(p) ?? 0) + 1);
2782
2893
  return Array.from(counts.entries()).sort((a, b) => b[1] - a[1])[0]?.[0];
2783
2894
  }
2784
- function extractDefaultValue(initializer) {
2895
+ function extractLiteralDefault(initializer) {
2785
2896
  if (ts4.isStringLiteral(initializer)) {
2786
- return initializer.text;
2897
+ return { literal: true, value: initializer.text };
2787
2898
  }
2788
2899
  if (ts4.isNumericLiteral(initializer)) {
2789
- return Number(initializer.text);
2900
+ return { literal: true, value: Number(initializer.text) };
2901
+ }
2902
+ if (ts4.isPrefixUnaryExpression(initializer) && initializer.operator === ts4.SyntaxKind.MinusToken && ts4.isNumericLiteral(initializer.operand)) {
2903
+ return { literal: true, value: -Number(initializer.operand.text) };
2790
2904
  }
2791
2905
  if (initializer.kind === ts4.SyntaxKind.TrueKeyword) {
2792
- return true;
2906
+ return { literal: true, value: true };
2793
2907
  }
2794
2908
  if (initializer.kind === ts4.SyntaxKind.FalseKeyword) {
2795
- return false;
2909
+ return { literal: true, value: false };
2796
2910
  }
2797
2911
  if (initializer.kind === ts4.SyntaxKind.NullKeyword) {
2798
- return null;
2912
+ return { literal: true, value: null };
2913
+ }
2914
+ return { literal: false, text: initializer.getText() };
2915
+ }
2916
+ function applyDefault(param, initializer) {
2917
+ const extracted = extractLiteralDefault(initializer);
2918
+ if (extracted.literal) {
2919
+ param.default = extracted.value;
2920
+ if (param.schema && typeof param.schema === "object" && !Array.isArray(param.schema)) {
2921
+ param.schema.default = extracted.value;
2922
+ }
2923
+ } else if (param.schema && typeof param.schema === "object" && !Array.isArray(param.schema)) {
2924
+ param.schema["x-ts-default"] = extracted.text;
2799
2925
  }
2800
- return initializer.getText();
2801
2926
  }
2802
2927
  function registerReferencedTypes(type, ctx, depth = 0) {
2803
2928
  if (depth > ctx.maxTypeDepth)
@@ -2826,6 +2951,10 @@ function registerReferencedTypes(type, ctx, depth = 0) {
2826
2951
  registerReferencedTypes(t, ctx, depth + 1);
2827
2952
  }
2828
2953
  }
2954
+ const typeSymbol = type.aliasSymbol ?? type.getSymbol();
2955
+ if (typeSymbol && ctx.shouldExpandExternal && !typeSymbol.getName().startsWith("__") && !ctx.shouldExpandExternal(typeSymbol)) {
2956
+ return;
2957
+ }
2829
2958
  if (type.flags & ts4.TypeFlags.Object) {
2830
2959
  const props = type.getProperties();
2831
2960
  const limit = ctx.maxProperties;
@@ -2960,6 +3089,16 @@ class TypeRegistry {
2960
3089
  return;
2961
3090
  if (this.has(name))
2962
3091
  return name;
3092
+ if (ctx.shouldExpandExternal && !ctx.shouldExpandExternal(symbol)) {
3093
+ this.add({
3094
+ id: name,
3095
+ name,
3096
+ kind: "external",
3097
+ external: true,
3098
+ schema: { "x-ts-type": renderTypeText(type, ctx.typeChecker) }
3099
+ });
3100
+ return name;
3101
+ }
2963
3102
  if (this.processing.has(name))
2964
3103
  return name;
2965
3104
  this.processing.add(name);
@@ -3007,10 +3146,15 @@ class TypeRegistry {
3007
3146
  schema["x-ts-type"] = text;
3008
3147
  }
3009
3148
  }
3149
+ let typeParameters;
3150
+ if (decl && (ts5.isTypeAliasDeclaration(decl) || ts5.isInterfaceDeclaration(decl) || ts5.isClassDeclaration(decl))) {
3151
+ typeParameters = extractTypeParameters(decl, checker);
3152
+ }
3010
3153
  return {
3011
3154
  id: name,
3012
3155
  name,
3013
3156
  kind,
3157
+ ...typeParameters && typeParameters.length > 0 ? { typeParameters } : {},
3014
3158
  schema,
3015
3159
  ...external ? { external: true } : {}
3016
3160
  };
@@ -3057,7 +3201,7 @@ class TypeRegistry {
3057
3201
  const elementTypes = checker.getTypeArguments(type) ?? [];
3058
3202
  return {
3059
3203
  type: "array",
3060
- prefixedItems: elementTypes.map((t) => buildSchema(t, checker, ctx)),
3204
+ prefixItems: elementTypes.map((t) => buildSchema(t, checker, ctx)),
3061
3205
  minItems: elementTypes.length,
3062
3206
  maxItems: elementTypes.length
3063
3207
  };
@@ -3066,6 +3210,9 @@ class TypeRegistry {
3066
3210
  const elementType = checker.getTypeArguments(type)?.[0];
3067
3211
  return elementType ? { type: "array", items: buildSchema(elementType, checker, ctx) } : { type: "array" };
3068
3212
  }
3213
+ if (type.flags & ts5.TypeFlags.Conditional) {
3214
+ return { "x-ts-type": renderTypeText(type, checker) };
3215
+ }
3069
3216
  return this.buildObjectSchemaFromProperties(type, checker, ctx);
3070
3217
  }
3071
3218
  buildEnumSchema(symbol, checker) {
@@ -3095,8 +3242,10 @@ class TypeRegistry {
3095
3242
  }
3096
3243
  buildObjectSchemaFromProperties(type, checker, ctx) {
3097
3244
  const properties = type.getProperties();
3098
- const stringIndex = checker.getIndexInfosOfType(type).find((i) => i.keyType.flags & ts5.TypeFlags.String);
3099
- if (properties.length === 0 && !stringIndex) {
3245
+ const indexInfos = checker.getIndexInfosOfType(type);
3246
+ const stringIndex = indexInfos.find((i) => i.keyType.flags & ts5.TypeFlags.String);
3247
+ const numberIndex = indexInfos.find((i) => i.keyType.flags & ts5.TypeFlags.Number);
3248
+ if (properties.length === 0 && !stringIndex && !numberIndex) {
3100
3249
  return { type: checker.typeToString(type) };
3101
3250
  }
3102
3251
  const props = {};
@@ -3123,7 +3272,8 @@ class TypeRegistry {
3123
3272
  }
3124
3273
  for (const prop of included.slice(0, limit)) {
3125
3274
  const propName = prop.getName();
3126
- const propType = checker.getTypeOfSymbol(prop);
3275
+ const rawPropType = checker.getTypeOfSymbol(prop);
3276
+ const propType = prop.flags & ts5.SymbolFlags.Optional ? stripUndefinedFromType(rawPropType, checker) : rawPropType;
3127
3277
  this.registerType(propType, ctx);
3128
3278
  let propSchema = buildSchema(propType, checker, ctx);
3129
3279
  const docComment = prop.getDocumentationComment(checker);
@@ -3148,7 +3298,11 @@ class TypeRegistry {
3148
3298
  type: "object",
3149
3299
  properties: props,
3150
3300
  ...required.length > 0 ? { required } : {},
3151
- ...stringIndex ? { additionalProperties: buildSchema(stringIndex.type, checker, ctx) } : {}
3301
+ ...stringIndex ? { additionalProperties: buildSchema(stringIndex.type, checker, ctx) } : {},
3302
+ ...numberIndex ? {
3303
+ patternProperties: { "^\\d+$": buildSchema(numberIndex.type, checker, ctx) },
3304
+ "x-ts-index-key": "number"
3305
+ } : {}
3152
3306
  };
3153
3307
  }
3154
3308
  }
@@ -3169,7 +3323,8 @@ function createContext(program, sourceFile, options = {}) {
3169
3323
  registeredTypes: new Set,
3170
3324
  includePrivate: options.includePrivate ?? false,
3171
3325
  maxProperties: options.maxProperties ?? 500,
3172
- onTruncation: options.onTruncation
3326
+ onTruncation: options.onTruncation,
3327
+ shouldExpandExternal: options.shouldExpandExternal
3173
3328
  };
3174
3329
  }
3175
3330
  function getInheritedMembers(classType, ownMemberNames, ctx, isStatic = false) {
@@ -3297,7 +3452,7 @@ function serializeInheritedMember(symbol, inheritedFrom, ctx, isStatic) {
3297
3452
  description,
3298
3453
  tags: tags.length > 0 ? tags : undefined,
3299
3454
  visibility,
3300
- schema: kind !== "method" ? buildSchema(type, checker, ctx) : undefined,
3455
+ schema: kind !== "method" ? buildSchema(type, checker, ctx) : decoratePropertySchema({ "x-ts-function": true }, symbol, type, checker),
3301
3456
  signatures,
3302
3457
  flags: Object.keys(flags).length > 0 ? flags : undefined
3303
3458
  };
@@ -3458,7 +3613,8 @@ function serializeProperty(node, ctx) {
3458
3613
  if (!ctx.includePrivate && (visibility === "private" || visibility === "protected")) {
3459
3614
  return null;
3460
3615
  }
3461
- const type = checker.getTypeAtLocation(node);
3616
+ const rawType = checker.getTypeAtLocation(node);
3617
+ const type = node.questionToken ? stripUndefinedFromType(rawType, checker) : rawType;
3462
3618
  registerReferencedTypes(type, ctx);
3463
3619
  let schema = buildSchema(type, checker, ctx);
3464
3620
  const flags = {};
@@ -3512,12 +3668,14 @@ function serializeMethod(node, ctx) {
3512
3668
  }
3513
3669
  const symbol = checker.getSymbolAtLocation(node.name ?? node);
3514
3670
  const { deprecated, reason: deprecationReason } = isSymbolDeprecated(symbol);
3671
+ const schema = symbol ? decoratePropertySchema({ "x-ts-function": true }, symbol, type, checker) : undefined;
3515
3672
  return {
3516
3673
  name,
3517
3674
  kind: "method",
3518
3675
  description,
3519
3676
  tags: tags.length > 0 ? tags : undefined,
3520
3677
  visibility,
3678
+ schema,
3521
3679
  signatures: signatures.length > 0 ? signatures : undefined,
3522
3680
  flags: Object.keys(flags).length > 0 ? flags : undefined,
3523
3681
  ...deprecated ? { deprecated: true, deprecationReason } : {}
@@ -3850,7 +4008,8 @@ function serializePropertySignature(node, ctx) {
3850
4008
  const { typeChecker: checker } = ctx;
3851
4009
  const name = node.name.getText();
3852
4010
  const { description, tags } = getJSDocComment(node);
3853
- const type = checker.getTypeAtLocation(node);
4011
+ const rawType = checker.getTypeAtLocation(node);
4012
+ const type = node.questionToken ? stripUndefinedFromType(rawType, checker) : rawType;
3854
4013
  let schema = buildSchema(type, checker, ctx);
3855
4014
  registerReferencedTypes(type, ctx);
3856
4015
  const flags = {};
@@ -3887,11 +4046,13 @@ function serializeMethodSignature(node, ctx) {
3887
4046
  flags.methodSyntax = true;
3888
4047
  const symbol = checker.getSymbolAtLocation(node.name);
3889
4048
  const { deprecated, reason: deprecationReason } = isSymbolDeprecated(symbol);
4049
+ const schema = symbol ? decoratePropertySchema({ "x-ts-function": true }, symbol, type, checker) : undefined;
3890
4050
  return {
3891
4051
  name,
3892
4052
  kind: "method",
3893
4053
  description,
3894
4054
  tags: tags.length > 0 ? tags : undefined,
4055
+ schema,
3895
4056
  signatures: signatures.length > 0 ? signatures : undefined,
3896
4057
  flags: Object.keys(flags).length > 0 ? flags : undefined,
3897
4058
  ...deprecated ? { deprecated: true, deprecationReason } : {}
@@ -3989,7 +4150,7 @@ function serializeTypeAlias(node, ctx) {
3989
4150
  }
3990
4151
  } else if (isInlineFunctionAlias(node.type) && type.getCallSignatures().length > 0) {
3991
4152
  schema = buildFunctionSchema(type.getCallSignatures(), ctx.typeChecker, ctx);
3992
- } else if ((ts10.isMappedTypeNode(node.type) || ts10.isConditionalTypeNode(node.type)) && type.getProperties().length > 0 && type.getCallSignatures().length === 0) {
4153
+ } else if ((ts10.isMappedTypeNode(node.type) || ts10.isConditionalTypeNode(node.type)) && type.getProperties().length > 0 && type.getCallSignatures().length === 0 && !(type.flags & ts10.TypeFlags.Conditional) && !(type.flags & (ts10.TypeFlags.StringLike | ts10.TypeFlags.NumberLike))) {
3993
4154
  schema = buildObjectSchema(type.getProperties(), ctx.typeChecker, ctx, type);
3994
4155
  members = serializeResolvedMembers(type, node, ctx);
3995
4156
  } else {
@@ -4112,10 +4273,7 @@ function serializeResolvedMembers(type, node, ctx) {
4112
4273
  flags.readonly = true;
4113
4274
  if (prop.flags & ts10.SymbolFlags.Method)
4114
4275
  flags.methodSyntax = true;
4115
- let schema;
4116
- if (kind === "property") {
4117
- schema = decoratePropertySchema(buildSchema(propType, checker, ctx), prop, propType, checker);
4118
- }
4276
+ const schema = kind === "property" ? decoratePropertySchema(buildSchema(propType, checker, ctx), prop, propType, checker) : decoratePropertySchema({ "x-ts-function": true }, prop, propType, checker);
4119
4277
  members.push({
4120
4278
  name: prop.getName(),
4121
4279
  kind,
@@ -4324,10 +4482,7 @@ function serializeVariable(node, statement, ctx) {
4324
4482
  }
4325
4483
 
4326
4484
  // src/types/schema-normalizer.ts
4327
- var SCHEMA_DIALECT_URLS = {
4328
- "draft-2020-12": "https://json-schema.org/draft/2020-12/schema",
4329
- "draft-07": "http://json-schema.org/draft-07/schema#"
4330
- };
4485
+ import { JSON_SCHEMA_DRAFT } from "@openpkg-ts/spec";
4331
4486
  var TS_PRIMITIVE_NORMALIZATIONS = {
4332
4487
  void: () => ({ type: "null", "x-ts-type": "void" }),
4333
4488
  never: () => ({ not: {} }),
@@ -4338,11 +4493,11 @@ var TS_PRIMITIVE_NORMALIZATIONS = {
4338
4493
  symbol: () => ({ type: "string", "x-ts-type": "symbol" })
4339
4494
  };
4340
4495
  function normalizeSchema(schema, options = {}) {
4341
- const { includeSchemaField = false, dialect = "draft-2020-12" } = options;
4496
+ const { includeSchemaField = false } = options;
4342
4497
  const normalized = normalizeSchemaInternal(schema, options);
4343
4498
  if (includeSchemaField && typeof normalized === "object") {
4344
4499
  return {
4345
- $schema: SCHEMA_DIALECT_URLS[dialect],
4500
+ $schema: JSON_SCHEMA_DRAFT,
4346
4501
  ...normalized
4347
4502
  };
4348
4503
  }
@@ -4363,6 +4518,9 @@ function normalizeSchemaInternal(schema, options) {
4363
4518
  result[key] = s[key];
4364
4519
  }
4365
4520
  }
4521
+ if (Array.isArray(s.typeArguments) && s.typeArguments.length > 0 && result["x-ts-type-arguments"] === undefined) {
4522
+ result["x-ts-type-arguments"] = s.typeArguments.map((arg) => normalizeSchemaInternal(arg, options));
4523
+ }
4366
4524
  }
4367
4525
  return result;
4368
4526
  }
@@ -4468,14 +4626,14 @@ function normalizeSignature(signature, options) {
4468
4626
  }
4469
4627
  function normalizeTupleType(schema, options) {
4470
4628
  const result = { type: "array" };
4471
- if ("items" in schema && Array.isArray(schema.items)) {
4472
- result.prefixedItems = schema.items.map((item) => normalizeSchemaInternal(item, options));
4629
+ const prefix = Array.isArray(schema.prefixItems) ? schema.prefixItems : Array.isArray(schema.prefixedItems) ? schema.prefixedItems : undefined;
4630
+ if (prefix) {
4631
+ result.prefixItems = prefix.map((item) => normalizeSchemaInternal(item, options));
4632
+ } else if ("items" in schema && Array.isArray(schema.items)) {
4633
+ result.prefixItems = schema.items.map((item) => normalizeSchemaInternal(item, options));
4473
4634
  result.minItems = schema.items.length;
4474
4635
  result.maxItems = schema.items.length;
4475
4636
  }
4476
- if ("prefixedItems" in schema && Array.isArray(schema.prefixedItems)) {
4477
- result.prefixedItems = schema.prefixedItems.map((item) => normalizeSchemaInternal(item, options));
4478
- }
4479
4637
  if ("minItems" in schema && typeof schema.minItems === "number") {
4480
4638
  result.minItems = schema.minItems;
4481
4639
  }
@@ -4492,8 +4650,9 @@ function normalizeArrayType(schema, options) {
4492
4650
  if ("items" in schema && schema.items && !Array.isArray(schema.items)) {
4493
4651
  result.items = normalizeSchemaInternal(schema.items, options);
4494
4652
  }
4495
- if ("prefixedItems" in schema && Array.isArray(schema.prefixedItems)) {
4496
- result.prefixedItems = schema.prefixedItems.map((item) => normalizeSchemaInternal(item, options));
4653
+ const arrayPrefix = Array.isArray(schema.prefixItems) ? schema.prefixItems : Array.isArray(schema.prefixedItems) ? schema.prefixedItems : undefined;
4654
+ if (arrayPrefix) {
4655
+ result.prefixItems = arrayPrefix.map((item) => normalizeSchemaInternal(item, options));
4497
4656
  }
4498
4657
  if ("minItems" in schema && typeof schema.minItems === "number") {
4499
4658
  result.minItems = schema.minItems;
@@ -4501,6 +4660,17 @@ function normalizeArrayType(schema, options) {
4501
4660
  if ("maxItems" in schema && typeof schema.maxItems === "number") {
4502
4661
  result.maxItems = schema.maxItems;
4503
4662
  }
4663
+ if (schema.uniqueItems === true) {
4664
+ result.uniqueItems = true;
4665
+ }
4666
+ if (schema.contains && typeof schema.contains === "object") {
4667
+ result.contains = normalizeSchemaInternal(schema.contains, options);
4668
+ }
4669
+ for (const keyword of ["title", "default"]) {
4670
+ if (keyword in schema && schema[keyword] !== undefined) {
4671
+ result[keyword] = schema[keyword];
4672
+ }
4673
+ }
4504
4674
  if ("description" in schema && schema.description) {
4505
4675
  result.description = schema.description;
4506
4676
  }
@@ -4525,6 +4695,23 @@ function normalizeObjectType(schema, options) {
4525
4695
  result.additionalProperties = normalizeSchemaInternal(schema.additionalProperties, options);
4526
4696
  }
4527
4697
  }
4698
+ for (const keyword of ["patternProperties", "$defs"]) {
4699
+ const value = schema[keyword];
4700
+ if (value && typeof value === "object" && !Array.isArray(value)) {
4701
+ result[keyword] = Object.fromEntries(Object.entries(value).map(([key, nested]) => [
4702
+ key,
4703
+ normalizeSchemaInternal(nested, options)
4704
+ ]));
4705
+ }
4706
+ }
4707
+ if (schema.propertyNames && typeof schema.propertyNames === "object") {
4708
+ result.propertyNames = normalizeSchemaInternal(schema.propertyNames, options);
4709
+ }
4710
+ for (const keyword of ["title", "default", "examples", "minProperties", "maxProperties"]) {
4711
+ if (keyword in schema && schema[keyword] !== undefined) {
4712
+ result[keyword] = schema[keyword];
4713
+ }
4714
+ }
4528
4715
  if ("description" in schema && schema.description) {
4529
4716
  result.description = schema.description;
4530
4717
  }
@@ -4631,7 +4818,7 @@ function isSchemaLike(value) {
4631
4818
  if (typeof value === "string")
4632
4819
  return true;
4633
4820
  const obj = value;
4634
- return "type" in obj || "$ref" in obj || "anyOf" in obj || "allOf" in obj || "oneOf" in obj || "properties" in obj || "items" in obj || "prefixedItems" in obj;
4821
+ return "type" in obj || "$ref" in obj || "anyOf" in obj || "allOf" in obj || "oneOf" in obj || "properties" in obj || "items" in obj || "prefixItems" in obj || "prefixedItems" in obj;
4635
4822
  }
4636
4823
  function mergeSchemaFields(target, source, excludeKeys) {
4637
4824
  if (typeof source !== "object" || source == null) {
@@ -4648,9 +4835,13 @@ function mergeSchemaFields(target, source, excludeKeys) {
4648
4835
  }
4649
4836
  return result;
4650
4837
  }
4838
+ function isVendorSchemaExport(exp) {
4839
+ return !!exp.tags?.some((t) => t.name === "schema-source" && t.text === "standard-json-schema");
4840
+ }
4651
4841
  function normalizeExport(exp, options = {}) {
4652
4842
  const result = { ...exp };
4653
- if (exp.schema) {
4843
+ const vendorSchema = isVendorSchemaExport(exp);
4844
+ if (exp.schema && !vendorSchema) {
4654
4845
  result.schema = normalizeSchema(exp.schema, options);
4655
4846
  }
4656
4847
  if (exp.signatures) {
@@ -4659,7 +4850,7 @@ function normalizeExport(exp, options = {}) {
4659
4850
  if (exp.members) {
4660
4851
  result.members = exp.members.map((member) => normalizeMember(member, options));
4661
4852
  }
4662
- if (shouldGenerateMembersSchema(exp.kind) && exp.members && exp.members.length > 0) {
4853
+ if (!vendorSchema && shouldGenerateMembersSchema(exp.kind) && exp.members && exp.members.length > 0) {
4663
4854
  result.schema = normalizeMembers(exp.members, options);
4664
4855
  }
4665
4856
  return result;
@@ -4710,10 +4901,15 @@ function normalizeMembers(members, options = {}) {
4710
4901
  const properties = {};
4711
4902
  const required = [];
4712
4903
  let additionalProperties;
4904
+ let numberIndexSchema;
4713
4905
  for (const member of members) {
4714
4906
  const { name, kind } = member;
4715
4907
  if (kind === "index" || kind === "index-signature") {
4716
- additionalProperties = normalizeMemberToSchema(member, options);
4908
+ if (name === "[number]") {
4909
+ numberIndexSchema = normalizeMemberToSchema(member, options);
4910
+ } else {
4911
+ additionalProperties = normalizeMemberToSchema(member, options);
4912
+ }
4717
4913
  continue;
4718
4914
  }
4719
4915
  if (!name)
@@ -4734,6 +4930,10 @@ function normalizeMembers(members, options = {}) {
4734
4930
  if (additionalProperties !== undefined) {
4735
4931
  result.additionalProperties = additionalProperties;
4736
4932
  }
4933
+ if (numberIndexSchema !== undefined) {
4934
+ result.patternProperties = { "^\\d+$": numberIndexSchema };
4935
+ result["x-ts-index-key"] = "number";
4936
+ }
4737
4937
  return result;
4738
4938
  }
4739
4939
  function memberDocExtras(member) {
@@ -4794,6 +4994,10 @@ function normalizeMethodMember(member, options) {
4794
4994
  if (member.flags?.methodSyntax === true) {
4795
4995
  result["x-ts-method"] = true;
4796
4996
  }
4997
+ const memberTypeText = member.schema && typeof member.schema === "object" ? member.schema["x-ts-type"] : undefined;
4998
+ if (typeof memberTypeText === "string") {
4999
+ result["x-ts-type"] = memberTypeText;
5000
+ }
4797
5001
  if (member.signatures && member.signatures.length > 0) {
4798
5002
  result["x-ts-signatures"] = member.signatures.map((sig) => normalizeSignature(sig, options));
4799
5003
  }
@@ -4848,9 +5052,9 @@ async function getExport(options) {
4848
5052
  const isNamespaceExportDecl = originalDecls.some((d) => ts11.isNamespaceExport(d) || ts11.isNamespaceImport(d));
4849
5053
  if (isNamespaceExportDecl) {
4850
5054
  const spec2 = serializeNamespaceForGet(targetSymbol, exportName, ctx);
4851
- const types2 = ctx.typeRegistry.getAll().map((t) => normalizeType(t, { dialect: "draft-2020-12" }));
5055
+ const types2 = ctx.typeRegistry.getAll().map((t) => normalizeType(t));
4852
5056
  return {
4853
- export: normalizeExport(spec2, { dialect: "draft-2020-12" }),
5057
+ export: normalizeExport(spec2),
4854
5058
  types: types2,
4855
5059
  errors
4856
5060
  };
@@ -4883,8 +5087,8 @@ async function getExport(options) {
4883
5087
  }
4884
5088
  return { export: null, types: [], errors: [`Could not serialize '${exportName}'`] };
4885
5089
  }
4886
- spec = normalizeExport(spec, { dialect: "draft-2020-12" });
4887
- const types = ctx.typeRegistry.getAll().map((t) => normalizeType(t, { dialect: "draft-2020-12" }));
5090
+ spec = normalizeExport(spec);
5091
+ const types = ctx.typeRegistry.getAll().map((t) => normalizeType(t));
4888
5092
  return { export: spec, types, errors };
4889
5093
  } catch (err) {
4890
5094
  errors.push(`Failed to serialize '${exportName}': ${err instanceof Error ? err.message : String(err)}`);
@@ -6012,12 +6216,7 @@ var NODE_MODULES_PKG = /node_modules\/(@[^/]+\/[^/]+|[^/]+)/;
6012
6216
  function isLibFile(fileName) {
6013
6217
  return fileName.includes("/typescript/lib/lib.") || fileName.includes("\\typescript\\lib\\lib.");
6014
6218
  }
6015
- function expandReachableTypes(exportedSymbols, ctx, opts) {
6016
- if (opts.followExternal === false)
6017
- return;
6018
- const checker = ctx.typeChecker;
6019
- const visited = new Set;
6020
- const MAX_DEPTH = 30;
6219
+ function createExternalExpansionPredicate(opts) {
6021
6220
  const packageAllowed = (pkg) => {
6022
6221
  if (pkg === "typescript")
6023
6222
  return false;
@@ -6027,6 +6226,25 @@ function expandReachableTypes(exportedSymbols, ctx, opts) {
6027
6226
  return true;
6028
6227
  return opts.workspacePackages.has(pkg);
6029
6228
  };
6229
+ return (symbol) => {
6230
+ const decl = symbol.declarations?.[0];
6231
+ if (!decl)
6232
+ return false;
6233
+ const fileName = decl.getSourceFile().fileName;
6234
+ if (isLibFile(fileName))
6235
+ return false;
6236
+ const match = fileName.match(NODE_MODULES_PKG);
6237
+ if (match)
6238
+ return packageAllowed(match[1]);
6239
+ return true;
6240
+ };
6241
+ }
6242
+ function expandReachableTypes(exportedSymbols, ctx, opts) {
6243
+ if (opts.followExternal === false)
6244
+ return;
6245
+ const checker = ctx.typeChecker;
6246
+ const visited = new Set;
6247
+ const MAX_DEPTH = 30;
6030
6248
  const entryPackageDir = findPackageDir(opts.entryFile);
6031
6249
  const packageLabel = (fileName) => {
6032
6250
  const match = fileName.match(NODE_MODULES_PKG);
@@ -6041,18 +6259,7 @@ function expandReachableTypes(exportedSymbols, ctx, opts) {
6041
6259
  }
6042
6260
  return (pkg ?? "local").replace(/^@/, "").replace(/\//g, "-");
6043
6261
  };
6044
- const symbolAllowed = (symbol) => {
6045
- const decl = symbol.declarations?.[0];
6046
- if (!decl)
6047
- return false;
6048
- const fileName = decl.getSourceFile().fileName;
6049
- if (isLibFile(fileName))
6050
- return false;
6051
- const match = fileName.match(NODE_MODULES_PKG);
6052
- if (match)
6053
- return packageAllowed(match[1]);
6054
- return true;
6055
- };
6262
+ const symbolAllowed = createExternalExpansionPredicate(opts);
6056
6263
  const visit = (type, depth) => {
6057
6264
  if (!type || depth > MAX_DEPTH || visited.has(type))
6058
6265
  return;
@@ -6564,7 +6771,11 @@ async function extract(options) {
6564
6771
  resolveExternalTypes,
6565
6772
  includePrivate,
6566
6773
  maxProperties,
6567
- onTruncation
6774
+ onTruncation,
6775
+ shouldExpandExternal: createExternalExpansionPredicate({
6776
+ followExternal: options.followExternal,
6777
+ workspacePackages: result.workspacePackages ?? new Map
6778
+ })
6568
6779
  });
6569
6780
  ctx.exportedIds = exportedIds;
6570
6781
  const filteredSymbols = exportedSymbols.filter((s) => shouldIncludeExport(s.getName(), only, ignore));
@@ -6734,7 +6945,7 @@ async function extract(options) {
6734
6945
  if (options.schemaExtraction === "hybrid") {
6735
6946
  const projectBaseDir2 = baseDir || path9.dirname(entryFile);
6736
6947
  const runtimeResult = await extractStandardSchemasFromProject(entryFile, projectBaseDir2, {
6737
- target: options.schemaTarget || "draft-2020-12",
6948
+ target: "draft-2020-12",
6738
6949
  timeout: 15000
6739
6950
  });
6740
6951
  if (runtimeResult.schemas.size > 0) {
@@ -6765,8 +6976,8 @@ async function extract(options) {
6765
6976
  });
6766
6977
  }
6767
6978
  }
6768
- const normalizedExports = exports.map((exp) => normalizeExport(exp, { dialect: "draft-2020-12" }));
6769
- const normalizedTypes = types.map((t) => normalizeType(t, { dialect: "draft-2020-12" }));
6979
+ const normalizedExports = exports.map((exp) => normalizeExport(exp));
6980
+ const normalizedTypes = types.map((t) => normalizeType(t));
6770
6981
  const spec = {
6771
6982
  ...includeSchema ? { $schema: SCHEMA_URL } : {},
6772
6983
  openpkg: SCHEMA_VERSION,
@@ -7073,6 +7284,485 @@ async function getPackageMeta(entryFile, baseDir) {
7073
7284
 
7074
7285
  // src/primitives/spec.ts
7075
7286
  var extractSpec = extract;
7287
+ // src/primitives/validate.ts
7288
+ import {
7289
+ assertSpec,
7290
+ getAvailableVersions,
7291
+ getValidationErrors,
7292
+ LATEST_VERSION,
7293
+ validateSpec as validateSpec2
7294
+ } from "@openpkg-ts/spec";
7295
+ // src/schema/json-schema.ts
7296
+ import { JSON_SCHEMA_DRAFT as JSON_SCHEMA_DRAFT2 } from "@openpkg-ts/spec";
7297
+
7298
+ // src/schema/ref-walker.ts
7299
+ var INTERNAL_REF_PREFIX = "#/types/";
7300
+ function isTsExtensionKey(key) {
7301
+ return (key.startsWith("x-ts-") || key === "x-enum-members") && key !== "x-deprecated-reason";
7302
+ }
7303
+ function stripTsExtensions(schema) {
7304
+ const walk = (value) => {
7305
+ if (Array.isArray(value))
7306
+ return value.map(walk);
7307
+ if (value && typeof value === "object") {
7308
+ const out = {};
7309
+ for (const [key, nested] of Object.entries(value)) {
7310
+ if (isTsExtensionKey(key))
7311
+ continue;
7312
+ out[key] = walk(nested);
7313
+ }
7314
+ return out;
7315
+ }
7316
+ return value;
7317
+ };
7318
+ return walk(schema);
7319
+ }
7320
+ function escapePointerToken(name) {
7321
+ return name.replace(/~/g, "~0").replace(/\//g, "~1");
7322
+ }
7323
+ function bundleRefs(root, spec, options = {}) {
7324
+ const { keepExtensions = false, typeParameterNames = [], onUnresolved = "permissive" } = options;
7325
+ const typeParams = new Set(typeParameterNames);
7326
+ const warnings = [];
7327
+ const defs = {};
7328
+ const defKeyByName = new Map;
7329
+ const lookupType = (name) => {
7330
+ const types = spec.types ?? [];
7331
+ return types.find((t) => t.id === name) ?? types.find((t) => t.name === name);
7332
+ };
7333
+ const assignDefKey = (name) => {
7334
+ const existing = defKeyByName.get(name);
7335
+ if (existing)
7336
+ return existing;
7337
+ let key = escapePointerToken(name);
7338
+ if (Object.hasOwn(defs, key) || Object.values(defKeyByName).includes(key)) {
7339
+ let n = 2;
7340
+ while (Object.hasOwn(defs, `${key}_${n}`) || Object.values(defKeyByName).includes(`${key}_${n}`)) {
7341
+ n++;
7342
+ }
7343
+ warnings.push(`def key collision for "${name}" — using "${key}_${n}"`);
7344
+ key = `${key}_${n}`;
7345
+ }
7346
+ defKeyByName.set(name, key);
7347
+ return key;
7348
+ };
7349
+ const bodyOfType = (type) => {
7350
+ if (type.schema)
7351
+ return normalizeSchema(type.schema);
7352
+ if (type.members && type.members.length > 0)
7353
+ return normalizeMembers(type.members);
7354
+ return {};
7355
+ };
7356
+ const rewrite = (node) => {
7357
+ if (Array.isArray(node))
7358
+ return node.map(rewrite);
7359
+ if (!node || typeof node !== "object")
7360
+ return node;
7361
+ const obj = node;
7362
+ const ref = obj.$ref;
7363
+ if (typeof ref === "string" && ref.startsWith(INTERNAL_REF_PREFIX)) {
7364
+ const name = ref.slice(INTERNAL_REF_PREFIX.length);
7365
+ const siblings = {};
7366
+ for (const [key2, value] of Object.entries(obj)) {
7367
+ if (key2 === "$ref")
7368
+ continue;
7369
+ siblings[key2] = rewrite(value);
7370
+ }
7371
+ if (typeParams.has(name)) {
7372
+ warnings.push(`type parameter "${name}" is not an addressable schema`);
7373
+ return keepExtensions ? { ...siblings, "x-ts-type": name } : {};
7374
+ }
7375
+ const builtin = BUILTIN_TYPE_SCHEMAS[name];
7376
+ if (builtin) {
7377
+ return { ...builtin, ...keepExtensions ? siblings : {} };
7378
+ }
7379
+ const type = lookupType(name);
7380
+ if (!type) {
7381
+ warnings.push(`unresolved ref "${ref}"`);
7382
+ if (onUnresolved === "error") {
7383
+ throw new Error(`bundleRefs: cannot resolve ${ref}`);
7384
+ }
7385
+ return keepExtensions ? { ...siblings, "x-ts-type": name } : {};
7386
+ }
7387
+ const key = assignDefKey(name);
7388
+ if (!Object.hasOwn(defs, key)) {
7389
+ defs[key] = {};
7390
+ defs[key] = rewrite(bodyOfType(type));
7391
+ }
7392
+ const refNode = { $ref: `#/$defs/${key}` };
7393
+ return { ...siblings, ...refNode };
7394
+ }
7395
+ const out = {};
7396
+ for (const [key, value] of Object.entries(obj)) {
7397
+ out[key] = rewrite(value);
7398
+ }
7399
+ return out;
7400
+ };
7401
+ let schema = rewrite(normalizeSchema(root));
7402
+ if (!keepExtensions) {
7403
+ schema = stripTsExtensions(schema);
7404
+ for (const key of Object.keys(defs)) {
7405
+ defs[key] = stripTsExtensions(defs[key]);
7406
+ }
7407
+ }
7408
+ return { schema, defs, warnings };
7409
+ }
7410
+
7411
+ // src/schema/json-schema.ts
7412
+ function typeParamNames(subject) {
7413
+ return (subject.typeParameters ?? []).map((p) => p.name);
7414
+ }
7415
+ function subjectSchema(subject) {
7416
+ if (subject.schema)
7417
+ return normalizeSchema(subject.schema);
7418
+ if (subject.members && subject.members.length > 0)
7419
+ return normalizeMembers(subject.members);
7420
+ return {};
7421
+ }
7422
+ function exportToJsonSchema(subject, spec, options = {}) {
7423
+ const { keepExtensions = false, includeSchemaField = true } = options;
7424
+ const { schema, defs } = bundleRefs(subjectSchema(subject), spec, {
7425
+ keepExtensions,
7426
+ typeParameterNames: typeParamNames(subject)
7427
+ });
7428
+ const doc = { ...schema };
7429
+ if (Object.keys(defs).length > 0)
7430
+ doc.$defs = defs;
7431
+ if (includeSchemaField)
7432
+ return { $schema: JSON_SCHEMA_DRAFT2, ...doc };
7433
+ return doc;
7434
+ }
7435
+ function toJsonSchema(spec, options = {}) {
7436
+ const { root, keepExtensions = false, includeSchemaField = true } = options;
7437
+ if (root) {
7438
+ const subject = spec.exports.find((e) => e.name === root) ?? (spec.types ?? []).find((t) => t.name === root);
7439
+ if (!subject) {
7440
+ throw new Error(`toJsonSchema: no export or type named "${root}"`);
7441
+ }
7442
+ return exportToJsonSchema(subject, spec, { keepExtensions, includeSchemaField });
7443
+ }
7444
+ const defs = {};
7445
+ const mergeInto = (name, bundled) => {
7446
+ for (const [key, value] of Object.entries(bundled.defs)) {
7447
+ if (!Object.hasOwn(defs, key))
7448
+ defs[key] = value;
7449
+ }
7450
+ if (!Object.hasOwn(defs, name))
7451
+ defs[name] = bundled.schema;
7452
+ };
7453
+ for (const type of spec.types ?? []) {
7454
+ mergeInto(type.name, bundleRefs(subjectSchema(type), spec, {
7455
+ keepExtensions,
7456
+ typeParameterNames: typeParamNames(type)
7457
+ }));
7458
+ }
7459
+ for (const exp of spec.exports) {
7460
+ if (!exp.schema && !(exp.members && exp.members.length > 0))
7461
+ continue;
7462
+ mergeInto(exp.name, bundleRefs(subjectSchema(exp), spec, {
7463
+ keepExtensions,
7464
+ typeParameterNames: typeParamNames(exp)
7465
+ }));
7466
+ }
7467
+ const doc = { $defs: defs };
7468
+ if (includeSchemaField)
7469
+ return { $schema: JSON_SCHEMA_DRAFT2, ...doc };
7470
+ return doc;
7471
+ }
7472
+
7473
+ // src/schema/as-standard-schema.ts
7474
+ var DRAFT_07_URL = "http://json-schema.org/draft-07/schema#";
7475
+ function rewriteDefsPointers(value) {
7476
+ if (Array.isArray(value))
7477
+ return value.map(rewriteDefsPointers);
7478
+ if (value && typeof value === "object") {
7479
+ const out = {};
7480
+ for (const [key, nested] of Object.entries(value)) {
7481
+ if (key === "$ref" && typeof nested === "string") {
7482
+ out.$ref = nested.replace("#/$defs/", "#/definitions/");
7483
+ } else {
7484
+ out[key] = rewriteDefsPointers(nested);
7485
+ }
7486
+ }
7487
+ return out;
7488
+ }
7489
+ return value;
7490
+ }
7491
+ function downlevelToDraft07(doc) {
7492
+ const convert = (value) => {
7493
+ if (Array.isArray(value))
7494
+ return value.map(convert);
7495
+ if (value && typeof value === "object") {
7496
+ const obj = value;
7497
+ const out = {};
7498
+ for (const [key, nested] of Object.entries(obj)) {
7499
+ if (key === "$defs") {
7500
+ out.definitions = convert(nested);
7501
+ } else if (key === "prefixItems" && Array.isArray(nested)) {
7502
+ out.items = convert(nested);
7503
+ } else {
7504
+ out[key] = convert(nested);
7505
+ }
7506
+ }
7507
+ return out;
7508
+ }
7509
+ return value;
7510
+ };
7511
+ const converted = rewriteDefsPointers(convert(doc));
7512
+ converted.$schema = DRAFT_07_URL;
7513
+ return converted;
7514
+ }
7515
+ function downlevelToOpenApi30(doc) {
7516
+ const convert = (value) => {
7517
+ if (Array.isArray(value))
7518
+ return value.map(convert);
7519
+ if (!value || typeof value !== "object")
7520
+ return value;
7521
+ const obj = value;
7522
+ const out = {};
7523
+ if (Array.isArray(obj.anyOf)) {
7524
+ const branches = obj.anyOf;
7525
+ const nulls = branches.filter((b) => b && b.type === "null");
7526
+ const rest = branches.filter((b) => !(b && b.type === "null"));
7527
+ if (nulls.length > 0 && rest.length === 1) {
7528
+ Object.assign(out, convert(rest[0]));
7529
+ out.nullable = true;
7530
+ for (const [key, nested] of Object.entries(obj)) {
7531
+ if (key !== "anyOf")
7532
+ out[key] = convert(nested);
7533
+ }
7534
+ return out;
7535
+ }
7536
+ }
7537
+ for (const [key, nested] of Object.entries(obj)) {
7538
+ if (key === "$schema")
7539
+ continue;
7540
+ if (key === "$defs") {
7541
+ out.definitions = convert(nested);
7542
+ } else if (key === "const") {
7543
+ out.enum = [nested];
7544
+ } else {
7545
+ out[key] = convert(nested);
7546
+ }
7547
+ }
7548
+ return out;
7549
+ };
7550
+ return rewriteDefsPointers(convert(doc));
7551
+ }
7552
+ function asStandardSchema(subject, spec, options = {}) {
7553
+ const resolved = typeof subject === "string" ? spec.exports.find((e) => e.name === subject) ?? (spec.types ?? []).find((t) => t.name === subject) : subject;
7554
+ if (!resolved) {
7555
+ throw new Error(`asStandardSchema: no export or type named "${subject}"`);
7556
+ }
7557
+ const cache = new Map;
7558
+ const build = (opts) => {
7559
+ const target = opts.target;
7560
+ const cached = cache.get(target);
7561
+ if (cached)
7562
+ return cached;
7563
+ const base = exportToJsonSchema(resolved, spec, {
7564
+ keepExtensions: options.keepExtensions,
7565
+ includeSchemaField: true
7566
+ });
7567
+ let result;
7568
+ switch (target) {
7569
+ case "draft-2020-12":
7570
+ result = base;
7571
+ break;
7572
+ case "draft-07":
7573
+ result = downlevelToDraft07(base);
7574
+ break;
7575
+ case "openapi-3.0":
7576
+ result = downlevelToOpenApi30(base);
7577
+ break;
7578
+ default:
7579
+ throw new Error(`asStandardSchema: unsupported target "${target}". Supported: draft-2020-12, draft-07, openapi-3.0`);
7580
+ }
7581
+ cache.set(target, result);
7582
+ return result;
7583
+ };
7584
+ return {
7585
+ "~standard": {
7586
+ version: 1,
7587
+ vendor: "openpkg",
7588
+ jsonSchema: {
7589
+ input: build,
7590
+ output: build
7591
+ }
7592
+ }
7593
+ };
7594
+ }
7595
+ // src/schema/tool-schema.ts
7596
+ var OPENAI_ALLOWED_KEYWORDS = new Set([
7597
+ "type",
7598
+ "properties",
7599
+ "required",
7600
+ "additionalProperties",
7601
+ "items",
7602
+ "prefixItems",
7603
+ "anyOf",
7604
+ "enum",
7605
+ "const",
7606
+ "description",
7607
+ "title",
7608
+ "$ref",
7609
+ "$defs",
7610
+ "format"
7611
+ ]);
7612
+ function isObject(v) {
7613
+ return !!v && typeof v === "object" && !Array.isArray(v);
7614
+ }
7615
+ function isObjectNode(node) {
7616
+ return node.type === "object" || "properties" in node;
7617
+ }
7618
+ function isFunctionOnly(schema) {
7619
+ if (!isObject(schema))
7620
+ return false;
7621
+ if (!("x-ts-function" in schema))
7622
+ return false;
7623
+ const keys = Object.keys(schema).filter((k) => !k.startsWith("x-ts-"));
7624
+ return keys.length === 0;
7625
+ }
7626
+ function openAiStrict(node, warnings) {
7627
+ if (Array.isArray(node))
7628
+ return node.map((n) => openAiStrict(n, warnings));
7629
+ if (!isObject(node))
7630
+ return node;
7631
+ const src = { ...node };
7632
+ if ("oneOf" in src && !("anyOf" in src)) {
7633
+ src.anyOf = src.oneOf;
7634
+ delete src.oneOf;
7635
+ }
7636
+ if (Array.isArray(src.allOf)) {
7637
+ const branches = src.allOf;
7638
+ warnings.push("openai-strict: allOf is unsupported — using first branch");
7639
+ delete src.allOf;
7640
+ if (isObject(branches[0]))
7641
+ Object.assign(src, branches[0]);
7642
+ }
7643
+ const out = {};
7644
+ for (const [key, value] of Object.entries(src)) {
7645
+ if (key.startsWith("x-"))
7646
+ continue;
7647
+ if (key === "properties" && isObject(value)) {
7648
+ const props = {};
7649
+ const kept = [];
7650
+ for (const [propName, propSchema] of Object.entries(value)) {
7651
+ if (isFunctionOnly(propSchema)) {
7652
+ warnings.push(`openai-strict: pruned function-typed property "${propName}"`);
7653
+ continue;
7654
+ }
7655
+ props[propName] = openAiStrict(propSchema, warnings);
7656
+ kept.push(propName);
7657
+ }
7658
+ out.properties = props;
7659
+ out.required = kept;
7660
+ continue;
7661
+ }
7662
+ if (key === "required")
7663
+ continue;
7664
+ if (key === "$defs" && isObject(value)) {
7665
+ out.$defs = Object.fromEntries(Object.entries(value).map(([k, v]) => [k, openAiStrict(v, warnings)]));
7666
+ continue;
7667
+ }
7668
+ if (!OPENAI_ALLOWED_KEYWORDS.has(key) && key !== "anyOf" && !key.startsWith("$")) {
7669
+ warnings.push(`openai-strict: dropped unsupported keyword "${key}"`);
7670
+ continue;
7671
+ }
7672
+ out[key] = openAiStrict(value, warnings);
7673
+ }
7674
+ if (isObjectNode(out)) {
7675
+ out.type = "object";
7676
+ if (!("properties" in out))
7677
+ out.properties = {};
7678
+ if (!("required" in out))
7679
+ out.required = Object.keys(out.properties);
7680
+ if (out.additionalProperties !== undefined && out.additionalProperties !== false) {
7681
+ warnings.push("openai-strict: open-ended additionalProperties dropped (record types unsupported)");
7682
+ }
7683
+ out.additionalProperties = false;
7684
+ }
7685
+ return out;
7686
+ }
7687
+ function anthropic(node, warnings) {
7688
+ if (Array.isArray(node))
7689
+ return node.map((n) => anthropic(n, warnings));
7690
+ if (!isObject(node))
7691
+ return node;
7692
+ const out = {};
7693
+ for (const [key, value] of Object.entries(node)) {
7694
+ if (key.startsWith("x-ts-") || key === "x-enum-members")
7695
+ continue;
7696
+ if (key === "properties" && isObject(value)) {
7697
+ const props = {};
7698
+ const dropped = new Set;
7699
+ for (const [propName, propSchema] of Object.entries(value)) {
7700
+ if (isFunctionOnly(propSchema)) {
7701
+ warnings.push(`anthropic: pruned function-typed property "${propName}"`);
7702
+ dropped.add(propName);
7703
+ continue;
7704
+ }
7705
+ props[propName] = anthropic(propSchema, warnings);
7706
+ }
7707
+ out.properties = props;
7708
+ if (Array.isArray(node.required)) {
7709
+ out.required = node.required.filter((r) => !dropped.has(r));
7710
+ }
7711
+ continue;
7712
+ }
7713
+ if (key === "required")
7714
+ continue;
7715
+ out[key] = anthropic(value, warnings);
7716
+ }
7717
+ return out;
7718
+ }
7719
+ function toToolSchema(exp, spec, options) {
7720
+ if (exp.kind !== "function" || !exp.signatures || exp.signatures.length === 0) {
7721
+ throw new TypeError(`toToolSchema: export "${exp.name}" is not a function with signatures (kind: ${exp.kind})`);
7722
+ }
7723
+ const sig = exp.signatures[options.signatureIndex ?? 0] ?? exp.signatures[0];
7724
+ const warnings = [];
7725
+ const properties = {};
7726
+ const required = [];
7727
+ for (const param of sig.parameters ?? []) {
7728
+ let paramSchema = normalizeSchema(param.schema);
7729
+ if (param.rest) {
7730
+ paramSchema = { type: "array", items: paramSchema };
7731
+ }
7732
+ if (param.description && !paramSchema.description) {
7733
+ paramSchema.description = param.description;
7734
+ }
7735
+ properties[param.name] = paramSchema;
7736
+ if (param.required !== false)
7737
+ required.push(param.name);
7738
+ }
7739
+ const wrapper = {
7740
+ type: "object",
7741
+ properties,
7742
+ ...required.length > 0 ? { required } : {}
7743
+ };
7744
+ const typeParameterNames = [
7745
+ ...(exp.typeParameters ?? []).map((p) => p.name),
7746
+ ...(sig.typeParameters ?? []).map((p) => p.name)
7747
+ ];
7748
+ const bundled = bundleRefs(wrapper, spec, { keepExtensions: true, typeParameterNames });
7749
+ warnings.push(...bundled.warnings);
7750
+ let parameters = { ...bundled.schema };
7751
+ if (Object.keys(bundled.defs).length > 0)
7752
+ parameters.$defs = bundled.defs;
7753
+ if (options.provider === "openai-strict") {
7754
+ parameters = openAiStrict(parameters, warnings);
7755
+ } else {
7756
+ parameters = stripTsExtensions(anthropic(parameters, warnings));
7757
+ }
7758
+ const description = exp.description ?? sig.description;
7759
+ return {
7760
+ name: exp.name,
7761
+ ...description ? { description } : {},
7762
+ parameters,
7763
+ warnings
7764
+ };
7765
+ }
7076
7766
  // src/types/utils.ts
7077
7767
  import ts18 from "typescript";
7078
7768
  function isExported(node) {
@@ -7092,13 +7782,16 @@ export {
7092
7782
  zodAdapter,
7093
7783
  withDescription,
7094
7784
  withDeprecated,
7785
+ validateSpec2 as validateSpec,
7095
7786
  valibotAdapter,
7096
7787
  typeboxAdapter,
7788
+ toToolSchema,
7097
7789
  toSearchIndexJSON,
7098
7790
  toSearchIndex,
7099
7791
  toPagefindRecords,
7100
7792
  toNavigation,
7101
7793
  toMarkdown,
7794
+ toJsonSchema,
7102
7795
  toJSONString,
7103
7796
  toJSON,
7104
7797
  toHTML,
@@ -7106,6 +7799,7 @@ export {
7106
7799
  toDocusaurusSidebarJS,
7107
7800
  toAlgoliaRecords,
7108
7801
  stripUndefinedFromType,
7802
+ stripTsExtensions,
7109
7803
  sortByName,
7110
7804
  shouldEmitAliasTypeText,
7111
7805
  serializeVariable,
@@ -7149,6 +7843,7 @@ export {
7149
7843
  isAnonymous,
7150
7844
  hasDeprecatedTag,
7151
7845
  groupByVisibility,
7846
+ getValidationErrors,
7152
7847
  getTypeOrigin,
7153
7848
  getSourceLocation,
7154
7849
  getProperties,
@@ -7161,6 +7856,7 @@ export {
7161
7856
  getExportKind,
7162
7857
  getExport,
7163
7858
  getDeprecationMessage,
7859
+ getAvailableVersions,
7164
7860
  toMarkdown as generateDocs,
7165
7861
  formatTypeParameters,
7166
7862
  formatSchema,
@@ -7182,6 +7878,7 @@ export {
7182
7878
  extractParameters,
7183
7879
  extract,
7184
7880
  exportToMarkdown,
7881
+ exportToJsonSchema,
7185
7882
  ensureNonEmptySchema,
7186
7883
  diffSpec2 as diffSpecs,
7187
7884
  diffSpec,
@@ -7192,10 +7889,13 @@ export {
7192
7889
  createDocs,
7193
7890
  categorizeBreakingChanges,
7194
7891
  calculateNextVersion,
7892
+ bundleRefs,
7195
7893
  buildSignatureString,
7196
7894
  buildSchema,
7197
7895
  buildObjectSchema,
7198
7896
  buildFunctionSchema,
7897
+ assertSpec,
7898
+ asStandardSchema,
7199
7899
  arktypeAdapter,
7200
7900
  analyzeSpec,
7201
7901
  TypeRegistry,
@@ -7203,6 +7903,7 @@ export {
7203
7903
  QueryBuilder,
7204
7904
  PRIMITIVES,
7205
7905
  NUMBER_PROTOTYPE_METHODS,
7906
+ LATEST_VERSION,
7206
7907
  CacheManager,
7207
7908
  CONFIG_FILENAME,
7208
7909
  BUILTIN_TYPE_SCHEMAS,