@workos/oagen 0.1.1 → 0.3.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 (47) hide show
  1. package/dist/cli/index.d.mts +2 -0
  2. package/dist/cli/index.mjs +6233 -0
  3. package/dist/cli/index.mjs.map +1 -0
  4. package/dist/compat/{index.d.ts → index.d.mts} +43 -114
  5. package/dist/compat/index.d.mts.map +1 -0
  6. package/dist/compat/index.mjs +4562 -0
  7. package/dist/compat/index.mjs.map +1 -0
  8. package/dist/diff-Cn6S74vV.mjs +2432 -0
  9. package/dist/diff-Cn6S74vV.mjs.map +1 -0
  10. package/dist/differ-B6472Yef.mjs +1100 -0
  11. package/dist/differ-B6472Yef.mjs.map +1 -0
  12. package/dist/index.d.mts +324 -0
  13. package/dist/index.d.mts.map +1 -0
  14. package/dist/index.mjs +1553 -0
  15. package/dist/index.mjs.map +1 -0
  16. package/dist/scripts/smoke/baseline.d.mts +1 -0
  17. package/dist/scripts/smoke/baseline.mjs +1951 -0
  18. package/dist/scripts/smoke/baseline.mjs.map +1 -0
  19. package/dist/scripts/smoke/sdk-test.d.mts +1 -0
  20. package/dist/scripts/smoke/sdk-test.mjs +111 -0
  21. package/dist/scripts/smoke/sdk-test.mjs.map +1 -0
  22. package/dist/smoke.d.mts +331 -0
  23. package/dist/smoke.d.mts.map +1 -0
  24. package/dist/smoke.mjs +2119 -0
  25. package/dist/smoke.mjs.map +1 -0
  26. package/dist/tree-sitter-DFRXfWg9.mjs +394 -0
  27. package/dist/tree-sitter-DFRXfWg9.mjs.map +1 -0
  28. package/dist/types-O25qZxMl.d.mts +397 -0
  29. package/dist/types-O25qZxMl.d.mts.map +1 -0
  30. package/dist/verify/index.d.mts +82 -0
  31. package/dist/verify/index.d.mts.map +1 -0
  32. package/dist/verify/index.mjs +327 -0
  33. package/dist/verify/index.mjs.map +1 -0
  34. package/package.json +19 -16
  35. package/dist/chunk-2JXFTDTO.js +0 -1669
  36. package/dist/chunk-CIX5NOBF.js +0 -263
  37. package/dist/chunk-IJ5E5AE7.js +0 -1040
  38. package/dist/cli/index.js +0 -5322
  39. package/dist/compat/index.js +0 -4491
  40. package/dist/index.d.ts +0 -277
  41. package/dist/index.js +0 -1565
  42. package/dist/scripts/smoke/baseline.js +0 -1813
  43. package/dist/scripts/smoke/sdk-test.js +0 -93
  44. package/dist/smoke.js +0 -2025
  45. package/dist/types-B5t9O9Wm.d.ts +0 -367
  46. package/dist/verify/index.d.ts +0 -76
  47. package/dist/verify/index.js +0 -336
