@workos/oagen 0.6.0 → 0.8.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 (53) hide show
  1. package/dist/cli/index.mjs +1339 -451
  2. package/dist/cli/index.mjs.map +1 -1
  3. package/dist/compat/index.d.mts +144 -57
  4. package/dist/compat/index.d.mts.map +1 -1
  5. package/dist/compat/index.mjs +365 -35
  6. package/dist/compat/index.mjs.map +1 -1
  7. package/dist/{diff-CltJMcwG.mjs → diff-BaH9qa8k.mjs} +100 -23
  8. package/dist/diff-BaH9qa8k.mjs.map +1 -0
  9. package/dist/differ-CTl98yoS.d.mts +116 -0
  10. package/dist/differ-CTl98yoS.d.mts.map +1 -0
  11. package/dist/index.d.mts +4 -2
  12. package/dist/index.d.mts.map +1 -1
  13. package/dist/index.mjs +177 -4
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/{differ-CG5ZErKl.mjs → overlay-TYxHOCow.mjs} +1112 -553
  16. package/dist/overlay-TYxHOCow.mjs.map +1 -0
  17. package/dist/scripts/smoke/baseline.mjs +176 -3
  18. package/dist/scripts/smoke/baseline.mjs.map +1 -1
  19. package/dist/smoke.d.mts +43 -0
  20. package/dist/smoke.d.mts.map +1 -1
  21. package/dist/smoke.mjs +176 -3
  22. package/dist/smoke.mjs.map +1 -1
  23. package/dist/tree-sitter-C53w_0WE.mjs.map +1 -1
  24. package/dist/{types-C7g5sLuX.d.mts → types-mTtkgy_h.d.mts} +213 -2
  25. package/dist/types-mTtkgy_h.d.mts.map +1 -0
  26. package/dist/verify/index.d.mts +4 -3
  27. package/dist/verify/index.d.mts.map +1 -1
  28. package/dist/verify/index.mjs +12 -10
  29. package/dist/verify/index.mjs.map +1 -1
  30. package/docs/architecture/emitter-contract.md +2 -2
  31. package/docs/architecture/extractor-contract.md +6 -5
  32. package/docs/architecture/pipeline.md +4 -3
  33. package/docs/architecture/workflows.md +5 -3
  34. package/docs/cli.md +148 -6
  35. package/docs/contributor/public-api.md +1 -1
  36. package/docs/core/compatibility-ir.md +200 -0
  37. package/docs/core/compatibility-policy.md +170 -0
  38. package/docs/core/compatibility.md +83 -0
  39. package/docs/core/index.md +5 -0
  40. package/docs/core/manifest-schema.md +76 -0
  41. package/docs/core/quickstart.md +9 -2
  42. package/docs/core/verify-compat.md +235 -0
  43. package/package.json +1 -1
  44. package/skills/generate-emitter/SKILL.md +12 -7
  45. package/skills/generate-emitter/references/project-scaffold.md +27 -6
  46. package/skills/generate-extractor/SKILL.md +14 -8
  47. package/skills/generate-sdk/SKILL.md +2 -2
  48. package/skills/generate-smoke-test/SKILL.md +9 -5
  49. package/skills/review-operations/SKILL.md +2 -2
  50. package/skills/verify-compat/SKILL.md +1 -1
  51. package/dist/diff-CltJMcwG.mjs.map +0 -1
  52. package/dist/differ-CG5ZErKl.mjs.map +0 -1
  53. package/dist/types-C7g5sLuX.d.mts.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { t as safeParse$1, v as ExtractorError } from "../tree-sitter-C53w_0WE.mjs";
2
- import { _ as registerExtractor, a as specDerivedHttpKeys, c as buildOverlayLookup, d as defaultIsNullableOnlyDifference, f as namedTypeWordsOverlap, g as getExtractor, h as typeExistsInSurface, i as specDerivedFieldPaths, l as patchOverlay, m as resolveHints, n as filterSurface, o as specDerivedMethodPaths, p as nodeHints, r as specDerivedEnumValues, s as specDerivedNames, t as diffSurfaces, u as NAMED_TYPE_RE } from "../differ-CG5ZErKl.mjs";
2
+ import { A as getDefaultPolicy, C as defaultSeverityForCategory, D as isCompatibleSchemaVersion, E as COMPAT_SCHEMA_VERSION, O as validateSnapshot, S as summarizeChanges, T as apiSurfaceToSnapshot, _ as nodeHints, a as registerExtractor, b as classifyAddedSymbol, c as filterSurface, d as specDerivedHttpKeys, f as specDerivedMethodPaths, g as namedTypeWordsOverlap, h as defaultIsNullableOnlyDifference, i as getExtractor, j as mergePolicy, k as ALL_LANGUAGE_IDS, l as specDerivedEnumValues, m as NAMED_TYPE_RE, n as isPatchableChange, o as diffSnapshots, p as specDerivedNames, r as patchOverlay, s as diffSurfaces, t as buildOverlayLookup, u as specDerivedFieldPaths, v as resolveHints, w as severityMeetsThreshold, x as classifySymbolChanges, y as typeExistsInSurface } from "../overlay-TYxHOCow.mjs";
3
3
  import path, { basename, dirname, join, relative, resolve } from "node:path";
