@workos/oagen 0.7.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 (52) hide show
  1. package/dist/cli/index.mjs +1123 -436
  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 +362 -41
  6. package/dist/compat/index.mjs.map +1 -1
  7. package/dist/{diff-BYadQQV7.mjs → diff-BaH9qa8k.mjs} +11 -2
  8. package/dist/{diff-BYadQQV7.mjs.map → diff-BaH9qa8k.mjs.map} +1 -1
  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 +3 -1
  12. package/dist/index.d.mts.map +1 -1
  13. package/dist/index.mjs +50 -10
  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 +49 -9
  18. package/dist/scripts/smoke/baseline.mjs.map +1 -1
  19. package/dist/smoke.d.mts +10 -0
  20. package/dist/smoke.d.mts.map +1 -1
  21. package/dist/smoke.mjs +49 -9
  22. package/dist/smoke.mjs.map +1 -1
  23. package/dist/tree-sitter-C53w_0WE.mjs.map +1 -1
  24. package/dist/{types-DJMbCtIO.d.mts → types-mTtkgy_h.d.mts} +180 -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/differ-CG5ZErKl.mjs.map +0 -1
  52. package/dist/types-DJMbCtIO.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 = extractParameterName(param, 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,20 +449,57 @@ 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: extractParameterName(decl, sym.name),
231
- type: checker.typeToString(type),
232
- optional: isOptional
474
+ name,
475
+ type: typeStr,
476
+ optional: isOptional,
477
+ passingStyle
233
478
  };
234
479
  }
235
- function extractParameterName(decl, fallback) {
236
- if (!decl || !ts.isParameter(decl)) return fallback;
237
- if (ts.isIdentifier(decl.name)) return decl.name.text;
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
+ };
238
489
  const index = decl.parent.parameters?.indexOf(decl) ?? -1;
239
490
  const suffix = index > 0 ? String(index) : "";
240
- if (ts.isObjectBindingPattern(decl.name)) return `options${suffix}`;
241
- if (ts.isArrayBindingPattern(decl.name)) return `args${suffix}`;
242
- return fallback;
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
502
+ };
243
503
  }
