@workos/oagen 0.29.1 → 0.29.2
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 +139 -8
- package/dist/cli/index.mjs.map +1 -1
- package/dist/compat/index.d.mts +2 -2
- package/dist/compat/index.mjs +1 -1
- package/dist/{differ-Co0fRniL.d.mts → differ-DxWejtEg.d.mts} +3 -3
- package/dist/differ-DxWejtEg.d.mts.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/{overlay-jsSkFJ2g.mjs → overlay-CihWp6dQ.mjs} +140 -9
- package/dist/overlay-CihWp6dQ.mjs.map +1 -0
- package/dist/{types-DqKSnLBP.d.mts → types-CEMBeTr9.d.mts} +4 -1
- package/dist/types-CEMBeTr9.d.mts.map +1 -0
- package/dist/verify/index.d.mts +2 -2
- package/dist/verify/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/differ-Co0fRniL.d.mts.map +0 -1
- package/dist/overlay-jsSkFJ2g.mjs.map +0 -1
- package/dist/types-DqKSnLBP.d.mts.map +0 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -8165,7 +8165,10 @@ function apiSurfaceToSnapshot(surface) {
|
|
|
8165
8165
|
}
|
|
8166
8166
|
return {
|
|
8167
8167
|
schemaVersion: "1",
|
|
8168
|
-
source: {
|
|
8168
|
+
source: {
|
|
8169
|
+
extractedAt: surface.extractedAt,
|
|
8170
|
+
language
|
|
8171
|
+
},
|
|
8169
8172
|
policies: getDefaultPolicy(language),
|
|
8170
8173
|
symbols
|
|
8171
8174
|
};
|
|
@@ -8395,10 +8398,138 @@ function severityMeetsThreshold(severity, threshold) {
|
|
|
8395
8398
|
//#endregion
|
|
8396
8399
|
//#region src/compat/classify.ts
|
|
8397
8400
|
/**
|
|
8401
|
+
* Normalize a type annotation string to a canonical form before equality
|
|
8402
|
+
* comparison. Emitters evolve their annotation style across releases (e.g.
|
|
8403
|
+
* Python's PEP 604 `X | None` replacing `Optional[X]`, PEP 585 lowercase
|
|
8404
|
+
* generics, or dropping quotes from forward refs); such changes are cosmetic
|
|
8405
|
+
* and must not register as breaking type changes. Only Python needs this
|
|
8406
|
+
* today — other languages encode optionality with a single stable spelling
|
|
8407
|
+
* (`*Foo`, `Option<T>`, `T?`, `T.nilable(Foo)`) so they use exact comparison.
|
|
8408
|
+
* String-literal *values* inside `Literal[...]` are preserved verbatim so a
|
|
8409
|
+
* genuine change to a wire value stays visible. When `language` is absent
|
|
8410
|
+
* (older snapshots) no normalization is applied, preserving prior behaviour.
|
|
8411
|
+
*/
|
|
8412
|
+
function normalizeType(type, language) {
|
|
8413
|
+
if (!type) return type;
|
|
8414
|
+
if (language !== "python") return type;
|
|
8415
|
+
return canonicalizePythonType(type);
|
|
8416
|
+
}
|
|
8417
|
+
/** PEP 585 capitalized builtins that map to their lowercase spellings, so
|
|
8418
|
+
* `List[str]` and `list[str]` compare equal. */
|
|
8419
|
+
const PEP585_BUILTINS = {
|
|
8420
|
+
List: "list",
|
|
8421
|
+
Dict: "dict",
|
|
8422
|
+
Tuple: "tuple",
|
|
8423
|
+
Set: "set",
|
|
8424
|
+
FrozenSet: "frozenset",
|
|
8425
|
+
Type: "type"
|
|
8426
|
+
};
|
|
8427
|
+
/** Recursively canonicalize a Python annotation. Structural forms are
|
|
8428
|
+
* normalized (`Optional[X]`→`X | None`, `Union[...]`→` | ` joins, PEP 585
|
|
8429
|
+
* builtins, quoted forward refs, parenthesized unions), but `Literal[...]`
|
|
8430
|
+
* arguments are preserved verbatim — their quotes and inner whitespace are
|
|
8431
|
+
* part of the wire value, not cosmetic. */
|
|
8432
|
+
function canonicalizePythonType(s) {
|
|
8433
|
+
const t = s.trim();
|
|
8434
|
+
if (t.length === 0) return t;
|
|
8435
|
+
if (t.startsWith("(") && t.endsWith(")")) return canonicalizePythonType(t.slice(1, -1));
|
|
8436
|
+
if (t.startsWith("Literal[") && t.endsWith("]")) return `Literal[${splitTopLevel(t.slice(8, -1), ",").map((p) => p.trim()).filter((p) => p.length > 0).map(canonicalizeLiteralArg).join(", ")}]`;
|
|
8437
|
+
if (t.startsWith("Optional[") && t.endsWith("]")) return `${canonicalizePythonType(t.slice(9, -1))} | None`;
|
|
8438
|
+
if (t.startsWith("Union[") && t.endsWith("]")) return splitTopLevel(t.slice(6, -1), ",").map((p) => p.trim()).filter((p) => p.length > 0).map(canonicalizePythonType).join(" | ");
|
|
8439
|
+
if (t.includes("|")) {
|
|
8440
|
+
const arms = splitTopLevel(t, "|").map((p) => p.trim()).filter((p) => p.length > 0);
|
|
8441
|
+
if (arms.length > 1) return arms.map(canonicalizePythonType).join(" | ");
|
|
8442
|
+
}
|
|
8443
|
+
const open = t.indexOf("[");
|
|
8444
|
+
if (open !== -1 && t.endsWith("]")) {
|
|
8445
|
+
const outer = t.slice(0, open).trim();
|
|
8446
|
+
const inner = t.slice(open + 1, -1);
|
|
8447
|
+
return `${PEP585_BUILTINS[outer] ?? outer}[${splitTopLevel(inner, ",").map((p) => p.trim()).filter((p) => p.length > 0).map(canonicalizePythonType).join(", ")}]`;
|
|
8448
|
+
}
|
|
8449
|
+
if (t.startsWith("\"") && t.endsWith("\"") && t.length >= 2 || t.startsWith("'") && t.endsWith("'") && t.length >= 2) return t.slice(1, -1);
|
|
8450
|
+
return PEP585_BUILTINS[t] ?? t;
|
|
8451
|
+
}
|
|
8452
|
+
/** Canonicalize one `Literal[...]` argument so semantically identical string
|
|
8453
|
+
* values compare equal regardless of quote style or escape form. The value
|
|
8454
|
+
* is decoded (backslash escapes processed) then re-encoded in a canonical
|
|
8455
|
+
* double-quoted form, so `Literal['say "hi"']` and `Literal["say \\"hi\\""]`
|
|
8456
|
+
* both canonicalize to `"say \"hi\""`. Bare (unquoted) arguments — e.g. an
|
|
8457
|
+
* enum name or a number — are returned unchanged. */
|
|
8458
|
+
function canonicalizeLiteralArg(arg) {
|
|
8459
|
+
const a = arg.trim();
|
|
8460
|
+
if (a.length < 2) return a;
|
|
8461
|
+
let quote = null;
|
|
8462
|
+
if (a.startsWith("\"") && a.endsWith("\"")) quote = "\"";
|
|
8463
|
+
else if (a.startsWith("'") && a.endsWith("'")) quote = "'";
|
|
8464
|
+
if (!quote) return a;
|
|
8465
|
+
return `"${encodeDoubleQuoted(decodeStringLiteral(a.slice(1, -1)))}"`;
|
|
8466
|
+
}
|
|
8467
|
+
/** Decode the backslash escapes inside a Python string literal's content.
|
|
8468
|
+
* Only the delimiter-relevant escapes (`\\`, `\'`, `\"`) are processed;
|
|
8469
|
+
* other sequences (`\n`, `\t`, `\u…`) are kept verbatim since they're
|
|
8470
|
+
* written identically regardless of quote style and so compare equal already. */
|
|
8471
|
+
function decodeStringLiteral(content) {
|
|
8472
|
+
let out = "";
|
|
8473
|
+
for (let i = 0; i < content.length; i++) {
|
|
8474
|
+
const c = content[i];
|
|
8475
|
+
if (c === "\\" && i + 1 < content.length) {
|
|
8476
|
+
const next = content[i + 1];
|
|
8477
|
+
if (next === "\\" || next === "\"" || next === "'") {
|
|
8478
|
+
out += next;
|
|
8479
|
+
i++;
|
|
8480
|
+
continue;
|
|
8481
|
+
}
|
|
8482
|
+
out += c + next;
|
|
8483
|
+
i++;
|
|
8484
|
+
continue;
|
|
8485
|
+
}
|
|
8486
|
+
out += c;
|
|
8487
|
+
}
|
|
8488
|
+
return out;
|
|
8489
|
+
}
|
|
8490
|
+
/** Re-encode a decoded value as a canonical double-quoted Python string. */
|
|
8491
|
+
function encodeDoubleQuoted(value) {
|
|
8492
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
8493
|
+
}
|
|
8494
|
+
/** Split on a separator that sits at bracket-depth 0 and outside string
|
|
8495
|
+
* literals, so commas/pipes inside `Literal["a,b"]` or `"x|y"` are not split.
|
|
8496
|
+
* An escaped matching quote (`\"` inside a double-quoted string) does not
|
|
8497
|
+
* close the string: only a quote preceded by an even number of backslashes
|
|
8498
|
+
* (zero counts) is a real closing delimiter. */
|
|
8499
|
+
function splitTopLevel(s, sep) {
|
|
8500
|
+
const parts = [];
|
|
8501
|
+
let depth = 0;
|
|
8502
|
+
let inStr = null;
|
|
8503
|
+
let start = 0;
|
|
8504
|
+
for (let i = 0; i < s.length; i++) {
|
|
8505
|
+
const c = s[i];
|
|
8506
|
+
if (inStr) {
|
|
8507
|
+
if (c === inStr) {
|
|
8508
|
+
let precedingBackslashes = 0;
|
|
8509
|
+
for (let j = i - 1; j >= 0 && s[j] === "\\"; j--) precedingBackslashes++;
|
|
8510
|
+
if (precedingBackslashes % 2 === 0) inStr = null;
|
|
8511
|
+
}
|
|
8512
|
+
continue;
|
|
8513
|
+
}
|
|
8514
|
+
if (c === "\"" || c === "'") {
|
|
8515
|
+
inStr = c;
|
|
8516
|
+
continue;
|
|
8517
|
+
}
|
|
8518
|
+
if (c === "[") depth++;
|
|
8519
|
+
else if (c === "]") depth--;
|
|
8520
|
+
else if (c === sep && depth === 0) {
|
|
8521
|
+
parts.push(s.slice(start, i));
|
|
8522
|
+
start = i + 1;
|
|
8523
|
+
}
|
|
8524
|
+
}
|
|
8525
|
+
parts.push(s.slice(start));
|
|
8526
|
+
return parts;
|
|
8527
|
+
}
|
|
8528
|
+
/**
|
|
8398
8529
|
* Classify changes between a baseline and candidate symbol.
|
|
8399
8530
|
* Returns one or more classified changes for the diff.
|
|
8400
8531
|
*/
|
|
8401
|
-
function classifySymbolChanges(baseline, candidate, policy) {
|
|
8532
|
+
function classifySymbolChanges(baseline, candidate, policy, language) {
|
|
8402
8533
|
const changes = [];
|
|
8403
8534
|
const specRef = baseline.schemaName ?? candidate?.schemaName;
|
|
8404
8535
|
if (!candidate) {
|
|
@@ -8422,8 +8553,8 @@ function classifySymbolChanges(baseline, candidate, policy) {
|
|
|
8422
8553
|
policy,
|
|
8423
8554
|
specRef
|
|
8424
8555
|
}));
|
|
8425
|
-
if (baseline.parameters && candidate.parameters) changes.push(...classifyParameterChanges(baseline, candidate, policy, specRef));
|
|
8426
|
-
if (baseline.returns && candidate.returns && baseline.returns.name !== candidate.returns.name) changes.push(makeChange({
|
|
8556
|
+
if (baseline.parameters && candidate.parameters) changes.push(...classifyParameterChanges(baseline, candidate, policy, specRef, language));
|
|
8557
|
+
if (baseline.returns && candidate.returns && normalizeType(baseline.returns.name, language) !== normalizeType(candidate.returns.name, language)) changes.push(makeChange({
|
|
8427
8558
|
category: "return_type_changed",
|
|
8428
8559
|
symbol: baseline.fqName,
|
|
8429
8560
|
old: { returnType: baseline.returns.name },
|
|
@@ -8441,7 +8572,7 @@ function classifySymbolChanges(baseline, candidate, policy) {
|
|
|
8441
8572
|
policy,
|
|
8442
8573
|
specRef
|
|
8443
8574
|
}));
|
|
8444
|
-
if (baseline.typeRef && candidate.typeRef && baseline.typeRef.name !== candidate.typeRef.name) changes.push(makeChange({
|
|
8575
|
+
if (baseline.typeRef && candidate.typeRef && normalizeType(baseline.typeRef.name, language) !== normalizeType(candidate.typeRef.name, language)) changes.push(makeChange({
|
|
8445
8576
|
category: "field_type_changed",
|
|
8446
8577
|
symbol: baseline.fqName,
|
|
8447
8578
|
old: { type: baseline.typeRef.name },
|
|
@@ -8463,7 +8594,7 @@ function classifySymbolChanges(baseline, candidate, policy) {
|
|
|
8463
8594
|
/**
|
|
8464
8595
|
* Classify parameter-level changes between two symbol versions.
|
|
8465
8596
|
*/
|
|
8466
|
-
function classifyParameterChanges(baseline, candidate, policy, specRef) {
|
|
8597
|
+
function classifyParameterChanges(baseline, candidate, policy, specRef, language) {
|
|
8467
8598
|
const changes = [];
|
|
8468
8599
|
const baseParams = baseline.parameters ?? [];
|
|
8469
8600
|
const candParams = candidate.parameters ?? [];
|
|
@@ -8512,7 +8643,7 @@ function classifyParameterChanges(baseline, candidate, policy, specRef) {
|
|
|
8512
8643
|
policy,
|
|
8513
8644
|
specRef
|
|
8514
8645
|
}));
|
|
8515
|
-
if (baseParam.type.name !== candParam.type.name) changes.push(makeChange({
|
|
8646
|
+
if (normalizeType(baseParam.type.name, language) !== normalizeType(candParam.type.name, language)) changes.push(makeChange({
|
|
8516
8647
|
category: "parameter_type_narrowed",
|
|
8517
8648
|
symbol: baseline.fqName,
|
|
8518
8649
|
old: {
|
|
@@ -8869,7 +9000,7 @@ function diffSnapshots(baseline, candidate, policy) {
|
|
|
8869
9000
|
for (const baseSym of baseline.symbols) {
|
|
8870
9001
|
if (isServiceWrapperConstructor(baseSym, serviceAccessors)) continue;
|
|
8871
9002
|
const candSym = candById.get(baseSym.id) ?? candByFqName.get(baseSym.fqName);
|
|
8872
|
-
changes.push(...classifySymbolChanges(baseSym, candSym, effectivePolicy));
|
|
9003
|
+
changes.push(...classifySymbolChanges(baseSym, candSym, effectivePolicy, baseline.source.language));
|
|
8873
9004
|
}
|
|
8874
9005
|
for (const candSym of candidate.symbols) if (!baseByFqName.has(candSym.fqName)) {
|
|
8875
9006
|
if (isServiceWrapperConstructor(candSym, serviceAccessors)) continue;
|