4
4
  import Parser from "tree-sitter";
5
5
  import ts from "typescript";
@@ -9,6 +9,225 @@ import Python from "tree-sitter-python";
9
9
  import Ruby from "tree-sitter-ruby";
10
10
  import Go from "tree-sitter-go";
11
11
  import RustLang from "tree-sitter-rust";
12
+ //#region src/compat/concepts.ts
13
+ /**
14
+ * Group classified changes from multiple languages into conceptual changes.
15
+ *
16
+ * Changes are grouped by their `conceptualChangeId` — a deterministic ID
17
+ * derived from the symbol, category, and match details. Changes with the
18
+ * same ID across different languages represent the same conceptual change.
19
+ */
20
+ function buildConceptualRollup(perLanguageResults) {
21
+ const grouped = /* @__PURE__ */ new Map();
22
+ for (const { diff: result, language } of perLanguageResults) for (const change of result.changes) {
23
+ const existing = grouped.get(change.conceptualChangeId);
24
+ if (existing) existing.impact[language] = change.severity;
25
+ else grouped.set(change.conceptualChangeId, {
26
+ id: change.conceptualChangeId,
27
+ symbol: change.symbol,
28
+ category: change.category,
29
+ impact: { [language]: change.severity },
30
+ message: change.message,
31
+ old: change.old,
32
+ new: change.new
33
+ });
34
+ }
35
+ return { conceptualChanges: [...grouped.values()] };
36
+ }
37
+ /** Get the highest severity across all languages for a conceptual change. */
38
+ function highestSeverity(change) {
39
+ const severities = Object.values(change.impact);
40
+ if (severities.includes("breaking")) return "breaking";
41
+ if (severities.includes("soft-risk")) return "soft-risk";
42
+ return "additive";
43
+ }
44
+ /** Count conceptual changes by highest severity. */
45
+ function summarizeConceptualChanges(rollup) {
46
+ let breaking = 0;
47
+ let softRisk = 0;
48
+ let additive = 0;
49
+ for (const change of rollup.conceptualChanges) {
50
+ const sev = highestSeverity(change);
51
+ if (sev === "breaking") breaking++;
52
+ else if (sev === "soft-risk") softRisk++;
53
+ else additive++;
54
+ }
55
+ return {
56
+ breaking,
57
+ softRisk,
58
+ additive
59
+ };
60
+ }
61
+ //#endregion
62
+ //#region src/compat/report.ts
63
+ /** Generate a machine-readable compat report from a diff result. */
64
+ function generateReport(diff, language) {
65
+ return {
66
+ schemaVersion: "1",
67
+ language: language ?? "unknown",
68
+ summary: diff.summary,
69
+ changes: diff.changes.map((c) => ({
70
+ severity: c.severity,
71
+ category: c.category,
72
+ symbol: c.symbol,
73
+ conceptualChangeId: c.conceptualChangeId,
74
+ provenance: c.provenance,
75
+ old: c.old,
76
+ new: c.new,
77
+ ...c.message ? { message: c.message } : {}
78
+ }))
79
+ };
80
+ }
81
+ /** Generate a human-readable summary string for terminal output. */
82
+ function formatHumanSummary(diff, opts) {
83
+ const lines = [];
84
+ lines.push(`Compat report${opts?.language ? ` for ${opts.language}` : ""}:`);
85
+ lines.push(` ${diff.summary.breaking} breaking, ${diff.summary.softRisk} soft-risk, ${diff.summary.additive} additive`);
86
+ if (diff.changes.length === 0) {
87
+ lines.push(" No compatibility changes detected.");
88
+ return lines.join("\n");
89
+ }
90
+ const breaking = diff.changes.filter((c) => c.severity === "breaking");
91
+ const softRisk = diff.changes.filter((c) => c.severity === "soft-risk");
92
+ const additive = diff.changes.filter((c) => c.severity === "additive");
93
+ if (breaking.length > 0) {
94
+ lines.push("");
95
+ lines.push(" Breaking:");
96
+ for (const c of breaking) {
97
+ lines.push(` [${c.category}] ${c.symbol} — ${c.message}`);
98
+ if (opts?.explain && c.provenance !== "unknown") lines.push(` provenance: ${c.provenance}`);
99
+ }
100
+ }
101
+ if (softRisk.length > 0) {
102
+ lines.push("");
103
+ lines.push(" Soft-risk:");
104
+ for (const c of softRisk) {
105
+ lines.push(` [${c.category}] ${c.symbol} — ${c.message}`);
106
+ if (opts?.explain && c.provenance !== "unknown") lines.push(` provenance: ${c.provenance}`);
107
+ }
108
+ }
109
+ if (additive.length > 0) {
110
+ lines.push("");
111
+ lines.push(` Additive: ${additive.length} new symbol(s)`);
112
+ }
113
+ return lines.join("\n");
114
+ }
115
+ /** Generate a conceptual cross-language report from multiple per-language results. */
116
+ function generateConceptualReport(perLanguageResults) {
117
+ return { conceptualChanges: buildConceptualRollup(perLanguageResults).conceptualChanges.map((c) => ({
118
+ id: c.id,
119
+ symbol: c.symbol,
120
+ category: c.category,
121
+ impact: c.impact
122
+ })) };
123
+ }
124
+ /** Format a conceptual cross-language summary for terminal output. */
125
+ function formatConceptualSummary(perLanguageResults) {
126
+ const rollup = buildConceptualRollup(perLanguageResults);
127
+ const summary = summarizeConceptualChanges(rollup);
128
+ const lines = [];
129
+ lines.push("Cross-language conceptual summary:");
130
+ lines.push(` ${summary.breaking} breaking, ${summary.softRisk} soft-risk, ${summary.additive} additive`);
131
+ if (rollup.conceptualChanges.length === 0) {
132
+ lines.push(" No cross-language changes.");
133
+ return lines.join("\n");
134
+ }
135
+ for (const change of rollup.conceptualChanges) {
136
+ const sev = highestSeverity(change);
137
+ const impactStr = Object.entries(change.impact).map(([lang, s]) => `${lang}: ${s}`).join(", ");
138
+ lines.push(` [${sev}] ${change.category}: ${change.symbol}`);
139
+ lines.push(` impact: ${impactStr}`);
140
+ }
141
+ return lines.join("\n");
142
+ }
143
+ //#endregion
144
+ //#region src/compat/approvals.ts
145
+ /** Validate that an approval is narrow enough to be accepted. */
146
+ function validateApproval(approval) {
147
+ const errors = [];
148
+ if (!approval.symbol || approval.symbol.trim() === "") errors.push("Approval must target a specific symbol");
149
+ if (!approval.category) errors.push("Approval must specify a change category");
150
+ if (!approval.reason || approval.reason.trim() === "") errors.push("Approval must include a reason");
151
+ if (approval.symbol === "*" || approval.symbol.endsWith(".*") || approval.symbol.endsWith("::*")) errors.push(`Approval symbol "${approval.symbol}" is too broad — must target a specific symbol`);
152
+ return {
153
+ valid: errors.length === 0,
154
+ errors
155
+ };
156
+ }
157
+ /** Validate all approvals in a config. Returns errors keyed by index. */
158
+ function validateApprovals(approvals) {
159
+ const result = /* @__PURE__ */ new Map();
160
+ for (let i = 0; i < approvals.length; i++) {
161
+ const validation = validateApproval(approvals[i]);
162
+ if (!validation.valid) result.set(i, validation.errors);
163
+ }
164
+ return result;
165
+ }
166
+ /**
167
+ * Match a classified change against a list of approvals.
168
+ * Returns the first matching approval, or null if none match.
169
+ */
170
+ function matchApproval(change, approvals, language) {
171
+ for (const approval of approvals) if (approvalMatchesChange(approval, change, language)) return approval;
172
+ return null;
173
+ }
174
+ /**
175
+ * Apply approvals to a list of changes.
176
+ * Returns matches for every change (approved or not).
177
+ */
178
+ function applyApprovals(changes, approvals, language) {
179
+ return changes.map((change) => {
180
+ const approval = matchApproval(change, approvals, language);
181
+ return {
182
+ change,
183
+ approval,
184
+ approved: approval !== null
185
+ };
186
+ });
187
+ }
188
+ /**
189
+ * Filter changes to only those NOT covered by approvals.
190
+ * These are the remaining unapproved changes that may cause failure.
191
+ */
192
+ function unapprovedChanges(changes, approvals, language) {
193
+ return changes.filter((change) => !matchApproval(change, approvals, language));
194
+ }
195
+ function approvalMatchesChange(approval, change, language) {
196
+ if (approval.approved === false) return false;
197
+ if (approval.category !== change.category) return false;
198
+ if (!symbolMatches(approval.symbol, change.symbol)) return false;
199
+ if (!languageMatches(approval.appliesTo, language)) return false;
200
+ if (approval.match && !narrowingMatches(approval.match, change)) return false;
201
+ return true;
202
+ }
203
+ function symbolMatches(approvalSymbol, changeSymbol) {
204
+ if (approvalSymbol === changeSymbol) return true;
205
+ return approvalSymbol.replace(/::/g, ".").replace(/\\/g, ".") === changeSymbol.replace(/::/g, ".").replace(/\\/g, ".");
206
+ }
207
+ function languageMatches(appliesTo, language) {
208
+ if (!appliesTo || appliesTo === "all-impacted-languages") return true;
209
+ return appliesTo.includes(language);
210
+ }
211
+ function narrowingMatches(match, change) {
212
+ if (match.parameter) {
213
+ const paramInOld = change.old.parameter === match.parameter;
214
+ const paramInNew = change.new.parameter === match.parameter;
215
+ if (!paramInOld && !paramInNew) return false;
216
+ }
217
+ if (match.member) {
218
+ const memberInOld = change.old.member === match.member;
219
+ const memberInNew = change.new.member === match.member;
220
+ if (!memberInOld && !memberInNew) return false;
221
+ }
222
+ if (match.oldName) {
223
+ if (!Object.values(change.old).some((v) => v === match.oldName)) return false;
224
+ }
225
+ if (match.newName) {
226
+ if (!Object.values(change.new).some((v) => v === match.newName)) return false;
227
+ }
228
+ return true;
229
+ }
230
+ //#endregion
12
231
  //#region src/compat/extractors/shared.ts