@@ -0,0 +1,4562 @@
1
+ import { t as safeParse$1, v as ExtractorError } from "../tree-sitter-DFRXfWg9.mjs";
2
+ import { _ as registerExtractor, a as specDerivedHttpKeys, c as buildOverlayLookup, d as defaultIsNullableOnlyDifference, f as namedTypeWordsOverlap, g as getExtractor, h as typeExistsInSurface, i as specDerivedFieldPaths, l as patchOverlay, m as resolveHints, n as filterSurface, o as specDerivedMethodPaths, p as nodeHints, r as specDerivedEnumValues, s as specDerivedNames, t as diffSurfaces, u as NAMED_TYPE_RE } from "../differ-B6472Yef.mjs";
3
+ import path, { basename, dirname, join, relative, resolve } from "node:path";
4
+ import Parser from "tree-sitter";
5
+ import ts from "typescript";
6
+ import { readFileSync, readdirSync, statSync } from "node:fs";
7
+ import PhpGrammar from "tree-sitter-php";
8
+ import Python from "tree-sitter-python";
9
+ import Ruby from "tree-sitter-ruby";
10
+ import Go from "tree-sitter-go";
11
+ import RustLang from "tree-sitter-rust";
12
+ //#region src/compat/extractors/shared.ts
13
+ /** Sort an object's keys alphabetically. Shared across all extractors for deterministic output. */
14
+ function sortRecord(record) {
15
+ const sorted = {};
16
+ for (const key of Object.keys(record).sort()) sorted[key] = record[key];
17
+ return sorted;
18
+ }
19
+ /** Collects per-file export names, deduplicating and sorting on finalization. */
20
+ var ExportCollector = class {
21
+ map = /* @__PURE__ */ new Map();
22
+ add(sourceFile, name) {
23
+ let set = this.map.get(sourceFile);
24
+ if (!set) {
25
+ set = /* @__PURE__ */ new Set();
26
+ this.map.set(sourceFile, set);
27
+ }
28
+ set.add(name);
29
+ }
30
+ toRecord() {
31
+ const result = {};
32
+ for (const [file, names] of this.map) result[file] = [...names].sort();
33
+ return result;
34
+ }
35
+ };
36
+ //#endregion
37
+ //#region src/compat/extractors/node.ts
38
+ const nodeExtractor = {
39
+ language: "node",
40
+ hints: nodeHints,
41
+ async extract(sdkPath) {
42
+ sdkPath = resolve(sdkPath);
43
+ const configPath = ts.findConfigFile(sdkPath, ts.sys.fileExists, "tsconfig.json");
44
+ if (!configPath) throw new ExtractorError(`No tsconfig.json found in ${sdkPath}`, `Create a tsconfig.json in "${sdkPath}" or verify the --sdk-path argument points to a TypeScript project root.`);
45
+ const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
46
+ const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, sdkPath);
47
+ const program = ts.createProgram(parsedConfig.fileNames, parsedConfig.options);
48
+ const checker = program.getTypeChecker();
49
+ const entryPoint = resolveEntryPoint(sdkPath, program);
50
+ const entrySourceFile = program.getSourceFile(entryPoint);
51
+ if (!entrySourceFile) throw new ExtractorError(`Entry point not found: ${entryPoint}`, `Ensure the file "${entryPoint}" exists and is included in the tsconfig.json "include" or "files" array.`);
52
+ const moduleSymbol = checker.getSymbolAtLocation(entrySourceFile);
53
+ if (!moduleSymbol) throw new ExtractorError("No exports found at entry point", `Verify that "${entryPoint}" has at least one \`export\` statement. The extractor needs public exports to build an API surface.`);
54
+ const exportedSymbols = checker.getExportsOfModule(moduleSymbol);
55
+ const classes = {};
56
+ const interfaces = {};
57
+ const typeAliases = {};
58
+ const enums = {};
59
+ for (const sym of exportedSymbols) {
60
+ const resolved = resolveAlias(sym, checker);
61
+ const declarations = resolved.getDeclarations();
62
+ if (!declarations || declarations.length === 0) continue;
63
+ const decl = declarations[0];
64
+ const sourceFile = relative(sdkPath, decl.getSourceFile().fileName);
65
+ if (ts.isClassDeclaration(decl)) classes[resolved.name] = {
66
+ ...extractClass(resolved, checker),
67
+ sourceFile
68
+ };
69
+ else if (ts.isInterfaceDeclaration(decl)) interfaces[resolved.name] = {
70
+ ...extractInterface(resolved, checker),
71
+ sourceFile
72
+ };
73
+ else if (ts.isTypeAliasDeclaration(decl)) typeAliases[resolved.name] = {
74
+ ...extractTypeAlias(resolved, checker),
75
+ sourceFile
76
+ };
77
+ else if (ts.isEnumDeclaration(decl)) enums[resolved.name] = {
78
+ ...extractEnum(resolved, checker),
79
+ sourceFile
80
+ };
81
+ }
82
+ followPropertyTypeClasses(exportedSymbols, checker, classes, sdkPath);
83
+ const exports = buildExportMap(entrySourceFile, checker, sdkPath, program);
84
+ return {
85
+ language: "node",
86
+ extractedFrom: sdkPath,
87
+ extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
88
+ classes: sortRecord(classes),
89
+ interfaces: sortRecord(interfaces),
90
+ typeAliases: sortRecord(typeAliases),
91
+ enums: sortRecord(enums),
92
+ exports: sortRecord(exports)
93
+ };
94
+ }
95
+ };
96
+ function followPropertyTypeClasses(exportedSymbols, checker, classes, sdkPath) {
97
+ const classSymbols = /* @__PURE__ */ new Map();
98
+ for (const sym of exportedSymbols) {
99
+ const resolved = resolveAlias(sym, checker);
100
+ if (resolved.getDeclarations()?.some((d) => ts.isClassDeclaration(d))) classSymbols.set(resolved.name, resolved);
101
+ }
102
+ const toVisit = [...classSymbols.values()];
103
+ const visited = new Set(classSymbols.keys());
104
+ while (toVisit.length > 0) {
105
+ const sym = toVisit.pop();
106
+ const type = checker.getDeclaredTypeOfSymbol(sym);
107
+ const decls = sym.getDeclarations();
108
+ if (!decls || decls.length === 0) continue;
109
+ for (const prop of type.getProperties()) {
110
+ const propDecls = prop.getDeclarations();
111
+ if (!propDecls || propDecls.length === 0) continue;
112
+ const propDecl = propDecls[0];
113
+ if ((ts.canHaveModifiers(propDecl) ? ts.getModifiers(propDecl) : void 0)?.some((m) => m.kind === ts.SyntaxKind.PrivateKeyword || m.kind === ts.SyntaxKind.ProtectedKeyword)) continue;
114
+ const propSymbol = checker.getTypeOfSymbolAtLocation(prop, propDecl).getSymbol();
115
+ if (!propSymbol) continue;
116
+ const propName = propSymbol.name;
117
+ if (visited.has(propName)) continue;
118
+ const propSymDecls = propSymbol.getDeclarations();
119
+ if (!propSymDecls?.some((d) => ts.isClassDeclaration(d))) continue;
120
+ visited.add(propName);
121
+ const sourceFile = relative(sdkPath, propSymDecls[0].getSourceFile().fileName);
122
+ classes[propName] = {
123
+ ...extractClass(propSymbol, checker),
124
+ sourceFile
125
+ };
126
+ toVisit.push(propSymbol);
127
+ }
128
+ }
129
+ }
130
+ function resolveEntryPoint(sdkPath, program) {
131
+ const pkgPath = resolve(sdkPath, "package.json");
132
+ try {
133
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
134
+ if (pkg.exports) {
135
+ const mainExport = typeof pkg.exports === "string" ? pkg.exports : pkg.exports["."]?.import ?? pkg.exports["."]?.default ?? pkg.exports["."];
136
+ if (typeof mainExport === "string") {
137
+ const resolved = resolveSourceFile(sdkPath, mainExport, program);
138
+ if (resolved) return resolved;
139
+ }
140
+ }
141
+ if (pkg.main) {
142
+ const resolved = resolveSourceFile(sdkPath, pkg.main, program);
143
+ if (resolved) return resolved;
144
+ }
145
+ } catch {}
146
+ const fallback = resolve(sdkPath, "src/index.ts");
147
+ if (program.getSourceFile(fallback)) return fallback;
148
+ throw new ExtractorError(`No entry point found. Tried: package.json exports/main, src/index.ts`, `Add an "exports" or "main" field to package.json in "${sdkPath}", or create a src/index.ts file that re-exports the public API.`);
149
+ }
150
+ function resolveSourceFile(sdkPath, entryPath, program) {
151
+ const candidates = [
152
+ resolve(sdkPath, entryPath),
153
+ resolve(sdkPath, entryPath.replace(/\.js$/, ".ts")),
154
+ resolve(sdkPath, entryPath.replace(/\.js$/, "/index.ts"))
155
+ ];
156
+ for (const candidate of candidates) if (program.getSourceFile(candidate)) return candidate;
157
+ }
158
+ function resolveAlias(sym, checker) {
159
+ if (sym.flags & ts.SymbolFlags.Alias) return checker.getAliasedSymbol(sym);
160
+ return sym;
161
+ }
162
+ function extractClass(sym, checker) {
163
+ const type = checker.getDeclaredTypeOfSymbol(sym);
164
+ const methods = {};
165
+ const properties = {};
166
+ const constructorParams = [];
167
+ const declarations = sym.getDeclarations();
168
+ if (declarations) {
169
+ for (const decl of declarations) if (ts.isClassDeclaration(decl)) {
170
+ for (const member of decl.members) if (ts.isConstructorDeclaration(member)) for (const param of member.parameters) {
171
+ const paramName = param.name.getText();
172
+ const paramType = param.type ? checker.typeToString(checker.getTypeFromTypeNode(param.type)) : "any";
173
+ constructorParams.push({
174
+ name: paramName,
175
+ type: paramType,
176
+ optional: !!param.questionToken || !!param.initializer
177
+ });
178
+ }
179
+ }
180
+ }
181
+ for (const prop of type.getProperties()) {
182
+ const propDeclarations = prop.getDeclarations();
183
+ if (propDeclarations && propDeclarations.length > 0) {
184
+ const decl = propDeclarations[0];
185
+ if ((ts.canHaveModifiers(decl) ? ts.getModifiers(decl) : void 0)?.some((m) => m.kind === ts.SyntaxKind.PrivateKeyword || m.kind === ts.SyntaxKind.ProtectedKeyword)) continue;
186
+ }
187
+ const propType = checker.getTypeOfSymbolAtLocation(prop, declarations[0]);
188
+ const callSignatures = propType.getCallSignatures();
189
+ if (callSignatures.length > 0) {
190
+ if (!methods[prop.name]) methods[prop.name] = [];
191
+ for (const sig of callSignatures) methods[prop.name].push({
192
+ name: prop.name,
193
+ params: sig.getParameters().map((p) => extractParam(p, checker)),
194
+ returnType: checker.typeToString(sig.getReturnType()),
195
+ async: checker.typeToString(sig.getReturnType()).startsWith("Promise<")
196
+ });
197
+ } else {
198
+ const typeStr = checker.typeToString(propType);
199
+ const isReadonly = propDeclarations?.some((d) => {
200
+ return (ts.canHaveModifiers(d) ? ts.getModifiers(d) : void 0)?.some((m) => m.kind === ts.SyntaxKind.ReadonlyKeyword);
201
+ });
202
+ properties[prop.name] = {
203
+ name: prop.name,
204
+ type: typeStr,
205
+ readonly: !!isReadonly
206
+ };
207
+ }
208
+ }
209
+ return {
210
+ name: sym.name,
211
+ methods: sortRecord(methods),
212
+ properties: sortRecord(properties),
213
+ constructorParams
214
+ };
215
+ }
216
+ function extractParam(sym, checker) {
217
+ const decl = sym.getDeclarations()?.[0];
218
+ const isOptional = decl && ts.isParameter(decl) ? !!decl.questionToken || !!decl.initializer : false;
219
+ const type = checker.getTypeOfSymbolAtLocation(sym, decl);
220
+ return {
221
+ name: sym.name,
222
+ type: checker.typeToString(type),
223
+ optional: isOptional
224
+ };
225
+ }
226
+ function extractInterface(sym, checker) {
227
+ const type = checker.getDeclaredTypeOfSymbol(sym);
228
+ const fields = {};
229
+ const extendsNames = [];
230
+ const declarations = sym.getDeclarations();
231
+ if (declarations) {
232
+ for (const decl of declarations) if (ts.isInterfaceDeclaration(decl) && decl.heritageClauses) for (const clause of decl.heritageClauses) for (const typeExpr of clause.types) extendsNames.push(typeExpr.getText());
233
+ }
234
+ for (const prop of type.getProperties()) {
235
+ const propType = checker.getTypeOfSymbolAtLocation(prop, declarations[0]);
236
+ const propDecl = prop.getDeclarations()?.[0];
237
+ const isOptional = propDecl && ts.isPropertySignature(propDecl) ? !!propDecl.questionToken : false;
238
+ let typeStr = checker.typeToString(propType);
239
+ if (isOptional) typeStr = stripUndefined(typeStr);
240
+ fields[prop.name] = {
241
+ name: prop.name,
242
+ type: typeStr,
243
+ optional: isOptional
244
+ };
245
+ }
246
+ if (declarations) {
247
+ for (const decl of declarations) if (ts.isInterfaceDeclaration(decl)) {
248
+ for (const member of decl.members) if (ts.isPropertySignature(member) && member.name) {
249
+ const memberName = member.name.getText();
250
+ if (!fields[memberName]) {
251
+ const memberType = member.type ? checker.typeToString(checker.getTypeFromTypeNode(member.type)) : "any";
252
+ const isOpt = !!member.questionToken;
253
+ fields[memberName] = {
254
+ name: memberName,
255
+ type: isOpt ? stripUndefined(memberType) : memberType,
256
+ optional: isOpt
257
+ };
258
+ }
259
+ }
260
+ }
261
+ }
262
+ return {
263
+ name: sym.name,
264
+ fields: sortRecord(fields),
265
+ extends: extendsNames.sort()
266
+ };
267
+ }
268
+ function extractTypeAlias(sym, checker) {
269
+ const type = checker.getDeclaredTypeOfSymbol(sym);
270
+ return {
271
+ name: sym.name,
272
+ value: checker.typeToString(type, void 0, ts.TypeFormatFlags.InTypeAlias | ts.TypeFormatFlags.NoTruncation)
273
+ };
274
+ }
275
+ function extractEnum(sym, checker) {
276
+ const members = {};
277
+ const type = checker.getDeclaredTypeOfSymbol(sym);
278
+ if (type.isUnion()) {
279
+ for (const memberType of type.types) if (memberType.symbol) {
280
+ const constValue = checker.getConstantValue(memberType.symbol.getDeclarations()[0]);
281
+ if (constValue !== void 0) members[memberType.symbol.name] = constValue;
282
+ }
283
+ }
284
+ return {
285
+ name: sym.name,
286
+ members: sortRecord(members)
287
+ };
288
+ }
289
+ function buildExportMap(entryFile, checker, sdkPath, program) {
290
+ const exports = {};
291
+ const visited = /* @__PURE__ */ new Set();
292
+ function walk(sourceFile) {
293
+ const absPath = sourceFile.fileName;
294
+ if (visited.has(absPath)) return;
295
+ visited.add(absPath);
296
+ const relPath = relative(sdkPath, absPath);
297
+ const moduleSymbol = checker.getSymbolAtLocation(sourceFile);
298
+ if (moduleSymbol) {
299
+ const names = checker.getExportsOfModule(moduleSymbol).map((s) => s.name);
300
+ if (names.length > 0) exports[relPath] = names.sort();
301
+ }
302
+ for (const stmt of sourceFile.statements) if (ts.isExportDeclaration(stmt) && stmt.moduleSpecifier && ts.isStringLiteral(stmt.moduleSpecifier)) {
303
+ const target = stmt.moduleSpecifier.text;
304
+ const resolved = resolveSourceFile(path.dirname(absPath), target, program);
305
+ if (resolved) {
306
+ const targetFile = program.getSourceFile(resolved);
307
+ if (targetFile) walk(targetFile);
308
+ }
309
+ }
310
+ }
311
+ walk(entryFile);
312
+ return exports;
313
+ }
314
+ function stripUndefined(typeStr) {
315
+ return typeStr.replace(/\s*\|\s*undefined$/, "").replace(/^undefined\s*\|\s*/, "");
316
+ }
317
+ //#endregion
318
+ //#region src/compat/extractors/php-parser.ts
319
+ /**
320
+ * PHP source parser — tree-sitter-based extraction of classes, interfaces,
321
+ * methods, properties, and constants from PHP source files.
322
+ */
323
+ const phpParser = new Parser();
324
+ phpParser.setLanguage(PhpGrammar.php_only);
325
+ function walkPhpFiles(dir) {
326
+ const results = [];
327
+ let entries;
328
+ try {
329
+ entries = readdirSync(dir);
330
+ } catch {
331
+ return results;
332
+ }
333
+ for (const entry of entries) {
334
+ const fullPath = join(dir, entry);
335
+ let stat;
336
+ try {
337
+ stat = statSync(fullPath);
338
+ } catch {
339
+ continue;
340
+ }
341
+ if (stat.isDirectory()) {
342
+ if (entry.startsWith(".") || entry === "vendor" || entry === "tests" || entry === "test") continue;
343
+ results.push(...walkPhpFiles(fullPath));
344
+ } else if (entry.endsWith(".php")) results.push(fullPath);
345
+ }
346
+ return results.sort();
347
+ }
348
+ function parsePhpDoc(commentText) {
349
+ const params = /* @__PURE__ */ new Map();
350
+ let returnType = "";
351
+ const lines = commentText.split("\n");
352
+ for (const line of lines) {
353
+ const paramMatch = line.match(/@param\s+(\S+)\s+\$(\w+)/);
354
+ if (paramMatch) params.set(paramMatch[2], paramMatch[1]);
355
+ const returnMatch = line.match(/@return\s+(\S+)/);
356
+ if (returnMatch) returnType = returnMatch[1];
357
+ }
358
+ return {
359
+ params,
360
+ returnType
361
+ };
362
+ }
363
+ /** Find the doc comment immediately preceding a node. */
364
+ function findDocComment(node) {
365
+ let sibling = node.previousNamedSibling;
366
+ if (sibling && sibling.type === "comment" && sibling.text.startsWith("/**")) return parsePhpDoc(sibling.text);
367
+ return null;
368
+ }
369
+ function getVisibility(node) {
370
+ for (const child of node.children) if (child.type === "visibility_modifier") {
371
+ const text = child.text.toLowerCase();
372
+ if (text === "public") return "public";
373
+ if (text === "protected") return "protected";
374
+ if (text === "private") return "private";
375
+ }
376
+ return "public";
377
+ }
378
+ function isStaticMethod(node) {
379
+ for (const child of node.children) if (child.type === "static_modifier") return true;
380
+ return false;
381
+ }
382
+ /** Parse a PHP array literal to extract string values: ["id", "name", ...] */
383
+ function parseArrayLiteral(node) {
384
+ const values = [];
385
+ for (const elemInit of node.descendantsOfType("array_element_initializer")) {
386
+ const strings = elemInit.namedChildren.filter((c) => c.type === "encapsed_string" || c.type === "string");
387
+ if (strings.length === 1) {
388
+ const contentNode = strings[0].namedChildren.find((c) => c.type === "string_content");
389
+ if (contentNode) values.push(contentNode.text);
390
+ }
391
+ }
392
+ return values;
393
+ }
394
+ /** Extract the name and value from a const_element node. */
395
+ function parseConstElement(constElement) {
396
+ const nameNode = constElement.namedChildren.find((c) => c.type === "name");
397
+ if (!nameNode) return null;
398
+ const constName = nameNode.text;
399
+ if (constName === "RESOURCE_ATTRIBUTES" || constName === "RESPONSE_TO_RESOURCE_KEY") return null;
400
+ const valueNode = constElement.namedChildren.find((c) => c.type !== "name");
401
+ if (!valueNode) return null;
402
+ if (valueNode.type === "encapsed_string") {
403
+ const contentNode = valueNode.namedChildren.find((c) => c.type === "string_content");
404
+ return {
405
+ name: constName,
406
+ value: contentNode ? contentNode.text : ""
407
+ };
408
+ }
409
+ if (valueNode.type === "string") {
410
+ const contentNode = valueNode.namedChildren.find((c) => c.type === "string_content");
411
+ return {
412
+ name: constName,
413
+ value: contentNode ? contentNode.text : ""
414
+ };
415
+ }
416
+ return {
417
+ name: constName,
418
+ value: valueNode.text
419
+ };
420
+ }
421
+ function parseMethods$2(classBody) {
422
+ const methods = [];
423
+ for (const child of classBody.namedChildren) {
424
+ if (child.type !== "method_declaration") continue;
425
+ const nameNode = child.childForFieldName("name");
426
+ if (!nameNode) continue;
427
+ const methodName = nameNode.text;
428
+ if (methodName.startsWith("__") && methodName !== "__construct") continue;
429
+ const visibility = getVisibility(child);
430
+ const isStatic = isStaticMethod(child);
431
+ const docInfo = findDocComment(child);
432
+ const params = [];
433
+ const paramsList = child.childForFieldName("parameters");
434
+ if (paramsList) for (const paramNode of paramsList.namedChildren) {
435
+ if (paramNode.type !== "simple_parameter") continue;
436
+ const paramNameNode = paramNode.namedChildren.find((c) => c.type === "variable_name");
437
+ if (!paramNameNode) continue;
438
+ const paramName = paramNameNode.text.replace(/^\$/, "");
439
+ let paramType = "";
440
+ const typeNode = paramNode.childForFieldName("type");
441
+ if (typeNode) paramType = typeNode.text;
442
+ if (!paramType && docInfo) paramType = docInfo.params.get(paramName) || "mixed";
443
+ if (!paramType) paramType = "mixed";
444
+ const hasDefault = paramNode.childForFieldName("default_value") !== null;
445
+ params.push({
446
+ name: paramName,
447
+ type: paramType,
448
+ optional: hasDefault
449
+ });
450
+ }
451
+ let returnType = "";
452
+ const returnTypeNode = child.childForFieldName("return_type");
453
+ if (returnTypeNode) returnType = returnTypeNode.text;
454
+ if (!returnType && docInfo) returnType = docInfo.returnType || "mixed";
455
+ if (!returnType) returnType = "mixed";
456
+ methods.push({
457
+ name: methodName,
458
+ visibility,
459
+ isStatic,
460
+ params,
461
+ returnType
462
+ });
463
+ }
464
+ return methods;
465
+ }
466
+ function parseProperties$1(classBody) {
467
+ const properties = [];
468
+ for (const child of classBody.namedChildren) {
469
+ if (child.type !== "property_declaration") continue;
470
+ const visibility = getVisibility(child);
471
+ for (const propElement of child.namedChildren) {
472
+ if (propElement.type !== "property_element") continue;
473
+ const varNameNode = propElement.namedChildren.find((c) => c.type === "variable_name");
474
+ if (!varNameNode) continue;
475
+ const propName = varNameNode.text.replace(/^\$/, "");
476
+ let propType = "mixed";
477
+ const typeNode = child.namedChildren.find((c) => c.type === "union_type" || c.type === "named_type" || c.type === "optional_type" || c.type === "primitive_type");
478
+ if (typeNode) propType = typeNode.text;
479
+ properties.push({
480
+ name: propName,
481
+ type: propType,
482
+ visibility
483
+ });
484
+ }
485
+ }
486
+ return properties;
487
+ }
488
+ function parseConstants(classBody) {
489
+ const constants = [];
490
+ for (const child of classBody.namedChildren) {
491
+ if (child.type !== "const_declaration") continue;
492
+ for (const constElement of child.namedChildren) {
493
+ if (constElement.type !== "const_element") continue;
494
+ const parsed = parseConstElement(constElement);
495
+ if (parsed) constants.push(parsed);
496
+ }
497
+ }
498
+ return constants;
499
+ }
500
+ function parseResourceAttributes(classBody) {
501
+ for (const child of classBody.namedChildren) {
502
+ if (child.type !== "const_declaration") continue;
503
+ for (const constElement of child.namedChildren) {
504
+ if (constElement.type !== "const_element") continue;
505
+ const nameNode = constElement.namedChildren.find((c) => c.type === "name");
506
+ if (!nameNode || nameNode.text !== "RESOURCE_ATTRIBUTES") continue;
507
+ const arrayNode = constElement.namedChildren.find((c) => c.type === "array_creation_expression");
508
+ if (!arrayNode) continue;
509
+ return parseArrayLiteral(arrayNode);
510
+ }
511
+ }
512
+ return [];
513
+ }
514
+ function extractNamespace$1(tree) {
515
+ for (const node of tree.rootNode.namedChildren) if (node.type === "namespace_definition") {
516
+ const nameNode = node.childForFieldName("name");
517
+ return nameNode ? nameNode.text : "";
518
+ }
519
+ return "";
520
+ }
521
+ function parseClassDeclarations(tree, sourceFile, namespace) {
522
+ const classes = [];
523
+ for (const node of tree.rootNode.descendantsOfType("class_declaration")) {
524
+ const nameNode = node.childForFieldName("name");
525
+ if (!nameNode) continue;
526
+ let extendsName = null;
527
+ const baseClause = node.namedChildren.find((c) => c.type === "base_clause");
528
+ if (baseClause) {
529
+ const baseNameNode = baseClause.namedChildren.find((c) => c.type === "name" || c.type === "qualified_name");
530
+ if (baseNameNode) {
531
+ const parts = baseNameNode.text.replace(/^\\/, "").split("\\");
532
+ extendsName = parts[parts.length - 1];
533
+ }
534
+ }
535
+ const bodyNode = node.childForFieldName("body");
536
+ if (!bodyNode) continue;
537
+ const methods = parseMethods$2(bodyNode);
538
+ const properties = parseProperties$1(bodyNode);
539
+ const constants = parseConstants(bodyNode);
540
+ const resourceAttributes = parseResourceAttributes(bodyNode);
541
+ classes.push({
542
+ name: nameNode.text,
543
+ namespace,
544
+ extends: extendsName,
545
+ isInterface: false,
546
+ methods,
547
+ properties,
548
+ constants,
549
+ resourceAttributes,
550
+ sourceFile
551
+ });
552
+ }
553
+ return classes;
554
+ }
555
+ function parseInterfaceDeclarations(tree, sourceFile, namespace) {
556
+ const interfaces = [];
557
+ for (const node of tree.rootNode.descendantsOfType("interface_declaration")) {
558
+ const nameNode = node.childForFieldName("name");
559
+ if (!nameNode) continue;
560
+ const bodyNode = node.childForFieldName("body");
561
+ if (!bodyNode) continue;
562
+ const methods = parseMethods$2(bodyNode);
563
+ interfaces.push({
564
+ name: nameNode.text,
565
+ namespace,
566
+ extends: null,
567
+ isInterface: true,
568
+ methods,
569
+ properties: [],
570
+ constants: [],
571
+ resourceAttributes: [],
572
+ sourceFile
573
+ });
574
+ }
575
+ return interfaces;
576
+ }
577
+ function parseEnumDeclarations(tree, sourceFile, namespace) {
578
+ const enums = [];
579
+ for (const node of tree.rootNode.descendantsOfType("enum_declaration")) {
580
+ const nameNode = node.childForFieldName("name");
581
+ if (!nameNode) continue;
582
+ const bodyNode = node.childForFieldName("body");
583
+ if (!bodyNode) continue;
584
+ const constants = [];
585
+ for (const child of bodyNode.namedChildren) {
586
+ if (child.type !== "enum_case") continue;
587
+ const caseNameNode = child.childForFieldName("name");
588
+ if (!caseNameNode) continue;
589
+ let value = caseNameNode.text;
590
+ const valueNode = child.namedChildren.find((c) => c.type === "encapsed_string" || c.type === "string" || c.type === "integer");
591
+ if (valueNode) {
592
+ const contentNode = valueNode.namedChildren.find((c) => c.type === "string_content");
593
+ value = contentNode ? contentNode.text : valueNode.text;
594
+ }
595
+ constants.push({
596
+ name: caseNameNode.text,
597
+ value
598
+ });
599
+ }
600
+ const methods = parseMethods$2(bodyNode);
601
+ enums.push({
602
+ name: nameNode.text,
603
+ namespace,
604
+ extends: null,
605
+ isInterface: false,
606
+ methods,
607
+ properties: [],
608
+ constants,
609
+ resourceAttributes: [],
610
+ sourceFile
611
+ });
612
+ }
613
+ return enums;
614
+ }
615
+ /** Parse a single PHP source file and return all extracted symbols. */
616
+ function parsePhpFile(filePath, sdkPath) {
617
+ const source = readFileSync(filePath, "utf-8");
618
+ const relPath = relative(sdkPath, filePath);
619
+ const tree = safeParse$1(phpParser, source);
620
+ const namespace = extractNamespace$1(tree);
621
+ const classDecls = parseClassDeclarations(tree, relPath, namespace);
622
+ const interfaceDecls = parseInterfaceDeclarations(tree, relPath, namespace);
623
+ const enumDecls = parseEnumDeclarations(tree, relPath, namespace);
624
+ return { classes: [
625
+ ...classDecls,
626
+ ...interfaceDecls,
627
+ ...enumDecls
628
+ ] };
629
+ }
630
+ //#endregion
631
+ //#region src/compat/extractors/php-surface.ts
632
+ /** Default resource base classes (empty — consumer provides via hints.modelBaseClasses). */
633
+ const DEFAULT_RESOURCE_BASES = [];
634
+ /** Default exception base classes. */
635
+ const DEFAULT_EXCEPTION_BASES$1 = ["Exception", "\\Exception"];
636
+ /** Check if a class is a resource class (extends a known resource base and has attributes). */
637
+ function isResourceClass(cls, resourceBases) {
638
+ return !!cls.extends && resourceBases.has(cls.extends) && cls.resourceAttributes.length > 0;
639
+ }
640
+ /** Check if a class is enum-like (only constants, no public methods). */
641
+ function isEnumClass(cls) {
642
+ if (cls.isInterface) return false;
643
+ if (cls.constants.length === 0) return false;
644
+ return cls.methods.filter((m) => m.visibility === "public" && m.name !== "__construct").length === 0 && cls.properties.length === 0 && !cls.extends;
645
+ }
646
+ /** Check if a class is an exception. */
647
+ function isExceptionClass$1(cls, exceptionBases) {
648
+ if (!cls.extends) return false;
649
+ return exceptionBases.has(cls.extends);
650
+ }
651
+ function buildSurface$6(allClasses, hints) {
652
+ const resourceBases = new Set(hints?.modelBaseClasses ?? DEFAULT_RESOURCE_BASES);
653
+ const exceptionBases = new Set(hints?.exceptionBaseClasses ?? DEFAULT_EXCEPTION_BASES$1);
654
+ const classes = {};
655
+ const interfaces = {};
656
+ const enums = {};
657
+ const collector = new ExportCollector();
658
+ for (const cls of allClasses) if (cls.isInterface) {
659
+ const fields = {};
660
+ for (const method of cls.methods) {
661
+ if (method.visibility !== "public") continue;
662
+ fields[method.name] = {
663
+ name: method.name,
664
+ type: method.returnType || "mixed",
665
+ optional: false
666
+ };
667
+ }
668
+ interfaces[cls.name] = {
669
+ name: cls.name,
670
+ sourceFile: cls.sourceFile,
671
+ fields: sortRecord(fields),
672
+ extends: []
673
+ };
674
+ collector.add(cls.sourceFile, cls.name);
675
+ } else if (isResourceClass(cls, resourceBases)) {
676
+ const fields = {};
677
+ for (const attr of cls.resourceAttributes) fields[attr] = {
678
+ name: attr,
679
+ type: "mixed",
680
+ optional: false
681
+ };
682
+ interfaces[cls.name] = {
683
+ name: cls.name,
684
+ sourceFile: cls.sourceFile,
685
+ fields: sortRecord(fields),
686
+ extends: cls.extends ? [cls.extends] : []
687
+ };
688
+ collector.add(cls.sourceFile, cls.name);
689
+ } else if (isEnumClass(cls)) {
690
+ const members = {};
691
+ for (const constant of cls.constants) members[constant.name] = constant.value;
692
+ enums[cls.name] = {
693
+ name: cls.name,
694
+ sourceFile: cls.sourceFile,
695
+ members: sortRecord(members)
696
+ };
697
+ collector.add(cls.sourceFile, cls.name);
698
+ } else {
699
+ const apiMethods = {};
700
+ const properties = {};
701
+ for (const method of cls.methods) {
702
+ if (method.visibility !== "public") continue;
703
+ const params = method.params.map((p) => ({
704
+ name: p.name,
705
+ type: p.type,
706
+ optional: p.optional
707
+ }));
708
+ if (!apiMethods[method.name]) apiMethods[method.name] = [];
709
+ apiMethods[method.name].push({
710
+ name: method.name,
711
+ params,
712
+ returnType: method.returnType || "mixed",
713
+ async: false
714
+ });
715
+ }
716
+ for (const prop of cls.properties) {
717
+ if (prop.visibility !== "public") continue;
718
+ properties[prop.name] = {
719
+ name: prop.name,
720
+ type: prop.type,
721
+ readonly: false
722
+ };
723
+ }
724
+ if (Object.keys(apiMethods).length > 0 || Object.keys(properties).length > 0 || isExceptionClass$1(cls, exceptionBases)) {
725
+ classes[cls.name] = {
726
+ name: cls.name,
727
+ sourceFile: cls.sourceFile,
728
+ methods: sortRecord(apiMethods),
729
+ properties: sortRecord(properties),
730
+ constructorParams: []
731
+ };
732
+ collector.add(cls.sourceFile, cls.name);
733
+ }
734
+ }
735
+ const exports = collector.toRecord();
736
+ return {
737
+ classes: sortRecord(classes),
738
+ interfaces: sortRecord(interfaces),
739
+ enums: sortRecord(enums),
740
+ exports: sortRecord(exports)
741
+ };
742
+ }
743
+ //#endregion
744
+ //#region src/compat/extractors/php.ts
745
+ /**
746
+ * PHP API surface extractor.
747
+ *
748
+ * Walks `.php` files under `lib/` (or `src/`), parses classes, interfaces,
749
+ * methods, properties, and constants, then builds an `ApiSurface`.
750
+ *
751
+ * Parser: `php-parser.ts` — tree-sitter-based PHP source analysis
752
+ * Surface: `php-surface.ts` — ApiSurface construction
753
+ */
754
+ const PHP_BODY_TYPES = new Set(["array", "mixed"]);
755
+ const phpHints = {
756
+ stripNullable(type) {
757
+ if (type.startsWith("?")) return type.slice(1);
758
+ const parts = type.split("|").filter((p) => p.trim().toLowerCase() !== "null");
759
+ if (parts.length < type.split("|").length) return parts.join("|");
760
+ return null;
761
+ },
762
+ isNullableOnlyDifference(a, b) {
763
+ return defaultIsNullableOnlyDifference(this, a, b);
764
+ },
765
+ isUnionReorder(a, b) {
766
+ const sortParts = (t) => t.split("|").map((s) => s.trim()).sort().join("|");
767
+ return a !== b && sortParts(a) === sortParts(b);
768
+ },
769
+ isGenericTypeParam(_type) {
770
+ return false;
771
+ },
772
+ isExtractionArtifact(type) {
773
+ return type === "mixed" || type === "Object";
774
+ },
775
+ tolerateCategoryMismatch: false,
776
+ extractReturnTypeName(returnType) {
777
+ const stripped = this.stripNullable(returnType) ?? returnType;
778
+ if ([
779
+ "string",
780
+ "int",
781
+ "float",
782
+ "bool",
783
+ "void",
784
+ "array",
785
+ "mixed"
786
+ ].includes(stripped)) return null;
787
+ return stripped.replace(/^\\/, "");
788
+ },
789
+ extractParamTypeName(paramType) {
790
+ const stripped = this.stripNullable(paramType) ?? paramType;
791
+ if ([
792
+ "string",
793
+ "int",
794
+ "float",
795
+ "bool",
796
+ "void",
797
+ "array",
798
+ "mixed",
799
+ "null",
800
+ "callable"
801
+ ].includes(stripped)) return null;
802
+ return stripped.replace(/^\\/, "");
803
+ },
804
+ propertyMatchesClass(propertyName, className) {
805
+ return propertyName.toLowerCase() === className.toLowerCase();
806
+ },
807
+ derivedModelNames(_modelName) {
808
+ return [];
809
+ },
810
+ isSignatureEquivalent(baseline, candidate, _candidateSurface) {
811
+ if (!(baseline.returnType === candidate.returnType || baseline.returnType === "mixed" || candidate.returnType === "mixed" || baseline.returnType === "void" && candidate.returnType === "void" || baseline.returnType === "array" && candidate.returnType === "array" || baseline.returnType.includes("\\Resource\\") || candidate.returnType.includes("\\Resource\\"))) return false;
812
+ if (baseline.params.length === candidate.params.length) {
813
+ let allTypesMatch = true;
814
+ for (let i = 0; i < baseline.params.length; i++) if (baseline.params[i].type !== candidate.params[i].type) {
815
+ allTypesMatch = false;
816
+ break;
817
+ }
818
+ if (allTypesMatch) return true;
819
+ }
820
+ if (candidate.params.some((p) => PHP_BODY_TYPES.has(p.type))) return true;
821
+ if (baseline.params.length >= 1 && candidate.params.length >= 1) {
822
+ let typeMatch = true;
823
+ const minLen = Math.min(baseline.params.length, candidate.params.length);
824
+ for (let i = 0; i < minLen; i++) if (baseline.params[i].type !== candidate.params[i].type) {
825
+ typeMatch = false;
826
+ break;
827
+ }
828
+ if (typeMatch) {
829
+ const extraCandOk = candidate.params.slice(minLen).every((p) => p.optional);
830
+ const extraBaseOk = baseline.params.slice(minLen).every((p) => p.optional);
831
+ if (extraCandOk && extraBaseOk) return true;
832
+ }
833
+ }
834
+ return false;
835
+ },
836
+ modelBaseClasses: ["BaseWorkOSResource"],
837
+ exceptionBaseClasses: ["Exception", "\\Exception"]
838
+ };
839
+ /** Create a PHP extractor with optional hint overrides. */
840
+ function createPhpExtractor(hintOverrides) {
841
+ const mergedHints = hintOverrides ? {
842
+ ...phpHints,
843
+ ...hintOverrides
844
+ } : phpHints;
845
+ return {
846
+ language: "php",
847
+ hints: mergedHints,
848
+ async extract(sdkPath) {
849
+ let sourceDir;
850
+ const libPath = resolve(sdkPath, "lib");
851
+ const srcPath = resolve(sdkPath, "src");
852
+ try {
853
+ statSync(libPath);
854
+ sourceDir = libPath;
855
+ } catch {
856
+ try {
857
+ statSync(srcPath);
858
+ sourceDir = srcPath;
859
+ } catch {
860
+ throw new ExtractorError(`No lib/ or src/ directory found in ${sdkPath}`, `Ensure the --sdk-path argument points to a PHP project root containing a lib/ or src/ directory.`);
861
+ }
862
+ }
863
+ const phpFiles = walkPhpFiles(sourceDir);
864
+ if (phpFiles.length === 0) throw new ExtractorError(`No .php files found in ${sourceDir}`, `Ensure the source directory contains PHP source files.`);
865
+ const allClasses = [];
866
+ for (const filePath of phpFiles) {
867
+ const parsed = parsePhpFile(filePath, sdkPath);
868
+ allClasses.push(...parsed.classes);
869
+ }
870
+ const { classes, interfaces, enums, exports } = buildSurface$6(allClasses, mergedHints);
871
+ return {
872
+ language: "php",
873
+ extractedFrom: sdkPath,
874
+ extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
875
+ classes: sortRecord(classes),
876
+ interfaces: sortRecord(interfaces),
877
+ typeAliases: {},
878
+ enums: sortRecord(enums),
879
+ exports: sortRecord(exports)
880
+ };
881
+ }
882
+ };
883
+ }
884
+ /** Default PHP extractor with generic language hints (no SDK-specific bases). */
885
+ const phpExtractor = createPhpExtractor();
886
+ //#endregion
887
+ //#region src/compat/extractors/python-parser.ts
888
+ /** Python source parser — tree-sitter-based extraction of classes, type aliases, and symbols. */
889
+ const pythonParser = new Parser();
890
+ pythonParser.setLanguage(Python);
891
+ function walkPythonFiles(dir) {
892
+ const results = [];
893
+ let entries;
894
+ try {
895
+ entries = readdirSync(dir);
896
+ } catch {
897
+ return results;
898
+ }
899
+ for (const entry of entries) {
900
+ const fullPath = join(dir, entry);
901
+ let stat;
902
+ try {
903
+ stat = statSync(fullPath);
904
+ } catch {
905
+ continue;
906
+ }
907
+ if (stat.isDirectory()) {
908
+ if (entry.startsWith(".") || entry === "__pycache__" || entry === "tests" || entry === "test" || entry === "node_modules") continue;
909
+ results.push(...walkPythonFiles(fullPath));
910
+ } else if (entry.endsWith(".py")) {
911
+ if (entry.endsWith("_test.py") || entry.startsWith("test_")) continue;
912
+ if (entry.startsWith("_") && entry !== "__init__.py") continue;
913
+ results.push(fullPath);
914
+ }
915
+ }
916
+ return results.sort();
917
+ }
918
+ /** Find the Python source root (prefers `src/`, falls back to top-level package). */
919
+ function findPythonSourceRoot(sdkPath) {
920
+ const srcDir = join(sdkPath, "src");
921
+ try {
922
+ if (statSync(srcDir).isDirectory()) {
923
+ const entries = readdirSync(srcDir);
924
+ for (const entry of entries) {
925
+ const pkgDir = join(srcDir, entry);
926
+ try {
927
+ if (statSync(pkgDir).isDirectory()) {
928
+ const initFile = join(pkgDir, "__init__.py");
929
+ try {
930
+ statSync(initFile);
931
+ return srcDir;
932
+ } catch {}
933
+ }
934
+ } catch {
935
+ continue;
936
+ }
937
+ }
938
+ }
939
+ } catch {}
940
+ try {
941
+ const entries = readdirSync(sdkPath);
942
+ for (const entry of entries) {
943
+ if (entry.startsWith(".") || entry === "tests" || entry === "test" || entry === "node_modules") continue;
944
+ const pkgDir = join(sdkPath, entry);
945
+ try {
946
+ if (statSync(pkgDir).isDirectory()) {
947
+ const initFile = join(pkgDir, "__init__.py");
948
+ try {
949
+ statSync(initFile);
950
+ return sdkPath;
951
+ } catch {}
952
+ }
953
+ } catch {
954
+ continue;
955
+ }
956
+ }
957
+ } catch {}
958
+ return null;
959
+ }
960
+ /** Get all decorator names from a decorated_definition or class_definition. */
961
+ function getDecorators(node) {
962
+ const decorators = [];
963
+ if (node.type === "decorated_definition") {
964
+ for (const child of node.namedChildren) if (child.type === "decorator") {
965
+ const exprNode = child.namedChildren[0];
966
+ if (exprNode) decorators.push(exprNode.text);
967
+ }
968
+ }
969
+ return decorators;
970
+ }
971
+ /** Extract base class names from a class definition's argument_list. */
972
+ function getBaseClasses(classNode) {
973
+ const bases = [];
974
+ const argList = classNode.childForFieldName("superclasses");
975
+ if (!argList) return bases;
976
+ for (const child of argList.namedChildren) if (child.type === "identifier" || child.type === "attribute") bases.push(child.text);
977
+ else if (child.type === "keyword_argument") {}
978
+ return bases;
979
+ }
980
+ /** Extract annotated fields from a class body. */
981
+ function extractClassFields(bodyNode) {
982
+ const fields = [];
983
+ for (const child of bodyNode.namedChildren) {
984
+ if (child.type !== "expression_statement") continue;
985
+ const stmtChild = child.namedChildren[0];
986
+ if (!stmtChild || stmtChild.type !== "assignment") continue;
987
+ const left = stmtChild.childForFieldName("left");
988
+ const typeNode = stmtChild.childForFieldName("type");
989
+ if (!left || !typeNode) continue;
990
+ if (left.type !== "identifier") continue;
991
+ const right = stmtChild.childForFieldName("right");
992
+ fields.push({
993
+ name: left.text,
994
+ type: typeNode.text,
995
+ hasDefault: right !== null
996
+ });
997
+ }
998
+ return fields;
999
+ }
1000
+ /** Extract methods from a class body. */
1001
+ function extractClassMethods(bodyNode) {
1002
+ const methods = [];
1003
+ for (const child of bodyNode.namedChildren) {
1004
+ let funcNode = null;
1005
+ let decorators = [];
1006
+ if (child.type === "decorated_definition") {
1007
+ decorators = getDecorators(child);
1008
+ funcNode = child.namedChildren.find((c) => c.type === "function_definition") || null;
1009
+ } else if (child.type === "function_definition") funcNode = child;
1010
+ if (!funcNode) continue;
1011
+ const nameNode = funcNode.childForFieldName("name");
1012
+ if (!nameNode) continue;
1013
+ const methodName = nameNode.text;
1014
+ if (methodName.startsWith("_") && methodName !== "__init__") continue;
1015
+ const isAsync = funcNode.namedChildren.some((c) => c.type === "async");
1016
+ const isProperty = decorators.includes("property");
1017
+ const isClassMethod = decorators.includes("classmethod");
1018
+ const isStaticMethod = decorators.includes("staticmethod");
1019
+ const params = extractFunctionParams(funcNode.childForFieldName("parameters"), isStaticMethod || isClassMethod);
1020
+ const returnTypeNode = funcNode.childForFieldName("return_type");
1021
+ const returnType = returnTypeNode ? returnTypeNode.text : "";
1022
+ methods.push({
1023
+ name: methodName,
1024
+ params,
1025
+ returnType,
1026
+ isAsync,
1027
+ isProperty,
1028
+ isClassMethod,
1029
+ isStaticMethod
1030
+ });
1031
+ }
1032
+ return methods;
1033
+ }
1034
+ /** Extract parameters from a function's parameter list. */
1035
+ function extractFunctionParams(paramsNode, skipFirst) {
1036
+ if (!paramsNode) return [];
1037
+ const params = [];
1038
+ let skippedFirst = false;
1039
+ for (const child of paramsNode.namedChildren) {
1040
+ if (!skippedFirst && !skipFirst && child.type === "identifier" && (child.text === "self" || child.text === "cls")) {
1041
+ skippedFirst = true;
1042
+ continue;
1043
+ }
1044
+ if (!skippedFirst && !skipFirst && child.type === "typed_parameter" && child.namedChildren[0]?.text === "self") {
1045
+ skippedFirst = true;
1046
+ continue;
1047
+ }
1048
+ if (child.text === "*") continue;
1049
+ if (child.type === "list_splat_pattern") {
1050
+ const nameNode = child.namedChildren.find((c) => c.type === "identifier");
1051
+ params.push({
1052
+ name: nameNode ? `*${nameNode.text}` : "*args",
1053
+ type: "",
1054
+ optional: true
1055
+ });
1056
+ continue;
1057
+ }
1058
+ if (child.type === "dictionary_splat_pattern") {
1059
+ const nameNode = child.namedChildren.find((c) => c.type === "identifier");
1060
+ params.push({
1061
+ name: nameNode ? `**${nameNode.text}` : "**kwargs",
1062
+ type: "",
1063
+ optional: true
1064
+ });
1065
+ continue;
1066
+ }
1067
+ if (child.type === "typed_parameter") {
1068
+ const nameNode = child.namedChildren.find((c) => c.type === "identifier");
1069
+ const typeNode = child.childForFieldName("type");
1070
+ if (nameNode) params.push({
1071
+ name: nameNode.text,
1072
+ type: typeNode ? typeNode.text : "",
1073
+ optional: false
1074
+ });
1075
+ } else if (child.type === "typed_default_parameter") {
1076
+ const nameNode = child.childForFieldName("name");
1077
+ const typeNode = child.childForFieldName("type");
1078
+ if (nameNode) params.push({
1079
+ name: nameNode.text,
1080
+ type: typeNode ? typeNode.text : "",
1081
+ optional: true
1082
+ });
1083
+ } else if (child.type === "default_parameter") {
1084
+ const nameNode = child.childForFieldName("name");
1085
+ if (nameNode) params.push({
1086
+ name: nameNode.text,
1087
+ type: "",
1088
+ optional: true
1089
+ });
1090
+ } else if (child.type === "identifier") params.push({
1091
+ name: child.text,
1092
+ type: "",
1093
+ optional: false
1094
+ });
1095
+ }
1096
+ return params;
1097
+ }
1098
+ /** Extract module-level type aliases (e.g., FooType = Literal["a", "b"]). */
1099
+ function extractTypeAliases(tree, sourceFile) {
1100
+ const aliases = [];
1101
+ for (const node of tree.rootNode.namedChildren) {
1102
+ if (node.type === "type_alias_statement") {
1103
+ const nameNode = node.childForFieldName("name");
1104
+ const valueNode = node.childForFieldName("value");
1105
+ if (nameNode && valueNode) aliases.push({
1106
+ name: nameNode.text,
1107
+ value: valueNode.text,
1108
+ sourceFile
1109
+ });
1110
+ continue;
1111
+ }
1112
+ if (node.type === "expression_statement") {
1113
+ const inner = node.namedChildren[0];
1114
+ if (!inner || inner.type !== "assignment") continue;
1115
+ const left = inner.childForFieldName("left");
1116
+ const right = inner.childForFieldName("right");
1117
+ if (!left || !right) continue;
1118
+ if (left.type !== "identifier") continue;
1119
+ const name = left.text;
1120
+ if (name.startsWith("_")) continue;
1121
+ if (/^[A-Z][A-Z_0-9]*$/.test(name)) continue;
1122
+ const value = right.text;
1123
+ if (right.type === "subscript" || right.type === "string" && value.includes("[")) {
1124
+ let cleanValue = value;
1125
+ if (right.type === "string") cleanValue = value.replace(/^["']|["']$/g, "");
1126
+ aliases.push({
1127
+ name,
1128
+ value: cleanValue,
1129
+ sourceFile
1130
+ });
1131
+ }
1132
+ }
1133
+ }
1134
+ return aliases;
1135
+ }
1136
+ /** Extract __all__ exports from a module. */
1137
+ function extractAllExports(tree) {
1138
+ const exports = [];
1139
+ for (const node of tree.rootNode.namedChildren) {
1140
+ if (node.type !== "expression_statement") continue;
1141
+ const inner = node.namedChildren[0];
1142
+ if (!inner || inner.type !== "assignment") continue;
1143
+ const left = inner.childForFieldName("left");
1144
+ const right = inner.childForFieldName("right");
1145
+ if (!left || !right) continue;
1146
+ if (left.text !== "__all__") continue;
1147
+ if (right.type === "list") {
1148
+ for (const elem of right.namedChildren) if (elem.type === "string") {
1149
+ const text = elem.text.replace(/^["']|["']$/g, "");
1150
+ exports.push(text);
1151
+ }
1152
+ }
1153
+ }
1154
+ return exports;
1155
+ }
1156
+ function extractClassesFromTree(tree, sourceFile) {
1157
+ const classes = [];
1158
+ for (const node of tree.rootNode.namedChildren) {
1159
+ let classNode = null;
1160
+ let decorators = [];
1161
+ if (node.type === "decorated_definition") {
1162
+ decorators = getDecorators(node);
1163
+ classNode = node.namedChildren.find((c) => c.type === "class_definition") || null;
1164
+ } else if (node.type === "class_definition") classNode = node;
1165
+ if (!classNode) continue;
1166
+ const nameNode = classNode.childForFieldName("name");
1167
+ if (!nameNode) continue;
1168
+ const className = nameNode.text;
1169
+ const bases = getBaseClasses(classNode);
1170
+ const bodyNode = classNode.childForFieldName("body");
1171
+ if (!bodyNode) {
1172
+ classes.push({
1173
+ name: className,
1174
+ bases,
1175
+ fields: [],
1176
+ methods: [],
1177
+ decorators,
1178
+ sourceFile
1179
+ });
1180
+ continue;
1181
+ }
1182
+ const fields = extractClassFields(bodyNode);
1183
+ const methods = extractClassMethods(bodyNode);
1184
+ classes.push({
1185
+ name: className,
1186
+ bases,
1187
+ fields,
1188
+ methods,
1189
+ decorators,
1190
+ sourceFile
1191
+ });
1192
+ }
1193
+ return classes;
1194
+ }
1195
+ /** Parse a single Python source file and return all extracted symbols. */
1196
+ function parsePythonFile(filePath, sdkPath) {
1197
+ const source = readFileSync(filePath, "utf-8");
1198
+ const relPath = relative(sdkPath, filePath);
1199
+ const tree = safeParse$1(pythonParser, source);
1200
+ return {
1201
+ classes: extractClassesFromTree(tree, relPath),
1202
+ typeAliases: extractTypeAliases(tree, relPath),
1203
+ allExports: extractAllExports(tree),
1204
+ sourceFile: relPath
1205
+ };
1206
+ }
1207
+ //#endregion
1208
+ //#region src/compat/extractors/python-surface.ts
1209
+ /** Default model base classes. Overridden by hints.modelBaseClasses when provided. */
1210
+ const DEFAULT_MODEL_BASES = [];
1211
+ /** Known dict-like base classes (→ ApiInterface). */
1212
+ const DICT_BASES = new Set(["TypedDict"]);
1213
+ /** Known protocol bases (→ ApiClass as service). */
1214
+ const PROTOCOL_BASES = new Set(["Protocol"]);
1215
+ /** Default exception bases. Overridden by hints.exceptionBaseClasses when provided. */
1216
+ const DEFAULT_EXCEPTION_BASES = ["Exception", "BaseException"];
1217
+ /** Check if a class has any of the given base classes (by simple name). */
1218
+ function hasBase(cls, bases) {
1219
+ return cls.bases.some((b) => bases.has(b));
1220
+ }
1221
+ /** Check if a class is a model class (inherits from BaseModel or configured model bases,
1222
+ * transitively from another class that does). */
1223
+ function isModelClass(cls, allClasses, modelBases) {
1224
+ if (hasBase(cls, modelBases)) return true;
1225
+ for (const base of cls.bases) {
1226
+ const parentCls = allClasses.get(base);
1227
+ if (parentCls && isModelClass(parentCls, allClasses, modelBases)) return true;
1228
+ }
1229
+ return false;
1230
+ }
1231
+ /** Check if a class is an exception class. */
1232
+ function isExceptionClass(cls, allClasses, exceptionBases) {
1233
+ if (hasBase(cls, exceptionBases)) return true;
1234
+ for (const base of cls.bases) {
1235
+ const parentCls = allClasses.get(base);
1236
+ if (parentCls && isExceptionClass(parentCls, allClasses, exceptionBases)) return true;
1237
+ }
1238
+ return false;
1239
+ }
1240
+ /** Parse Literal["a", "b", "c"] into member strings. */
1241
+ function parseLiteralMembers(value) {
1242
+ const match = value.match(/^Literal\[(.+)\]$/s);
1243
+ if (!match) return null;
1244
+ const inner = match[1];
1245
+ const members = {};
1246
+ const parts = [];
1247
+ let depth = 0;
1248
+ let current = "";
1249
+ for (const char of inner) {
1250
+ if (char === "[" || char === "(") depth++;
1251
+ else if (char === "]" || char === ")") depth--;
1252
+ else if (char === "," && depth === 0) {
1253
+ parts.push(current.trim());
1254
+ current = "";
1255
+ continue;
1256
+ }
1257
+ current += char;
1258
+ }
1259
+ if (current.trim()) parts.push(current.trim());
1260
+ for (const part of parts) {
1261
+ const stripped = part.replace(/^["']|["']$/g, "");
1262
+ if (stripped) members[stripped] = stripped;
1263
+ }
1264
+ return Object.keys(members).length > 0 ? members : null;
1265
+ }
1266
+ /** Unwrap SyncOrAsync[T] → T. */
1267
+ function unwrapReturnType(returnType) {
1268
+ const syncOrAsyncMatch = returnType.match(/^SyncOrAsync\[(.+)\]$/);
1269
+ if (syncOrAsyncMatch) return syncOrAsyncMatch[1];
1270
+ const awaitableMatch = returnType.match(/^Awaitable\[(.+)\]$/);
1271
+ if (awaitableMatch) return awaitableMatch[1];
1272
+ return returnType;
1273
+ }
1274
+ function buildSurface$5(parsedFiles, hints) {
1275
+ const MODEL_BASES = new Set(hints?.modelBaseClasses ?? DEFAULT_MODEL_BASES);
1276
+ const EXCEPTION_BASES = new Set(hints?.exceptionBaseClasses ?? DEFAULT_EXCEPTION_BASES);
1277
+ const classes = {};
1278
+ const interfaces = {};
1279
+ const typeAliases = {};
1280
+ const enums = {};
1281
+ const allClassesByName = /* @__PURE__ */ new Map();
1282
+ for (const file of parsedFiles) for (const cls of file.classes) allClassesByName.set(cls.name, cls);
1283
+ const protocolNames = /* @__PURE__ */ new Set();
1284
+ for (const file of parsedFiles) for (const cls of file.classes) if (hasBase(cls, PROTOCOL_BASES)) protocolNames.add(cls.name);
1285
+ const collector = new ExportCollector();
1286
+ for (const file of parsedFiles) {
1287
+ for (const alias of file.typeAliases) {
1288
+ const literalMembers = parseLiteralMembers(alias.value);
1289
+ if (literalMembers) {
1290
+ enums[alias.name] = {
1291
+ name: alias.name,
1292
+ sourceFile: alias.sourceFile,
1293
+ members: sortRecord(literalMembers)
1294
+ };
1295
+ collector.add(alias.sourceFile, alias.name);
1296
+ } else {
1297
+ typeAliases[alias.name] = {
1298
+ name: alias.name,
1299
+ sourceFile: alias.sourceFile,
1300
+ value: alias.value
1301
+ };
1302
+ collector.add(alias.sourceFile, alias.name);
1303
+ }
1304
+ }
1305
+ for (const cls of file.classes) {
1306
+ if (hasBase(cls, PROTOCOL_BASES)) {
1307
+ const apiMethods = {};
1308
+ for (const method of cls.methods) {
1309
+ if (method.name === "__init__") continue;
1310
+ const params = method.params.map((p) => ({
1311
+ name: p.name,
1312
+ type: p.type,
1313
+ optional: p.optional
1314
+ }));
1315
+ const returnType = unwrapReturnType(method.returnType);
1316
+ if (!apiMethods[method.name]) apiMethods[method.name] = [];
1317
+ apiMethods[method.name].push({
1318
+ name: method.name,
1319
+ params,
1320
+ returnType,
1321
+ async: method.isAsync
1322
+ });
1323
+ }
1324
+ classes[cls.name] = {
1325
+ name: cls.name,
1326
+ sourceFile: cls.sourceFile,
1327
+ methods: sortRecord(apiMethods),
1328
+ properties: {},
1329
+ constructorParams: []
1330
+ };
1331
+ collector.add(cls.sourceFile, cls.name);
1332
+ continue;
1333
+ }
1334
+ if (isModelClass(cls, allClassesByName, MODEL_BASES)) {
1335
+ const fields = {};
1336
+ for (const field of cls.fields) fields[field.name] = {
1337
+ name: field.name,
1338
+ type: field.type,
1339
+ optional: field.hasDefault
1340
+ };
1341
+ const extendsArr = cls.bases.filter((b) => !MODEL_BASES.has(b) && allClassesByName.has(b));
1342
+ interfaces[cls.name] = {
1343
+ name: cls.name,
1344
+ sourceFile: cls.sourceFile,
1345
+ fields: sortRecord(fields),
1346
+ extends: extendsArr
1347
+ };
1348
+ collector.add(cls.sourceFile, cls.name);
1349
+ continue;
1350
+ }
1351
+ if (hasBase(cls, DICT_BASES)) {
1352
+ const fields = {};
1353
+ for (const field of cls.fields) fields[field.name] = {
1354
+ name: field.name,
1355
+ type: field.type,
1356
+ optional: field.hasDefault
1357
+ };
1358
+ interfaces[cls.name] = {
1359
+ name: cls.name,
1360
+ sourceFile: cls.sourceFile,
1361
+ fields: sortRecord(fields),
1362
+ extends: []
1363
+ };
1364
+ collector.add(cls.sourceFile, cls.name);
1365
+ continue;
1366
+ }
1367
+ if (isExceptionClass(cls, allClassesByName, EXCEPTION_BASES)) {
1368
+ const apiMethods = {};
1369
+ for (const method of cls.methods) {
1370
+ if (method.name === "__init__") continue;
1371
+ const params = method.params.map((p) => ({
1372
+ name: p.name,
1373
+ type: p.type,
1374
+ optional: p.optional
1375
+ }));
1376
+ if (!apiMethods[method.name]) apiMethods[method.name] = [];
1377
+ apiMethods[method.name].push({
1378
+ name: method.name,
1379
+ params,
1380
+ returnType: unwrapReturnType(method.returnType),
1381
+ async: method.isAsync
1382
+ });
1383
+ }
1384
+ const fields = {};
1385
+ for (const field of cls.fields) fields[field.name] = {
1386
+ name: field.name,
1387
+ type: field.type,
1388
+ optional: field.hasDefault
1389
+ };
1390
+ classes[cls.name] = {
1391
+ name: cls.name,
1392
+ sourceFile: cls.sourceFile,
1393
+ methods: sortRecord(apiMethods),
1394
+ properties: {},
1395
+ constructorParams: []
1396
+ };
1397
+ collector.add(cls.sourceFile, cls.name);
1398
+ continue;
1399
+ }
1400
+ if (cls.methods.length > 0) {
1401
+ const apiMethods = {};
1402
+ const constructorParams = [];
1403
+ for (const method of cls.methods) {
1404
+ if (method.name === "__init__") {
1405
+ constructorParams.push(...method.params.map((p) => ({
1406
+ name: p.name,
1407
+ type: p.type,
1408
+ optional: p.optional
1409
+ })));
1410
+ continue;
1411
+ }
1412
+ const params = method.params.map((p) => ({
1413
+ name: p.name,
1414
+ type: p.type,
1415
+ optional: p.optional
1416
+ }));
1417
+ if (!apiMethods[method.name]) apiMethods[method.name] = [];
1418
+ apiMethods[method.name].push({
1419
+ name: method.name,
1420
+ params,
1421
+ returnType: unwrapReturnType(method.returnType),
1422
+ async: method.isAsync
1423
+ });
1424
+ }
1425
+ classes[cls.name] = {
1426
+ name: cls.name,
1427
+ sourceFile: cls.sourceFile,
1428
+ methods: sortRecord(apiMethods),
1429
+ properties: {},
1430
+ constructorParams
1431
+ };
1432
+ collector.add(cls.sourceFile, cls.name);
1433
+ continue;
1434
+ }
1435
+ if (cls.fields.length > 0) {
1436
+ const fields = {};
1437
+ for (const field of cls.fields) fields[field.name] = {
1438
+ name: field.name,
1439
+ type: field.type,
1440
+ optional: field.hasDefault
1441
+ };
1442
+ interfaces[cls.name] = {
1443
+ name: cls.name,
1444
+ sourceFile: cls.sourceFile,
1445
+ fields: sortRecord(fields),
1446
+ extends: []
1447
+ };
1448
+ collector.add(cls.sourceFile, cls.name);
1449
+ }
1450
+ }
1451
+ if (file.allExports.length > 0) for (const name of file.allExports) collector.add(file.sourceFile, name);
1452
+ }
1453
+ const exports = collector.toRecord();
1454
+ return {
1455
+ classes: sortRecord(classes),
1456
+ interfaces: sortRecord(interfaces),
1457
+ typeAliases: sortRecord(typeAliases),
1458
+ enums: sortRecord(enums),
1459
+ exports: sortRecord(exports)
1460
+ };
1461
+ }
1462
+ //#endregion
1463
+ //#region src/compat/extractors/python.ts
1464
+ /**
1465
+ * Python API surface extractor.
1466
+ *
1467
+ * Walks `.py` files under `src/` (fallback to top-level package dirs),
1468
+ * parses classes, type aliases, and module exports, then builds an
1469
+ * `ApiSurface` with Protocol-based services, Pydantic model interfaces,
1470
+ * Literal type enums, and exception classes.
1471
+ *
1472
+ * Parser: `python-parser.ts` — tree-sitter-based Python source analysis
1473
+ * Surface: `python-surface.ts` — ApiSurface construction
1474
+ */
1475
+ const PYTHON_PRIMITIVE_TYPES = new Set([
1476
+ "str",
1477
+ "int",
1478
+ "float",
1479
+ "bool",
1480
+ "bytes"
1481
+ ]);
1482
+ const pythonHints = {
1483
+ stripNullable(type) {
1484
+ const normalized = type.replace(/\s+/g, " ").trim();
1485
+ const optMatch = normalized.match(/^Optional\[(.+)\]$/);
1486
+ if (optMatch) return optMatch[1].trim();
1487
+ const nrMatch = normalized.match(/^NotRequired\[(.+)\]$/);
1488
+ if (nrMatch) return nrMatch[1].trim();
1489
+ const parts = normalized.split("|").map((p) => p.trim()).filter((p) => p !== "None");
1490
+ if (parts.length < normalized.split("|").length) return parts.join(" | ");
1491
+ return null;
1492
+ },
1493
+ isNullableOnlyDifference(a, b) {
1494
+ return defaultIsNullableOnlyDifference(this, a, b);
1495
+ },
1496
+ isUnionReorder(a, b) {
1497
+ const parseUnion = (t) => {
1498
+ const unionMatch = t.match(/^Union\[(.+)\]$/);
1499
+ if (unionMatch) return unionMatch[1].split(",").map((s) => s.trim()).sort();
1500
+ return t.split("|").map((s) => s.trim()).sort();
1501
+ };
1502
+ return a !== b && parseUnion(a).join(",") === parseUnion(b).join(",");
1503
+ },
1504
+ isGenericTypeParam(type) {
1505
+ return /^[A-Z]$/.test(type) || /^T[A-Z][a-zA-Z]*$/.test(type);
1506
+ },
1507
+ isExtractionArtifact(type) {
1508
+ return type === "Any" || type === "object";
1509
+ },
1510
+ tolerateCategoryMismatch: true,
1511
+ extractReturnTypeName(returnType) {
1512
+ let inner = returnType;
1513
+ const syncOrAsyncMatch = inner.match(/^SyncOrAsync\[(.+)\]$/);
1514
+ if (syncOrAsyncMatch) inner = syncOrAsyncMatch[1];
1515
+ const awaitableMatch = inner.match(/^Awaitable\[(.+)\]$/);
1516
+ if (awaitableMatch) inner = awaitableMatch[1];
1517
+ const optMatch = inner.match(/^Optional\[(.+)\]$/);
1518
+ if (optMatch) inner = optMatch[1];
1519
+ for (const pattern of this.listResourcePatterns ?? []) {
1520
+ const prefix = pattern + "[";
1521
+ if (inner.startsWith(prefix)) {
1522
+ const rest = inner.slice(prefix.length);
1523
+ const end = rest.search(/[,\]]/);
1524
+ if (end > 0) {
1525
+ inner = rest.slice(0, end).trim();
1526
+ break;
1527
+ }
1528
+ }
1529
+ }
1530
+ const seqMatch = inner.match(/^Sequence\[(.+)\]$/);
1531
+ if (seqMatch) inner = seqMatch[1];
1532
+ if ([
1533
+ "str",
1534
+ "int",
1535
+ "float",
1536
+ "bool",
1537
+ "None",
1538
+ "bytes",
1539
+ "dict",
1540
+ "list"
1541
+ ].includes(inner)) return null;
1542
+ return inner;
1543
+ },
1544
+ extractParamTypeName(paramType) {
1545
+ let inner = paramType;
1546
+ const optMatch = inner.match(/^Optional\[(.+)\]$/);
1547
+ if (optMatch) inner = optMatch[1];
1548
+ if ([
1549
+ "str",
1550
+ "int",
1551
+ "float",
1552
+ "bool",
1553
+ "None",
1554
+ "bytes",
1555
+ "dict",
1556
+ "list"
1557
+ ].includes(inner)) return null;
1558
+ return inner;
1559
+ },
1560
+ propertyMatchesClass(propertyName, className) {
1561
+ return propertyName.replace(/_/g, "").toLowerCase() === className.toLowerCase();
1562
+ },
1563
+ derivedModelNames(modelName) {
1564
+ return [`${modelName}Response`];
1565
+ },
1566
+ isTypeEquivalent(baselineType, candidateType, candidateSurface) {
1567
+ const stripNullable = (t) => {
1568
+ const normalized = t.replace(/\s+/g, " ").trim();
1569
+ const optMatch = normalized.match(/^Optional\[(.+)\]$/);
1570
+ if (optMatch) return optMatch[1].trim();
1571
+ const nrMatch = normalized.match(/^NotRequired\[(.+)\]$/);
1572
+ if (nrMatch) return nrMatch[1].trim();
1573
+ const parts = normalized.split("|").map((p) => p.trim()).filter((p) => p !== "None");
1574
+ if (parts.length < normalized.split("|").length) return parts.join(" | ");
1575
+ return normalized;
1576
+ };
1577
+ const baseClean = stripNullable(baselineType);
1578
+ const candClean = stripNullable(candidateType);
1579
+ const literalMatch = (t) => t.match(/^Literal\[(['"])(.+)\1\]$/);
1580
+ const baseLit = literalMatch(baseClean);
1581
+ const candLit = literalMatch(candClean);
1582
+ if (baseLit && candLit && baseLit[2] === candLit[2]) return true;
1583
+ const stripWrappers = (t) => {
1584
+ let inner = t;
1585
+ const louMatch = inner.match(/^LiteralOrUntyped\[(.+)\]$/);
1586
+ if (louMatch) inner = louMatch[1];
1587
+ return inner;
1588
+ };
1589
+ const baseUnwrapped = stripWrappers(baseClean);
1590
+ const candUnwrapped = stripWrappers(candClean);
1591
+ if (baseUnwrapped !== baseClean || candUnwrapped !== candClean) {
1592
+ if (baseUnwrapped === candClean || candUnwrapped === baseClean) return true;
1593
+ if (baseUnwrapped === candUnwrapped) return true;
1594
+ if (baseUnwrapped.startsWith("Literal[") && candidateSurface.enums[candClean]) return true;
1595
+ if (candUnwrapped.startsWith("Literal[") && candidateSurface.enums?.[baseClean]) return true;
1596
+ }
1597
+ const seqPattern = /^(?:Sequence|list|List)\[(.+)\]$/;
1598
+ const baseSeq = baseClean.match(seqPattern);
1599
+ const candSeq = candClean.match(seqPattern);
1600
+ if (baseSeq && candSeq) {
1601
+ if (baseSeq[1] === candSeq[1]) return true;
1602
+ const baseInner = baseSeq[1];
1603
+ const candInner = candSeq[1];
1604
+ if (NAMED_TYPE_RE.test(baseInner) && NAMED_TYPE_RE.test(candInner)) {
1605
+ if (typeExistsInSurface(candInner, candidateSurface)) {
1606
+ if (candInner.includes(baseInner) || baseInner.includes(candInner)) return true;
1607
+ if (namedTypeWordsOverlap(baseInner, candInner)) return true;
1608
+ }
1609
+ }
1610
+ const primitiveTypes = PYTHON_PRIMITIVE_TYPES;
1611
+ if (primitiveTypes.has(baseInner) && NAMED_TYPE_RE.test(candInner) || primitiveTypes.has(candInner) && NAMED_TYPE_RE.test(baseInner)) return true;
1612
+ }
1613
+ if (baseSeq && !candSeq) {
1614
+ const candListMatch = candClean.match(/^(?:list|List)\[(.+)\]$/);
1615
+ if (candListMatch) {
1616
+ const baseInner = baseSeq[1];
1617
+ const candInner = candListMatch[1];
1618
+ if (baseInner === candInner) return true;
1619
+ const primitiveTypes = PYTHON_PRIMITIVE_TYPES;
1620
+ if (primitiveTypes.has(baseInner) && NAMED_TYPE_RE.test(candInner) || primitiveTypes.has(candInner) && NAMED_TYPE_RE.test(baseInner)) return true;
1621
+ }
1622
+ }
1623
+ const mappingPattern = /^(?:Mapping|dict|Dict)\[(.+)\]$/;
1624
+ const baseMapping = baseClean.match(mappingPattern);
1625
+ const candMapping = candClean.match(mappingPattern);
1626
+ if (baseMapping && candMapping) return true;
1627
+ const dictLikePattern = /^(?:dict|Dict|Mapping)\[str,\s*.+\]$/;
1628
+ if (dictLikePattern.test(baseClean) && dictLikePattern.test(candClean)) return true;
1629
+ const isMapType = (t) => dictLikePattern.test(t) || /^(?:Metadata|AuditLog\w*Metadata)$/.test(t);
1630
+ if (isMapType(baseClean) && isMapType(candClean)) return true;
1631
+ if (NAMED_TYPE_RE.test(baseClean) && dictLikePattern.test(candClean)) {
1632
+ if (/(?:Metadata|Attributes)$/.test(baseClean)) return true;
1633
+ }
1634
+ if (NAMED_TYPE_RE.test(candClean) && dictLikePattern.test(baseClean)) {
1635
+ if (/(?:Metadata|Attributes)$/.test(candClean)) return true;
1636
+ }
1637
+ if (baseClean === "str" && candClean.startsWith("Literal[")) return true;
1638
+ if (candClean === "str" && baseClean.startsWith("Literal[")) return true;
1639
+ if (baseClean.startsWith("Literal[") && candidateSurface.enums[candClean]) return true;
1640
+ if (candClean.startsWith("Literal[") && candidateSurface.enums?.[baseClean]) return true;
1641
+ if (baseClean === "str" && candidateSurface.enums[candClean]) return true;
1642
+ if (candClean === "str" && candidateSurface.enums?.[baseClean]) return true;
1643
+ const numericTypes = new Set(["int", "float"]);
1644
+ if (numericTypes.has(baseClean) && numericTypes.has(candClean)) return true;
1645
+ if (NAMED_TYPE_RE.test(baseClean) && NAMED_TYPE_RE.test(candClean)) {
1646
+ if (typeExistsInSurface(candClean, candidateSurface)) {
1647
+ if (candClean.includes(baseClean) || baseClean.includes(candClean)) return true;
1648
+ const baseNoResp = baseClean.replace(/Response$/, "");
1649
+ const candNoResp = candClean.replace(/Response$/, "");
1650
+ if (candNoResp.includes(baseNoResp) || baseNoResp.includes(candNoResp)) return true;
1651
+ if (namedTypeWordsOverlap(baseClean, candClean)) return true;
1652
+ }
1653
+ }
1654
+ return false;
1655
+ },
1656
+ isSignatureEquivalent(baseline, candidate, _candidateSurface) {
1657
+ if (baseline.returnType !== candidate.returnType) {
1658
+ const baseRet = baseline.returnType;
1659
+ const candRet = candidate.returnType;
1660
+ if (!(NAMED_TYPE_RE.test(baseRet) && NAMED_TYPE_RE.test(candRet))) return false;
1661
+ if (!candRet.includes(baseRet) && !baseRet.includes(candRet)) {
1662
+ if (!namedTypeWordsOverlap(baseRet, candRet)) return false;
1663
+ }
1664
+ }
1665
+ const bodyDictTypes = new Set([
1666
+ "dict[str, Any]",
1667
+ "Dict[str, Any]",
1668
+ "dict"
1669
+ ]);
1670
+ const candBodyIdx = candidate.params.findIndex((p) => bodyDictTypes.has(p.type) && (p.name === "payload" || p.name === "body" || p.name === "data"));
1671
+ if (candBodyIdx < 0) return false;
1672
+ for (let i = 0; i < candBodyIdx; i++) {
1673
+ if (i >= baseline.params.length) return false;
1674
+ if (baseline.params[i].name !== candidate.params[i].name) return false;
1675
+ if (baseline.params[i].type !== candidate.params[i].type) return false;
1676
+ }
1677
+ return true;
1678
+ },
1679
+ modelBaseClasses: [],
1680
+ exceptionBaseClasses: ["Exception", "BaseException"],
1681
+ listResourcePatterns: []
1682
+ };
1683
+ /** Create a Python extractor with optional hint overrides. */
1684
+ function createPythonExtractor(hintOverrides) {
1685
+ const mergedHints = hintOverrides ? {
1686
+ ...pythonHints,
1687
+ ...hintOverrides
1688
+ } : pythonHints;
1689
+ return {
1690
+ language: "python",
1691
+ hints: mergedHints,
1692
+ async extract(sdkPath) {
1693
+ const sourceRoot = findPythonSourceRoot(sdkPath);
1694
+ if (!sourceRoot) throw new ExtractorError(`No Python package found in ${sdkPath}`, `Ensure the --sdk-path argument points to a Python project root containing a package directory with __init__.py.`);
1695
+ const pyFiles = walkPythonFiles(sourceRoot);
1696
+ if (pyFiles.length === 0) throw new ExtractorError(`No .py files found in ${sdkPath}`, `Ensure the project contains Python source files.`);
1697
+ const parsedFiles = [];
1698
+ for (const filePath of pyFiles) {
1699
+ const parsed = parsePythonFile(filePath, sdkPath);
1700
+ parsedFiles.push(parsed);
1701
+ }
1702
+ const { classes, interfaces, typeAliases, enums, exports } = buildSurface$5(parsedFiles, mergedHints);
1703
+ return {
1704
+ language: "python",
1705
+ extractedFrom: sdkPath,
1706
+ extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
1707
+ classes,
1708
+ interfaces,
1709
+ typeAliases,
1710
+ enums,
1711
+ exports
1712
+ };
1713
+ }
1714
+ };
1715
+ }
1716
+ /** Default Python extractor with generic language hints (no SDK-specific bases). */
1717
+ const pythonExtractor = createPythonExtractor();
1718
+ //#endregion
1719
+ //#region src/compat/extractors/ruby-parser.ts
1720
+ /**
1721
+ * Ruby entity extraction — tree-sitter-based parsing of classes, service modules,
1722
+ * and enum modules from Ruby source files.
1723
+ */
1724
+ const rubyParser = new Parser();
1725
+ rubyParser.setLanguage(Ruby);
1726
+ function safeParse(source) {
1727
+ return safeParse$1(rubyParser, source);
1728
+ }
1729
+ /** Parse a Ruby source string and return all class declarations (excluding service modules). */
1730
+ function extractClasses(source) {
1731
+ const tree = safeParse(source);
1732
+ const classes = [];
1733
+ const serviceRanges = findServiceModulesFromTree(tree.rootNode).map((n) => [n.startPosition.row, n.endPosition.row]);
1734
+ for (const node of tree.rootNode.descendantsOfType("class")) {
1735
+ if (node.type !== "class") continue;
1736
+ if (isInsideRanges(node.startPosition.row, serviceRanges)) continue;
1737
+ if (node.descendantsOfType("singleton_class").length > 0) continue;
1738
+ const nameNode = node.childForFieldName("name");
1739
+ if (!nameNode) continue;
1740
+ const className = nameNode.text;
1741
+ const bodyNode = node.childForFieldName("body");
1742
+ const methods = {};
1743
+ const properties = {};
1744
+ const constructorParams = [];
1745
+ if (bodyNode) {
1746
+ extractAttributes(bodyNode, properties);
1747
+ const extractedMethods = extractMethodsFromBody(bodyNode);
1748
+ for (const method of extractedMethods) if (method.name === "initialize") constructorParams.push(...method.params);
1749
+ else {
1750
+ if (!methods[method.name]) methods[method.name] = [];
1751
+ methods[method.name].push(method);
1752
+ }
1753
+ }
1754
+ classes.push({
1755
+ name: className,
1756
+ methods: sortRecord(methods),
1757
+ properties: sortRecord(properties),
1758
+ constructorParams
1759
+ });
1760
+ }
1761
+ return classes;
1762
+ }
1763
+ /** Extract service modules (modules with `class << self` at immediate depth). */
1764
+ function extractServiceModules(source) {
1765
+ const tree = safeParse(source);
1766
+ const services = [];
1767
+ for (const moduleNode of findServiceModulesFromTree(tree.rootNode)) {
1768
+ const nameNode = moduleNode.childForFieldName("name");
1769
+ if (!nameNode) continue;
1770
+ const moduleName = nameNode.text;
1771
+ const bodyNode = moduleNode.childForFieldName("body");
1772
+ if (!bodyNode) continue;
1773
+ const singletonNode = findImmediateSingletonClass(bodyNode);
1774
+ if (!singletonNode) continue;
1775
+ const singletonBody = singletonNode.childForFieldName("body");
1776
+ if (!singletonBody) continue;
1777
+ const methods = {};
1778
+ const properties = {};
1779
+ const extractedMethods = extractMethodsFromBody(singletonBody);
1780
+ for (const method of extractedMethods) {
1781
+ if (!methods[method.name]) methods[method.name] = [];
1782
+ methods[method.name].push(method);
1783
+ }
1784
+ extractConstantsAsProperties(singletonBody, properties);
1785
+ if (Object.keys(methods).length > 0 || Object.keys(properties).length > 0) services.push({
1786
+ name: moduleName,
1787
+ methods: sortRecord(methods),
1788
+ properties: sortRecord(properties),
1789
+ constructorParams: []
1790
+ });
1791
+ }
1792
+ return services;
1793
+ }
1794
+ /** Extract enum-like modules (modules with string/number constants, no class << self). */
1795
+ function extractEnumModules$1(source) {
1796
+ const tree = safeParse(source);
1797
+ const enums = [];
1798
+ const serviceRanges = findServiceModulesFromTree(tree.rootNode).map((n) => [n.startPosition.row, n.endPosition.row]);
1799
+ for (const node of tree.rootNode.descendantsOfType("module")) {
1800
+ if (isInsideRanges(node.startPosition.row, serviceRanges)) continue;
1801
+ if (node.descendantsOfType("singleton_class").length > 0) continue;
1802
+ const nameNode = node.childForFieldName("name");
1803
+ if (!nameNode) continue;
1804
+ const bodyNode = node.childForFieldName("body");
1805
+ if (!bodyNode) continue;
1806
+ const constants = extractEnumConstants(bodyNode);
1807
+ if (Object.keys(constants).length < 2) continue;
1808
+ enums.push({
1809
+ name: nameNode.text,
1810
+ members: sortRecord(constants)
1811
+ });
1812
+ }
1813
+ return enums;
1814
+ }
1815
+ /** Extract autoload declarations to build the export map. */
1816
+ function extractAutoloads(source) {
1817
+ const tree = safeParse(source);
1818
+ const names = [];
1819
+ for (const callNode of tree.rootNode.descendantsOfType("call")) {
1820
+ const methodNode = callNode.childForFieldName("method");
1821
+ if (!methodNode || methodNode.text !== "autoload") continue;
1822
+ const argsNode = callNode.childForFieldName("arguments");
1823
+ if (!argsNode) continue;
1824
+ const firstArg = argsNode.namedChildren[0];
1825
+ if (firstArg && (firstArg.type === "simple_symbol" || firstArg.type === "symbol")) names.push(firstArg.text.replace(/^:/, ""));
1826
+ }
1827
+ return names;
1828
+ }
1829
+ /** Find module nodes that have `class << self` at their immediate body level. */
1830
+ function findServiceModulesFromTree(rootNode) {
1831
+ const result = [];
1832
+ for (const moduleNode of rootNode.descendantsOfType("module")) {
1833
+ const bodyNode = moduleNode.childForFieldName("body");
1834
+ if (!bodyNode) continue;
1835
+ if (findImmediateSingletonClass(bodyNode)) result.push(moduleNode);
1836
+ }
1837
+ return result;
1838
+ }
1839
+ /** Find a singleton_class (class << self) at the immediate body level (not nested). */
1840
+ function findImmediateSingletonClass(bodyNode) {
1841
+ for (const child of bodyNode.namedChildren) if (child.type === "singleton_class") return child;
1842
+ return null;
1843
+ }
1844
+ /** Extract methods from a body_statement, respecting private/protected/public markers. */
1845
+ function extractMethodsFromBody(bodyNode) {
1846
+ const methods = [];
1847
+ let isPrivate = false;
1848
+ for (const child of bodyNode.namedChildren) {
1849
+ if (child.type === "identifier") {
1850
+ if (child.text === "private") {
1851
+ isPrivate = true;
1852
+ continue;
1853
+ }
1854
+ if (child.text === "public") {
1855
+ isPrivate = false;
1856
+ continue;
1857
+ }
1858
+ if (child.text === "protected") {
1859
+ isPrivate = true;
1860
+ continue;
1861
+ }
1862
+ }
1863
+ if (isPrivate) continue;
1864
+ if (child.type === "method") {
1865
+ const nameNode = child.childForFieldName("name");
1866
+ if (!nameNode) continue;
1867
+ const params = extractMethodParams(child);
1868
+ methods.push({
1869
+ name: nameNode.text,
1870
+ params,
1871
+ returnType: "Object",
1872
+ async: false
1873
+ });
1874
+ }
1875
+ }
1876
+ return methods;
1877
+ }
1878
+ /** Extract parameters from a method node. */
1879
+ function extractMethodParams(methodNode) {
1880
+ const paramsNode = methodNode.childForFieldName("parameters");
1881
+ if (!paramsNode) return [];
1882
+ const params = [];
1883
+ for (const paramNode of paramsNode.namedChildren) switch (paramNode.type) {
1884
+ case "identifier":
1885
+ params.push({
1886
+ name: paramNode.text,
1887
+ type: "Object",
1888
+ optional: false
1889
+ });
1890
+ break;
1891
+ case "optional_parameter": {
1892
+ const nameNode = paramNode.childForFieldName("name");
1893
+ if (nameNode) params.push({
1894
+ name: nameNode.text,
1895
+ type: "Object",
1896
+ optional: true
1897
+ });
1898
+ break;
1899
+ }
1900
+ case "keyword_parameter": {
1901
+ const nameNode = paramNode.childForFieldName("name");
1902
+ if (nameNode) {
1903
+ const valueNode = paramNode.childForFieldName("value");
1904
+ params.push({
1905
+ name: nameNode.text,
1906
+ type: "Object",
1907
+ optional: valueNode !== null
1908
+ });
1909
+ }
1910
+ break;
1911
+ }
1912
+ case "splat_parameter": {
1913
+ const nameNode = paramNode.childForFieldName("name");
1914
+ params.push({
1915
+ name: nameNode ? `*${nameNode.text}` : "*args",
1916
+ type: "Object",
1917
+ optional: true
1918
+ });
1919
+ break;
1920
+ }
1921
+ case "hash_splat_parameter": {
1922
+ const nameNode = paramNode.childForFieldName("name");
1923
+ params.push({
1924
+ name: nameNode ? `**${nameNode.text}` : "**kwargs",
1925
+ type: "Object",
1926
+ optional: true
1927
+ });
1928
+ break;
1929
+ }
1930
+ case "block_parameter": {
1931
+ const nameNode = paramNode.childForFieldName("name");
1932
+ params.push({
1933
+ name: nameNode ? `&${nameNode.text}` : "&block",
1934
+ type: "Object",
1935
+ optional: true
1936
+ });
1937
+ break;
1938
+ }
1939
+ default: break;
1940
+ }
1941
+ return params;
1942
+ }
1943
+ /** Extract attr_accessor/attr_reader calls into properties. */
1944
+ function extractAttributes(bodyNode, properties) {
1945
+ for (const callNode of bodyNode.descendantsOfType("call")) {
1946
+ const methodNode = callNode.childForFieldName("method");
1947
+ if (!methodNode) continue;
1948
+ const methodName = methodNode.text;
1949
+ if (methodName !== "attr_accessor" && methodName !== "attr_reader") continue;
1950
+ const isReadonly = methodName === "attr_reader";
1951
+ const argsNode = callNode.childForFieldName("arguments");
1952
+ if (!argsNode) continue;
1953
+ for (const argNode of argsNode.namedChildren) if (argNode.type === "simple_symbol" || argNode.type === "symbol") {
1954
+ const name = argNode.text.replace(/^:/, "");
1955
+ properties[name] = {
1956
+ name,
1957
+ type: "Object",
1958
+ readonly: isReadonly
1959
+ };
1960
+ }
1961
+ }
1962
+ }
1963
+ /** Extract UPPER_CASE constants from a body as readonly properties. */
1964
+ function extractConstantsAsProperties(bodyNode, properties) {
1965
+ for (const child of bodyNode.namedChildren) {
1966
+ if (child.type !== "assignment") continue;
1967
+ const leftNode = child.childForFieldName("left");
1968
+ if (!leftNode || leftNode.type !== "constant") continue;
1969
+ const name = leftNode.text;
1970
+ if (!/^[A-Z][A-Z_0-9]*$/.test(name)) continue;
1971
+ properties[name] = {
1972
+ name,
1973
+ type: "Object",
1974
+ readonly: true
1975
+ };
1976
+ }
1977
+ }
1978
+ /** Extract enum-style constants (ConstantName = 'value') from a module body. */
1979
+ function extractEnumConstants(bodyNode) {
1980
+ const constants = {};
1981
+ for (const child of bodyNode.namedChildren) {
1982
+ if (child.type !== "assignment") continue;
1983
+ const leftNode = child.childForFieldName("left");
1984
+ if (!leftNode || leftNode.type !== "constant") continue;
1985
+ const name = leftNode.text;
1986
+ if (name === "ALL") continue;
1987
+ const rightNode = child.childForFieldName("right");
1988
+ if (!rightNode) continue;
1989
+ const value = extractLiteralValue(rightNode);
1990
+ if (value !== void 0) constants[name] = value;
1991
+ }
1992
+ return constants;
1993
+ }
1994
+ /** Extract a literal string or number value from a tree-sitter node. */
1995
+ function extractLiteralValue(node) {
1996
+ if (node.type === "string" || node.type === "string_content") {
1997
+ const text = node.text;
1998
+ if (text.startsWith("'") && text.endsWith("'") || text.startsWith("\"") && text.endsWith("\"")) return text.slice(1, -1);
1999
+ return text;
2000
+ }
2001
+ if (node.type === "integer") return Number(node.text);
2002
+ if (node.type === "float") return Number(node.text);
2003
+ if (node.type === "string") {
2004
+ const contentNode = node.namedChildren.find((c) => c.type === "string_content");
2005
+ if (contentNode) return contentNode.text;
2006
+ }
2007
+ }
2008
+ /** Check if a line number falls inside any of the given ranges. */
2009
+ function isInsideRanges(line, ranges) {
2010
+ for (const [start, end] of ranges) if (line > start && line < end) return true;
2011
+ return false;
2012
+ }
2013
+ //#endregion
2014
+ //#region src/compat/extractors/ruby.ts
2015
+ /**
2016
+ * Ruby API surface extractor.
2017
+ *
2018
+ * Walks `.rb` files under `lib/`, parses class/module declarations,
2019
+ * and builds an `ApiSurface` with classes, service modules, and enum modules.
2020
+ *
2021
+ * Parser: `ruby-parser.ts` — tree-sitter-based class/module/enum extraction
2022
+ */
2023
+ const rubyHints = {
2024
+ stripNullable(type) {
2025
+ const match = type.match(/^T\.nilable\((.+)\)$/);
2026
+ return match ? match[1] : null;
2027
+ },
2028
+ isNullableOnlyDifference(a, b) {
2029
+ return defaultIsNullableOnlyDifference(this, a, b);
2030
+ },
2031
+ isUnionReorder(_a, _b) {
2032
+ return false;
2033
+ },
2034
+ isGenericTypeParam(type) {
2035
+ return type === "T.untyped";
2036
+ },
2037
+ isExtractionArtifact(type) {
2038
+ return type === "T.untyped" || type === "BasicObject";
2039
+ },
2040
+ tolerateCategoryMismatch: false,
2041
+ extractReturnTypeName(returnType) {
2042
+ return returnType || null;
2043
+ },
2044
+ extractParamTypeName(paramType) {
2045
+ if ([
2046
+ "String",
2047
+ "Integer",
2048
+ "Float",
2049
+ "Boolean",
2050
+ "NilClass"
2051
+ ].includes(paramType)) return null;
2052
+ return paramType;
2053
+ },
2054
+ propertyMatchesClass(propertyName, className) {
2055
+ return propertyName.replace(/_/g, "").toLowerCase() === className.toLowerCase();
2056
+ },
2057
+ derivedModelNames(_modelName) {
2058
+ return [];
2059
+ }
2060
+ };
2061
+ /** Recursively collect all .rb files under a directory. */
2062
+ function collectRbFiles(dir) {
2063
+ const result = [];
2064
+ const entries = readdirSync(dir);
2065
+ for (const entry of entries.sort()) {
2066
+ const full = join(dir, entry);
2067
+ if (statSync(full).isDirectory()) result.push(...collectRbFiles(full));
2068
+ else if (entry.endsWith(".rb")) result.push(full);
2069
+ }
2070
+ return result;
2071
+ }
2072
+ const rubyExtractor = {
2073
+ language: "ruby",
2074
+ hints: rubyHints,
2075
+ async extract(sdkPath) {
2076
+ const libPath = resolve(sdkPath, "lib");
2077
+ try {
2078
+ statSync(libPath);
2079
+ } catch {
2080
+ throw new ExtractorError(`No lib/ directory found in ${sdkPath}`, `Verify the --sdk-path argument points to a Ruby project root containing a lib/ directory.`);
2081
+ }
2082
+ const rbFiles = collectRbFiles(libPath);
2083
+ if (rbFiles.length === 0) throw new ExtractorError(`No .rb files found in ${libPath}`, `Ensure the lib/ directory contains Ruby source files.`);
2084
+ const classes = {};
2085
+ const enums = {};
2086
+ const exports = {};
2087
+ for (const absPath of rbFiles) {
2088
+ const relPath = relative(sdkPath, absPath);
2089
+ const source = readFileSync(absPath, "utf-8");
2090
+ const fileExports = [];
2091
+ const extractedClasses = extractClasses(source);
2092
+ for (const cls of extractedClasses) {
2093
+ classes[cls.name] = {
2094
+ ...cls,
2095
+ sourceFile: relPath
2096
+ };
2097
+ fileExports.push(cls.name);
2098
+ }
2099
+ const extractedServices = extractServiceModules(source);
2100
+ for (const svc of extractedServices) {
2101
+ classes[svc.name] = {
2102
+ ...svc,
2103
+ sourceFile: relPath
2104
+ };
2105
+ fileExports.push(svc.name);
2106
+ }
2107
+ const extractedEnums = extractEnumModules$1(source);
2108
+ for (const en of extractedEnums) {
2109
+ enums[en.name] = {
2110
+ ...en,
2111
+ sourceFile: relPath
2112
+ };
2113
+ fileExports.push(en.name);
2114
+ }
2115
+ const autoloadNames = extractAutoloads(source);
2116
+ if (autoloadNames.length > 0) fileExports.push(...autoloadNames);
2117
+ if (fileExports.length > 0) exports[relPath] = [...new Set(fileExports)].sort();
2118
+ }
2119
+ return {
2120
+ language: "ruby",
2121
+ extractedFrom: sdkPath,
2122
+ extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
2123
+ classes: sortRecord(classes),
2124
+ interfaces: {},
2125
+ typeAliases: {},
2126
+ enums: sortRecord(enums),
2127
+ exports: sortRecord(exports)
2128
+ };
2129
+ }
2130
+ };
2131
+ //#endregion
2132
+ //#region src/compat/extractors/go-parser.ts
2133
+ /**
2134
+ * Go source parser — tree-sitter-based extraction of structs, functions, types,
2135
+ * and const blocks from Go source files.
2136
+ */
2137
+ const goParser = new Parser();
2138
+ goParser.setLanguage(Go);
2139
+ function walkGoFiles(dir) {
2140
+ const results = [];
2141
+ let entries;
2142
+ try {
2143
+ entries = readdirSync(dir);
2144
+ } catch {
2145
+ return results;
2146
+ }
2147
+ for (const entry of entries) {
2148
+ const fullPath = join(dir, entry);
2149
+ let stat;
2150
+ try {
2151
+ stat = statSync(fullPath);
2152
+ } catch {
2153
+ continue;
2154
+ }
2155
+ if (stat.isDirectory()) {
2156
+ if (entry.startsWith(".") || entry === "vendor" || entry === "testdata" || entry === "internal") continue;
2157
+ results.push(...walkGoFiles(fullPath));
2158
+ } else if (entry.endsWith(".go") && !entry.endsWith("_test.go")) results.push(fullPath);
2159
+ }
2160
+ return results.sort();
2161
+ }
2162
+ /** Get the text of a child node by field name, or null. */
2163
+ function fieldText(node, field) {
2164
+ const child = node.childForFieldName(field);
2165
+ return child ? child.text : null;
2166
+ }
2167
+ /** Extract the JSON field name from a struct tag. */
2168
+ function extractJsonTag(tagText) {
2169
+ const match = tagText.match(/json:"([^"]*)"/);
2170
+ if (!match) return null;
2171
+ const parts = match[1].split(",");
2172
+ const name = parts[0].trim();
2173
+ const omitempty = parts.includes("omitempty");
2174
+ if (name === "-") return null;
2175
+ return {
2176
+ name,
2177
+ omitempty
2178
+ };
2179
+ }
2180
+ /** Render a type node back to its Go source representation. */
2181
+ function typeNodeText(node) {
2182
+ return node.text;
2183
+ }
2184
+ function parseStructFields$1(structNode) {
2185
+ const fields = [];
2186
+ const fieldList = structNode.namedChildren.find((c) => c.type === "field_declaration_list");
2187
+ if (!fieldList) return fields;
2188
+ for (const fieldDecl of fieldList.namedChildren) {
2189
+ if (fieldDecl.type !== "field_declaration") continue;
2190
+ const nameNodes = fieldDecl.namedChildren.filter((c) => c.type === "field_identifier");
2191
+ if (nameNodes.length === 0) {
2192
+ const typeNode = fieldDecl.namedChildren.find((c) => c.type !== "raw_string_literal" && c.type !== "interpreted_string_literal");
2193
+ if (typeNode) {
2194
+ const typeName = typeNode.text.replace(/^\*/, "");
2195
+ if (/^[A-Z]/.test(typeName)) fields.push({
2196
+ name: typeName,
2197
+ type: typeNode.text,
2198
+ optional: false
2199
+ });
2200
+ }
2201
+ continue;
2202
+ }
2203
+ const typeNode = fieldDecl.namedChildren.find((c) => c.type !== "field_identifier" && c.type !== "raw_string_literal" && c.type !== "interpreted_string_literal");
2204
+ if (!typeNode) continue;
2205
+ const fieldType = typeNodeText(typeNode);
2206
+ const tagNode = fieldDecl.namedChildren.find((c) => c.type === "raw_string_literal" || c.type === "interpreted_string_literal");
2207
+ const jsonTag = tagNode ? extractJsonTag(tagNode.text) : null;
2208
+ for (const nameNode of nameNodes) {
2209
+ const name = nameNode.text;
2210
+ if (!/^[A-Z]/.test(name)) continue;
2211
+ fields.push({
2212
+ name,
2213
+ type: fieldType,
2214
+ jsonTag: jsonTag?.name || void 0,
2215
+ optional: jsonTag?.omitempty || false
2216
+ });
2217
+ }
2218
+ }
2219
+ return fields;
2220
+ }
2221
+ function parseTypeDecls(tree, sourceFile, packageName) {
2222
+ const structs = [];
2223
+ const types = [];
2224
+ for (const node of tree.rootNode.namedChildren) {
2225
+ if (node.type !== "type_declaration") continue;
2226
+ for (const spec of node.namedChildren) {
2227
+ const name = fieldText(spec, "name");
2228
+ if (!name || !/^[A-Z]/.test(name)) continue;
2229
+ if (spec.type === "type_spec") {
2230
+ const typeNode = spec.childForFieldName("type");
2231
+ if (!typeNode) continue;
2232
+ if (typeNode.type === "struct_type") structs.push({
2233
+ name,
2234
+ fields: parseStructFields$1(typeNode),
2235
+ sourceFile,
2236
+ packageName
2237
+ });
2238
+ else if (typeNode.type !== "interface_type") types.push({
2239
+ name,
2240
+ underlyingType: typeNodeText(typeNode),
2241
+ isAlias: false,
2242
+ sourceFile,
2243
+ packageName
2244
+ });
2245
+ } else if (spec.type === "type_alias") {
2246
+ const typeNode = spec.childForFieldName("type");
2247
+ if (typeNode) types.push({
2248
+ name,
2249
+ underlyingType: typeNodeText(typeNode),
2250
+ isAlias: true,
2251
+ sourceFile,
2252
+ packageName
2253
+ });
2254
+ }
2255
+ }
2256
+ }
2257
+ return {
2258
+ structs,
2259
+ types
2260
+ };
2261
+ }
2262
+ /** Strip surrounding quotes from a string value. */
2263
+ function stripQuotes(value) {
2264
+ if (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'")) return value.slice(1, -1);
2265
+ const convMatch = value.match(/^\w+(?:\.\w+)?\("([^"]*)"\)$/);
2266
+ if (convMatch) return convMatch[1];
2267
+ return value;
2268
+ }
2269
+ function parseConstBlocks(tree, sourceFile, packageName) {
2270
+ const consts = [];
2271
+ for (const node of tree.rootNode.namedChildren) {
2272
+ if (node.type !== "const_declaration") continue;
2273
+ let lastType = "";
2274
+ for (const spec of node.namedChildren) {
2275
+ if (spec.type !== "const_spec") continue;
2276
+ const nameNodes = spec.namedChildren.filter((c) => c.type === "identifier");
2277
+ if (nameNodes.length === 0) continue;
2278
+ const constName = nameNodes[0].text;
2279
+ if (!/^[A-Z]/.test(constName)) continue;
2280
+ const typeNode = spec.childForFieldName("type");
2281
+ if (typeNode) lastType = typeNode.text;
2282
+ const valueNode = spec.childForFieldName("value");
2283
+ if (valueNode) {
2284
+ const valueText = valueNode.text.trim();
2285
+ const strippedValue = stripQuotes(valueText);
2286
+ if (!typeNode) {
2287
+ const convMatch = valueText.match(/^([A-Z]\w*)\((.+)\)$/);
2288
+ if (convMatch) {
2289
+ consts.push({
2290
+ name: constName,
2291
+ typeName: convMatch[1],
2292
+ value: stripQuotes(convMatch[2].trim()),
2293
+ sourceFile,
2294
+ packageName
2295
+ });
2296
+ continue;
2297
+ }
2298
+ }
2299
+ if (lastType) consts.push({
2300
+ name: constName,
2301
+ typeName: lastType,
2302
+ value: strippedValue,
2303
+ sourceFile,
2304
+ packageName
2305
+ });
2306
+ } else if (lastType) consts.push({
2307
+ name: constName,
2308
+ typeName: lastType,
2309
+ value: constName,
2310
+ sourceFile,
2311
+ packageName
2312
+ });
2313
+ }
2314
+ }
2315
+ return consts;
2316
+ }
2317
+ function parseParamList(paramListNode) {
2318
+ if (!paramListNode) return [];
2319
+ const params = [];
2320
+ let unnamedIndex = 0;
2321
+ for (const paramDecl of paramListNode.namedChildren) {
2322
+ if (paramDecl.type !== "parameter_declaration" && paramDecl.type !== "variadic_parameter_declaration") continue;
2323
+ const nameNode = paramDecl.namedChildren.find((c) => c.type === "identifier");
2324
+ const typeNode = paramDecl.childForFieldName("type");
2325
+ if (!typeNode) continue;
2326
+ const name = nameNode ? nameNode.text : `_arg${unnamedIndex++}`;
2327
+ params.push({
2328
+ name,
2329
+ type: typeNodeText(typeNode)
2330
+ });
2331
+ }
2332
+ return params;
2333
+ }
2334
+ function parseReturnTypes(resultNode) {
2335
+ if (!resultNode) return [];
2336
+ if (resultNode.type === "parameter_list") return resultNode.namedChildren.filter((c) => c.type === "parameter_declaration").map((c) => {
2337
+ const t = c.childForFieldName("type");
2338
+ return t ? typeNodeText(t) : c.text;
2339
+ });
2340
+ return [typeNodeText(resultNode)];
2341
+ }
2342
+ function parseFunctions(tree, sourceFile, packageName) {
2343
+ const funcs = [];
2344
+ for (const node of tree.rootNode.namedChildren) if (node.type === "function_declaration") {
2345
+ const name = fieldText(node, "name");
2346
+ if (!name || !/^[A-Z]/.test(name)) continue;
2347
+ funcs.push({
2348
+ receiverType: null,
2349
+ name,
2350
+ params: parseParamList(node.childForFieldName("parameters")),
2351
+ returnTypes: parseReturnTypes(node.childForFieldName("result")),
2352
+ sourceFile,
2353
+ packageName
2354
+ });
2355
+ } else if (node.type === "method_declaration") {
2356
+ const name = fieldText(node, "name");
2357
+ if (!name || !/^[A-Z]/.test(name)) continue;
2358
+ const receiverNode = node.childForFieldName("receiver");
2359
+ let receiverType = null;
2360
+ if (receiverNode) {
2361
+ const paramDecl = receiverNode.namedChildren.find((c) => c.type === "parameter_declaration");
2362
+ if (paramDecl) {
2363
+ const typeNode = paramDecl.childForFieldName("type");
2364
+ if (typeNode) receiverType = typeNode.text.replace(/^\*/, "");
2365
+ }
2366
+ }
2367
+ funcs.push({
2368
+ receiverType,
2369
+ name,
2370
+ params: parseParamList(node.childForFieldName("parameters")),
2371
+ returnTypes: parseReturnTypes(node.childForFieldName("result")),
2372
+ sourceFile,
2373
+ packageName
2374
+ });
2375
+ }
2376
+ return funcs;
2377
+ }
2378
+ function extractPackageName$1(tree) {
2379
+ const pkgClause = tree.rootNode.namedChildren.find((c) => c.type === "package_clause");
2380
+ if (!pkgClause) return "unknown";
2381
+ const nameNode = pkgClause.namedChildren.find((c) => c.type === "package_identifier");
2382
+ return nameNode ? nameNode.text : "unknown";
2383
+ }
2384
+ /** Parse a single Go source file and return all extracted symbols. */
2385
+ function parseGoFile(filePath, sdkPath) {
2386
+ const source = readFileSync(filePath, "utf-8");
2387
+ const relPath = relative(sdkPath, filePath);
2388
+ const tree = safeParse$1(goParser, source);
2389
+ const packageName = extractPackageName$1(tree);
2390
+ const { structs, types } = parseTypeDecls(tree, relPath, packageName);
2391
+ return {
2392
+ structs,
2393
+ types,
2394
+ funcs: parseFunctions(tree, relPath, packageName),
2395
+ consts: parseConstBlocks(tree, relPath, packageName)
2396
+ };
2397
+ }
2398
+ //#endregion
2399
+ //#region src/compat/extractors/go-surface.ts
2400
+ /**
2401
+ * Go surface builder — transforms parsed Go symbols into an ApiSurface
2402
+ * with proper package-qualified names for collision avoidance.
2403
+ */
2404
+ function goTypeToString(t) {
2405
+ return t.replace(/json\.RawMessage/g, "[]byte").replace(/\bcontext\.Context\b/g, "context.Context");
2406
+ }
2407
+ function buildReturnType(returnTypes) {
2408
+ const nonError = returnTypes.filter((r) => r !== "error");
2409
+ if (nonError.length === 0) return "error";
2410
+ if (nonError.length === 1) return goTypeToString(nonError[0]);
2411
+ return `(${nonError.map(goTypeToString).join(", ")})`;
2412
+ }
2413
+ /** Get the package directory name from a source file relative path. */
2414
+ function getPackageDirName(relPath) {
2415
+ return basename(dirname(relPath)) || "root";
2416
+ }
2417
+ /**
2418
+ * When multiple packages define the same exported symbol, qualify with the
2419
+ * package directory name (e.g., "organizations.Client", "sso.Client").
2420
+ * Unique symbols keep their bare name.
2421
+ */
2422
+ function buildQualifiedNames(items) {
2423
+ const nameToPackages = /* @__PURE__ */ new Map();
2424
+ for (const item of items) {
2425
+ if (!nameToPackages.has(item.name)) nameToPackages.set(item.name, /* @__PURE__ */ new Set());
2426
+ nameToPackages.get(item.name).add(item.packageName);
2427
+ }
2428
+ const result = /* @__PURE__ */ new Map();
2429
+ for (const item of items) {
2430
+ const key = `${item.sourceFile}:${item.name}`;
2431
+ const packages = nameToPackages.get(item.name);
2432
+ result.set(key, packages.size > 1 ? `${getPackageDirName(item.sourceFile)}.${item.name}` : item.name);
2433
+ }
2434
+ return result;
2435
+ }
2436
+ function buildSurface$4(allStructs, allTypes, allFuncs, allConsts) {
2437
+ const classes = {};
2438
+ const interfaces = {};
2439
+ const typeAliases = {};
2440
+ const enums = {};
2441
+ const constsByPkgAndType = /* @__PURE__ */ new Map();
2442
+ for (const c of allConsts) {
2443
+ const key = `${c.packageName}:${c.typeName}`;
2444
+ if (!constsByPkgAndType.has(key)) constsByPkgAndType.set(key, []);
2445
+ constsByPkgAndType.get(key).push(c);
2446
+ }
2447
+ const methodsByPkgAndReceiver = /* @__PURE__ */ new Map();
2448
+ const packageFuncs = [];
2449
+ for (const fn of allFuncs) if (fn.receiverType) {
2450
+ const key = `${fn.packageName}:${fn.receiverType}`;
2451
+ if (!methodsByPkgAndReceiver.has(key)) methodsByPkgAndReceiver.set(key, []);
2452
+ methodsByPkgAndReceiver.get(key).push(fn);
2453
+ } else packageFuncs.push(fn);
2454
+ const allSymbols = [];
2455
+ for (const s of allStructs) allSymbols.push({
2456
+ name: s.name,
2457
+ packageName: s.packageName,
2458
+ sourceFile: s.sourceFile
2459
+ });
2460
+ for (const t of allTypes) allSymbols.push({
2461
+ name: t.name,
2462
+ packageName: t.packageName,
2463
+ sourceFile: t.sourceFile
2464
+ });
2465
+ const qualifiedNames = buildQualifiedNames(allSymbols);
2466
+ for (const typeDecl of allTypes) {
2467
+ const qName = qualifiedNames.get(`${typeDecl.sourceFile}:${typeDecl.name}`) || typeDecl.name;
2468
+ const typeConsts = constsByPkgAndType.get(`${typeDecl.packageName}:${typeDecl.name}`);
2469
+ if (typeDecl.isAlias) typeAliases[qName] = {
2470
+ name: qName,
2471
+ sourceFile: typeDecl.sourceFile,
2472
+ value: goTypeToString(typeDecl.underlyingType)
2473
+ };
2474
+ else if (typeConsts && typeConsts.length > 0) {
2475
+ const members = {};
2476
+ for (const c of typeConsts) {
2477
+ const memberKey = c.name.startsWith(typeDecl.name) ? c.name.slice(typeDecl.name.length) || c.name : c.name;
2478
+ members[memberKey] = c.value;
2479
+ }
2480
+ enums[qName] = {
2481
+ name: qName,
2482
+ sourceFile: typeDecl.sourceFile,
2483
+ members: sortRecord(members)
2484
+ };
2485
+ } else typeAliases[qName] = {
2486
+ name: qName,
2487
+ sourceFile: typeDecl.sourceFile,
2488
+ value: goTypeToString(typeDecl.underlyingType)
2489
+ };
2490
+ }
2491
+ for (const s of allStructs) {
2492
+ const qName = qualifiedNames.get(`${s.sourceFile}:${s.name}`) || s.name;
2493
+ const methods = methodsByPkgAndReceiver.get(`${s.packageName}:${s.name}`);
2494
+ if (methods && methods.length > 0) {
2495
+ const apiMethods = {};
2496
+ const properties = {};
2497
+ for (const field of s.fields) properties[field.name] = {
2498
+ name: field.name,
2499
+ type: goTypeToString(field.type),
2500
+ readonly: false
2501
+ };
2502
+ for (const fn of methods) {
2503
+ const params = fn.params.filter((p) => p.type !== "context.Context").map((p) => ({
2504
+ name: p.name,
2505
+ type: goTypeToString(p.type),
2506
+ optional: false
2507
+ }));
2508
+ if (!apiMethods[fn.name]) apiMethods[fn.name] = [];
2509
+ apiMethods[fn.name].push({
2510
+ name: fn.name,
2511
+ params,
2512
+ returnType: buildReturnType(fn.returnTypes),
2513
+ async: false
2514
+ });
2515
+ }
2516
+ classes[qName] = {
2517
+ name: qName,
2518
+ sourceFile: s.sourceFile,
2519
+ methods: sortRecord(apiMethods),
2520
+ properties: sortRecord(properties),
2521
+ constructorParams: []
2522
+ };
2523
+ } else {
2524
+ const fields = {};
2525
+ for (const field of s.fields) {
2526
+ const fieldName = field.jsonTag || field.name;
2527
+ fields[fieldName] = {
2528
+ name: fieldName,
2529
+ type: goTypeToString(field.type),
2530
+ optional: field.optional
2531
+ };
2532
+ }
2533
+ interfaces[qName] = {
2534
+ name: qName,
2535
+ sourceFile: s.sourceFile,
2536
+ fields: sortRecord(fields),
2537
+ extends: []
2538
+ };
2539
+ }
2540
+ }
2541
+ const collector = new ExportCollector();
2542
+ for (const [name, cls] of Object.entries(classes)) if (cls.sourceFile) collector.add(cls.sourceFile, name);
2543
+ for (const [name, iface] of Object.entries(interfaces)) if (iface.sourceFile) collector.add(iface.sourceFile, name);
2544
+ for (const [name, ta] of Object.entries(typeAliases)) if (ta.sourceFile) collector.add(ta.sourceFile, name);
2545
+ for (const [name, en] of Object.entries(enums)) if (en.sourceFile) collector.add(en.sourceFile, name);
2546
+ for (const c of allConsts) {
2547
+ const typeDecl = allTypes.find((t) => t.name === c.typeName && t.packageName === c.packageName);
2548
+ if (typeDecl) collector.add(typeDecl.sourceFile, c.name);
2549
+ }
2550
+ for (const fn of packageFuncs) collector.add(fn.sourceFile, fn.name);
2551
+ const exports = collector.toRecord();
2552
+ return {
2553
+ classes: sortRecord(classes),
2554
+ interfaces: sortRecord(interfaces),
2555
+ typeAliases: sortRecord(typeAliases),
2556
+ enums: sortRecord(enums),
2557
+ exports: sortRecord(exports)
2558
+ };
2559
+ }
2560
+ //#endregion
2561
+ //#region src/compat/extractors/go.ts
2562
+ /**
2563
+ * Go API surface extractor.
2564
+ *
2565
+ * Walks `.go` files (excluding `_test.go` and `internal/`), parses exported
2566
+ * structs, functions, type declarations, and const blocks, then builds an
2567
+ * `ApiSurface` with package-qualified names to avoid collisions when
2568
+ * multiple packages define the same symbol (e.g., `Client`).
2569
+ *
2570
+ * Parser: `go-parser.ts` — regex-based Go source analysis
2571
+ * Surface: `go-surface.ts` — ApiSurface construction & name qualification
2572
+ */
2573
+ const GO_ACRONYM_RE = new RegExp([
2574
+ "ID",
2575
+ "URL",
2576
+ "API",
2577
+ "HTTP",
2578
+ "HTTPS",
2579
+ "JSON",
2580
+ "XML",
2581
+ "SQL",
2582
+ "HTML",
2583
+ "CSS",
2584
+ "URI",
2585
+ "SSO",
2586
+ "IP",
2587
+ "TLS",
2588
+ "SSL",
2589
+ "DNS",
2590
+ "TCP",
2591
+ "UDP",
2592
+ "SSH",
2593
+ "JWT",
2594
+ "MFA",
2595
+ "SAML",
2596
+ "SCIM"
2597
+ ].flatMap((a) => [a, a.charAt(0) + a.slice(1).toLowerCase()]).join("|"), "g");
2598
+ /**
2599
+ * Normalize a Go type string so that acronym casing differences don't cause
2600
+ * false mismatches. Lowercases all known Go acronyms to a canonical form.
2601
+ */
2602
+ function normalizeGoAcronyms(type) {
2603
+ return type.replace(GO_ACRONYM_RE, (match) => match.toLowerCase());
2604
+ }
2605
+ const goExtractor = {
2606
+ language: "go",
2607
+ hints: {
2608
+ stripNullable(type) {
2609
+ if (type.startsWith("*")) return type.slice(1);
2610
+ return null;
2611
+ },
2612
+ isNullableOnlyDifference(a, b) {
2613
+ return defaultIsNullableOnlyDifference(this, a, b);
2614
+ },
2615
+ isUnionReorder(_a, _b) {
2616
+ return false;
2617
+ },
2618
+ isGenericTypeParam(type) {
2619
+ return type === "interface{}" || type === "any";
2620
+ },
2621
+ isExtractionArtifact(type) {
2622
+ return type === "interface{}" || type === "any";
2623
+ },
2624
+ tolerateCategoryMismatch: false,
2625
+ extractReturnTypeName(returnType) {
2626
+ return returnType.replace(/^\*/, "").replace(/\[\]/, "") || null;
2627
+ },
2628
+ extractParamTypeName(paramType) {
2629
+ const stripped = paramType.replace(/^\*/, "");
2630
+ if ([
2631
+ "string",
2632
+ "int",
2633
+ "int64",
2634
+ "float64",
2635
+ "bool"
2636
+ ].includes(stripped)) return null;
2637
+ return stripped;
2638
+ },
2639
+ propertyMatchesClass(propertyName, className) {
2640
+ return propertyName.toLowerCase() === className.toLowerCase();
2641
+ },
2642
+ derivedModelNames(_modelName) {
2643
+ return [];
2644
+ },
2645
+ isTypeEquivalent(baselineType, candidateType, candidateSurface) {
2646
+ const normBase = normalizeGoAcronyms(baselineType);
2647
+ const normCand = normalizeGoAcronyms(candidateType);
2648
+ if (normBase === normCand) return true;
2649
+ const stripType = (t) => t.replace(/^\*/, "").replace(/^\[\]/, "");
2650
+ const strippedBase = stripType(normBase);
2651
+ const strippedCand = stripType(normCand);
2652
+ if (strippedBase === strippedCand) return true;
2653
+ const unqualBase = strippedBase.includes(".") ? strippedBase.split(".").pop() : strippedBase;
2654
+ const unqualCand = strippedCand.includes(".") ? strippedCand.split(".").pop() : strippedCand;
2655
+ if (unqualBase === unqualCand) return true;
2656
+ const candRaw = stripType(candidateType);
2657
+ if (unqualCand.endsWith(unqualBase) && unqualCand.length > unqualBase.length) {
2658
+ if (candidateSurface.enums[candRaw] != null || candidateSurface.interfaces[candRaw] != null || candidateSurface.classes[candRaw] != null || candidateSurface.typeAliases[candRaw] != null) return true;
2659
+ }
2660
+ return false;
2661
+ }
2662
+ },
2663
+ async extract(sdkPath) {
2664
+ const goFiles = walkGoFiles(sdkPath);
2665
+ if (goFiles.length === 0) throw new ExtractorError(`No .go files found in ${sdkPath}`, `Ensure the --sdk-path argument points to a Go project root containing .go source files.`);
2666
+ const allStructs = [];
2667
+ const allTypes = [];
2668
+ const allFuncs = [];
2669
+ const allConsts = [];
2670
+ for (const filePath of goFiles) {
2671
+ const parsed = parseGoFile(filePath, sdkPath);
2672
+ allStructs.push(...parsed.structs);
2673
+ allTypes.push(...parsed.types);
2674
+ allFuncs.push(...parsed.funcs);
2675
+ allConsts.push(...parsed.consts);
2676
+ }
2677
+ const { classes, interfaces, typeAliases, enums, exports } = buildSurface$4(allStructs, allTypes, allFuncs, allConsts);
2678
+ return {
2679
+ language: "go",
2680
+ extractedFrom: sdkPath,
2681
+ extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
2682
+ classes,
2683
+ interfaces,
2684
+ typeAliases,
2685
+ enums,
2686
+ exports
2687
+ };
2688
+ }
2689
+ };
2690
+ //#endregion
2691
+ //#region src/compat/extractors/rust-parser.ts
2692
+ /**
2693
+ * Rust source parser — tree-sitter-based extraction of structs, enums,
2694
+ * impl blocks, traits, type aliases, and functions from Rust source files.
2695
+ */
2696
+ const rustParser = new Parser();
2697
+ rustParser.setLanguage(RustLang);
2698
+ function walkRustFiles(dir) {
2699
+ const results = [];
2700
+ let entries;
2701
+ try {
2702
+ entries = readdirSync(dir);
2703
+ } catch {
2704
+ return results;
2705
+ }
2706
+ for (const entry of entries) {
2707
+ const fullPath = join(dir, entry);
2708
+ let stat;
2709
+ try {
2710
+ stat = statSync(fullPath);
2711
+ } catch {
2712
+ continue;
2713
+ }
2714
+ if (stat.isDirectory()) {
2715
+ if (entry.startsWith(".") || entry === "target" || entry === "benches" || entry === "examples") continue;
2716
+ results.push(...walkRustFiles(fullPath));
2717
+ } else if (entry.endsWith(".rs") && !entry.endsWith("_test.rs")) results.push(fullPath);
2718
+ }
2719
+ return results.sort();
2720
+ }
2721
+ /** Extract the serde rename value from an attribute_item preceding a node. */
2722
+ function extractSerdeRename(attributeNode) {
2723
+ const attr = attributeNode.namedChildren.find((c) => c.type === "attribute");
2724
+ if (!attr) return null;
2725
+ const ident = attr.namedChildren.find((c) => c.type === "identifier");
2726
+ if (!ident || ident.text !== "serde") return null;
2727
+ const tokenTree = attr.namedChildren.find((c) => c.type === "token_tree");
2728
+ if (!tokenTree) return null;
2729
+ if (!tokenTree.namedChildren.find((c) => c.type === "identifier" && c.text === "rename")) return null;
2730
+ const strLit = tokenTree.namedChildren.find((c) => c.type === "string_literal");
2731
+ if (!strLit) return null;
2732
+ const content = strLit.namedChildren.find((c) => c.type === "string_content");
2733
+ return content ? content.text : null;
2734
+ }
2735
+ /** Check if a node is preceded by a serde(skip) or serde(skip_serializing) attribute. */
2736
+ function hasSerdeSkip(attributeNode) {
2737
+ const attr = attributeNode.namedChildren.find((c) => c.type === "attribute");
2738
+ if (!attr) return false;
2739
+ const ident = attr.namedChildren.find((c) => c.type === "identifier");
2740
+ if (!ident || ident.text !== "serde") return false;
2741
+ const tokenTree = attr.namedChildren.find((c) => c.type === "token_tree");
2742
+ if (!tokenTree) return false;
2743
+ return tokenTree.namedChildren.some((c) => c.type === "identifier" && (c.text === "skip" || c.text === "skip_serializing"));
2744
+ }
2745
+ /** Check if a type is Option<T> and return the inner type. */
2746
+ function isOptionType(typeText) {
2747
+ return typeText.startsWith("Option<") && typeText.endsWith(">");
2748
+ }
2749
+ /** Check if a node has `pub` visibility. */
2750
+ function isPublic(node) {
2751
+ return node.namedChildren.some((c) => c.type === "visibility_modifier");
2752
+ }
2753
+ function parseStructFields(bodyNode) {
2754
+ const fields = [];
2755
+ let pendingAttr = null;
2756
+ for (const child of bodyNode.namedChildren) {
2757
+ if (child.type === "attribute_item") {
2758
+ pendingAttr = child;
2759
+ continue;
2760
+ }
2761
+ if (child.type === "field_declaration") {
2762
+ if (!isPublic(child)) {
2763
+ pendingAttr = null;
2764
+ continue;
2765
+ }
2766
+ if (pendingAttr && hasSerdeSkip(pendingAttr)) {
2767
+ pendingAttr = null;
2768
+ continue;
2769
+ }
2770
+ const nameNode = child.namedChildren.find((c) => c.type === "field_identifier");
2771
+ const typeNode = child.childForFieldName("type");
2772
+ if (!nameNode || !typeNode) {
2773
+ pendingAttr = null;
2774
+ continue;
2775
+ }
2776
+ const fieldName = nameNode.text;
2777
+ const fieldType = typeNode.text;
2778
+ const serdeRename = pendingAttr ? extractSerdeRename(pendingAttr) : null;
2779
+ fields.push({
2780
+ name: fieldName,
2781
+ type: fieldType,
2782
+ serdeRename: serdeRename || void 0,
2783
+ optional: isOptionType(fieldType)
2784
+ });
2785
+ pendingAttr = null;
2786
+ } else pendingAttr = null;
2787
+ }
2788
+ return fields;
2789
+ }
2790
+ function parseStructs(tree, sourceFile) {
2791
+ const structs = [];
2792
+ for (const node of tree.rootNode.descendantsOfType("struct_item")) {
2793
+ if (!isPublic(node)) continue;
2794
+ const nameNode = node.childForFieldName("name");
2795
+ if (!nameNode) continue;
2796
+ const bodyNode = node.childForFieldName("body");
2797
+ if (!bodyNode || bodyNode.type !== "field_declaration_list") {
2798
+ structs.push({
2799
+ name: nameNode.text,
2800
+ fields: [],
2801
+ sourceFile
2802
+ });
2803
+ continue;
2804
+ }
2805
+ structs.push({
2806
+ name: nameNode.text,
2807
+ fields: parseStructFields(bodyNode),
2808
+ sourceFile
2809
+ });
2810
+ }
2811
+ return structs;
2812
+ }
2813
+ function parseEnums$1(tree, sourceFile) {
2814
+ const enums = [];
2815
+ for (const node of tree.rootNode.descendantsOfType("enum_item")) {
2816
+ if (!isPublic(node)) continue;
2817
+ const nameNode = node.childForFieldName("name");
2818
+ if (!nameNode) continue;
2819
+ const bodyNode = node.childForFieldName("body");
2820
+ if (!bodyNode) continue;
2821
+ const variants = [];
2822
+ let pendingAttr = null;
2823
+ for (const child of bodyNode.namedChildren) {
2824
+ if (child.type === "attribute_item") {
2825
+ pendingAttr = child;
2826
+ continue;
2827
+ }
2828
+ if (child.type === "enum_variant") {
2829
+ const variantName = child.childForFieldName("name");
2830
+ if (variantName) {
2831
+ const serdeRename = pendingAttr ? extractSerdeRename(pendingAttr) : null;
2832
+ variants.push({
2833
+ name: variantName.text,
2834
+ serdeRename: serdeRename || void 0
2835
+ });
2836
+ }
2837
+ pendingAttr = null;
2838
+ } else pendingAttr = null;
2839
+ }
2840
+ if (variants.length > 0) enums.push({
2841
+ name: nameNode.text,
2842
+ variants,
2843
+ sourceFile
2844
+ });
2845
+ }
2846
+ return enums;
2847
+ }
2848
+ function parseParams(paramsNode) {
2849
+ const params = [];
2850
+ for (const child of paramsNode.namedChildren) {
2851
+ if (child.type === "self_parameter") continue;
2852
+ if (child.type === "parameter") {
2853
+ const patternNode = child.childForFieldName("pattern");
2854
+ const typeNode = child.childForFieldName("type");
2855
+ if (patternNode && typeNode) params.push({
2856
+ name: patternNode.text,
2857
+ type: typeNode.text
2858
+ });
2859
+ }
2860
+ }
2861
+ return params;
2862
+ }
2863
+ function extractReturnType(funcNode) {
2864
+ const retNode = funcNode.childForFieldName("return_type");
2865
+ if (!retNode) return "()";
2866
+ return retNode.text;
2867
+ }
2868
+ function isAsyncFunc(funcNode) {
2869
+ return funcNode.namedChildren.some((c) => c.type === "function_modifiers" && c.text.includes("async"));
2870
+ }
2871
+ function parseImplBlocks(tree, sourceFile) {
2872
+ const funcs = [];
2873
+ for (const implNode of tree.rootNode.descendantsOfType("impl_item")) {
2874
+ const typeNode = implNode.childForFieldName("type");
2875
+ if (!typeNode) continue;
2876
+ const receiverType = typeNode.text;
2877
+ const bodyNode = implNode.childForFieldName("body");
2878
+ if (!bodyNode) continue;
2879
+ for (const child of bodyNode.namedChildren) {
2880
+ if (child.type !== "function_item") continue;
2881
+ if (!isPublic(child)) continue;
2882
+ const nameNode = child.childForFieldName("name");
2883
+ if (!nameNode) continue;
2884
+ const paramsNode = child.childForFieldName("parameters");
2885
+ funcs.push({
2886
+ receiverType,
2887
+ name: nameNode.text,
2888
+ params: paramsNode ? parseParams(paramsNode) : [],
2889
+ returnType: extractReturnType(child),
2890
+ isAsync: isAsyncFunc(child),
2891
+ sourceFile
2892
+ });
2893
+ }
2894
+ }
2895
+ return funcs;
2896
+ }
2897
+ function parseTraits(tree, sourceFile) {
2898
+ const traits = [];
2899
+ for (const traitNode of tree.rootNode.descendantsOfType("trait_item")) {
2900
+ if (!isPublic(traitNode)) continue;
2901
+ const nameNode = traitNode.childForFieldName("name");
2902
+ if (!nameNode) continue;
2903
+ const bodyNode = traitNode.childForFieldName("body");
2904
+ if (!bodyNode) continue;
2905
+ const methods = [];
2906
+ for (const child of bodyNode.namedChildren) {
2907
+ if (child.type !== "function_signature_item" && child.type !== "function_item") continue;
2908
+ const methodName = child.childForFieldName("name");
2909
+ if (!methodName) continue;
2910
+ const paramsNode = child.childForFieldName("parameters");
2911
+ methods.push({
2912
+ receiverType: null,
2913
+ name: methodName.text,
2914
+ params: paramsNode ? parseParams(paramsNode) : [],
2915
+ returnType: extractReturnType(child),
2916
+ isAsync: isAsyncFunc(child),
2917
+ sourceFile
2918
+ });
2919
+ }
2920
+ if (methods.length > 0) traits.push({
2921
+ name: nameNode.text,
2922
+ methods,
2923
+ sourceFile
2924
+ });
2925
+ }
2926
+ return traits;
2927
+ }
2928
+ function parseTypeAliases$1(tree, sourceFile) {
2929
+ const aliases = [];
2930
+ for (const node of tree.rootNode.descendantsOfType("type_item")) {
2931
+ if (!isPublic(node)) continue;
2932
+ const nameNode = node.childForFieldName("name");
2933
+ const typeNode = node.childForFieldName("type");
2934
+ if (!nameNode || !typeNode) continue;
2935
+ aliases.push({
2936
+ name: nameNode.text,
2937
+ underlyingType: typeNode.text,
2938
+ sourceFile
2939
+ });
2940
+ }
2941
+ return aliases;
2942
+ }
2943
+ function parseRustFile(filePath, sdkPath) {
2944
+ const source = readFileSync(filePath, "utf-8");
2945
+ const relPath = relative(sdkPath, filePath);
2946
+ const tree = safeParse$1(rustParser, source);
2947
+ return {
2948
+ structs: parseStructs(tree, relPath),
2949
+ enums: parseEnums$1(tree, relPath),
2950
+ funcs: parseImplBlocks(tree, relPath),
2951
+ traits: parseTraits(tree, relPath),
2952
+ typeAliases: parseTypeAliases$1(tree, relPath)
2953
+ };
2954
+ }
2955
+ //#endregion
2956
+ //#region src/compat/extractors/rust-surface.ts
2957
+ function rustTypeToString(t) {
2958
+ return t;
2959
+ }
2960
+ /** Strip Result<T, E> to just T. */
2961
+ function unwrapResultType(returnType) {
2962
+ const match = returnType.match(/^Result\s*<\s*(.+)\s*,\s*.+\s*>$/);
2963
+ if (match) return match[1].trim();
2964
+ return returnType;
2965
+ }
2966
+ function buildSurface$3(allStructs, allEnums, allFuncs, allTraits, allTypeAliases) {
2967
+ const classes = {};
2968
+ const interfaces = {};
2969
+ const typeAliases = {};
2970
+ const enums = {};
2971
+ const methodsByReceiver = /* @__PURE__ */ new Map();
2972
+ for (const fn of allFuncs) {
2973
+ if (!fn.receiverType) continue;
2974
+ if (!methodsByReceiver.has(fn.receiverType)) methodsByReceiver.set(fn.receiverType, []);
2975
+ methodsByReceiver.get(fn.receiverType).push(fn);
2976
+ }
2977
+ for (const s of allStructs) {
2978
+ const methods = methodsByReceiver.get(s.name);
2979
+ if (methods && methods.length > 0) {
2980
+ const apiMethods = {};
2981
+ const properties = {};
2982
+ for (const field of s.fields) {
2983
+ const fieldName = field.serdeRename || field.name;
2984
+ properties[fieldName] = {
2985
+ name: fieldName,
2986
+ type: rustTypeToString(field.type),
2987
+ readonly: false
2988
+ };
2989
+ }
2990
+ for (const fn of methods) {
2991
+ const params = fn.params.map((p) => ({
2992
+ name: p.name,
2993
+ type: rustTypeToString(p.type),
2994
+ optional: p.type.startsWith("Option<")
2995
+ }));
2996
+ const returnType = unwrapResultType(fn.returnType);
2997
+ if (!apiMethods[fn.name]) apiMethods[fn.name] = [];
2998
+ apiMethods[fn.name].push({
2999
+ name: fn.name,
3000
+ params,
3001
+ returnType: rustTypeToString(returnType),
3002
+ async: fn.isAsync
3003
+ });
3004
+ }
3005
+ classes[s.name] = {
3006
+ name: s.name,
3007
+ sourceFile: s.sourceFile,
3008
+ methods: sortRecord(apiMethods),
3009
+ properties: sortRecord(properties),
3010
+ constructorParams: []
3011
+ };
3012
+ } else {
3013
+ const fields = {};
3014
+ for (const field of s.fields) {
3015
+ const fieldName = field.serdeRename || field.name;
3016
+ fields[fieldName] = {
3017
+ name: fieldName,
3018
+ type: rustTypeToString(field.type),
3019
+ optional: field.optional
3020
+ };
3021
+ }
3022
+ interfaces[s.name] = {
3023
+ name: s.name,
3024
+ sourceFile: s.sourceFile,
3025
+ fields: sortRecord(fields),
3026
+ extends: []
3027
+ };
3028
+ }
3029
+ }
3030
+ for (const e of allEnums) {
3031
+ const members = {};
3032
+ for (const v of e.variants) members[v.name] = v.serdeRename || v.name;
3033
+ enums[e.name] = {
3034
+ name: e.name,
3035
+ sourceFile: e.sourceFile,
3036
+ members: sortRecord(members)
3037
+ };
3038
+ }
3039
+ for (const ta of allTypeAliases) typeAliases[ta.name] = {
3040
+ name: ta.name,
3041
+ sourceFile: ta.sourceFile,
3042
+ value: rustTypeToString(ta.underlyingType)
3043
+ };
3044
+ for (const trait of allTraits) {
3045
+ const apiMethods = {};
3046
+ for (const fn of trait.methods) {
3047
+ const params = fn.params.map((p) => ({
3048
+ name: p.name,
3049
+ type: rustTypeToString(p.type),
3050
+ optional: p.type.startsWith("Option<")
3051
+ }));
3052
+ const returnType = unwrapResultType(fn.returnType);
3053
+ if (!apiMethods[fn.name]) apiMethods[fn.name] = [];
3054
+ apiMethods[fn.name].push({
3055
+ name: fn.name,
3056
+ params,
3057
+ returnType: rustTypeToString(returnType),
3058
+ async: fn.isAsync
3059
+ });
3060
+ }
3061
+ classes[trait.name] = {
3062
+ name: trait.name,
3063
+ sourceFile: trait.sourceFile,
3064
+ methods: sortRecord(apiMethods),
3065
+ properties: {},
3066
+ constructorParams: []
3067
+ };
3068
+ }
3069
+ const collector = new ExportCollector();
3070
+ for (const [name, cls] of Object.entries(classes)) if (cls.sourceFile) collector.add(cls.sourceFile, name);
3071
+ for (const [name, iface] of Object.entries(interfaces)) if (iface.sourceFile) collector.add(iface.sourceFile, name);
3072
+ for (const [name, ta] of Object.entries(typeAliases)) if (ta.sourceFile) collector.add(ta.sourceFile, name);
3073
+ for (const [name, en] of Object.entries(enums)) if (en.sourceFile) collector.add(en.sourceFile, name);
3074
+ const exports = collector.toRecord();
3075
+ return {
3076
+ classes: sortRecord(classes),
3077
+ interfaces: sortRecord(interfaces),
3078
+ typeAliases: sortRecord(typeAliases),
3079
+ enums: sortRecord(enums),
3080
+ exports: sortRecord(exports)
3081
+ };
3082
+ }
3083
+ const rustExtractor = {
3084
+ language: "rust",
3085
+ hints: {
3086
+ stripNullable(type) {
3087
+ const match = type.match(/^Option<(.+)>$/);
3088
+ return match ? match[1] : null;
3089
+ },
3090
+ isNullableOnlyDifference(a, b) {
3091
+ return defaultIsNullableOnlyDifference(this, a, b);
3092
+ },
3093
+ isUnionReorder(_a, _b) {
3094
+ return false;
3095
+ },
3096
+ isGenericTypeParam(type) {
3097
+ return /^[A-Z]$/.test(type) || /^T[A-Z][a-zA-Z]*$/.test(type);
3098
+ },
3099
+ isExtractionArtifact(type) {
3100
+ return type === "serde_json::Value" || type === "Box<dyn std::error::Error>";
3101
+ },
3102
+ tolerateCategoryMismatch: false,
3103
+ extractReturnTypeName(returnType) {
3104
+ let inner = returnType;
3105
+ const resultMatch = inner.match(/^Result\s*<\s*(.+)\s*,\s*.+\s*>$/);
3106
+ if (resultMatch) inner = resultMatch[1].trim();
3107
+ const vecMatch = inner.match(/^Vec\s*<\s*(.+)\s*>$/);
3108
+ if (vecMatch) inner = vecMatch[1].trim();
3109
+ const optMatch = inner.match(/^Option\s*<\s*(.+)\s*>$/);
3110
+ if (optMatch) inner = optMatch[1].trim();
3111
+ inner = inner.replace(/^&(?:mut\s+)?(?:'[a-z]+\s+)?/, "");
3112
+ if ([
3113
+ "()",
3114
+ "String",
3115
+ "str",
3116
+ "bool",
3117
+ "i32",
3118
+ "i64",
3119
+ "u32",
3120
+ "u64",
3121
+ "f32",
3122
+ "f64",
3123
+ "usize"
3124
+ ].includes(inner)) return null;
3125
+ return inner;
3126
+ },
3127
+ extractParamTypeName(paramType) {
3128
+ let inner = paramType.replace(/^&(?:mut\s+)?(?:'[a-z]+\s+)?/, "");
3129
+ if ([
3130
+ "String",
3131
+ "&str",
3132
+ "str",
3133
+ "bool",
3134
+ "i32",
3135
+ "i64",
3136
+ "u32",
3137
+ "u64",
3138
+ "f32",
3139
+ "f64",
3140
+ "usize"
3141
+ ].includes(inner)) return null;
3142
+ return inner;
3143
+ },
3144
+ propertyMatchesClass(propertyName, className) {
3145
+ return propertyName.replace(/_/g, "").toLowerCase() === className.toLowerCase();
3146
+ },
3147
+ derivedModelNames(modelName) {
3148
+ return [`${modelName}Response`];
3149
+ }
3150
+ },
3151
+ async extract(sdkPath) {
3152
+ const rsFiles = walkRustFiles(sdkPath);
3153
+ if (rsFiles.length === 0) throw new ExtractorError(`No .rs files found in ${sdkPath}`, `Ensure the --sdk-path argument points to a Rust project root containing .rs source files.`);
3154
+ const allStructs = [];
3155
+ const allEnums = [];
3156
+ const allFuncs = [];
3157
+ const allTraits = [];
3158
+ const allTypeAliases = [];
3159
+ for (const filePath of rsFiles) {
3160
+ const parsed = parseRustFile(filePath, sdkPath);
3161
+ allStructs.push(...parsed.structs);
3162
+ allEnums.push(...parsed.enums);
3163
+ allFuncs.push(...parsed.funcs);
3164
+ allTraits.push(...parsed.traits);
3165
+ allTypeAliases.push(...parsed.typeAliases);
3166
+ }
3167
+ const { classes, interfaces, typeAliases, enums, exports } = buildSurface$3(allStructs, allEnums, allFuncs, allTraits, allTypeAliases);
3168
+ return {
3169
+ language: "rust",
3170
+ extractedFrom: sdkPath,
3171
+ extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
3172
+ classes,
3173
+ interfaces,
3174
+ typeAliases,
3175
+ enums,
3176
+ exports
3177
+ };
3178
+ }
3179
+ };
3180
+ //#endregion
3181
+ //#region src/compat/extractors/kotlin-parser.ts
3182
+ /**
3183
+ * Kotlin source parser — regex-based extraction of classes, data classes,
3184
+ * enum classes, functions, and type aliases from Kotlin source files.
3185
+ */
3186
+ function walkKotlinFiles(dir) {
3187
+ const results = [];
3188
+ let entries;
3189
+ try {
3190
+ entries = readdirSync(dir);
3191
+ } catch {
3192
+ return results;
3193
+ }
3194
+ for (const entry of entries) {
3195
+ const fullPath = join(dir, entry);
3196
+ let stat;
3197
+ try {
3198
+ stat = statSync(fullPath);
3199
+ } catch {
3200
+ continue;
3201
+ }
3202
+ if (stat.isDirectory()) {
3203
+ if (entry.startsWith(".") || entry === "build" || entry === "test" || entry === "androidTest") continue;
3204
+ results.push(...walkKotlinFiles(fullPath));
3205
+ } else if (entry.endsWith(".kt") && !entry.endsWith("Test.kt")) results.push(fullPath);
3206
+ }
3207
+ return results.sort();
3208
+ }
3209
+ /** Extract the package name from a Kotlin source file. */
3210
+ function extractPackageName(source) {
3211
+ const match = source.match(/^package\s+([\w.]+)/m);
3212
+ return match ? match[1] : "unknown";
3213
+ }
3214
+ /** Strip single-line and block comments from source to avoid false matches. */
3215
+ function stripComments$1(source) {
3216
+ let result = source.replace(/\/\*[\s\S]*?\*\//g, "");
3217
+ result = result.replace(/\/\/[^\n]*/g, "");
3218
+ return result;
3219
+ }
3220
+ /**
3221
+ * Extract the @JsonProperty("name") annotation value before a parameter.
3222
+ * Looks for the pattern immediately preceding the parameter declaration.
3223
+ */
3224
+ function extractJsonPropertyName$1(text) {
3225
+ const match = text.match(/@JsonProperty\("([^"]+)"\)/);
3226
+ return match ? match[1] : null;
3227
+ }
3228
+ /**
3229
+ * Parse constructor parameters from a data class or class declaration.
3230
+ * Handles multi-line parameter lists with annotations.
3231
+ */
3232
+ function parseConstructorParams(paramsBlock) {
3233
+ const fields = [];
3234
+ const params = splitTopLevelCommas(paramsBlock);
3235
+ for (const param of params) {
3236
+ const trimmed = param.trim();
3237
+ if (!trimmed) continue;
3238
+ const jsonName = extractJsonPropertyName$1(trimmed);
3239
+ const paramMatch = trimmed.match(/(?:(?:override|private|protected)\s+)?(?:val|var)\s+(\w+)\s*:\s*([^=,]+?)(?:\s*=\s*(.+))?$/m);
3240
+ if (!paramMatch) continue;
3241
+ const name = paramMatch[1];
3242
+ const rawType = paramMatch[2].trim();
3243
+ const hasDefault = paramMatch[3] !== void 0;
3244
+ const isNullable = rawType.endsWith("?");
3245
+ const type = rawType;
3246
+ fields.push({
3247
+ name,
3248
+ type,
3249
+ jsonName: jsonName || void 0,
3250
+ optional: isNullable || hasDefault
3251
+ });
3252
+ }
3253
+ return fields;
3254
+ }
3255
+ /** Split a string by top-level commas, ignoring commas inside angle brackets, parens, or strings. */
3256
+ function splitTopLevelCommas(text) {
3257
+ const result = [];
3258
+ let depth = 0;
3259
+ let current = "";
3260
+ let inString = false;
3261
+ let stringChar = "";
3262
+ for (let i = 0; i < text.length; i++) {
3263
+ const ch = text[i];
3264
+ if (inString) {
3265
+ current += ch;
3266
+ if (ch === stringChar && text[i - 1] !== "\\") inString = false;
3267
+ continue;
3268
+ }
3269
+ if (ch === "\"" || ch === "'") {
3270
+ inString = true;
3271
+ stringChar = ch;
3272
+ current += ch;
3273
+ continue;
3274
+ }
3275
+ if (ch === "<" || ch === "(" || ch === "[") {
3276
+ depth++;
3277
+ current += ch;
3278
+ } else if (ch === ">" || ch === ")" || ch === "]") {
3279
+ depth--;
3280
+ current += ch;
3281
+ } else if (ch === "," && depth === 0) {
3282
+ result.push(current);
3283
+ current = "";
3284
+ } else current += ch;
3285
+ }
3286
+ if (current.trim()) result.push(current);
3287
+ return result;
3288
+ }
3289
+ function parseEnumClass(source, sourceFile, packageName) {
3290
+ const enums = [];
3291
+ const enumRegex = /enum\s+class\s+(\w+)(?:\([^)]*\))?\s*\{([^}]*)\}/g;
3292
+ let match;
3293
+ while ((match = enumRegex.exec(source)) !== null) {
3294
+ const name = match[1];
3295
+ const body = match[2];
3296
+ const members = {};
3297
+ const memberRegex = /(\w+)\s*(?:\("([^"]*)"\))?/g;
3298
+ let memberMatch;
3299
+ while ((memberMatch = memberRegex.exec(body)) !== null) {
3300
+ const memberName = memberMatch[1];
3301
+ if ([
3302
+ "val",
3303
+ "var",
3304
+ "fun",
3305
+ "override",
3306
+ "companion",
3307
+ "object"
3308
+ ].includes(memberName)) continue;
3309
+ members[memberName] = memberMatch[2] !== void 0 ? memberMatch[2] : memberName;
3310
+ }
3311
+ if (Object.keys(members).length > 0) enums.push({
3312
+ name,
3313
+ members,
3314
+ sourceFile,
3315
+ packageName
3316
+ });
3317
+ }
3318
+ return enums;
3319
+ }
3320
+ function parseDataClasses(source, sourceFile, packageName) {
3321
+ const dataClasses = [];
3322
+ const dataClassStartRegex = /data\s+class\s+(\w+)\s*(?:@\w+(?:\([^)]*\))?\s+)?(?:constructor\s*)?\(/g;
3323
+ let match;
3324
+ while ((match = dataClassStartRegex.exec(source)) !== null) {
3325
+ const name = match[1];
3326
+ const startIdx = match.index + match[0].length;
3327
+ let depth = 1;
3328
+ let idx = startIdx;
3329
+ while (idx < source.length && depth > 0) {
3330
+ if (source[idx] === "(") depth++;
3331
+ else if (source[idx] === ")") depth--;
3332
+ idx++;
3333
+ }
3334
+ const fields = parseConstructorParams(source.slice(startIdx, idx - 1));
3335
+ dataClasses.push({
3336
+ name,
3337
+ fields,
3338
+ sourceFile,
3339
+ packageName
3340
+ });
3341
+ }
3342
+ return dataClasses;
3343
+ }
3344
+ function parseClasses$1(source, sourceFile, packageName) {
3345
+ const classes = [];
3346
+ const classStartRegex = /(?<!data\s)(?<!enum\s)(?<!abstract\s)(?<!sealed\s)(?:open\s+)?class\s+(\w+)\s*(?:\(([^)]*)\))?\s*(?::\s*[^{]+)?\s*\{/g;
3347
+ let match;
3348
+ while ((match = classStartRegex.exec(source)) !== null) {
3349
+ const name = match[1];
3350
+ const constructorBlock = match[2] || "";
3351
+ const braceStart = source.indexOf("{", match.index + match[0].length - 1);
3352
+ let depth = 1;
3353
+ let idx = braceStart + 1;
3354
+ while (idx < source.length && depth > 0) {
3355
+ if (source[idx] === "{") depth++;
3356
+ else if (source[idx] === "}") depth--;
3357
+ idx++;
3358
+ }
3359
+ const classBody = source.slice(braceStart + 1, idx - 1);
3360
+ const constructorParams = [];
3361
+ if (constructorBlock.trim()) {
3362
+ const params = splitTopLevelCommas(constructorBlock);
3363
+ for (const param of params) {
3364
+ const paramMatch = param.trim().match(/(?:(?:private|protected)\s+)?(?:val|var)\s+(\w+)\s*:\s*(.+)/);
3365
+ if (paramMatch) constructorParams.push({
3366
+ name: paramMatch[1],
3367
+ type: paramMatch[2].trim()
3368
+ });
3369
+ }
3370
+ }
3371
+ const methods = parseMethods$1(classBody);
3372
+ if (methods.length > 0) classes.push({
3373
+ name,
3374
+ constructorParams,
3375
+ methods,
3376
+ sourceFile,
3377
+ packageName
3378
+ });
3379
+ }
3380
+ return classes;
3381
+ }
3382
+ /** Parse method declarations from a class body. */
3383
+ function parseMethods$1(classBody) {
3384
+ const methods = [];
3385
+ const methodRegex = /(private|protected|internal|public)?\s*(?:suspend\s+)?fun\s+(\w+)\s*\(([^)]*)\)\s*(?::\s*([^\s{]+))?/g;
3386
+ let match;
3387
+ while ((match = methodRegex.exec(classBody)) !== null) {
3388
+ const visibility = match[1] || "public";
3389
+ const name = match[2];
3390
+ const paramsStr = match[3];
3391
+ const returnType = match[4] || "Unit";
3392
+ if (visibility === "private" || visibility === "protected") continue;
3393
+ const params = [];
3394
+ if (paramsStr.trim()) {
3395
+ const paramParts = splitTopLevelCommas(paramsStr);
3396
+ for (const part of paramParts) {
3397
+ const paramMatch = part.trim().match(/(\w+)\s*:\s*([^=]+?)(?:\s*=\s*(.+))?$/);
3398
+ if (paramMatch) {
3399
+ const paramType = paramMatch[2].trim();
3400
+ const hasDefault = paramMatch[3] !== void 0;
3401
+ params.push({
3402
+ name: paramMatch[1],
3403
+ type: paramType,
3404
+ optional: paramType.endsWith("?") || hasDefault
3405
+ });
3406
+ }
3407
+ }
3408
+ }
3409
+ methods.push({
3410
+ name,
3411
+ params,
3412
+ returnType,
3413
+ visibility
3414
+ });
3415
+ }
3416
+ return methods;
3417
+ }
3418
+ function parseTypeAliases(source, sourceFile, packageName) {
3419
+ const aliases = [];
3420
+ const aliasRegex = /typealias\s+(\w+)\s*=\s*([^\n]+)/g;
3421
+ let match;
3422
+ while ((match = aliasRegex.exec(source)) !== null) aliases.push({
3423
+ name: match[1],
3424
+ value: match[2].trim(),
3425
+ sourceFile,
3426
+ packageName
3427
+ });
3428
+ return aliases;
3429
+ }
3430
+ /** Parse a single Kotlin source file and return all extracted symbols. */
3431
+ function parseKotlinFile(filePath, sdkPath) {
3432
+ const source = readFileSync(filePath, "utf-8");
3433
+ const relPath = relative(sdkPath, filePath);
3434
+ const cleaned = stripComments$1(source);
3435
+ const packageName = extractPackageName(cleaned);
3436
+ return {
3437
+ dataClasses: parseDataClasses(cleaned, relPath, packageName),
3438
+ classes: parseClasses$1(cleaned, relPath, packageName),
3439
+ enums: parseEnumClass(cleaned, relPath, packageName),
3440
+ typeAliases: parseTypeAliases(cleaned, relPath, packageName)
3441
+ };
3442
+ }
3443
+ //#endregion
3444
+ //#region src/compat/extractors/kotlin-surface.ts
3445
+ function buildSurface$2(allDataClasses, allClasses, allEnums, allTypeAliases) {
3446
+ const classes = {};
3447
+ const interfaces = {};
3448
+ const typeAliases = {};
3449
+ const enums = {};
3450
+ const collector = new ExportCollector();
3451
+ for (const dc of allDataClasses) {
3452
+ const fields = {};
3453
+ for (const field of dc.fields) {
3454
+ const fieldName = field.jsonName || field.name;
3455
+ fields[fieldName] = {
3456
+ name: fieldName,
3457
+ type: field.type,
3458
+ optional: field.optional
3459
+ };
3460
+ }
3461
+ interfaces[dc.name] = {
3462
+ name: dc.name,
3463
+ sourceFile: dc.sourceFile,
3464
+ fields: sortRecord(fields),
3465
+ extends: []
3466
+ };
3467
+ collector.add(dc.sourceFile, dc.name);
3468
+ }
3469
+ for (const cls of allClasses) {
3470
+ const methods = {};
3471
+ const properties = {};
3472
+ for (const method of cls.methods) {
3473
+ const params = method.params.map((p) => ({
3474
+ name: p.name,
3475
+ type: p.type,
3476
+ optional: p.optional
3477
+ }));
3478
+ if (!methods[method.name]) methods[method.name] = [];
3479
+ methods[method.name].push({
3480
+ name: method.name,
3481
+ params,
3482
+ returnType: method.returnType,
3483
+ async: false
3484
+ });
3485
+ }
3486
+ const constructorParams = cls.constructorParams.map((p) => ({
3487
+ name: p.name,
3488
+ type: p.type,
3489
+ optional: false
3490
+ }));
3491
+ classes[cls.name] = {
3492
+ name: cls.name,
3493
+ sourceFile: cls.sourceFile,
3494
+ methods: sortRecord(methods),
3495
+ properties: sortRecord(properties),
3496
+ constructorParams
3497
+ };
3498
+ collector.add(cls.sourceFile, cls.name);
3499
+ }
3500
+ for (const en of allEnums) {
3501
+ enums[en.name] = {
3502
+ name: en.name,
3503
+ sourceFile: en.sourceFile,
3504
+ members: sortRecord(en.members)
3505
+ };
3506
+ collector.add(en.sourceFile, en.name);
3507
+ }
3508
+ for (const ta of allTypeAliases) {
3509
+ typeAliases[ta.name] = {
3510
+ name: ta.name,
3511
+ sourceFile: ta.sourceFile,
3512
+ value: ta.value
3513
+ };
3514
+ collector.add(ta.sourceFile, ta.name);
3515
+ }
3516
+ const exports = collector.toRecord();
3517
+ return {
3518
+ classes: sortRecord(classes),
3519
+ interfaces: sortRecord(interfaces),
3520
+ typeAliases: sortRecord(typeAliases),
3521
+ enums: sortRecord(enums),
3522
+ exports: sortRecord(exports)
3523
+ };
3524
+ }
3525
+ const kotlinExtractor = {
3526
+ language: "kotlin",
3527
+ hints: {
3528
+ stripNullable(type) {
3529
+ if (type.endsWith("?")) return type.slice(0, -1);
3530
+ return null;
3531
+ },
3532
+ isNullableOnlyDifference(a, b) {
3533
+ return defaultIsNullableOnlyDifference(this, a, b);
3534
+ },
3535
+ isUnionReorder(_a, _b) {
3536
+ return false;
3537
+ },
3538
+ isGenericTypeParam(type) {
3539
+ return /^[A-Z]$/.test(type) || /^T[A-Z][a-zA-Z]*$/.test(type);
3540
+ },
3541
+ isExtractionArtifact(type) {
3542
+ return type === "Any" || type === "Any?";
3543
+ },
3544
+ tolerateCategoryMismatch: true,
3545
+ extractReturnTypeName(returnType) {
3546
+ let inner = returnType;
3547
+ if (inner.endsWith("?")) inner = inner.slice(0, -1);
3548
+ const listMatch = inner.match(/^(?:List|MutableList|ArrayList)<(.+)>$/);
3549
+ if (listMatch) inner = listMatch[1];
3550
+ const paginatedMatch = inner.match(/^PaginatedList<(.+)>$/);
3551
+ if (paginatedMatch) inner = paginatedMatch[1];
3552
+ if ([
3553
+ "String",
3554
+ "Int",
3555
+ "Long",
3556
+ "Float",
3557
+ "Double",
3558
+ "Boolean",
3559
+ "Unit",
3560
+ "Void"
3561
+ ].includes(inner)) return null;
3562
+ return inner;
3563
+ },
3564
+ extractParamTypeName(paramType) {
3565
+ let inner = paramType;
3566
+ if (inner.endsWith("?")) inner = inner.slice(0, -1);
3567
+ if ([
3568
+ "String",
3569
+ "Int",
3570
+ "Long",
3571
+ "Float",
3572
+ "Double",
3573
+ "Boolean"
3574
+ ].includes(inner)) return null;
3575
+ return inner;
3576
+ },
3577
+ propertyMatchesClass(propertyName, className) {
3578
+ return propertyName.toLowerCase() === className.toLowerCase();
3579
+ },
3580
+ derivedModelNames(modelName) {
3581
+ return [`${modelName}Response`];
3582
+ }
3583
+ },
3584
+ async extract(sdkPath) {
3585
+ const ktFiles = walkKotlinFiles(sdkPath);
3586
+ if (ktFiles.length === 0) throw new ExtractorError(`No .kt files found in ${sdkPath}`, `Ensure the --sdk-path argument points to a Kotlin project root containing .kt source files.`);
3587
+ const allDataClasses = [];
3588
+ const allClasses = [];
3589
+ const allEnums = [];
3590
+ const allTypeAliases = [];
3591
+ for (const filePath of ktFiles) {
3592
+ const parsed = parseKotlinFile(filePath, sdkPath);
3593
+ allDataClasses.push(...parsed.dataClasses);
3594
+ allClasses.push(...parsed.classes);
3595
+ allEnums.push(...parsed.enums);
3596
+ allTypeAliases.push(...parsed.typeAliases);
3597
+ }
3598
+ const { classes, interfaces, typeAliases, enums, exports } = buildSurface$2(allDataClasses, allClasses, allEnums, allTypeAliases);
3599
+ return {
3600
+ language: "kotlin",
3601
+ extractedFrom: sdkPath,
3602
+ extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
3603
+ classes,
3604
+ interfaces,
3605
+ typeAliases,
3606
+ enums,
3607
+ exports
3608
+ };
3609
+ }
3610
+ };
3611
+ //#endregion
3612
+ //#region src/compat/extractors/dotnet-parser.ts
3613
+ /**
3614
+ * C# (.NET) source parser — regex-based extraction of classes, enums,
3615
+ * properties, and methods from C# source files.
3616
+ */
3617
+ function walkCSharpFiles(dir) {
3618
+ const results = [];
3619
+ let entries;
3620
+ try {
3621
+ entries = readdirSync(dir);
3622
+ } catch {
3623
+ return results;
3624
+ }
3625
+ for (const entry of entries) {
3626
+ const fullPath = join(dir, entry);
3627
+ let stat;
3628
+ try {
3629
+ stat = statSync(fullPath);
3630
+ } catch {
3631
+ continue;
3632
+ }
3633
+ if (stat.isDirectory()) {
3634
+ if (entry.startsWith(".") || entry === "bin" || entry === "obj" || entry === "test" || entry === "tests") continue;
3635
+ results.push(...walkCSharpFiles(fullPath));
3636
+ } else if (entry.endsWith(".cs") && !entry.endsWith("Test.cs") && !entry.endsWith("Tests.cs")) results.push(fullPath);
3637
+ }
3638
+ return results.sort();
3639
+ }
3640
+ /** Extract the namespace from a C# source file. */
3641
+ function extractNamespace(source) {
3642
+ const fileScopedMatch = source.match(/^namespace\s+([\w.]+)\s*;/m);
3643
+ if (fileScopedMatch) return fileScopedMatch[1];
3644
+ const blockMatch = source.match(/^namespace\s+([\w.]+)\s*\{/m);
3645
+ if (blockMatch) return blockMatch[1];
3646
+ return "unknown";
3647
+ }
3648
+ /** Strip single-line and block comments from source. */
3649
+ function stripComments(source) {
3650
+ let result = source.replace(/\/\*[\s\S]*?\*\//g, "");
3651
+ result = result.replace(/\/\/[^\n]*/g, "");
3652
+ return result;
3653
+ }
3654
+ /** Extract [JsonProperty("name")] value from text preceding a property. */
3655
+ function extractJsonPropertyName(text) {
3656
+ const match = text.match(/\[JsonProperty\("([^"]+)"\)\]/);
3657
+ return match ? match[1] : null;
3658
+ }
3659
+ /** Extract [EnumMember(Value = "name")] value from text. */
3660
+ function extractEnumMemberValue(text) {
3661
+ const match = text.match(/\[EnumMember\(Value\s*=\s*"([^"]+)"\)\]/);
3662
+ return match ? match[1] : null;
3663
+ }
3664
+ function parseEnums(source, sourceFile) {
3665
+ const enums = [];
3666
+ const ns = extractNamespace(source);
3667
+ const enumRegex = /public\s+enum\s+(\w+)\s*\{([^}]*)\}/g;
3668
+ let match;
3669
+ while ((match = enumRegex.exec(source)) !== null) {
3670
+ const name = match[1];
3671
+ const body = match[2];
3672
+ const members = {};
3673
+ const lines = body.split(",");
3674
+ for (const line of lines) {
3675
+ const trimmed = line.trim();
3676
+ if (!trimmed) continue;
3677
+ const enumValue = extractEnumMemberValue(trimmed);
3678
+ const memberNameMatch = trimmed.match(/(\w+)\s*$/);
3679
+ if (memberNameMatch) {
3680
+ const memberName = memberNameMatch[1];
3681
+ members[memberName] = enumValue || memberName;
3682
+ }
3683
+ }
3684
+ if (Object.keys(members).length > 0) enums.push({
3685
+ name,
3686
+ namespace: ns,
3687
+ members,
3688
+ sourceFile
3689
+ });
3690
+ }
3691
+ return enums;
3692
+ }
3693
+ function parseClasses(source, sourceFile) {
3694
+ const classes = [];
3695
+ const ns = extractNamespace(source);
3696
+ const classStartRegex = /public\s+class\s+(\w+)(?:\s*:\s*[\w.,\s<>]+)?\s*\{/g;
3697
+ let match;
3698
+ while ((match = classStartRegex.exec(source)) !== null) {
3699
+ const name = match[1];
3700
+ const braceStart = source.indexOf("{", match.index + match[0].length - 1);
3701
+ let depth = 1;
3702
+ let idx = braceStart + 1;
3703
+ while (idx < source.length && depth > 0) {
3704
+ if (source[idx] === "{") depth++;
3705
+ else if (source[idx] === "}") depth--;
3706
+ idx++;
3707
+ }
3708
+ const classBody = source.slice(braceStart + 1, idx - 1);
3709
+ const properties = parseProperties(classBody);
3710
+ const methods = parseMethods(classBody);
3711
+ const constructorParams = parseConstructor(classBody, name);
3712
+ const isService = methods.length > 0;
3713
+ classes.push({
3714
+ name,
3715
+ namespace: ns,
3716
+ properties,
3717
+ methods,
3718
+ constructorParams,
3719
+ isService,
3720
+ sourceFile
3721
+ });
3722
+ }
3723
+ return classes;
3724
+ }
3725
+ /** Parse properties from a class body. */
3726
+ function parseProperties(classBody) {
3727
+ const properties = [];
3728
+ const lines = classBody.split("\n");
3729
+ let pendingJsonName = null;
3730
+ for (const line of lines) {
3731
+ const trimmed = line.trim();
3732
+ const jsonPropMatch = extractJsonPropertyName(trimmed);
3733
+ if (jsonPropMatch) {
3734
+ pendingJsonName = jsonPropMatch;
3735
+ continue;
3736
+ }
3737
+ const propMatch = trimmed.match(/public\s+(\S+(?:<[^>]+>)?(?:\?)?)\s+(\w+)\s*\{\s*get;\s*(set;\s*)?\}/);
3738
+ if (propMatch) {
3739
+ const type = propMatch[1];
3740
+ const name = propMatch[2];
3741
+ const hasSetter = !!propMatch[3];
3742
+ properties.push({
3743
+ name,
3744
+ type,
3745
+ jsonName: pendingJsonName || void 0,
3746
+ readonly: !hasSetter
3747
+ });
3748
+ pendingJsonName = null;
3749
+ continue;
3750
+ }
3751
+ if (!trimmed.startsWith("[") && trimmed.length > 0) pendingJsonName = null;
3752
+ }
3753
+ return properties;
3754
+ }
3755
+ /** Parse methods from a class body. */
3756
+ function parseMethods(classBody) {
3757
+ const methods = [];
3758
+ const methodRegex = /(private|protected|internal|public)\s+(?:(static)\s+)?(?:(async)\s+)?(\S+(?:<[^>]+>)?)\s+(\w+)\s*\(([^)]*)\)/g;
3759
+ let match;
3760
+ while ((match = methodRegex.exec(classBody)) !== null) {
3761
+ const visibility = match[1];
3762
+ const isAsync = match[3] === "async";
3763
+ const returnType = match[4];
3764
+ const name = match[5];
3765
+ const paramsStr = match[6];
3766
+ if (visibility === "private" || visibility === "protected") continue;
3767
+ const params = [];
3768
+ if (paramsStr.trim()) {
3769
+ const paramParts = paramsStr.split(",");
3770
+ for (const part of paramParts) {
3771
+ const paramMatch = part.trim().match(/(\S+(?:<[^>]+>)?(?:\?)?)\s+(\w+)(?:\s*=\s*(.+))?/);
3772
+ if (paramMatch) {
3773
+ const paramType = paramMatch[1];
3774
+ const paramName = paramMatch[2];
3775
+ const hasDefault = paramMatch[3] !== void 0;
3776
+ params.push({
3777
+ name: paramName,
3778
+ type: paramType,
3779
+ optional: paramType.endsWith("?") || hasDefault
3780
+ });
3781
+ }
3782
+ }
3783
+ }
3784
+ methods.push({
3785
+ name,
3786
+ params,
3787
+ returnType,
3788
+ visibility,
3789
+ isAsync
3790
+ });
3791
+ }
3792
+ return methods;
3793
+ }
3794
+ /** Parse constructor parameters. */
3795
+ function parseConstructor(classBody, className) {
3796
+ const params = [];
3797
+ const ctorPrefix = `public ${className}`;
3798
+ const ctorIdx = classBody.indexOf(ctorPrefix);
3799
+ if (ctorIdx < 0) return params;
3800
+ const match = classBody.slice(ctorIdx + ctorPrefix.length).match(/^\s*\(([^)]*)\)/);
3801
+ if (!match) return params;
3802
+ const paramsStr = match[1];
3803
+ if (!paramsStr.trim()) return params;
3804
+ const paramParts = paramsStr.split(",");
3805
+ for (const part of paramParts) {
3806
+ const paramMatch = part.trim().match(/(\S+(?:<[^>]+>)?)\s+(\w+)/);
3807
+ if (paramMatch) params.push({
3808
+ name: paramMatch[2],
3809
+ type: paramMatch[1]
3810
+ });
3811
+ }
3812
+ return params;
3813
+ }
3814
+ /** Parse a single C# source file and return all extracted symbols. */
3815
+ function parseCSharpFile(filePath, sdkPath) {
3816
+ const source = readFileSync(filePath, "utf-8");
3817
+ const relPath = relative(sdkPath, filePath);
3818
+ const cleaned = stripComments(source);
3819
+ return {
3820
+ classes: parseClasses(cleaned, relPath),
3821
+ enums: parseEnums(cleaned, relPath)
3822
+ };
3823
+ }
3824
+ //#endregion
3825
+ //#region src/compat/extractors/dotnet-surface.ts
3826
+ function buildSurface$1(allClasses, allEnums, _hints) {
3827
+ const classes = {};
3828
+ const interfaces = {};
3829
+ const enums = {};
3830
+ const collector = new ExportCollector();
3831
+ for (const cls of allClasses) {
3832
+ if (cls.isService) {
3833
+ const methods = {};
3834
+ const properties = {};
3835
+ for (const method of cls.methods) {
3836
+ const params = method.params.map((p) => ({
3837
+ name: p.name,
3838
+ type: p.type,
3839
+ optional: p.optional
3840
+ }));
3841
+ if (!methods[method.name]) methods[method.name] = [];
3842
+ methods[method.name].push({
3843
+ name: method.name,
3844
+ params,
3845
+ returnType: method.returnType,
3846
+ async: method.isAsync
3847
+ });
3848
+ }
3849
+ for (const prop of cls.properties) {
3850
+ const propName = prop.jsonName || prop.name;
3851
+ properties[propName] = {
3852
+ name: propName,
3853
+ type: prop.type,
3854
+ readonly: prop.readonly
3855
+ };
3856
+ }
3857
+ const constructorParams = cls.constructorParams.map((p) => ({
3858
+ name: p.name,
3859
+ type: p.type,
3860
+ optional: false
3861
+ }));
3862
+ classes[cls.name] = {
3863
+ name: cls.name,
3864
+ sourceFile: cls.sourceFile,
3865
+ methods: sortRecord(methods),
3866
+ properties: sortRecord(properties),
3867
+ constructorParams
3868
+ };
3869
+ } else {
3870
+ const fields = {};
3871
+ for (const prop of cls.properties) {
3872
+ const fieldName = prop.jsonName || prop.name;
3873
+ const isNullable = prop.type.endsWith("?");
3874
+ fields[fieldName] = {
3875
+ name: fieldName,
3876
+ type: prop.type,
3877
+ optional: isNullable
3878
+ };
3879
+ }
3880
+ interfaces[cls.name] = {
3881
+ name: cls.name,
3882
+ sourceFile: cls.sourceFile,
3883
+ fields: sortRecord(fields),
3884
+ extends: []
3885
+ };
3886
+ }
3887
+ collector.add(cls.sourceFile, cls.name);
3888
+ }
3889
+ for (const en of allEnums) {
3890
+ enums[en.name] = {
3891
+ name: en.name,
3892
+ sourceFile: en.sourceFile,
3893
+ members: sortRecord(en.members)
3894
+ };
3895
+ collector.add(en.sourceFile, en.name);
3896
+ }
3897
+ const exports = collector.toRecord();
3898
+ return {
3899
+ classes: sortRecord(classes),
3900
+ interfaces: sortRecord(interfaces),
3901
+ enums: sortRecord(enums),
3902
+ exports: sortRecord(exports)
3903
+ };
3904
+ }
3905
+ //#endregion
3906
+ //#region src/compat/extractors/dotnet.ts
3907
+ /**
3908
+ * DotNet (C#) API surface extractor.
3909
+ *
3910
+ * Walks `.cs` files (excluding test, bin, and obj directories), parses
3911
+ * classes, enums, properties, and methods, then builds an `ApiSurface`.
3912
+ *
3913
+ * Parser: `dotnet-parser.ts` — regex-based C# source analysis
3914
+ * Surface: `dotnet-surface.ts` — ApiSurface construction
3915
+ */
3916
+ const DOTNET_PRIMITIVE_TYPES = new Set([
3917
+ "string",
3918
+ "int",
3919
+ "long",
3920
+ "float",
3921
+ "double",
3922
+ "bool",
3923
+ "decimal"
3924
+ ]);
3925
+ const dotnetHints = {
3926
+ stripNullable(type) {
3927
+ const nullableMatch = type.match(/^Nullable<(.+)>$/);
3928
+ if (nullableMatch) return nullableMatch[1];
3929
+ if (type.endsWith("?")) return type.slice(0, -1);
3930
+ return null;
3931
+ },
3932
+ isNullableOnlyDifference(a, b) {
3933
+ return defaultIsNullableOnlyDifference(this, a, b);
3934
+ },
3935
+ isUnionReorder(_a, _b) {
3936
+ return false;
3937
+ },
3938
+ isGenericTypeParam(type) {
3939
+ return /^[A-Z]$/.test(type) || /^T[A-Z][a-zA-Z]*$/.test(type);
3940
+ },
3941
+ isExtractionArtifact(type) {
3942
+ return type === "object" || type === "dynamic";
3943
+ },
3944
+ tolerateCategoryMismatch: false,
3945
+ extractReturnTypeName(returnType) {
3946
+ let inner = returnType;
3947
+ const taskMatch = inner.match(/^Task<(.+)>$/);
3948
+ if (taskMatch) inner = taskMatch[1];
3949
+ if (inner === "Task") return null;
3950
+ if (inner.endsWith("?")) inner = inner.slice(0, -1);
3951
+ const listMatch = inner.match(/^(?:List|IList|IEnumerable|IReadOnlyList)<(.+)>$/);
3952
+ if (listMatch) inner = listMatch[1];
3953
+ if ([
3954
+ "string",
3955
+ "int",
3956
+ "long",
3957
+ "float",
3958
+ "double",
3959
+ "bool",
3960
+ "void",
3961
+ "decimal"
3962
+ ].includes(inner)) return null;
3963
+ return inner;
3964
+ },
3965
+ extractParamTypeName(paramType) {
3966
+ let inner = paramType;
3967
+ if (inner.endsWith("?")) inner = inner.slice(0, -1);
3968
+ if ([
3969
+ "string",
3970
+ "int",
3971
+ "long",
3972
+ "float",
3973
+ "double",
3974
+ "bool",
3975
+ "decimal"
3976
+ ].includes(inner)) return null;
3977
+ return inner;
3978
+ },
3979
+ propertyMatchesClass(propertyName, className) {
3980
+ return propertyName.toLowerCase() === className.toLowerCase();
3981
+ },
3982
+ derivedModelNames(modelName) {
3983
+ return [`${modelName}Response`];
3984
+ },
3985
+ isTypeEquivalent(baselineType, candidateType, candidateSurface) {
3986
+ const stripNullable = (t) => {
3987
+ if (t.endsWith("?")) return t.slice(0, -1);
3988
+ const m = t.match(/^Nullable<(.+)>$/);
3989
+ return m ? m[1] : t;
3990
+ };
3991
+ const baseClean = stripNullable(baselineType);
3992
+ const candClean = stripNullable(candidateType);
3993
+ const dictPattern = /^Dictionary<string,\s*(string|object|dynamic|Object)>$/;
3994
+ if (dictPattern.test(baseClean) && dictPattern.test(candClean)) return true;
3995
+ const arrayMatch = baseClean.match(/^(.+)\[\]$/);
3996
+ const listMatch = candClean.match(/^(?:List|IList|IEnumerable|IReadOnlyList)<(.+)>$/);
3997
+ if (arrayMatch && listMatch) {
3998
+ const baseInner = arrayMatch[1];
3999
+ const candInner = listMatch[1];
4000
+ if (baseInner === candInner) return true;
4001
+ if (NAMED_TYPE_RE.test(baseInner) && NAMED_TYPE_RE.test(candInner)) {
4002
+ if (typeExistsInSurface(candInner, candidateSurface)) {
4003
+ if (candInner.includes(baseInner) || baseInner.includes(candInner)) return true;
4004
+ const baseNoResp = baseInner.replace(/Response$/, "");
4005
+ const candNoResp = candInner.replace(/Response$/, "");
4006
+ if (candNoResp.includes(baseNoResp) || baseNoResp.includes(candNoResp)) return true;
4007
+ if (namedTypeWordsOverlap(baseInner, candInner)) return true;
4008
+ }
4009
+ }
4010
+ if (DOTNET_PRIMITIVE_TYPES.has(baseInner) && NAMED_TYPE_RE.test(candInner) || DOTNET_PRIMITIVE_TYPES.has(candInner) && NAMED_TYPE_RE.test(baseInner)) return true;
4011
+ }
4012
+ const baseListMatch = baseClean.match(/^(?:List|IList|IEnumerable|IReadOnlyList)<(.+)>$/);
4013
+ const candArrayMatch = candClean.match(/^(.+)\[\]$/);
4014
+ if (baseListMatch && candArrayMatch) {
4015
+ const baseInner = baseListMatch[1];
4016
+ const candInner = candArrayMatch[1];
4017
+ if (baseInner === candInner) return true;
4018
+ if (NAMED_TYPE_RE.test(baseInner) && NAMED_TYPE_RE.test(candInner)) {
4019
+ if (typeExistsInSurface(candInner, candidateSurface)) {
4020
+ if (candInner.includes(baseInner) || baseInner.includes(candInner)) return true;
4021
+ }
4022
+ }
4023
+ }
4024
+ const genericPattern = /^(?:List|IList|IEnumerable|IReadOnlyList)<(.+)>$/;
4025
+ const baseGeneric = baseClean.match(genericPattern);
4026
+ const candGeneric = candClean.match(genericPattern);
4027
+ if (baseGeneric && candGeneric) {
4028
+ const baseInner = baseGeneric[1];
4029
+ const candInner = candGeneric[1];
4030
+ if (baseInner === candInner) return true;
4031
+ if (NAMED_TYPE_RE.test(baseInner) && NAMED_TYPE_RE.test(candInner)) {
4032
+ if (typeExistsInSurface(candInner, candidateSurface)) {
4033
+ if (candInner.includes(baseInner) || baseInner.includes(candInner)) return true;
4034
+ const baseNoResp = baseInner.replace(/Response$/, "");
4035
+ const candNoResp = candInner.replace(/Response$/, "");
4036
+ if (candNoResp.includes(baseNoResp) || baseNoResp.includes(candNoResp)) return true;
4037
+ if (namedTypeWordsOverlap(baseInner, candInner)) return true;
4038
+ }
4039
+ }
4040
+ }
4041
+ if (NAMED_TYPE_RE.test(baseClean) && NAMED_TYPE_RE.test(candClean)) {
4042
+ if (typeExistsInSurface(candClean, candidateSurface)) {
4043
+ if (candClean.includes(baseClean) || baseClean.includes(candClean)) return true;
4044
+ const baseNoResp = baseClean.replace(/Response$/, "");
4045
+ const candNoResp = candClean.replace(/Response$/, "");
4046
+ if (candNoResp.includes(baseNoResp) || baseNoResp.includes(candNoResp)) return true;
4047
+ if (namedTypeWordsOverlap(baseClean, candClean)) return true;
4048
+ }
4049
+ }
4050
+ const numericTypes = new Set([
4051
+ "int",
4052
+ "long",
4053
+ "float",
4054
+ "double",
4055
+ "decimal"
4056
+ ]);
4057
+ if (numericTypes.has(baseClean) && numericTypes.has(candClean)) return true;
4058
+ if (baseClean === "DateTime" && candClean === "string" || baseClean === "string" && candClean === "DateTime") return true;
4059
+ if (baseClean === "string" && candidateSurface.enums[candClean]) return true;
4060
+ if (candClean === "string" && candidateSurface.enums?.[baseClean]) return true;
4061
+ return false;
4062
+ },
4063
+ modelBaseClasses: [],
4064
+ exceptionBaseClasses: ["Exception"]
4065
+ };
4066
+ /** Create a DotNet extractor with optional hint overrides. */
4067
+ function createDotnetExtractor(hintOverrides) {
4068
+ const mergedHints = hintOverrides ? {
4069
+ ...dotnetHints,
4070
+ ...hintOverrides
4071
+ } : dotnetHints;
4072
+ return {
4073
+ language: "dotnet",
4074
+ hints: mergedHints,
4075
+ async extract(sdkPath) {
4076
+ let sourceDir;
4077
+ const srcPath = resolve(sdkPath, "src");
4078
+ try {
4079
+ statSync(srcPath);
4080
+ sourceDir = srcPath;
4081
+ } catch {
4082
+ sourceDir = sdkPath;
4083
+ }
4084
+ const csFiles = walkCSharpFiles(sourceDir);
4085
+ if (csFiles.length === 0) throw new ExtractorError(`No .cs files found in ${sdkPath}`, `Ensure the --sdk-path argument points to a .NET project root containing .cs source files.`);
4086
+ const allClasses = [];
4087
+ const allEnums = [];
4088
+ for (const filePath of csFiles) {
4089
+ const parsed = parseCSharpFile(filePath, sdkPath);
4090
+ allClasses.push(...parsed.classes);
4091
+ allEnums.push(...parsed.enums);
4092
+ }
4093
+ const { classes, interfaces, enums, exports } = buildSurface$1(allClasses, allEnums, mergedHints);
4094
+ return {
4095
+ language: "dotnet",
4096
+ extractedFrom: sdkPath,
4097
+ extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
4098
+ classes,
4099
+ interfaces,
4100
+ typeAliases: {},
4101
+ enums,
4102
+ exports
4103
+ };
4104
+ }
4105
+ };
4106
+ }
4107
+ /** Default DotNet extractor with generic language hints. */
4108
+ const dotnetExtractor = createDotnetExtractor();
4109
+ //#endregion
4110
+ //#region src/compat/extractors/elixir-parser.ts
4111
+ /**
4112
+ * Elixir source parser — regex-based extraction of modules, structs,
4113
+ * functions, and type specs from Elixir source files.
4114
+ */
4115
+ function walkElixirFiles(dir) {
4116
+ const results = [];
4117
+ let entries;
4118
+ try {
4119
+ entries = readdirSync(dir);
4120
+ } catch {
4121
+ return results;
4122
+ }
4123
+ for (const entry of entries) {
4124
+ const fullPath = join(dir, entry);
4125
+ let stat;
4126
+ try {
4127
+ stat = statSync(fullPath);
4128
+ } catch {
4129
+ continue;
4130
+ }
4131
+ if (stat.isDirectory()) {
4132
+ if (entry.startsWith(".") || entry === "_build" || entry === "deps" || entry === "test") continue;
4133
+ results.push(...walkElixirFiles(fullPath));
4134
+ } else if (entry.endsWith(".ex") && !entry.endsWith("_test.exs")) results.push(fullPath);
4135
+ }
4136
+ return results.sort();
4137
+ }
4138
+ /**
4139
+ * Strip comments and heredoc strings from Elixir source code.
4140
+ * This prevents false matches of keywords like `do`/`end` inside strings
4141
+ * and doc comments.
4142
+ */
4143
+ function stripCommentsAndHeredocs(source) {
4144
+ let result = source.replace(/"""[\s\S]*?"""/g, "\"\"\"\"\"\"");
4145
+ result = result.replace(/#[^\n]*/g, "");
4146
+ return result;
4147
+ }
4148
+ /**
4149
+ * Given the full source and the position right after `defmodule ... do`,
4150
+ * find the module body text up to the matching `end`.
4151
+ *
4152
+ * Uses a character-by-character scan that skips string literals and
4153
+ * counts block-level `do`/`end` keywords only when they appear as
4154
+ * standalone tokens (word boundaries).
4155
+ */
4156
+ function extractModuleBody(source, startIdx) {
4157
+ let depth = 1;
4158
+ let i = startIdx;
4159
+ while (i < source.length && depth > 0) {
4160
+ const ch = source[i];
4161
+ if (ch === "\"") {
4162
+ i++;
4163
+ while (i < source.length && source[i] !== "\"") {
4164
+ if (source[i] === "\\") i++;
4165
+ i++;
4166
+ }
4167
+ i++;
4168
+ continue;
4169
+ }
4170
+ if (ch === "'") {
4171
+ i++;
4172
+ while (i < source.length && source[i] !== "'") {
4173
+ if (source[i] === "\\") i++;
4174
+ i++;
4175
+ }
4176
+ i++;
4177
+ continue;
4178
+ }
4179
+ if (isWordBoundary(source, i)) {
4180
+ if (source.slice(i, i + 2) === "do" && isWordEnd(source, i + 2)) {
4181
+ if (source[i + 2] !== ":") depth++;
4182
+ i += 2;
4183
+ continue;
4184
+ }
4185
+ if (source.slice(i, i + 2) === "fn" && isWordEnd(source, i + 2)) {
4186
+ depth++;
4187
+ i += 2;
4188
+ continue;
4189
+ }
4190
+ if (source.slice(i, i + 3) === "end" && isWordEnd(source, i + 3)) {
4191
+ depth--;
4192
+ if (depth === 0) return source.slice(startIdx, i);
4193
+ i += 3;
4194
+ continue;
4195
+ }
4196
+ }
4197
+ i++;
4198
+ }
4199
+ return null;
4200
+ }
4201
+ function isWordBoundary(source, idx) {
4202
+ if (idx === 0) return true;
4203
+ const prev = source[idx - 1];
4204
+ return !/[a-zA-Z0-9_]/.test(prev);
4205
+ }
4206
+ function isWordEnd(source, idx) {
4207
+ if (idx >= source.length) return true;
4208
+ return !/[a-zA-Z0-9_]/.test(source[idx]);
4209
+ }
4210
+ /** Find all defmodule blocks and extract their bodies in a single pass. */
4211
+ function extractModules(source) {
4212
+ const modules = [];
4213
+ const moduleRegex = /defmodule\s+([\w.]+)\s+do\b/g;
4214
+ let match;
4215
+ while ((match = moduleRegex.exec(source)) !== null) {
4216
+ const body = extractModuleBody(source, match.index + match[0].length);
4217
+ if (body) modules.push({
4218
+ name: match[1],
4219
+ body
4220
+ });
4221
+ }
4222
+ return modules;
4223
+ }
4224
+ function extractStructs(modules, sourceFile) {
4225
+ const structs = [];
4226
+ for (const { name: moduleName, body: moduleBody } of modules) {
4227
+ const structMatch = moduleBody.match(/defstruct\s+\[([^\]]*)\]/);
4228
+ if (!structMatch) continue;
4229
+ const fieldsStr = structMatch[1];
4230
+ const fields = [];
4231
+ const fieldRegex = /:(\w+)/g;
4232
+ let fieldMatch;
4233
+ while ((fieldMatch = fieldRegex.exec(fieldsStr)) !== null) fields.push(fieldMatch[1]);
4234
+ if (fields.length > 0) structs.push({
4235
+ moduleName,
4236
+ fields,
4237
+ sourceFile
4238
+ });
4239
+ }
4240
+ return structs;
4241
+ }
4242
+ function extractFunctions(modules, sourceFile) {
4243
+ const functions = [];
4244
+ for (const { name: moduleName, body: moduleBody } of modules) {
4245
+ const funcRegex = /(def|defp)\s+(\w+)(?:\(([^)]*)\))?(?:\s+do\b|\s*,\s*do:)/g;
4246
+ let funcMatch;
4247
+ while ((funcMatch = funcRegex.exec(moduleBody)) !== null) {
4248
+ const isPrivate = funcMatch[1] === "defp";
4249
+ const name = funcMatch[2];
4250
+ const paramsStr = funcMatch[3] || "";
4251
+ if (name === "defstruct" || name === "defmodule") continue;
4252
+ const params = [];
4253
+ if (paramsStr.trim()) {
4254
+ const paramParts = paramsStr.split(",");
4255
+ for (const part of paramParts) {
4256
+ const paramNameMatch = part.trim().match(/^(\w+)/);
4257
+ if (paramNameMatch) params.push(paramNameMatch[1]);
4258
+ }
4259
+ }
4260
+ functions.push({
4261
+ moduleName,
4262
+ name,
4263
+ arity: params.length,
4264
+ params,
4265
+ isPrivate,
4266
+ sourceFile
4267
+ });
4268
+ }
4269
+ }
4270
+ return functions;
4271
+ }
4272
+ function extractTypeSpecs(modules, sourceFile) {
4273
+ const specs = [];
4274
+ for (const { name: moduleName, body: moduleBody } of modules) {
4275
+ const typeRegex = /@type\s+(\w+)\s*::\s*/g;
4276
+ let typeMatch;
4277
+ while ((typeMatch = typeRegex.exec(moduleBody)) !== null) {
4278
+ const typeName = typeMatch[1];
4279
+ const defStart = typeMatch.index + typeMatch[0].length;
4280
+ let defEnd = defStart;
4281
+ let braceDepth = 0;
4282
+ for (let j = defStart; j < moduleBody.length; j++) {
4283
+ const ch = moduleBody[j];
4284
+ if (ch === "{") braceDepth++;
4285
+ else if (ch === "}") {
4286
+ braceDepth--;
4287
+ if (braceDepth < 0) break;
4288
+ if (braceDepth === 0) {
4289
+ defEnd = j + 1;
4290
+ break;
4291
+ }
4292
+ } else if (braceDepth === 0 && ch === "\n") {
4293
+ const remaining = moduleBody.slice(j + 1);
4294
+ if (remaining.match(/^\s*\S/)) {
4295
+ const nextContent = remaining.trimStart();
4296
+ if (nextContent.startsWith("@") || nextContent.startsWith("def ") || nextContent.startsWith("defp ") || nextContent.startsWith("defstruct") || nextContent.startsWith("defmodule")) {
4297
+ defEnd = j;
4298
+ break;
4299
+ }
4300
+ } else {
4301
+ defEnd = j;
4302
+ break;
4303
+ }
4304
+ }
4305
+ defEnd = j + 1;
4306
+ }
4307
+ const definition = moduleBody.slice(defStart, defEnd).trim();
4308
+ specs.push({
4309
+ moduleName,
4310
+ name: typeName,
4311
+ definition,
4312
+ sourceFile
4313
+ });
4314
+ }
4315
+ }
4316
+ return specs;
4317
+ }
4318
+ function extractEnumModules(modules, sourceFile) {
4319
+ const enumModules = [];
4320
+ for (const { name: moduleName, body: moduleBody } of modules) {
4321
+ if (!moduleBody.match(/def\s+values[\s,]/)) continue;
4322
+ if (moduleBody.match(/defstruct\b/)) continue;
4323
+ const members = {};
4324
+ const valueFuncRegex = /def\s+(\w+),\s*do:\s*"([^"]+)"/g;
4325
+ let valueFuncMatch;
4326
+ while ((valueFuncMatch = valueFuncRegex.exec(moduleBody)) !== null) {
4327
+ const funcName = valueFuncMatch[1];
4328
+ const funcValue = valueFuncMatch[2];
4329
+ if (funcName === "values") continue;
4330
+ members[funcName] = funcValue;
4331
+ }
4332
+ if (Object.keys(members).length > 0) enumModules.push({
4333
+ moduleName,
4334
+ members,
4335
+ sourceFile
4336
+ });
4337
+ }
4338
+ return enumModules;
4339
+ }
4340
+ /** Parse a single Elixir source file and return all extracted symbols. */
4341
+ function parseElixirFile(filePath, sdkPath) {
4342
+ const source = readFileSync(filePath, "utf-8");
4343
+ const relPath = relative(sdkPath, filePath);
4344
+ const modules = extractModules(stripCommentsAndHeredocs(source));
4345
+ return {
4346
+ structs: extractStructs(modules, relPath),
4347
+ functions: extractFunctions(modules, relPath),
4348
+ typeSpecs: extractTypeSpecs(modules, relPath),
4349
+ enumModules: extractEnumModules(modules, relPath),
4350
+ moduleNames: modules.map((m) => m.name)
4351
+ };
4352
+ }
4353
+ //#endregion
4354
+ //#region src/compat/extractors/elixir-surface.ts
4355
+ /** Get the short name from a fully qualified Elixir module name. */
4356
+ function shortName(moduleName) {
4357
+ const parts = moduleName.split(".");
4358
+ return parts[parts.length - 1];
4359
+ }
4360
+ function buildSurface(allStructs, allFunctions, allEnumModules, allTypeSpecs) {
4361
+ const classes = {};
4362
+ const interfaces = {};
4363
+ const enums = {};
4364
+ const collector = new ExportCollector();
4365
+ const structModuleNames = new Set(allStructs.map((s) => s.moduleName));
4366
+ const enumModuleNames = new Set(allEnumModules.map((e) => e.moduleName));
4367
+ for (const struct of allStructs) {
4368
+ const name = shortName(struct.moduleName);
4369
+ const fields = {};
4370
+ const typeSpec = allTypeSpecs.find((ts) => ts.moduleName === struct.moduleName && ts.name === "t");
4371
+ for (const field of struct.fields) {
4372
+ let fieldType = "any";
4373
+ let optional = false;
4374
+ if (typeSpec) {
4375
+ const fieldPrefix = field + ":";
4376
+ const prefixIdx = typeSpec.definition.indexOf(fieldPrefix);
4377
+ const fieldTypeMatch = prefixIdx >= 0 ? typeSpec.definition.slice(prefixIdx + fieldPrefix.length).match(/^\s*([^,}]+)/) : null;
4378
+ if (fieldTypeMatch) {
4379
+ fieldType = fieldTypeMatch[1].trim();
4380
+ if (fieldType.includes("| nil") || fieldType.includes("nil |")) {
4381
+ optional = true;
4382
+ fieldType = fieldType.replace(/\s*\|\s*nil/g, "").replace(/nil\s*\|\s*/g, "").trim();
4383
+ }
4384
+ }
4385
+ }
4386
+ fields[field] = {
4387
+ name: field,
4388
+ type: fieldType,
4389
+ optional
4390
+ };
4391
+ }
4392
+ interfaces[name] = {
4393
+ name,
4394
+ sourceFile: struct.sourceFile,
4395
+ fields: sortRecord(fields),
4396
+ extends: []
4397
+ };
4398
+ collector.add(struct.sourceFile, name);
4399
+ }
4400
+ for (const enumModule of allEnumModules) {
4401
+ const name = shortName(enumModule.moduleName);
4402
+ enums[name] = {
4403
+ name,
4404
+ sourceFile: enumModule.sourceFile,
4405
+ members: sortRecord(enumModule.members)
4406
+ };
4407
+ collector.add(enumModule.sourceFile, name);
4408
+ }
4409
+ const funcsByModule = /* @__PURE__ */ new Map();
4410
+ for (const func of allFunctions) {
4411
+ if (func.isPrivate) continue;
4412
+ if (structModuleNames.has(func.moduleName) && !funcsByModule.has(func.moduleName)) {}
4413
+ if (enumModuleNames.has(func.moduleName)) continue;
4414
+ if (!funcsByModule.has(func.moduleName)) funcsByModule.set(func.moduleName, []);
4415
+ funcsByModule.get(func.moduleName).push(func);
4416
+ }
4417
+ for (const [moduleName, funcs] of funcsByModule) {
4418
+ if (structModuleNames.has(moduleName)) continue;
4419
+ const name = shortName(moduleName);
4420
+ const methods = {};
4421
+ const seenFuncs = /* @__PURE__ */ new Set();
4422
+ for (const func of funcs) {
4423
+ if (seenFuncs.has(func.name)) continue;
4424
+ seenFuncs.add(func.name);
4425
+ const params = func.params.map((p) => ({
4426
+ name: p,
4427
+ type: "any",
4428
+ optional: false
4429
+ }));
4430
+ if (!methods[func.name]) methods[func.name] = [];
4431
+ methods[func.name].push({
4432
+ name: func.name,
4433
+ params,
4434
+ returnType: "any",
4435
+ async: false
4436
+ });
4437
+ }
4438
+ if (Object.keys(methods).length > 0) {
4439
+ classes[name] = {
4440
+ name,
4441
+ sourceFile: funcs[0].sourceFile,
4442
+ methods: sortRecord(methods),
4443
+ properties: {},
4444
+ constructorParams: []
4445
+ };
4446
+ collector.add(funcs[0].sourceFile, name);
4447
+ }
4448
+ }
4449
+ const exports = collector.toRecord();
4450
+ return {
4451
+ classes: sortRecord(classes),
4452
+ interfaces: sortRecord(interfaces),
4453
+ enums: sortRecord(enums),
4454
+ exports: sortRecord(exports)
4455
+ };
4456
+ }
4457
+ const elixirExtractor = {
4458
+ language: "elixir",
4459
+ hints: {
4460
+ stripNullable(type) {
4461
+ if (type.includes("| nil")) return type.replace(/\s*\|\s*nil/g, "").trim();
4462
+ if (type.includes("nil |")) return type.replace(/nil\s*\|\s*/g, "").trim();
4463
+ return null;
4464
+ },
4465
+ isNullableOnlyDifference(a, b) {
4466
+ return defaultIsNullableOnlyDifference(this, a, b);
4467
+ },
4468
+ isUnionReorder(a, b) {
4469
+ const sortParts = (t) => t.split("|").map((s) => s.trim()).sort().join("|");
4470
+ return a !== b && sortParts(a) === sortParts(b);
4471
+ },
4472
+ isGenericTypeParam(type) {
4473
+ return type === "any" || type === "term";
4474
+ },
4475
+ isExtractionArtifact(type) {
4476
+ return type === "any" || type === "term" || type === "any()";
4477
+ },
4478
+ tolerateCategoryMismatch: true,
4479
+ extractReturnTypeName(returnType) {
4480
+ let inner = returnType;
4481
+ const okMatch = inner.match(/^\{:ok,\s*(.+)\}$/);
4482
+ if (okMatch) inner = okMatch[1].trim();
4483
+ const listMatch = inner.match(/^\[(.+)\]$/);
4484
+ if (listMatch) inner = listMatch[1].trim();
4485
+ if ([
4486
+ "any",
4487
+ "term",
4488
+ "atom",
4489
+ "binary",
4490
+ "boolean",
4491
+ "integer",
4492
+ "float",
4493
+ "number",
4494
+ "pid"
4495
+ ].includes(inner)) return null;
4496
+ const typeCallMatch = inner.match(/^(\w+)\.t\(\)$/);
4497
+ if (typeCallMatch) return typeCallMatch[1];
4498
+ return inner || null;
4499
+ },
4500
+ extractParamTypeName(paramType) {
4501
+ if ([
4502
+ "any",
4503
+ "term",
4504
+ "atom",
4505
+ "binary",
4506
+ "boolean",
4507
+ "integer",
4508
+ "float",
4509
+ "number",
4510
+ "pid",
4511
+ "keyword"
4512
+ ].includes(paramType)) return null;
4513
+ const typeCallMatch = paramType.match(/^(\w+)\.t\(\)$/);
4514
+ if (typeCallMatch) return typeCallMatch[1];
4515
+ return paramType;
4516
+ },
4517
+ propertyMatchesClass(propertyName, className) {
4518
+ return propertyName.replace(/_/g, "").toLowerCase() === className.toLowerCase();
4519
+ },
4520
+ derivedModelNames(modelName) {
4521
+ return [`${modelName}Response`];
4522
+ }
4523
+ },
4524
+ async extract(sdkPath) {
4525
+ let sourceDir;
4526
+ const libPath = resolve(sdkPath, "lib");
4527
+ try {
4528
+ statSync(libPath);
4529
+ sourceDir = libPath;
4530
+ } catch {
4531
+ sourceDir = sdkPath;
4532
+ }
4533
+ const exFiles = walkElixirFiles(sourceDir);
4534
+ if (exFiles.length === 0) throw new ExtractorError(`No .ex files found in ${sdkPath}`, `Ensure the --sdk-path argument points to an Elixir project root containing .ex source files in lib/.`);
4535
+ const allStructs = [];
4536
+ const allFunctions = [];
4537
+ const allEnumModules = [];
4538
+ const allTypeSpecs = [];
4539
+ for (const filePath of exFiles) {
4540
+ const parsed = parseElixirFile(filePath, sdkPath);
4541
+ allStructs.push(...parsed.structs);
4542
+ allFunctions.push(...parsed.functions);
4543
+ allEnumModules.push(...parsed.enumModules);
4544
+ allTypeSpecs.push(...parsed.typeSpecs);
4545
+ }
4546
+ const { classes, interfaces, enums, exports } = buildSurface(allStructs, allFunctions, allEnumModules, allTypeSpecs);
4547
+ return {
4548
+ language: "elixir",
4549
+ extractedFrom: sdkPath,
4550
+ extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
4551
+ classes,
4552
+ interfaces,
4553
+ typeAliases: {},
4554
+ enums,
4555
+ exports
4556
+ };
4557
+ }
4558
+ };
4559
+ //#endregion
4560
+ export { buildOverlayLookup, diffSurfaces, dotnetExtractor, elixirExtractor, filterSurface, getExtractor, goExtractor, kotlinExtractor, nodeExtractor, nodeHints, patchOverlay, phpExtractor, pythonExtractor, registerExtractor, resolveHints, rubyExtractor, rustExtractor, specDerivedEnumValues, specDerivedFieldPaths, specDerivedHttpKeys, specDerivedMethodPaths, specDerivedNames };
4561
+
4562
+ //# sourceMappingURL=index.mjs.map