@workos/oagen 0.9.0 → 0.9.1
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 +346 -265
- package/dist/cli/index.mjs.map +1 -1
- package/docs/agents/architecture.md +1 -1
- package/docs/architecture/emitter-contract.md +3 -3
- package/docs/architecture/pipeline.md +1 -1
- package/docs/cli.md +10 -11
- package/package.json +1 -1
- package/skills/generate-emitter/SKILL.md +8 -11
- package/skills/generate-smoke-test/SKILL.md +6 -6
- package/skills/integrate/SKILL.md +1 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -6313,12 +6313,351 @@ async function extractCommand(opts) {
|
|
|
6313
6313
|
console.log(`Extracted ${symbolCount} symbols → ${opts.output}`);
|
|
6314
6314
|
}
|
|
6315
6315
|
//#endregion
|
|
6316
|
+
//#region src/compat/policy.ts
|
|
6317
|
+
/** Built-in language defaults. */
|
|
6318
|
+
const LANGUAGE_DEFAULTS = {
|
|
6319
|
+
php: {
|
|
6320
|
+
callerUsesParamNames: true,
|
|
6321
|
+
constructorOrderMatters: true,
|
|
6322
|
+
methodParameterNamesArePublicApi: true,
|
|
6323
|
+
constructorParameterNamesArePublicApi: true,
|
|
6324
|
+
overloadsArePublicApi: false,
|
|
6325
|
+
arityIsPublicApi: false
|
|
6326
|
+
},
|
|
6327
|
+
python: {
|
|
6328
|
+
callerUsesParamNames: true,
|
|
6329
|
+
constructorOrderMatters: true,
|
|
6330
|
+
methodParameterNamesArePublicApi: true,
|
|
6331
|
+
constructorParameterNamesArePublicApi: true,
|
|
6332
|
+
overloadsArePublicApi: false,
|
|
6333
|
+
arityIsPublicApi: false
|
|
6334
|
+
},
|
|
6335
|
+
ruby: {
|
|
6336
|
+
callerUsesParamNames: true,
|
|
6337
|
+
constructorOrderMatters: true,
|
|
6338
|
+
methodParameterNamesArePublicApi: true,
|
|
6339
|
+
constructorParameterNamesArePublicApi: true,
|
|
6340
|
+
overloadsArePublicApi: false,
|
|
6341
|
+
arityIsPublicApi: false
|
|
6342
|
+
},
|
|
6343
|
+
go: {
|
|
6344
|
+
callerUsesParamNames: false,
|
|
6345
|
+
constructorOrderMatters: true,
|
|
6346
|
+
methodParameterNamesArePublicApi: false,
|
|
6347
|
+
constructorParameterNamesArePublicApi: false,
|
|
6348
|
+
overloadsArePublicApi: false,
|
|
6349
|
+
arityIsPublicApi: true
|
|
6350
|
+
},
|
|
6351
|
+
kotlin: {
|
|
6352
|
+
callerUsesParamNames: true,
|
|
6353
|
+
constructorOrderMatters: false,
|
|
6354
|
+
methodParameterNamesArePublicApi: true,
|
|
6355
|
+
constructorParameterNamesArePublicApi: true,
|
|
6356
|
+
overloadsArePublicApi: true,
|
|
6357
|
+
arityIsPublicApi: false
|
|
6358
|
+
},
|
|
6359
|
+
dotnet: {
|
|
6360
|
+
callerUsesParamNames: true,
|
|
6361
|
+
constructorOrderMatters: false,
|
|
6362
|
+
methodParameterNamesArePublicApi: true,
|
|
6363
|
+
constructorParameterNamesArePublicApi: true,
|
|
6364
|
+
overloadsArePublicApi: true,
|
|
6365
|
+
arityIsPublicApi: false
|
|
6366
|
+
},
|
|
6367
|
+
elixir: {
|
|
6368
|
+
callerUsesParamNames: true,
|
|
6369
|
+
constructorOrderMatters: false,
|
|
6370
|
+
methodParameterNamesArePublicApi: true,
|
|
6371
|
+
constructorParameterNamesArePublicApi: false,
|
|
6372
|
+
overloadsArePublicApi: false,
|
|
6373
|
+
arityIsPublicApi: true
|
|
6374
|
+
},
|
|
6375
|
+
rust: {
|
|
6376
|
+
callerUsesParamNames: false,
|
|
6377
|
+
constructorOrderMatters: true,
|
|
6378
|
+
methodParameterNamesArePublicApi: false,
|
|
6379
|
+
constructorParameterNamesArePublicApi: false,
|
|
6380
|
+
overloadsArePublicApi: false,
|
|
6381
|
+
arityIsPublicApi: true
|
|
6382
|
+
},
|
|
6383
|
+
node: {
|
|
6384
|
+
callerUsesParamNames: false,
|
|
6385
|
+
constructorOrderMatters: false,
|
|
6386
|
+
methodParameterNamesArePublicApi: false,
|
|
6387
|
+
constructorParameterNamesArePublicApi: false,
|
|
6388
|
+
overloadsArePublicApi: false,
|
|
6389
|
+
arityIsPublicApi: false
|
|
6390
|
+
}
|
|
6391
|
+
};
|
|
6392
|
+
/** Get the built-in default policy for a language. */
|
|
6393
|
+
function getDefaultPolicy(language) {
|
|
6394
|
+
return { ...LANGUAGE_DEFAULTS[language] };
|
|
6395
|
+
}
|
|
6396
|
+
/** Merge user overrides onto language defaults. Only provided keys are overridden. */
|
|
6397
|
+
function mergePolicy(defaults, overrides) {
|
|
6398
|
+
return {
|
|
6399
|
+
...defaults,
|
|
6400
|
+
...overrides
|
|
6401
|
+
};
|
|
6402
|
+
}
|
|
6403
|
+
//#endregion
|
|
6404
|
+
//#region src/compat/schema.ts
|
|
6405
|
+
/** Validate the basic structure of a parsed compat snapshot. */
|
|
6406
|
+
function validateSnapshot(data) {
|
|
6407
|
+
if (typeof data !== "object" || data === null) return false;
|
|
6408
|
+
const obj = data;
|
|
6409
|
+
return typeof obj.schemaVersion === "string" && typeof obj.source === "object" && obj.source !== null && typeof obj.policies === "object" && obj.policies !== null && Array.isArray(obj.symbols);
|
|
6410
|
+
}
|
|
6411
|
+
//#endregion
|
|
6412
|
+
//#region src/compat/ir.ts
|
|
6413
|
+
/**
|
|
6414
|
+
* Convert a legacy ApiSurface to a CompatSnapshot.
|
|
6415
|
+
*
|
|
6416
|
+
* This bridge allows existing extractors (which produce ApiSurface) to feed
|
|
6417
|
+
* into the new classified diff engine without requiring immediate migration.
|
|
6418
|
+
*/
|
|
6419
|
+
function apiSurfaceToSnapshot(surface) {
|
|
6420
|
+
const language = surface.language || "node";
|
|
6421
|
+
const symbols = [];
|
|
6422
|
+
for (const [className, cls] of Object.entries(surface.classes)) {
|
|
6423
|
+
symbols.push({
|
|
6424
|
+
id: `class:${className}`,
|
|
6425
|
+
kind: "service_accessor",
|
|
6426
|
+
fqName: className,
|
|
6427
|
+
displayName: className,
|
|
6428
|
+
visibility: "public",
|
|
6429
|
+
stability: cls.deprecationMessage ? "deprecated" : "stable",
|
|
6430
|
+
sourceKind: "generated_service_wrapper"
|
|
6431
|
+
});
|
|
6432
|
+
if (cls.constructorParams.length > 0) symbols.push({
|
|
6433
|
+
id: `ctor:${className}`,
|
|
6434
|
+
kind: "constructor",
|
|
6435
|
+
fqName: `${className}.constructor`,
|
|
6436
|
+
ownerFqName: className,
|
|
6437
|
+
displayName: `new ${className}`,
|
|
6438
|
+
visibility: "public",
|
|
6439
|
+
stability: "stable",
|
|
6440
|
+
sourceKind: "generated_resource_constructor",
|
|
6441
|
+
parameters: cls.constructorParams.map((p, i) => apiParamToCompatParam(p, i, language))
|
|
6442
|
+
});
|
|
6443
|
+
for (const [methodName, overloads] of Object.entries(cls.methods)) for (let oi = 0; oi < overloads.length; oi++) {
|
|
6444
|
+
const method = overloads[oi];
|
|
6445
|
+
const suffix = overloads.length > 1 ? `#${oi}` : "";
|
|
6446
|
+
symbols.push({
|
|
6447
|
+
id: `method:${className}.${methodName}${suffix}`,
|
|
6448
|
+
kind: "callable",
|
|
6449
|
+
fqName: `${className}.${methodName}`,
|
|
6450
|
+
ownerFqName: className,
|
|
6451
|
+
displayName: `${className}.${methodName}`,
|
|
6452
|
+
visibility: "public",
|
|
6453
|
+
stability: "stable",
|
|
6454
|
+
sourceKind: "generated_service_wrapper",
|
|
6455
|
+
parameters: method.params.map((p, i) => apiParamToCompatParam(p, i, language)),
|
|
6456
|
+
returns: { name: method.returnType }
|
|
6457
|
+
});
|
|
6458
|
+
}
|
|
6459
|
+
for (const [propName, prop] of Object.entries(cls.properties)) symbols.push({
|
|
6460
|
+
id: `prop:${className}.${propName}`,
|
|
6461
|
+
kind: "property",
|
|
6462
|
+
fqName: `${className}.${propName}`,
|
|
6463
|
+
ownerFqName: className,
|
|
6464
|
+
displayName: `${className}.${propName}`,
|
|
6465
|
+
visibility: "public",
|
|
6466
|
+
stability: "stable",
|
|
6467
|
+
sourceKind: "generated_service_wrapper",
|
|
6468
|
+
typeRef: { name: prop.type }
|
|
6469
|
+
});
|
|
6470
|
+
}
|
|
6471
|
+
for (const [ifaceName, iface] of Object.entries(surface.interfaces)) {
|
|
6472
|
+
symbols.push({
|
|
6473
|
+
id: `iface:${ifaceName}`,
|
|
6474
|
+
kind: "alias",
|
|
6475
|
+
fqName: ifaceName,
|
|
6476
|
+
displayName: ifaceName,
|
|
6477
|
+
visibility: "public",
|
|
6478
|
+
stability: "stable",
|
|
6479
|
+
sourceKind: "generated_resource_constructor"
|
|
6480
|
+
});
|
|
6481
|
+
for (const [fieldName, field] of Object.entries(iface.fields)) symbols.push({
|
|
6482
|
+
id: `field:${ifaceName}.${fieldName}`,
|
|
6483
|
+
kind: "field",
|
|
6484
|
+
fqName: `${ifaceName}.${fieldName}`,
|
|
6485
|
+
ownerFqName: ifaceName,
|
|
6486
|
+
displayName: `${ifaceName}.${fieldName}`,
|
|
6487
|
+
visibility: "public",
|
|
6488
|
+
stability: "stable",
|
|
6489
|
+
sourceKind: "generated_resource_constructor",
|
|
6490
|
+
typeRef: { name: field.type }
|
|
6491
|
+
});
|
|
6492
|
+
if (iface.hasCustomConstructor) symbols.push({
|
|
6493
|
+
id: `ctor:${ifaceName}`,
|
|
6494
|
+
kind: "constructor",
|
|
6495
|
+
fqName: `${ifaceName}.constructor`,
|
|
6496
|
+
ownerFqName: ifaceName,
|
|
6497
|
+
displayName: `new ${ifaceName}`,
|
|
6498
|
+
visibility: "public",
|
|
6499
|
+
stability: "stable",
|
|
6500
|
+
sourceKind: "generated_resource_constructor"
|
|
6501
|
+
});
|
|
6502
|
+
}
|
|
6503
|
+
for (const [aliasName] of Object.entries(surface.typeAliases)) symbols.push({
|
|
6504
|
+
id: `alias:${aliasName}`,
|
|
6505
|
+
kind: "alias",
|
|
6506
|
+
fqName: aliasName,
|
|
6507
|
+
displayName: aliasName,
|
|
6508
|
+
visibility: "public",
|
|
6509
|
+
stability: "stable",
|
|
6510
|
+
sourceKind: "generated_resource_constructor"
|
|
6511
|
+
});
|
|
6512
|
+
for (const [enumName, enumDef] of Object.entries(surface.enums)) {
|
|
6513
|
+
symbols.push({
|
|
6514
|
+
id: `enum:${enumName}`,
|
|
6515
|
+
kind: "enum",
|
|
6516
|
+
fqName: enumName,
|
|
6517
|
+
displayName: enumName,
|
|
6518
|
+
visibility: "public",
|
|
6519
|
+
stability: "stable",
|
|
6520
|
+
sourceKind: "generated_enum"
|
|
6521
|
+
});
|
|
6522
|
+
for (const [memberName, memberValue] of Object.entries(enumDef.members)) symbols.push({
|
|
6523
|
+
id: `enum_member:${enumName}.${memberName}`,
|
|
6524
|
+
kind: "enum_member",
|
|
6525
|
+
fqName: `${enumName}.${memberName}`,
|
|
6526
|
+
ownerFqName: enumName,
|
|
6527
|
+
displayName: `${enumName}.${memberName}`,
|
|
6528
|
+
visibility: "public",
|
|
6529
|
+
stability: "stable",
|
|
6530
|
+
sourceKind: "generated_enum",
|
|
6531
|
+
value: memberValue
|
|
6532
|
+
});
|
|
6533
|
+
}
|
|
6534
|
+
return {
|
|
6535
|
+
schemaVersion: "1",
|
|
6536
|
+
source: { extractedAt: surface.extractedAt },
|
|
6537
|
+
policies: getDefaultPolicy(language),
|
|
6538
|
+
symbols
|
|
6539
|
+
};
|
|
6540
|
+
}
|
|
6541
|
+
/** Convert a legacy ApiParam to a CompatParameter. */
|
|
6542
|
+
function apiParamToCompatParam(param, position, language) {
|
|
6543
|
+
const policy = getDefaultPolicy(language);
|
|
6544
|
+
return {
|
|
6545
|
+
publicName: param.name,
|
|
6546
|
+
position,
|
|
6547
|
+
required: !param.optional,
|
|
6548
|
+
nullable: false,
|
|
6549
|
+
hasDefault: param.optional,
|
|
6550
|
+
passing: param.passingStyle ?? inferPassingStyle(language),
|
|
6551
|
+
type: { name: param.type },
|
|
6552
|
+
sensitivity: {
|
|
6553
|
+
order: policy.constructorOrderMatters,
|
|
6554
|
+
publicName: policy.methodParameterNamesArePublicApi,
|
|
6555
|
+
requiredness: true,
|
|
6556
|
+
type: true
|
|
6557
|
+
}
|
|
6558
|
+
};
|
|
6559
|
+
}
|
|
6560
|
+
/** Infer the default parameter passing style for a language. */
|
|
6561
|
+
function inferPassingStyle(language) {
|
|
6562
|
+
switch (language) {
|
|
6563
|
+
case "python":
|
|
6564
|
+
case "ruby":
|
|
6565
|
+
case "elixir": return "keyword";
|
|
6566
|
+
case "php":
|
|
6567
|
+
case "kotlin":
|
|
6568
|
+
case "dotnet": return "named";
|
|
6569
|
+
case "node": return "options_object";
|
|
6570
|
+
default: return "positional";
|
|
6571
|
+
}
|
|
6572
|
+
}
|
|
6573
|
+
//#endregion
|
|
6316
6574
|
//#region src/cli/compat-extract.ts
|
|
6317
6575
|
const SNAPSHOT_FILENAME = ".oagen-compat-snapshot.json";
|
|
6576
|
+
/** Normalize a name for case-insensitive matching: strip underscores and lowercase. */
|
|
6577
|
+
function normalize(name) {
|
|
6578
|
+
return name.replace(/_/g, "").toLowerCase();
|
|
6579
|
+
}
|
|
6580
|
+
/**
|
|
6581
|
+
* Build a map of normalized service name → Set of generated method names
|
|
6582
|
+
* from the manifest's operations record.
|
|
6583
|
+
*/
|
|
6584
|
+
function buildServiceMethodMap(operations) {
|
|
6585
|
+
const result = /* @__PURE__ */ new Map();
|
|
6586
|
+
for (const value of Object.values(operations)) {
|
|
6587
|
+
if (typeof value !== "object" || value === null) continue;
|
|
6588
|
+
const { sdkMethod, service } = value;
|
|
6589
|
+
if (typeof sdkMethod !== "string" || typeof service !== "string") continue;
|
|
6590
|
+
const key = normalize(service);
|
|
6591
|
+
if (!result.has(key)) result.set(key, /* @__PURE__ */ new Set());
|
|
6592
|
+
result.get(key).add(sdkMethod);
|
|
6593
|
+
}
|
|
6594
|
+
return result;
|
|
6595
|
+
}
|
|
6596
|
+
/**
|
|
6597
|
+
* Filter an ApiSurface to only include generated symbols based on the manifest.
|
|
6598
|
+
*
|
|
6599
|
+
* Two levels of filtering:
|
|
6600
|
+
* 1. File-level: classes/interfaces/enums from files not in the manifest are
|
|
6601
|
+
* dropped entirely (they are wholly hand-written).
|
|
6602
|
+
* 2. Method-level: for service classes (those whose name matches a manifest
|
|
6603
|
+
* service), only methods listed in the manifest operations are kept.
|
|
6604
|
+
* Constructors and properties are always preserved since they are structural.
|
|
6605
|
+
* Non-service classes (models, enums, etc.) keep all their symbols.
|
|
6606
|
+
*/
|
|
6607
|
+
function filterSurfaceByManifest(surface, manifest) {
|
|
6608
|
+
const manifestFiles = new Set(manifest.files);
|
|
6609
|
+
const serviceMethodMap = manifest.operations ? buildServiceMethodMap(manifest.operations) : /* @__PURE__ */ new Map();
|
|
6610
|
+
let filesExcluded = 0;
|
|
6611
|
+
let methodsExcluded = 0;
|
|
6612
|
+
const filterByFile = (record) => {
|
|
6613
|
+
const result = {};
|
|
6614
|
+
for (const [name, entry] of Object.entries(record)) if (entry.sourceFile && manifestFiles.has(entry.sourceFile)) result[name] = entry;
|
|
6615
|
+
else filesExcluded++;
|
|
6616
|
+
return result;
|
|
6617
|
+
};
|
|
6618
|
+
const filteredClasses = {};
|
|
6619
|
+
for (const [name, cls] of Object.entries(surface.classes)) {
|
|
6620
|
+
if (cls.sourceFile && !manifestFiles.has(cls.sourceFile)) {
|
|
6621
|
+
filesExcluded++;
|
|
6622
|
+
continue;
|
|
6623
|
+
}
|
|
6624
|
+
const generatedMethods = serviceMethodMap.get(normalize(name));
|
|
6625
|
+
if (generatedMethods) {
|
|
6626
|
+
const methods = {};
|
|
6627
|
+
for (const [methodName, overloads] of Object.entries(cls.methods)) if (generatedMethods.has(methodName)) methods[methodName] = overloads;
|
|
6628
|
+
else methodsExcluded++;
|
|
6629
|
+
filteredClasses[name] = {
|
|
6630
|
+
...cls,
|
|
6631
|
+
methods
|
|
6632
|
+
};
|
|
6633
|
+
} else filteredClasses[name] = cls;
|
|
6634
|
+
}
|
|
6635
|
+
const filteredExports = {};
|
|
6636
|
+
for (const [filePath, symbols] of Object.entries(surface.exports)) if (manifestFiles.has(filePath)) filteredExports[filePath] = symbols;
|
|
6637
|
+
return {
|
|
6638
|
+
filtered: {
|
|
6639
|
+
...surface,
|
|
6640
|
+
classes: filteredClasses,
|
|
6641
|
+
interfaces: filterByFile(surface.interfaces),
|
|
6642
|
+
typeAliases: filterByFile(surface.typeAliases),
|
|
6643
|
+
enums: filterByFile(surface.enums),
|
|
6644
|
+
exports: filteredExports
|
|
6645
|
+
},
|
|
6646
|
+
filesExcluded,
|
|
6647
|
+
methodsExcluded
|
|
6648
|
+
};
|
|
6649
|
+
}
|
|
6318
6650
|
async function compatExtractCommand(opts) {
|
|
6319
6651
|
const extractor = getExtractor(opts.lang);
|
|
6320
6652
|
console.log(`Extracting ${opts.lang} compat snapshot from ${opts.sdkPath}...`);
|
|
6321
|
-
|
|
6653
|
+
let surface = await extractor.extract(opts.sdkPath);
|
|
6654
|
+
const manifest = await readManifest(opts.sdkPath);
|
|
6655
|
+
if (manifest) {
|
|
6656
|
+
const { filtered, filesExcluded, methodsExcluded } = filterSurfaceByManifest(surface, manifest);
|
|
6657
|
+
surface = filtered;
|
|
6658
|
+
if (filesExcluded > 0 || methodsExcluded > 0) console.log(`Manifest filter: excluded ${filesExcluded} hand-written file(s), ${methodsExcluded} hand-written method(s)`);
|
|
6659
|
+
}
|
|
6660
|
+
const snapshot = apiSurfaceToSnapshot(surface);
|
|
6322
6661
|
if (opts.spec) {
|
|
6323
6662
|
const specContent = readFileSync(opts.spec, "utf-8");
|
|
6324
6663
|
snapshot.source.specSha = createHash("sha256").update(specContent).digest("hex");
|
|
@@ -6349,20 +6688,12 @@ function enrichWithSpecContext(snapshot, spec) {
|
|
|
6349
6688
|
const match = opLookup.get(sym.fqName);
|
|
6350
6689
|
if (match) {
|
|
6351
6690
|
sym.operationId = match.operationId;
|
|
6352
|
-
sym.route = {
|
|
6353
|
-
method: match.method,
|
|
6354
|
-
path: match.path
|
|
6355
|
-
};
|
|
6356
|
-
}
|
|
6357
|
-
}
|
|
6358
|
-
}
|
|
6359
|
-
//#endregion
|
|
6360
|
-
//#region src/compat/schema.ts
|
|
6361
|
-
/** Validate the basic structure of a parsed compat snapshot. */
|
|
6362
|
-
function validateSnapshot(data) {
|
|
6363
|
-
if (typeof data !== "object" || data === null) return false;
|
|
6364
|
-
const obj = data;
|
|
6365
|
-
return typeof obj.schemaVersion === "string" && typeof obj.source === "object" && obj.source !== null && typeof obj.policies === "object" && obj.policies !== null && Array.isArray(obj.symbols);
|
|
6691
|
+
sym.route = {
|
|
6692
|
+
method: match.method,
|
|
6693
|
+
path: match.path
|
|
6694
|
+
};
|
|
6695
|
+
}
|
|
6696
|
+
}
|
|
6366
6697
|
}
|
|
6367
6698
|
//#endregion
|
|
6368
6699
|
//#region src/compat/config.ts
|
|
@@ -7150,256 +7481,6 @@ function narrowingMatches(match, change) {
|
|
|
7150
7481
|
return true;
|
|
7151
7482
|
}
|
|
7152
7483
|
//#endregion
|
|
7153
|
-
//#region src/compat/policy.ts
|
|
7154
|
-
/** Built-in language defaults. */
|
|
7155
|
-
const LANGUAGE_DEFAULTS = {
|
|
7156
|
-
php: {
|
|
7157
|
-
callerUsesParamNames: true,
|
|
7158
|
-
constructorOrderMatters: true,
|
|
7159
|
-
methodParameterNamesArePublicApi: true,
|
|
7160
|
-
constructorParameterNamesArePublicApi: true,
|
|
7161
|
-
overloadsArePublicApi: false,
|
|
7162
|
-
arityIsPublicApi: false
|
|
7163
|
-
},
|
|
7164
|
-
python: {
|
|
7165
|
-
callerUsesParamNames: true,
|
|
7166
|
-
constructorOrderMatters: true,
|
|
7167
|
-
methodParameterNamesArePublicApi: true,
|
|
7168
|
-
constructorParameterNamesArePublicApi: true,
|
|
7169
|
-
overloadsArePublicApi: false,
|
|
7170
|
-
arityIsPublicApi: false
|
|
7171
|
-
},
|
|
7172
|
-
ruby: {
|
|
7173
|
-
callerUsesParamNames: true,
|
|
7174
|
-
constructorOrderMatters: true,
|
|
7175
|
-
methodParameterNamesArePublicApi: true,
|
|
7176
|
-
constructorParameterNamesArePublicApi: true,
|
|
7177
|
-
overloadsArePublicApi: false,
|
|
7178
|
-
arityIsPublicApi: false
|
|
7179
|
-
},
|
|
7180
|
-
go: {
|
|
7181
|
-
callerUsesParamNames: false,
|
|
7182
|
-
constructorOrderMatters: true,
|
|
7183
|
-
methodParameterNamesArePublicApi: false,
|
|
7184
|
-
constructorParameterNamesArePublicApi: false,
|
|
7185
|
-
overloadsArePublicApi: false,
|
|
7186
|
-
arityIsPublicApi: true
|
|
7187
|
-
},
|
|
7188
|
-
kotlin: {
|
|
7189
|
-
callerUsesParamNames: true,
|
|
7190
|
-
constructorOrderMatters: false,
|
|
7191
|
-
methodParameterNamesArePublicApi: true,
|
|
7192
|
-
constructorParameterNamesArePublicApi: true,
|
|
7193
|
-
overloadsArePublicApi: true,
|
|
7194
|
-
arityIsPublicApi: false
|
|
7195
|
-
},
|
|
7196
|
-
dotnet: {
|
|
7197
|
-
callerUsesParamNames: true,
|
|
7198
|
-
constructorOrderMatters: false,
|
|
7199
|
-
methodParameterNamesArePublicApi: true,
|
|
7200
|
-
constructorParameterNamesArePublicApi: true,
|
|
7201
|
-
overloadsArePublicApi: true,
|
|
7202
|
-
arityIsPublicApi: false
|
|
7203
|
-
},
|
|
7204
|
-
elixir: {
|
|
7205
|
-
callerUsesParamNames: true,
|
|
7206
|
-
constructorOrderMatters: false,
|
|
7207
|
-
methodParameterNamesArePublicApi: true,
|
|
7208
|
-
constructorParameterNamesArePublicApi: false,
|
|
7209
|
-
overloadsArePublicApi: false,
|
|
7210
|
-
arityIsPublicApi: true
|
|
7211
|
-
},
|
|
7212
|
-
rust: {
|
|
7213
|
-
callerUsesParamNames: false,
|
|
7214
|
-
constructorOrderMatters: true,
|
|
7215
|
-
methodParameterNamesArePublicApi: false,
|
|
7216
|
-
constructorParameterNamesArePublicApi: false,
|
|
7217
|
-
overloadsArePublicApi: false,
|
|
7218
|
-
arityIsPublicApi: true
|
|
7219
|
-
},
|
|
7220
|
-
node: {
|
|
7221
|
-
callerUsesParamNames: false,
|
|
7222
|
-
constructorOrderMatters: false,
|
|
7223
|
-
methodParameterNamesArePublicApi: false,
|
|
7224
|
-
constructorParameterNamesArePublicApi: false,
|
|
7225
|
-
overloadsArePublicApi: false,
|
|
7226
|
-
arityIsPublicApi: false
|
|
7227
|
-
}
|
|
7228
|
-
};
|
|
7229
|
-
/** Get the built-in default policy for a language. */
|
|
7230
|
-
function getDefaultPolicy(language) {
|
|
7231
|
-
return { ...LANGUAGE_DEFAULTS[language] };
|
|
7232
|
-
}
|
|
7233
|
-
/** Merge user overrides onto language defaults. Only provided keys are overridden. */
|
|
7234
|
-
function mergePolicy(defaults, overrides) {
|
|
7235
|
-
return {
|
|
7236
|
-
...defaults,
|
|
7237
|
-
...overrides
|
|
7238
|
-
};
|
|
7239
|
-
}
|
|
7240
|
-
//#endregion
|
|
7241
|
-
//#region src/compat/ir.ts
|
|
7242
|
-
/**
|
|
7243
|
-
* Convert a legacy ApiSurface to a CompatSnapshot.
|
|
7244
|
-
*
|
|
7245
|
-
* This bridge allows existing extractors (which produce ApiSurface) to feed
|
|
7246
|
-
* into the new classified diff engine without requiring immediate migration.
|
|
7247
|
-
*/
|
|
7248
|
-
function apiSurfaceToSnapshot(surface) {
|
|
7249
|
-
const language = surface.language || "node";
|
|
7250
|
-
const symbols = [];
|
|
7251
|
-
for (const [className, cls] of Object.entries(surface.classes)) {
|
|
7252
|
-
symbols.push({
|
|
7253
|
-
id: `class:${className}`,
|
|
7254
|
-
kind: "service_accessor",
|
|
7255
|
-
fqName: className,
|
|
7256
|
-
displayName: className,
|
|
7257
|
-
visibility: "public",
|
|
7258
|
-
stability: cls.deprecationMessage ? "deprecated" : "stable",
|
|
7259
|
-
sourceKind: "generated_service_wrapper"
|
|
7260
|
-
});
|
|
7261
|
-
if (cls.constructorParams.length > 0) symbols.push({
|
|
7262
|
-
id: `ctor:${className}`,
|
|
7263
|
-
kind: "constructor",
|
|
7264
|
-
fqName: `${className}.constructor`,
|
|
7265
|
-
ownerFqName: className,
|
|
7266
|
-
displayName: `new ${className}`,
|
|
7267
|
-
visibility: "public",
|
|
7268
|
-
stability: "stable",
|
|
7269
|
-
sourceKind: "generated_resource_constructor",
|
|
7270
|
-
parameters: cls.constructorParams.map((p, i) => apiParamToCompatParam(p, i, language))
|
|
7271
|
-
});
|
|
7272
|
-
for (const [methodName, overloads] of Object.entries(cls.methods)) for (let oi = 0; oi < overloads.length; oi++) {
|
|
7273
|
-
const method = overloads[oi];
|
|
7274
|
-
const suffix = overloads.length > 1 ? `#${oi}` : "";
|
|
7275
|
-
symbols.push({
|
|
7276
|
-
id: `method:${className}.${methodName}${suffix}`,
|
|
7277
|
-
kind: "callable",
|
|
7278
|
-
fqName: `${className}.${methodName}`,
|
|
7279
|
-
ownerFqName: className,
|
|
7280
|
-
displayName: `${className}.${methodName}`,
|
|
7281
|
-
visibility: "public",
|
|
7282
|
-
stability: "stable",
|
|
7283
|
-
sourceKind: "generated_service_wrapper",
|
|
7284
|
-
parameters: method.params.map((p, i) => apiParamToCompatParam(p, i, language)),
|
|
7285
|
-
returns: { name: method.returnType }
|
|
7286
|
-
});
|
|
7287
|
-
}
|
|
7288
|
-
for (const [propName, prop] of Object.entries(cls.properties)) symbols.push({
|
|
7289
|
-
id: `prop:${className}.${propName}`,
|
|
7290
|
-
kind: "property",
|
|
7291
|
-
fqName: `${className}.${propName}`,
|
|
7292
|
-
ownerFqName: className,
|
|
7293
|
-
displayName: `${className}.${propName}`,
|
|
7294
|
-
visibility: "public",
|
|
7295
|
-
stability: "stable",
|
|
7296
|
-
sourceKind: "generated_service_wrapper",
|
|
7297
|
-
typeRef: { name: prop.type }
|
|
7298
|
-
});
|
|
7299
|
-
}
|
|
7300
|
-
for (const [ifaceName, iface] of Object.entries(surface.interfaces)) {
|
|
7301
|
-
symbols.push({
|
|
7302
|
-
id: `iface:${ifaceName}`,
|
|
7303
|
-
kind: "alias",
|
|
7304
|
-
fqName: ifaceName,
|
|
7305
|
-
displayName: ifaceName,
|
|
7306
|
-
visibility: "public",
|
|
7307
|
-
stability: "stable",
|
|
7308
|
-
sourceKind: "generated_resource_constructor"
|
|
7309
|
-
});
|
|
7310
|
-
for (const [fieldName, field] of Object.entries(iface.fields)) symbols.push({
|
|
7311
|
-
id: `field:${ifaceName}.${fieldName}`,
|
|
7312
|
-
kind: "field",
|
|
7313
|
-
fqName: `${ifaceName}.${fieldName}`,
|
|
7314
|
-
ownerFqName: ifaceName,
|
|
7315
|
-
displayName: `${ifaceName}.${fieldName}`,
|
|
7316
|
-
visibility: "public",
|
|
7317
|
-
stability: "stable",
|
|
7318
|
-
sourceKind: "generated_resource_constructor",
|
|
7319
|
-
typeRef: { name: field.type }
|
|
7320
|
-
});
|
|
7321
|
-
if (iface.hasCustomConstructor) symbols.push({
|
|
7322
|
-
id: `ctor:${ifaceName}`,
|
|
7323
|
-
kind: "constructor",
|
|
7324
|
-
fqName: `${ifaceName}.constructor`,
|
|
7325
|
-
ownerFqName: ifaceName,
|
|
7326
|
-
displayName: `new ${ifaceName}`,
|
|
7327
|
-
visibility: "public",
|
|
7328
|
-
stability: "stable",
|
|
7329
|
-
sourceKind: "generated_resource_constructor"
|
|
7330
|
-
});
|
|
7331
|
-
}
|
|
7332
|
-
for (const [aliasName] of Object.entries(surface.typeAliases)) symbols.push({
|
|
7333
|
-
id: `alias:${aliasName}`,
|
|
7334
|
-
kind: "alias",
|
|
7335
|
-
fqName: aliasName,
|
|
7336
|
-
displayName: aliasName,
|
|
7337
|
-
visibility: "public",
|
|
7338
|
-
stability: "stable",
|
|
7339
|
-
sourceKind: "generated_resource_constructor"
|
|
7340
|
-
});
|
|
7341
|
-
for (const [enumName, enumDef] of Object.entries(surface.enums)) {
|
|
7342
|
-
symbols.push({
|
|
7343
|
-
id: `enum:${enumName}`,
|
|
7344
|
-
kind: "enum",
|
|
7345
|
-
fqName: enumName,
|
|
7346
|
-
displayName: enumName,
|
|
7347
|
-
visibility: "public",
|
|
7348
|
-
stability: "stable",
|
|
7349
|
-
sourceKind: "generated_enum"
|
|
7350
|
-
});
|
|
7351
|
-
for (const [memberName, memberValue] of Object.entries(enumDef.members)) symbols.push({
|
|
7352
|
-
id: `enum_member:${enumName}.${memberName}`,
|
|
7353
|
-
kind: "enum_member",
|
|
7354
|
-
fqName: `${enumName}.${memberName}`,
|
|
7355
|
-
ownerFqName: enumName,
|
|
7356
|
-
displayName: `${enumName}.${memberName}`,
|
|
7357
|
-
visibility: "public",
|
|
7358
|
-
stability: "stable",
|
|
7359
|
-
sourceKind: "generated_enum",
|
|
7360
|
-
value: memberValue
|
|
7361
|
-
});
|
|
7362
|
-
}
|
|
7363
|
-
return {
|
|
7364
|
-
schemaVersion: "1",
|
|
7365
|
-
source: { extractedAt: surface.extractedAt },
|
|
7366
|
-
policies: getDefaultPolicy(language),
|
|
7367
|
-
symbols
|
|
7368
|
-
};
|
|
7369
|
-
}
|
|
7370
|
-
/** Convert a legacy ApiParam to a CompatParameter. */
|
|
7371
|
-
function apiParamToCompatParam(param, position, language) {
|
|
7372
|
-
const policy = getDefaultPolicy(language);
|
|
7373
|
-
return {
|
|
7374
|
-
publicName: param.name,
|
|
7375
|
-
position,
|
|
7376
|
-
required: !param.optional,
|
|
7377
|
-
nullable: false,
|
|
7378
|
-
hasDefault: param.optional,
|
|
7379
|
-
passing: param.passingStyle ?? inferPassingStyle(language),
|
|
7380
|
-
type: { name: param.type },
|
|
7381
|
-
sensitivity: {
|
|
7382
|
-
order: policy.constructorOrderMatters,
|
|
7383
|
-
publicName: policy.methodParameterNamesArePublicApi,
|
|
7384
|
-
requiredness: true,
|
|
7385
|
-
type: true
|
|
7386
|
-
}
|
|
7387
|
-
};
|
|
7388
|
-
}
|
|
7389
|
-
/** Infer the default parameter passing style for a language. */
|
|
7390
|
-
function inferPassingStyle(language) {
|
|
7391
|
-
switch (language) {
|
|
7392
|
-
case "python":
|
|
7393
|
-
case "ruby":
|
|
7394
|
-
case "elixir": return "keyword";
|
|
7395
|
-
case "php":
|
|
7396
|
-
case "kotlin":
|
|
7397
|
-
case "dotnet": return "named";
|
|
7398
|
-
case "node": return "options_object";
|
|
7399
|
-
default: return "positional";
|
|
7400
|
-
}
|
|
7401
|
-
}
|
|
7402
|
-
//#endregion
|
|
7403
7484
|
//#region src/verify/run-compat-check.ts
|
|
7404
7485
|
async function runCompatCheck(baseline, outputDir, lang, spec, policyOverrides) {
|
|
7405
7486
|
const extractor = getExtractor(lang);
|