13
232
  /** Sort an object's keys alphabetically. Shared across all extractors for deterministic output. */
14
233
  function sortRecord(record) {
@@ -38,6 +257,9 @@ var ExportCollector = class {
38
257
  const nodeExtractor = {
39
258
  language: "node",
40
259
  hints: nodeHints,
260
+ async extractSnapshot(sdkPath) {
261
+ return apiSurfaceToSnapshot(await this.extract(sdkPath));
262
+ },
41
263
  async extract(sdkPath) {
42
264
  sdkPath = resolve(sdkPath);
43
265
  const configPath = ts.findConfigFile(sdkPath, ts.sys.fileExists, "tsconfig.json");
@@ -176,12 +398,13 @@ function extractClass(sym, checker) {
176
398
  else deprecationMessage = "";
177
399
  }
178
400
  for (const member of decl.members) if (ts.isConstructorDeclaration(member)) for (const param of member.parameters) {
179
- const paramName = param.name.getText();
401
+ const { name: paramName, isDestructured } = extractParameterInfo(param, param.name.getText());
180
402
  const paramType = param.type ? checker.typeToString(checker.getTypeFromTypeNode(param.type)) : "any";
181
403
  constructorParams.push({
182
404
  name: paramName,
183
405
  type: paramType,
184
- optional: !!param.questionToken || !!param.initializer
406
+ optional: !!param.questionToken || !!param.initializer,
407
+ passingStyle: isDestructured ? "options_object" : "positional"
185
408
  });
186
409
  }
187
410
  }
@@ -226,10 +449,56 @@ function extractParam(sym, checker) {
226
449
  const decl = sym.getDeclarations()?.[0];
227
450
  const isOptional = decl && ts.isParameter(decl) ? !!decl.questionToken || !!decl.initializer : false;
228
451
  const type = checker.getTypeOfSymbolAtLocation(sym, decl);
452
+ const { name, isDestructured } = extractParameterInfo(decl, sym.name);
453
+ const typeStr = checker.typeToString(type);
454
+ let passingStyle;
455
+ if (isDestructured) passingStyle = "options_object";
456
+ else {
457
+ const resolvedType = checker.getTypeOfSymbolAtLocation(sym, decl);
458
+ const isObjectLike = resolvedType.isClassOrInterface() || (resolvedType.getFlags() & ts.TypeFlags.Object) !== 0;
459
+ const isPrimitive = [
460
+ "string",
461
+ "number",
462
+ "boolean",
463
+ "undefined",
464
+ "null",
465
+ "void",
466
+ "never",
467
+ "any",
468
+ "unknown"
469
+ ].includes(typeStr);
470
+ if (isObjectLike && !isPrimitive && !typeStr.startsWith("Promise<")) passingStyle = "options_object";
471
+ else passingStyle = "positional";
472
+ }
229
473
  return {
230
- name: sym.name,
231
- type: checker.typeToString(type),
232
- optional: isOptional
474
+ name,
475
+ type: typeStr,
476
+ optional: isOptional,
477
+ passingStyle
478
+ };
479
+ }
480
+ function extractParameterInfo(decl, fallback) {
481
+ if (!decl || !ts.isParameter(decl)) return {
482
+ name: fallback,
483
+ isDestructured: false
484
+ };
485
+ if (ts.isIdentifier(decl.name)) return {
486
+ name: decl.name.text,
487
+ isDestructured: false
488
+ };
489
+ const index = decl.parent.parameters?.indexOf(decl) ?? -1;
490
+ const suffix = index > 0 ? String(index) : "";
491
+ if (ts.isObjectBindingPattern(decl.name)) return {
492
+ name: `options${suffix}`,
493
+ isDestructured: true
494
+ };
495
+ if (ts.isArrayBindingPattern(decl.name)) return {
496
+ name: `args${suffix}`,
497
+ isDestructured: false
498
+ };
499
+ return {
500
+ name: fallback,
501
+ isDestructured: false
233
502
  };
234
503
  }
235
504
  function extractInterface(sym, checker) {
@@ -717,7 +986,8 @@ function buildSurface$6(allClasses, hints) {
717
986
  const params = method.params.map((p) => ({
718
987
  name: p.name,
719
988
  type: p.type,
720
- optional: p.optional
989
+ optional: p.optional,
990
+ passingStyle: "named"
721
991
  }));
722
992
  if (!apiMethods[method.name]) apiMethods[method.name] = [];
723
993
  apiMethods[method.name].push({
@@ -859,6 +1129,9 @@ function createPhpExtractor(hintOverrides) {
859
1129
  return {
860
1130
  language: "php",
861
1131
  hints: mergedHints,
1132
+ async extractSnapshot(sdkPath) {
1133
+ return apiSurfaceToSnapshot(await this.extract(sdkPath));
1134
+ },
862
1135
  async extract(sdkPath) {
863
1136
  let sourceDir;
864
1137
  const libPath = resolve(sdkPath, "lib");
@@ -1050,6 +1323,7 @@ function extractFunctionParams(paramsNode, skipFirst) {
1050
1323
  if (!paramsNode) return [];
1051
1324
  const params = [];
1052
1325
  let skippedFirst = false;
1326
+ let seenStarMarker = false;
1053
1327
  for (const child of paramsNode.namedChildren) {
1054
1328
  if (!skippedFirst && !skipFirst && child.type === "identifier" && (child.text === "self" || child.text === "cls")) {
1055
1329
  skippedFirst = true;
@@ -1059,13 +1333,18 @@ function extractFunctionParams(paramsNode, skipFirst) {
1059
1333
  skippedFirst = true;
1060
1334
  continue;
1061
1335
  }
1062
- if (child.text === "*") continue;
1336
+ if (child.text === "*") {
1337
+ seenStarMarker = true;
1338
+ continue;
1339
+ }
1063
1340
  if (child.type === "list_splat_pattern") {
1341
+ seenStarMarker = true;
1064
1342
  const nameNode = child.namedChildren.find((c) => c.type === "identifier");
1065
1343
  params.push({
1066
1344
  name: nameNode ? `*${nameNode.text}` : "*args",
1067
1345
  type: "",
1068
- optional: true
1346
+ optional: true,
1347
+ passingStyle: "positional"
1069
1348
  });
1070
1349
  continue;
1071
1350
  }
@@ -1074,17 +1353,20 @@ function extractFunctionParams(paramsNode, skipFirst) {
1074
1353
  params.push({
1075
1354
  name: nameNode ? `**${nameNode.text}` : "**kwargs",
1076
1355
  type: "",
1077
- optional: true
1356
+ optional: true,
1357
+ passingStyle: "keyword"
1078
1358
  });
1079
1359
  continue;
1080
1360
  }
1361
+ const passingStyle = seenStarMarker ? "keyword" : "keyword_or_positional";
1081
1362
  if (child.type === "typed_parameter") {
1082
1363
  const nameNode = child.namedChildren.find((c) => c.type === "identifier");
1083
1364
  const typeNode = child.childForFieldName("type");
1084
1365
  if (nameNode) params.push({
1085
1366
  name: nameNode.text,
1086
1367
  type: typeNode ? typeNode.text : "",
1087
- optional: false
1368
+ optional: false,
1369
+ passingStyle
1088
1370
  });
1089
1371
  } else if (child.type === "typed_default_parameter") {
1090
1372
  const nameNode = child.childForFieldName("name");
@@ -1092,19 +1374,22 @@ function extractFunctionParams(paramsNode, skipFirst) {
1092
1374
  if (nameNode) params.push({
1093
1375
  name: nameNode.text,
1094
1376
  type: typeNode ? typeNode.text : "",
1095
- optional: true
1377
+ optional: true,
1378
+ passingStyle
1096
1379
  });
1097
1380
  } else if (child.type === "default_parameter") {
1098
1381
  const nameNode = child.childForFieldName("name");
1099
1382
  if (nameNode) params.push({
1100
1383
  name: nameNode.text,
1101
1384
  type: "",
1102
- optional: true
1385
+ optional: true,
1386
+ passingStyle
1103
1387
  });
1104
1388
  } else if (child.type === "identifier") params.push({
1105
1389
  name: child.text,
1106
1390
  type: "",
1107
- optional: false
1391
+ optional: false,
1392
+ passingStyle
1108
1393
  });
1109
1394
  }
1110
1395
  return params;
@@ -1324,7 +1609,8 @@ function buildSurface$5(parsedFiles, hints) {
1324
1609
  const params = method.params.map((p) => ({
1325
1610
  name: p.name,
1326
1611
  type: p.type,
1327
- optional: p.optional
1612
+ optional: p.optional,
1613
+ ...p.passingStyle ? { passingStyle: p.passingStyle } : {}
1328
1614
  }));
1329
1615
  const returnType = unwrapReturnType(method.returnType);
1330
1616
  if (!apiMethods[method.name]) apiMethods[method.name] = [];
@@ -1385,7 +1671,8 @@ function buildSurface$5(parsedFiles, hints) {
1385
1671
  const params = method.params.map((p) => ({
1386
1672
  name: p.name,
1387
1673
  type: p.type,
1388
- optional: p.optional
1674
+ optional: p.optional,
1675
+ ...p.passingStyle ? { passingStyle: p.passingStyle } : {}
1389
1676
  }));
1390
1677
  if (!apiMethods[method.name]) apiMethods[method.name] = [];
1391
1678
  apiMethods[method.name].push({
@@ -1419,14 +1706,16 @@ function buildSurface$5(parsedFiles, hints) {
1419
1706
  constructorParams.push(...method.params.map((p) => ({
1420
1707
  name: p.name,
1421
1708
  type: p.type,
1422
- optional: p.optional
1709
+ optional: p.optional,
1710
+ ...p.passingStyle ? { passingStyle: p.passingStyle } : {}
1423
1711
  })));
1424
1712
  continue;
1425
1713
  }
1426
1714
  const params = method.params.map((p) => ({
1427
1715
  name: p.name,
1428
1716
  type: p.type,
1429
- optional: p.optional
1717
+ optional: p.optional,
1718
+ ...p.passingStyle ? { passingStyle: p.passingStyle } : {}
1430
1719
  }));
1431
1720
  if (!apiMethods[method.name]) apiMethods[method.name] = [];
1432
1721
  apiMethods[method.name].push({
@@ -1703,6 +1992,9 @@ function createPythonExtractor(hintOverrides) {
1703
1992
  return {
1704
1993
  language: "python",
1705
1994
  hints: mergedHints,
1995
+ async extractSnapshot(sdkPath) {
1996
+ return apiSurfaceToSnapshot(await this.extract(sdkPath));
1997
+ },
1706
1998
  async extract(sdkPath) {
1707
1999
  const sourceRoot = findPythonSourceRoot(sdkPath);
1708
2000
  if (!sourceRoot) throw new ExtractorError(`No Python package found in ${sdkPath}`, `Ensure the --sdk-path argument points to a Python project root containing a package directory with __init__.py.`);
@@ -1899,7 +2191,8 @@ function extractMethodParams(methodNode) {
1899
2191
  params.push({
1900
2192
  name: paramNode.text,
1901
2193
  type: "Object",
1902
- optional: false
2194
+ optional: false,
2195
+ passingStyle: "positional"
1903
2196
  });
1904
2197
  break;
1905
2198
  case "optional_parameter": {
@@ -1907,7 +2200,8 @@ function extractMethodParams(methodNode) {
1907
2200
  if (nameNode) params.push({
1908
2201
  name: nameNode.text,
1909
2202
  type: "Object",
1910
- optional: true
2203
+ optional: true,
2204
+ passingStyle: "positional"
1911
2205
  });
1912
2206
  break;
1913
2207
  }
@@ -1918,7 +2212,8 @@ function extractMethodParams(methodNode) {
1918
2212
  params.push({
1919
2213
  name: nameNode.text,
1920
2214
  type: "Object",
1921
- optional: valueNode !== null
2215
+ optional: valueNode !== null,
2216
+ passingStyle: "keyword"
1922
2217
  });
1923
2218
  }
1924
2219
  break;
@@ -1928,7 +2223,8 @@ function extractMethodParams(methodNode) {
1928
2223
  params.push({
1929
2224
  name: nameNode ? `*${nameNode.text}` : "*args",
1930
2225
  type: "Object",
1931
- optional: true
2226
+ optional: true,
2227
+ passingStyle: "positional"
1932
2228
  });
1933
2229
  break;
1934
2230
  }
@@ -1937,7 +2233,8 @@ function extractMethodParams(methodNode) {
1937
2233
  params.push({
1938
2234
  name: nameNode ? `**${nameNode.text}` : "**kwargs",
1939
2235
  type: "Object",
1940
- optional: true
2236
+ optional: true,
2237
+ passingStyle: "keyword"
1941
2238
  });
1942
2239
  break;
1943
2240
  }
@@ -1946,7 +2243,8 @@ function extractMethodParams(methodNode) {
1946
2243
  params.push({
1947
2244
  name: nameNode ? `&${nameNode.text}` : "&block",
1948
2245
  type: "Object",
1949
- optional: true
2246
+ optional: true,
2247
+ passingStyle: "positional"
1950
2248
  });
1951
2249
  break;
1952
2250
  }
@@ -2086,6 +2384,9 @@ function collectRbFiles(dir) {
2086
2384
  const rubyExtractor = {
2087
2385
  language: "ruby",
2088
2386
  hints: rubyHints,
2387
+ async extractSnapshot(sdkPath) {
2388
+ return apiSurfaceToSnapshot(await this.extract(sdkPath));
2389
+ },
2089
2390
  async extract(sdkPath) {
2090
2391
  const libPath = resolve(sdkPath, "lib");
2091
2392
  try {
@@ -2517,7 +2818,8 @@ function buildSurface$4(allStructs, allTypes, allFuncs, allConsts) {
2517
2818
  const params = fn.params.filter((p) => p.type !== "context.Context").map((p) => ({
2518
2819
  name: p.name,
2519
2820
  type: goTypeToString(p.type),
2520
- optional: false
2821
+ optional: false,
2822
+ passingStyle: "positional"
2521
2823
  }));
2522
2824
  if (!apiMethods[fn.name]) apiMethods[fn.name] = [];
2523
2825
  apiMethods[fn.name].push({
@@ -2674,6 +2976,9 @@ const goExtractor = {
2674
2976
  return false;
2675
2977
  }
2676
2978
  },
2979
+ async extractSnapshot(sdkPath) {
2980
+ return apiSurfaceToSnapshot(await this.extract(sdkPath));
2981
+ },
2677
2982
  async extract(sdkPath) {
2678
2983
  const goFiles = walkGoFiles(sdkPath);
2679
2984
  if (goFiles.length === 0) throw new ExtractorError(`No .go files found in ${sdkPath}`, `Ensure the --sdk-path argument points to a Go project root containing .go source files.`);
@@ -3005,7 +3310,8 @@ function buildSurface$3(allStructs, allEnums, allFuncs, allTraits, allTypeAliase
3005
3310
  const params = fn.params.map((p) => ({
3006
3311
  name: p.name,
3007
3312
  type: rustTypeToString(p.type),
3008
- optional: p.type.startsWith("Option<")
3313
+ optional: p.type.startsWith("Option<"),
3314
+ passingStyle: "positional"
3009
3315
  }));
3010
3316
  const returnType = unwrapResultType(fn.returnType);
3011
3317
  if (!apiMethods[fn.name]) apiMethods[fn.name] = [];
@@ -3162,6 +3468,9 @@ const rustExtractor = {
3162
3468
  return [`${modelName}Response`];
3163
3469
  }
3164
3470
  },
3471
+ async extractSnapshot(sdkPath) {
3472
+ return apiSurfaceToSnapshot(await this.extract(sdkPath));
3473
+ },
3165
3474
  async extract(sdkPath) {
3166
3475
  const rsFiles = walkRustFiles(sdkPath);
3167
3476
  if (rsFiles.length === 0) throw new ExtractorError(`No .rs files found in ${sdkPath}`, `Ensure the --sdk-path argument points to a Rust project root containing .rs source files.`);
@@ -3487,7 +3796,8 @@ function buildSurface$2(allDataClasses, allClasses, allEnums, allTypeAliases) {
3487
3796
  const params = method.params.map((p) => ({
3488
3797
  name: p.name,
3489
3798
  type: p.type,
3490
- optional: p.optional
3799
+ optional: p.optional,
3800
+ passingStyle: "named"
3491
3801
  }));
3492
3802
  if (!methods[method.name]) methods[method.name] = [];
3493
3803
  methods[method.name].push({
@@ -3500,7 +3810,8 @@ function buildSurface$2(allDataClasses, allClasses, allEnums, allTypeAliases) {
3500
3810
  const constructorParams = cls.constructorParams.map((p) => ({
3501
3811
  name: p.name,
3502
3812
  type: p.type,
3503
- optional: false
3813
+ optional: false,
3814
+ passingStyle: "named"
3504
3815
  }));
3505
3816
  classes[cls.name] = {
3506
3817
  name: cls.name,
@@ -3595,6 +3906,9 @@ const kotlinExtractor = {
3595
3906
  return [`${modelName}Response`];
3596
3907
  }
3597
3908
  },
3909
+ async extractSnapshot(sdkPath) {
3910
+ return apiSurfaceToSnapshot(await this.extract(sdkPath));
3911
+ },
3598
3912
  async extract(sdkPath) {
3599
3913
  const ktFiles = walkKotlinFiles(sdkPath);
3600
3914
  if (ktFiles.length === 0) throw new ExtractorError(`No .kt files found in ${sdkPath}`, `Ensure the --sdk-path argument points to a Kotlin project root containing .kt source files.`);
@@ -3850,7 +4164,8 @@ function buildSurface$1(allClasses, allEnums, _hints) {
3850
4164
  const params = method.params.map((p) => ({
3851
4165
  name: p.name,
3852
4166
  type: p.type,
3853
- optional: p.optional
4167
+ optional: p.optional,
4168
+ passingStyle: "named"
3854
4169
  }));
3855
4170
  if (!methods[method.name]) methods[method.name] = [];
3856
4171
  methods[method.name].push({
@@ -3871,7 +4186,8 @@ function buildSurface$1(allClasses, allEnums, _hints) {
3871
4186
  const constructorParams = cls.constructorParams.map((p) => ({
3872
4187
  name: p.name,
3873
4188
  type: p.type,
3874
- optional: false
4189
+ optional: false,
4190
+ passingStyle: "named"
3875
4191
  }));
3876
4192
  classes[cls.name] = {
3877
4193
  name: cls.name,
@@ -4086,6 +4402,9 @@ function createDotnetExtractor(hintOverrides) {
4086
4402
  return {
4087
4403
  language: "dotnet",
4088
4404
  hints: mergedHints,
4405
+ async extractSnapshot(sdkPath) {
4406
+ return apiSurfaceToSnapshot(await this.extract(sdkPath));
4407
+ },
4089
4408
  async extract(sdkPath) {
4090
4409
  let sourceDir;
4091
4410
  const srcPath = resolve(sdkPath, "src");
@@ -4264,11 +4583,17 @@ function extractFunctions(modules, sourceFile) {
4264
4583
  const paramsStr = funcMatch[3] || "";
4265
4584
  if (name === "defstruct" || name === "defmodule") continue;
4266
4585
  const params = [];
4586
+ const paramStyles = [];
4267
4587
  if (paramsStr.trim()) {
4268
4588
  const paramParts = paramsStr.split(",");
4269
4589
  for (const part of paramParts) {
4270
- const paramNameMatch = part.trim().match(/^(\w+)/);
4271
- if (paramNameMatch) params.push(paramNameMatch[1]);
4590
+ const trimmed = part.trim();
4591
+ const paramNameMatch = trimmed.match(/^(\w+)/);
4592
+ if (paramNameMatch) {
4593
+ params.push(paramNameMatch[1]);
4594
+ const isKeywordList = /\\\\\s*\[/.test(trimmed);
4595
+ paramStyles.push(isKeywordList ? "keyword" : "positional");
4596
+ }
4272
4597
  }
4273
4598
  }
4274
4599
  functions.push({
@@ -4276,6 +4601,7 @@ function extractFunctions(modules, sourceFile) {
4276
4601
  name,
4277
4602
  arity: params.length,
4278
4603
  params,
4604
+ paramStyles,
4279
4605
  isPrivate,
4280
4606
  sourceFile
4281
4607
  });
@@ -4436,10 +4762,11 @@ function buildSurface(allStructs, allFunctions, allEnumModules, allTypeSpecs) {
4436
4762
  for (const func of funcs) {
4437
4763
  if (seenFuncs.has(func.name)) continue;
4438
4764
  seenFuncs.add(func.name);
4439
- const params = func.params.map((p) => ({
4765
+ const params = func.params.map((p, i) => ({
4440
4766
  name: p,
4441
4767
  type: "any",
4442
- optional: false
4768
+ optional: false,
4769
+ passingStyle: func.paramStyles[i] ?? "positional"
4443
4770
  }));
4444
4771
  if (!methods[func.name]) methods[func.name] = [];
4445
4772
  methods[func.name].push({
@@ -4535,6 +4862,9 @@ const elixirExtractor = {
4535
4862
  return [`${modelName}Response`];
4536
4863
  }
4537
4864
  },
4865
+ async extractSnapshot(sdkPath) {
4866
+ return apiSurfaceToSnapshot(await this.extract(sdkPath));
4867
+ },
4538
4868
  async extract(sdkPath) {
4539
4869
  let sourceDir;
4540
4870
  const libPath = resolve(sdkPath, "lib");
@@ -4571,6 +4901,6 @@ const elixirExtractor = {
4571
4901
  }
4572
4902
  };
4573
4903
  //#endregion
4574
- export { buildOverlayLookup, diffSurfaces, dotnetExtractor, elixirExtractor, filterSurface, getExtractor, goExtractor, kotlinExtractor, nodeExtractor, nodeHints, patchOverlay, phpExtractor, pythonExtractor, registerExtractor, resolveHints, rubyExtractor, rustExtractor, specDerivedEnumValues, specDerivedFieldPaths, specDerivedHttpKeys, specDerivedMethodPaths, specDerivedNames };
4904
+ export { ALL_LANGUAGE_IDS, COMPAT_SCHEMA_VERSION, apiSurfaceToSnapshot, applyApprovals, buildConceptualRollup, buildOverlayLookup, classifyAddedSymbol, classifySymbolChanges, defaultSeverityForCategory, diffSnapshots, diffSurfaces, dotnetExtractor, elixirExtractor, filterSurface, formatConceptualSummary, formatHumanSummary, generateConceptualReport, generateReport, getDefaultPolicy, getExtractor, goExtractor, highestSeverity, isCompatibleSchemaVersion, isPatchableChange, kotlinExtractor, matchApproval, mergePolicy, nodeExtractor, nodeHints, patchOverlay, phpExtractor, pythonExtractor, registerExtractor, resolveHints, rubyExtractor, rustExtractor, severityMeetsThreshold, specDerivedEnumValues, specDerivedFieldPaths, specDerivedHttpKeys, specDerivedMethodPaths, specDerivedNames, summarizeChanges, summarizeConceptualChanges, unapprovedChanges, validateApproval, validateApprovals, validateSnapshot };
4575
4905
 
4576
4906
  //# sourceMappingURL=index.mjs.map