@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.
- package/dist/cli/index.mjs +1123 -436
- package/dist/cli/index.mjs.map +1 -1
- package/dist/compat/index.d.mts +144 -57
- package/dist/compat/index.d.mts.map +1 -1
- package/dist/compat/index.mjs +362 -41
- package/dist/compat/index.mjs.map +1 -1
- package/dist/{diff-BYadQQV7.mjs → diff-BaH9qa8k.mjs} +11 -2
- package/dist/{diff-BYadQQV7.mjs.map → diff-BaH9qa8k.mjs.map} +1 -1
- package/dist/differ-CTl98yoS.d.mts +116 -0
- package/dist/differ-CTl98yoS.d.mts.map +1 -0
- package/dist/index.d.mts +3 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +50 -10
- package/dist/index.mjs.map +1 -1
- package/dist/{differ-CG5ZErKl.mjs → overlay-TYxHOCow.mjs} +1112 -553
- package/dist/overlay-TYxHOCow.mjs.map +1 -0
- package/dist/scripts/smoke/baseline.mjs +49 -9
- package/dist/scripts/smoke/baseline.mjs.map +1 -1
- package/dist/smoke.d.mts +10 -0
- package/dist/smoke.d.mts.map +1 -1
- package/dist/smoke.mjs +49 -9
- package/dist/smoke.mjs.map +1 -1
- package/dist/tree-sitter-C53w_0WE.mjs.map +1 -1
- package/dist/{types-DJMbCtIO.d.mts → types-mTtkgy_h.d.mts} +180 -2
- package/dist/types-mTtkgy_h.d.mts.map +1 -0
- package/dist/verify/index.d.mts +4 -3
- package/dist/verify/index.d.mts.map +1 -1
- package/dist/verify/index.mjs +12 -10
- package/dist/verify/index.mjs.map +1 -1
- package/docs/architecture/emitter-contract.md +2 -2
- package/docs/architecture/extractor-contract.md +6 -5
- package/docs/architecture/pipeline.md +4 -3
- package/docs/architecture/workflows.md +5 -3
- package/docs/cli.md +148 -6
- package/docs/contributor/public-api.md +1 -1
- package/docs/core/compatibility-ir.md +200 -0
- package/docs/core/compatibility-policy.md +170 -0
- package/docs/core/compatibility.md +83 -0
- package/docs/core/index.md +5 -0
- package/docs/core/manifest-schema.md +76 -0
- package/docs/core/quickstart.md +9 -2
- package/docs/core/verify-compat.md +235 -0
- package/package.json +1 -1
- package/skills/generate-emitter/SKILL.md +12 -7
- package/skills/generate-emitter/references/project-scaffold.md +27 -6
- package/skills/generate-extractor/SKILL.md +14 -8
- package/skills/generate-sdk/SKILL.md +2 -2
- package/skills/generate-smoke-test/SKILL.md +9 -5
- package/skills/review-operations/SKILL.md +2 -2
- package/skills/verify-compat/SKILL.md +1 -1
- package/dist/differ-CG5ZErKl.mjs.map +0 -1
- package/dist/types-DJMbCtIO.d.mts.map +0 -1
package/dist/compat/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as safeParse$1, v as ExtractorError } from "../tree-sitter-C53w_0WE.mjs";
|
|
2
|
-
import { _ as
|
|
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 =
|
|
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
|
|
231
|
-
type:
|
|
232
|
-
optional: isOptional
|
|
474
|
+
name,
|
|
475
|
+
type: typeStr,
|
|
476
|
+
optional: isOptional,
|
|
477
|
+
passingStyle
|
|
233
478
|
};
|
|
234
479
|
}
|
|
235
|
-
function
|
|
236
|
-
if (!decl || !ts.isParameter(decl)) return
|
|
237
|
-
|
|
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
|
|
241
|
-
|
|
242
|
-
|
|
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 === "*")
|
|
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
|
|
4280
|
-
|
|
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
|