244
504
  function extractInterface(sym, checker) {
245
505
  const type = checker.getDeclaredTypeOfSymbol(sym);
@@ -726,7 +986,8 @@ function buildSurface$6(allClasses, hints) {
726
986
  const params = method.params.map((p) => ({
727
987
  name: p.name,
728
988
  type: p.type,
729
- optional: p.optional
989
+ optional: p.optional,
990
+ passingStyle: "named"
730
991
  }));
731
992
  if (!apiMethods[method.name]) apiMethods[method.name] = [];
732
993
  apiMethods[method.name].push({
@@ -868,6 +1129,9 @@ function createPhpExtractor(hintOverrides) {
868
1129
  return {
869
1130
  language: "php",
870
1131
  hints: mergedHints,
1132
+ async extractSnapshot(sdkPath) {
1133
+ return apiSurfaceToSnapshot(await this.extract(sdkPath));
1134
+ },
871
1135
  async extract(sdkPath) {
872
1136
  let sourceDir;
873
1137
  const libPath = resolve(sdkPath, "lib");
@@ -1059,6 +1323,7 @@ function extractFunctionParams(paramsNode, skipFirst) {
1059
1323
  if (!paramsNode) return [];
1060
1324
  const params = [];
1061
1325
  let skippedFirst = false;
1326
+ let seenStarMarker = false;
1062
1327
  for (const child of paramsNode.namedChildren) {
1063
1328
  if (!skippedFirst && !skipFirst && child.type === "identifier" && (child.text === "self" || child.text === "cls")) {
1064
1329
  skippedFirst = true;
@@ -1068,13 +1333,18 @@ function extractFunctionParams(paramsNode, skipFirst) {
1068
1333
  skippedFirst = true;
1069
1334
  continue;
1070
1335
  }
1071
- if (child.text === "*") continue;
1336
+ if (child.text === "*") {
1337
+ seenStarMarker = true;
1338
+ continue;
1339
+ }
1072
1340
  if (child.type === "list_splat_pattern") {
1341
+ seenStarMarker = true;
1073
1342
  const nameNode = child.namedChildren.find((c) => c.type === "identifier");
1074
1343
  params.push({
1075
1344
  name: nameNode ? `*${nameNode.text}` : "*args",
1076
1345
  type: "",
1077
- optional: true
1346
+ optional: true,
1347
+ passingStyle: "positional"
1078
1348
  });
1079
1349
  continue;
1080
1350
  }
@@ -1083,17 +1353,20 @@ function extractFunctionParams(paramsNode, skipFirst) {
1083
1353
  params.push({
1084
1354
  name: nameNode ? `**${nameNode.text}` : "**kwargs",
1085
1355
  type: "",
1086
- optional: true
1356
+ optional: true,
1357
+ passingStyle: "keyword"
1087
1358
  });
1088
1359
  continue;
1089
1360
  }
1361
+ const passingStyle = seenStarMarker ? "keyword" : "keyword_or_positional";
1090
1362
  if (child.type === "typed_parameter") {
1091
1363
  const nameNode = child.namedChildren.find((c) => c.type === "identifier");
1092
1364
  const typeNode = child.childForFieldName("type");
1093
1365
  if (nameNode) params.push({
1094
1366
  name: nameNode.text,
1095
1367
  type: typeNode ? typeNode.text : "",
1096
- optional: false
1368
+ optional: false,
1369
+ passingStyle
1097
1370
  });
1098
1371
  } else if (child.type === "typed_default_parameter") {
1099
1372
  const nameNode = child.childForFieldName("name");
@@ -1101,19 +1374,22 @@ function extractFunctionParams(paramsNode, skipFirst) {
1101
1374
  if (nameNode) params.push({
1102
1375
  name: nameNode.text,
1103
1376
  type: typeNode ? typeNode.text : "",
1104
- optional: true
1377
+ optional: true,
1378
+ passingStyle
1105
1379
  });
1106
1380
  } else if (child.type === "default_parameter") {
1107
1381
  const nameNode = child.childForFieldName("name");
1108
1382
  if (nameNode) params.push({
1109
1383
  name: nameNode.text,
1110
1384
  type: "",
1111
- optional: true
1385
+ optional: true,
1386
+ passingStyle
1112
1387
  });
1113
1388
  } else if (child.type === "identifier") params.push({
1114
1389
  name: child.text,
1115
1390
  type: "",
1116
- optional: false
1391
+ optional: false,
1392
+ passingStyle
1117
1393
  });
1118
1394
  }
1119
1395
  return params;
@@ -1333,7 +1609,8 @@ function buildSurface$5(parsedFiles, hints) {
1333
1609
  const params = method.params.map((p) => ({
1334
1610
  name: p.name,
1335
1611
  type: p.type,
1336
- optional: p.optional
1612
+ optional: p.optional,
1613
+ ...p.passingStyle ? { passingStyle: p.passingStyle } : {}
1337
1614
  }));
1338
1615
  const returnType = unwrapReturnType(method.returnType);
1339
1616
  if (!apiMethods[method.name]) apiMethods[method.name] = [];
@@ -1394,7 +1671,8 @@ function buildSurface$5(parsedFiles, hints) {
1394
1671
  const params = method.params.map((p) => ({
1395
1672
  name: p.name,
1396
1673
  type: p.type,
1397
- optional: p.optional
1674
+ optional: p.optional,
1675
+ ...p.passingStyle ? { passingStyle: p.passingStyle } : {}
1398
1676
  }));
1399
1677
  if (!apiMethods[method.name]) apiMethods[method.name] = [];
1400
1678
  apiMethods[method.name].push({
@@ -1428,14 +1706,16 @@ function buildSurface$5(parsedFiles, hints) {
1428
1706
  constructorParams.push(...method.params.map((p) => ({
1429
1707
  name: p.name,
1430
1708
  type: p.type,
1431
- optional: p.optional
1709
+ optional: p.optional,
1710
+ ...p.passingStyle ? { passingStyle: p.passingStyle } : {}
1432
1711
  })));
1433
1712
  continue;
1434
1713
  }
1435
1714
  const params = method.params.map((p) => ({
1436
1715
  name: p.name,
1437
1716
  type: p.type,
1438
- optional: p.optional
1717
+ optional: p.optional,
1718
+ ...p.passingStyle ? { passingStyle: p.passingStyle } : {}
1439
1719
  }));
1440
1720
  if (!apiMethods[method.name]) apiMethods[method.name] = [];
1441
1721
  apiMethods[method.name].push({
@@ -1712,6 +1992,9 @@ function createPythonExtractor(hintOverrides) {
1712
1992
  return {
1713
1993
  language: "python",
1714
1994
  hints: mergedHints,
1995
+ async extractSnapshot(sdkPath) {
1996
+ return apiSurfaceToSnapshot(await this.extract(sdkPath));
1997
+ },
1715
1998
  async extract(sdkPath) {
1716
1999
  const sourceRoot = findPythonSourceRoot(sdkPath);
1717
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.`);
@@ -1908,7 +2191,8 @@ function extractMethodParams(methodNode) {
1908
2191
  params.push({
1909
2192
  name: paramNode.text,
1910
2193
  type: "Object",
1911
- optional: false
2194
+ optional: false,
2195
+ passingStyle: "positional"
1912
2196
  });
1913
2197
  break;
1914
2198
  case "optional_parameter": {
@@ -1916,7 +2200,8 @@ function extractMethodParams(methodNode) {
1916
2200
  if (nameNode) params.push({
1917
2201
  name: nameNode.text,
1918
2202
  type: "Object",
1919
- optional: true
2203
+ optional: true,
2204
+ passingStyle: "positional"
1920
2205
  });
1921
2206
  break;
1922
2207
  }
@@ -1927,7 +2212,8 @@ function extractMethodParams(methodNode) {
1927
2212
  params.push({
1928
2213
  name: nameNode.text,
1929
2214
  type: "Object",
1930
- optional: valueNode !== null
2215
+ optional: valueNode !== null,
2216
+ passingStyle: "keyword"
1931
2217
  });
1932
2218
  }
1933
2219
  break;
@@ -1937,7 +2223,8 @@ function extractMethodParams(methodNode) {
1937
2223
  params.push({
1938
2224
  name: nameNode ? `*${nameNode.text}` : "*args",
1939
2225
  type: "Object",
1940
- optional: true
2226
+ optional: true,
2227
+ passingStyle: "positional"
1941
2228
  });
1942
2229
  break;
1943
2230
  }
@@ -1946,7 +2233,8 @@ function extractMethodParams(methodNode) {
1946
2233
  params.push({
1947
2234
  name: nameNode ? `**${nameNode.text}` : "**kwargs",
1948
2235
  type: "Object",
1949
- optional: true
2236
+ optional: true,
2237
+ passingStyle: "keyword"
1950
2238
  });
1951
2239
  break;
1952
2240
  }
@@ -1955,7 +2243,8 @@ function extractMethodParams(methodNode) {
1955
2243
  params.push({
1956
2244
  name: nameNode ? `&${nameNode.text}` : "&block",
1957
2245
  type: "Object",
1958
- optional: true
2246
+ optional: true,
2247
+ passingStyle: "positional"
1959
2248
  });
1960
2249
  break;
1961
2250
  }
@@ -2095,6 +2384,9 @@ function collectRbFiles(dir) {
2095
2384
  const rubyExtractor = {
2096
2385
  language: "ruby",
2097
2386
  hints: rubyHints,
2387
+ async extractSnapshot(sdkPath) {
2388
+ return apiSurfaceToSnapshot(await this.extract(sdkPath));
2389
+ },
2098
2390
  async extract(sdkPath) {
2099
2391
  const libPath = resolve(sdkPath, "lib");
2100
2392
  try {
@@ -2526,7 +2818,8 @@ function buildSurface$4(allStructs, allTypes, allFuncs, allConsts) {
2526
2818
  const params = fn.params.filter((p) => p.type !== "context.Context").map((p) => ({
2527
2819
  name: p.name,
2528
2820
  type: goTypeToString(p.type),
2529
- optional: false
2821
+ optional: false,
2822
+ passingStyle: "positional"
2530
2823
  }));
2531
2824
  if (!apiMethods[fn.name]) apiMethods[fn.name] = [];
2532
2825
  apiMethods[fn.name].push({
@@ -2683,6 +2976,9 @@ const goExtractor = {
2683
2976
  return false;
2684
2977
  }
2685
2978
  },
2979
+ async extractSnapshot(sdkPath) {
2980
+ return apiSurfaceToSnapshot(await this.extract(sdkPath));
2981
+ },
2686
2982
  async extract(sdkPath) {
2687
2983
  const goFiles = walkGoFiles(sdkPath);
2688
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.`);
@@ -3014,7 +3310,8 @@ function buildSurface$3(allStructs, allEnums, allFuncs, allTraits, allTypeAliase
3014
3310
  const params = fn.params.map((p) => ({
3015
3311
  name: p.name,
3016
3312
  type: rustTypeToString(p.type),
3017
- optional: p.type.startsWith("Option<")
3313
+ optional: p.type.startsWith("Option<"),
3314
+ passingStyle: "positional"
3018
3315
  }));
3019
3316
  const returnType = unwrapResultType(fn.returnType);
3020
3317
  if (!apiMethods[fn.name]) apiMethods[fn.name] = [];
@@ -3171,6 +3468,9 @@ const rustExtractor = {
3171
3468
  return [`${modelName}Response`];
3172
3469
  }
3173
3470
  },
3471
+ async extractSnapshot(sdkPath) {
3472
+ return apiSurfaceToSnapshot(await this.extract(sdkPath));
3473
+ },
3174
3474
  async extract(sdkPath) {
3175
3475
  const rsFiles = walkRustFiles(sdkPath);
3176
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.`);
@@ -3496,7 +3796,8 @@ function buildSurface$2(allDataClasses, allClasses, allEnums, allTypeAliases) {
3496
3796
  const params = method.params.map((p) => ({
3497
3797
  name: p.name,
3498
3798
  type: p.type,
3499
- optional: p.optional
3799
+ optional: p.optional,
3800
+ passingStyle: "named"
3500
3801
  }));
3501
3802
  if (!methods[method.name]) methods[method.name] = [];
3502
3803
  methods[method.name].push({
@@ -3509,7 +3810,8 @@ function buildSurface$2(allDataClasses, allClasses, allEnums, allTypeAliases) {
3509
3810
  const constructorParams = cls.constructorParams.map((p) => ({
3510
3811
  name: p.name,
3511
3812
  type: p.type,
3512
- optional: false
3813
+ optional: false,
3814
+ passingStyle: "named"
3513
3815
  }));
3514
3816
  classes[cls.name] = {
3515
3817
  name: cls.name,
@@ -3604,6 +3906,9 @@ const kotlinExtractor = {
3604
3906
  return [`${modelName}Response`];
3605
3907
  }
3606
3908
  },
3909
+ async extractSnapshot(sdkPath) {
3910
+ return apiSurfaceToSnapshot(await this.extract(sdkPath));
3911
+ },
3607
3912
  async extract(sdkPath) {
3608
3913
  const ktFiles = walkKotlinFiles(sdkPath);
3609
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.`);
@@ -3859,7 +4164,8 @@ function buildSurface$1(allClasses, allEnums, _hints) {
3859
4164
  const params = method.params.map((p) => ({
3860
4165
  name: p.name,
3861
4166
  type: p.type,
3862
- optional: p.optional
4167
+ optional: p.optional,
4168
+ passingStyle: "named"
3863
4169
  }));
3864
4170
  if (!methods[method.name]) methods[method.name] = [];
3865
4171
  methods[method.name].push({
@@ -3880,7 +4186,8 @@ function buildSurface$1(allClasses, allEnums, _hints) {
3880
4186
  const constructorParams = cls.constructorParams.map((p) => ({
3881
4187
  name: p.name,
3882
4188
  type: p.type,
3883
- optional: false
4189
+ optional: false,
4190
+ passingStyle: "named"
3884
4191
  }));
3885
4192
  classes[cls.name] = {
3886
4193
  name: cls.name,
@@ -4095,6 +4402,9 @@ function createDotnetExtractor(hintOverrides) {
4095
4402
  return {
4096
4403
  language: "dotnet",
4097
4404
  hints: mergedHints,
4405
+ async extractSnapshot(sdkPath) {
4406
+ return apiSurfaceToSnapshot(await this.extract(sdkPath));
4407
+ },
4098
4408
  async extract(sdkPath) {
4099
4409
  let sourceDir;
4100
4410
  const srcPath = resolve(sdkPath, "src");
@@ -4273,11 +4583,17 @@ function extractFunctions(modules, sourceFile) {
4273
4583
  const paramsStr = funcMatch[3] || "";
4274
4584
  if (name === "defstruct" || name === "defmodule") continue;
4275
4585
  const params = [];
4586
+ const paramStyles = [];
4276
4587
  if (paramsStr.trim()) {
4277
4588
  const paramParts = paramsStr.split(",");
4278
4589
  for (const part of paramParts) {
4279
- const paramNameMatch = part.trim().match(/^(\w+)/);
4280
- 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
+ }
4281
4597
  }
4282
4598
  }
4283
4599
  functions.push({
@@ -4285,6 +4601,7 @@ function extractFunctions(modules, sourceFile) {
4285
4601
  name,
4286
4602
  arity: params.length,
4287
4603
  params,
4604
+ paramStyles,
4288
4605
  isPrivate,
4289
4606
  sourceFile
4290
4607
  });
@@ -4445,10 +4762,11 @@ function buildSurface(allStructs, allFunctions, allEnumModules, allTypeSpecs) {
4445
4762
  for (const func of funcs) {
4446
4763
  if (seenFuncs.has(func.name)) continue;
4447
4764
  seenFuncs.add(func.name);
4448
- const params = func.params.map((p) => ({
4765
+ const params = func.params.map((p, i) => ({
4449
4766
  name: p,
4450
4767
  type: "any",
4451
- optional: false
4768
+ optional: false,
4769
+ passingStyle: func.paramStyles[i] ?? "positional"
4452
4770
  }));
4453
4771
  if (!methods[func.name]) methods[func.name] = [];
4454
4772
  methods[func.name].push({
@@ -4544,6 +4862,9 @@ const elixirExtractor = {
4544
4862
  return [`${modelName}Response`];
4545
4863
  }
4546
4864
  },
4865
+ async extractSnapshot(sdkPath) {
4866
+ return apiSurfaceToSnapshot(await this.extract(sdkPath));
4867
+ },
4547
4868
  async extract(sdkPath) {
4548
4869
  let sourceDir;
4549
4870
  const libPath = resolve(sdkPath, "lib");
@@ -4580,6 +4901,6 @@ const elixirExtractor = {
4580
4901
  }
4581
4902
  };
4582
4903
  //#endregion
4583
- 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 };
4584
4905
 
4585
4906
  //# sourceMappingURL=index.mjs.map