@workos/oagen 0.18.0 → 0.18.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.
@@ -1811,7 +1811,7 @@ const HTTP_METHODS = [
1811
1811
  "options",
1812
1812
  "trace"
1813
1813
  ];
1814
- function extractOperations(paths, operationIdTransform) {
1814
+ function extractOperations(paths, operationIdTransform, componentSchemas) {
1815
1815
  if (!paths) return {
1816
1816
  services: [],
1817
1817
  inlineModels: []
@@ -1824,7 +1824,7 @@ function extractOperations(paths, operationIdTransform) {
1824
1824
  const op = pathItem[method];
1825
1825
  if (!op) continue;
1826
1826
  const serviceName = inferServiceName(path, op.tags?.[0]);
1827
- const { operation, inlineModels: opModels } = buildOperation(method, path, op, pathLevelParams, operationIdTransform, serviceName);
1827
+ const { operation, inlineModels: opModels } = buildOperation(method, path, op, pathLevelParams, operationIdTransform, serviceName, componentSchemas);
1828
1828
  inlineModels.push(...opModels);
1829
1829
  const ops = serviceMap.get(serviceName) ?? [];
1830
1830
  ops.push(operation);
@@ -2067,14 +2067,14 @@ function extractBodyParameterGroups(op, operationContext) {
2067
2067
  }
2068
2068
  return groups.length > 0 ? groups : void 0;
2069
2069
  }
2070
- function buildOperation(method, path, op, pathLevelParams, operationIdTransform, serviceName) {
2070
+ function buildOperation(method, path, op, pathLevelParams, operationIdTransform, serviceName, componentSchemas) {
2071
2071
  const allParams = [...pathLevelParams, ...op.parameters ?? []];
2072
2072
  const hasIdempotencyHeader = allParams.some((p) => p.in === "header" && p.name.toLowerCase() === "idempotency-key");
2073
2073
  const opContext = serviceName;
2074
- const pathParams = extractParams(allParams, "path", opContext);
2075
- const queryParams = extractParams(allParams, "query", opContext);
2076
- const headerParams = extractParams(allParams, "header", opContext).filter((p) => p.name.toLowerCase() !== "idempotency-key");
2077
- const cookieParams = extractParams(allParams, "cookie", opContext);
2074
+ const pathParams = extractParams(allParams, "path", opContext, componentSchemas);
2075
+ const queryParams = extractParams(allParams, "query", opContext, componentSchemas);
2076
+ const headerParams = extractParams(allParams, "header", opContext, componentSchemas).filter((p) => p.name.toLowerCase() !== "idempotency-key");
2077
+ const cookieParams = extractParams(allParams, "cookie", opContext, componentSchemas);
2078
2078
  const reqBodyModels = [];
2079
2079
  const { body: requestBody, encoding: requestBodyEncoding } = extractRequestBody(op.requestBody, op, reqBodyModels);
2080
2080
  const { response, successResponses, errors, inlineModels, isPaginated, dataPath: responseDataPath, itemType: responseItemType } = extractResponses(op.responses, op, path, method);
@@ -2146,21 +2146,42 @@ function buildDescription(summary, description) {
2146
2146
  if (summary && description && summary !== description) return `${summary}\n\n${description}`;
2147
2147
  return description ?? summary;
2148
2148
  }
2149
- function extractParams(params, location, operationContext) {
2150
- return params.filter((p) => p.in === location).map((p) => ({
2151
- name: p.name,
2152
- type: p.schema ? schemaToTypeRef(p.schema, p.name, operationContext ? toPascalCase(operationContext) : void 0) : {
2153
- kind: "primitive",
2154
- type: "string"
2155
- },
2156
- required: p.required ?? false,
2157
- description: p.description,
2158
- deprecated: p.deprecated || p.schema?.deprecated || void 0,
2159
- default: p.schema?.default,
2160
- example: p.example ?? p.schema?.example,
2161
- style: p.style,
2162
- explode: p.explode
2163
- }));
2149
+ function extractParams(params, location, operationContext, componentSchemas) {
2150
+ return params.filter((p) => p.in === location).map((p) => {
2151
+ const refTarget = resolveParamSchemaRef(p.schema, componentSchemas);
2152
+ return {
2153
+ name: p.name,
2154
+ type: p.schema ? schemaToTypeRef(p.schema, p.name, operationContext ? toPascalCase(operationContext) : void 0) : {
2155
+ kind: "primitive",
2156
+ type: "string"
2157
+ },
2158
+ required: p.required ?? false,
2159
+ description: p.description,
2160
+ deprecated: p.deprecated || p.schema?.deprecated || refTarget?.deprecated || void 0,
2161
+ default: p.schema?.default ?? refTarget?.default,
2162
+ example: p.example ?? p.schema?.example ?? refTarget?.example,
2163
+ style: p.style,
2164
+ explode: p.explode
2165
+ };
2166
+ });
2167
+ }
2168
+ /**
2169
+ * If a parameter's schema is a `$ref` to `#/components/schemas/X`, return the
2170
+ * referenced component schema. Used to surface schema-level metadata (default,
2171
+ * example, deprecated) that wouldn't otherwise be visible at the param level.
2172
+ *
2173
+ * Refs are not pre-resolved by the bundler (`dereference: false` in refs.ts),
2174
+ * so without this, a param like `schema: { $ref: '.../PaginationOrder' }`
2175
+ * silently loses the target's `default: desc` and the SDK stops emitting the
2176
+ * spec-mandated default value in generated method signatures.
2177
+ */
2178
+ function resolveParamSchemaRef(schema, componentSchemas) {
2179
+ if (!schema || !componentSchemas) return void 0;
2180
+ const ref = schema.$ref;
2181
+ if (!ref) return void 0;
2182
+ const match = /^#\/components\/schemas\/(.+)$/.exec(ref);
2183
+ if (!match) return void 0;
2184
+ return componentSchemas[match[1]];
2164
2185
  }
2165
2186
  function extractRequestBody(body, op, inlineModels) {
2166
2187
  if (!body?.content) return {};
@@ -2514,7 +2535,7 @@ async function parseSpec(specPath, options) {
2514
2535
  const version = spec.openapi ?? "";
2515
2536
  if (!version.startsWith("3.")) throw new SpecParseError(`Unsupported OpenAPI version: ${version}. oagen requires OpenAPI 3.x`, `Update the spec to OpenAPI 3.0 or 3.1. If you are using Swagger 2.x, convert it first with \`npx swagger2openapi ${specPath}\`.`);
2516
2537
  const { models, enums } = extractSchemas(spec.components?.schemas, { schemaNameTransform: options?.schemaNameTransform });
2517
- const { services, inlineModels } = extractOperations(spec.paths, options?.operationIdTransform);
2538
+ const { services, inlineModels } = extractOperations(spec.paths, options?.operationIdTransform, spec.components?.schemas);
2518
2539
  clearSchemaNameTransform();
2519
2540
  const finalModels = collapseJsonSuffixModels(mergeFieldInlineModels(mergeInlineResponseModels(models, inlineModels), extractInlineModelsFromSchemas(spec.components?.schemas)), services);
2520
2541
  collectInlineEnumsFromModels(finalModels, enums);
@@ -6223,6 +6244,11 @@ function classifyFieldChange(kind, fieldName, isRequired) {
6223
6244
  fieldName,
6224
6245
  classification: "breaking"
6225
6246
  };
6247
+ case "field-format-changed": return {
6248
+ kind,
6249
+ fieldName,
6250
+ classification: "additive"
6251
+ };
6226
6252
  case "field-required-changed": return {
6227
6253
  kind,
6228
6254
  fieldName,
@@ -6253,11 +6279,21 @@ function classifyParamChange(kind, paramName, isRequired) {
6253
6279
  paramName,
6254
6280
  classification: "breaking"
6255
6281
  };
6282
+ case "param-format-changed": return {
6283
+ kind,
6284
+ paramName,
6285
+ classification: "additive"
6286
+ };
6256
6287
  case "param-required-changed": return {
6257
6288
  kind,
6258
6289
  paramName,
6259
6290
  classification: isRequired ? "breaking" : "additive"
6260
6291
  };
6292
+ case "param-default-changed": return {
6293
+ kind,
6294
+ paramName,
6295
+ classification: "breaking"
6296
+ };
6261
6297
  }
6262
6298
  }
6263
6299
  //#endregion
@@ -6301,7 +6337,11 @@ function diffFields(oldModel, newModel) {
6301
6337
  for (const [name, newField] of newByName) {
6302
6338
  const oldField = oldByName.get(name);
6303
6339
  if (!oldField) continue;
6304
- if (!typeRefsEqual(oldField.type, newField.type)) changes.push({
6340
+ if (!typeRefsEqual(oldField.type, newField.type)) if (primitivesDifferOnlyByFormat(oldField.type, newField.type)) changes.push({
6341
+ ...classifyFieldChange("field-format-changed", name),
6342
+ details: formatChangeDetails(oldField.type, newField.type)
6343
+ });
6344
+ else changes.push({
6305
6345
  kind: "field-type-changed",
6306
6346
  fieldName: name,
6307
6347
  classification: "breaking",
@@ -6312,6 +6352,23 @@ function diffFields(oldModel, newModel) {
6312
6352
  }
6313
6353
  return changes;
6314
6354
  }
6355
+ /**
6356
+ * True when both refs are primitives with the same base `type` but different
6357
+ * `format`. Used to downgrade format-only diffs from breaking type changes to
6358
+ * additive `field-format-changed` / `param-format-changed` events: the wire
6359
+ * base type is unchanged, so the diff is a hint to language emitters rather
6360
+ * than a source-level break.
6361
+ */
6362
+ function primitivesDifferOnlyByFormat(a, b) {
6363
+ if (a.kind !== "primitive" || b.kind !== "primitive") return false;
6364
+ if (a.type !== b.type) return false;
6365
+ return (a.format ?? "") !== (b.format ?? "");
6366
+ }
6367
+ function formatChangeDetails(a, b) {
6368
+ const oldFmt = a.kind === "primitive" ? a.format : void 0;
6369
+ const newFmt = b.kind === "primitive" ? b.format : void 0;
6370
+ return `format ${oldFmt ?? "<unset>"} → ${newFmt ?? "<unset>"}`;
6371
+ }
6315
6372
  function typeRefsEqual(a, b) {
6316
6373
  if (a.kind !== b.kind) return false;
6317
6374
  switch (a.kind) {
@@ -6413,28 +6470,57 @@ function classifyErrorsChange(oldErrors, newErrors) {
6413
6470
  }
6414
6471
  function diffParams(oldOp, newOp) {
6415
6472
  const changes = [];
6473
+ const tagged = (loc, params) => params.map((p) => ({
6474
+ p,
6475
+ loc
6476
+ }));
6416
6477
  const allOldParams = [
6417
- ...oldOp.pathParams,
6418
- ...oldOp.queryParams,
6419
- ...oldOp.headerParams
6478
+ ...tagged("path", oldOp.pathParams),
6479
+ ...tagged("query", oldOp.queryParams),
6480
+ ...tagged("header", oldOp.headerParams)
6420
6481
  ];
6421
6482
  const allNewParams = [
6422
- ...newOp.pathParams,
6423
- ...newOp.queryParams,
6424
- ...newOp.headerParams
6483
+ ...tagged("path", newOp.pathParams),
6484
+ ...tagged("query", newOp.queryParams),
6485
+ ...tagged("header", newOp.headerParams)
6425
6486
  ];
6426
- const oldByName = new Map(allOldParams.map((p) => [p.name, p]));
6427
- const newByName = new Map(allNewParams.map((p) => [p.name, p]));
6428
- for (const [name, param] of newByName) if (!oldByName.has(name)) changes.push(classifyParamChange("param-added", name, param.required));
6487
+ const oldByName = new Map(allOldParams.map((e) => [e.p.name, e]));
6488
+ const newByName = new Map(allNewParams.map((e) => [e.p.name, e]));
6489
+ for (const [name, entry] of newByName) if (!oldByName.has(name)) changes.push(classifyParamChange("param-added", name, entry.p.required));
6429
6490
  for (const [name] of oldByName) if (!newByName.has(name)) changes.push(classifyParamChange("param-removed", name));
6430
- for (const [name, newParam] of newByName) {
6431
- const oldParam = oldByName.get(name);
6432
- if (!oldParam) continue;
6433
- if (!typeRefsEqual(oldParam.type, newParam.type)) changes.push(classifyParamChange("param-type-changed", name));
6434
- if (oldParam.required !== newParam.required) changes.push(classifyParamChange("param-required-changed", name, newParam.required));
6491
+ for (const [name, newEntry] of newByName) {
6492
+ const oldEntry = oldByName.get(name);
6493
+ if (!oldEntry) continue;
6494
+ if (!typeRefsEqual(oldEntry.p.type, newEntry.p.type)) if (primitivesDifferOnlyByFormat(oldEntry.p.type, newEntry.p.type)) {
6495
+ const change = classifyParamChange("param-format-changed", name);
6496
+ change.details = formatChangeDetails(oldEntry.p.type, newEntry.p.type);
6497
+ changes.push(change);
6498
+ } else changes.push(classifyParamChange("param-type-changed", name));
6499
+ if (oldEntry.p.required !== newEntry.p.required) changes.push(classifyParamChange("param-required-changed", name, newEntry.p.required));
6500
+ const oldDefault = serializeDefault(oldEntry.p.default);
6501
+ const newDefault = serializeDefault(newEntry.p.default);
6502
+ if (oldDefault !== newDefault) {
6503
+ const change = classifyParamChange("param-default-changed", name);
6504
+ change.oldDefault = oldDefault;
6505
+ change.newDefault = newDefault;
6506
+ change.paramLocation = newEntry.loc;
6507
+ change.details = `default ${oldDefault ?? "<unset>"} → ${newDefault ?? "<unset>"}`;
6508
+ changes.push(change);
6509
+ }
6435
6510
  }
6436
6511
  return changes;
6437
6512
  }
6513
+ function serializeDefault(value) {
6514
+ if (value === void 0) return null;
6515
+ if (value === null) return "null";
6516
+ if (typeof value === "string") return value;
6517
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
6518
+ try {
6519
+ return JSON.stringify(value);
6520
+ } catch {
6521
+ return String(value);
6522
+ }
6523
+ }
6438
6524
  //#endregion
6439
6525
  //#region src/differ/services.ts
6440
6526
  function diffServices(oldServices, newServices) {
@@ -6466,13 +6552,38 @@ function diffSpecs(oldSpec, newSpec) {
6466
6552
  ...diffEnums(oldSpec.enums, newSpec.enums),
6467
6553
  ...diffServices(oldSpec.services, newSpec.services)
6468
6554
  ];
6555
+ const behaviorChanges = collectBehaviorChanges(changes);
6469
6556
  return {
6470
6557
  oldVersion: oldSpec.version,
6471
6558
  newVersion: newSpec.version,
6472
6559
  changes,
6473
- summary: summarize(changes)
6560
+ behaviorChanges,
6561
+ summary: {
6562
+ ...summarize(changes),
6563
+ behaviorChanges: behaviorChanges.length
6564
+ }
6474
6565
  };
6475
6566
  }
6567
+ function collectBehaviorChanges(changes) {
6568
+ const out = [];
6569
+ for (const c of changes) {
6570
+ if (c.kind !== "operation-modified") continue;
6571
+ for (const pc of c.paramChanges) {
6572
+ if (pc.kind !== "param-default-changed") continue;
6573
+ out.push({
6574
+ kind: "param-default-changed",
6575
+ serviceName: c.serviceName,
6576
+ operationName: c.operationName,
6577
+ paramName: pc.paramName,
6578
+ paramLocation: pc.paramLocation ?? "query",
6579
+ oldDefault: pc.oldDefault ?? null,
6580
+ newDefault: pc.newDefault ?? null,
6581
+ classification: "breaking"
6582
+ });
6583
+ }
6584
+ }
6585
+ return out;
6586
+ }
6476
6587
  function diffEnums(oldEnums, newEnums) {
6477
6588
  const changes = [];
6478
6589
  const oldByName = new Map(oldEnums.map((e) => [e.name, e]));