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