@workos/oagen 0.17.3 → 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.
@@ -937,13 +937,16 @@ function deriveInlineVariantMapping(variants, discriminatorProperty, contextName
937
937
  return mapping;
938
938
  }
939
939
  function extractEnum(name, schema) {
940
+ const values = (schema.enum ?? []).map((v) => ({
941
+ name: toUpperSnakeCase(String(v)),
942
+ value: typeof v === "number" ? v : String(v),
943
+ description: void 0
944
+ }));
945
+ const rawDefault = schema.default;
940
946
  return {
941
947
  name,
942
- values: (schema.enum ?? []).map((v) => ({
943
- name: toUpperSnakeCase(String(v)),
944
- value: typeof v === "number" ? v : String(v),
945
- description: void 0
946
- }))
948
+ values,
949
+ default: rawDefault !== void 0 && values.some((v) => v.value === rawDefault) ? rawDefault : void 0
947
950
  };
948
951
  }
949
952
  function extractModel(name, schema, schemas) {
@@ -1808,7 +1811,7 @@ const HTTP_METHODS = [
1808
1811
  "options",
1809
1812
  "trace"
1810
1813
  ];
1811
- function extractOperations(paths, operationIdTransform) {
1814
+ function extractOperations(paths, operationIdTransform, componentSchemas) {
1812
1815
  if (!paths) return {
1813
1816
  services: [],
1814
1817
  inlineModels: []
@@ -1821,7 +1824,7 @@ function extractOperations(paths, operationIdTransform) {
1821
1824
  const op = pathItem[method];
1822
1825
  if (!op) continue;
1823
1826
  const serviceName = inferServiceName(path, op.tags?.[0]);
1824
- const { operation, inlineModels: opModels } = buildOperation(method, path, op, pathLevelParams, operationIdTransform, serviceName);
1827
+ const { operation, inlineModels: opModels } = buildOperation(method, path, op, pathLevelParams, operationIdTransform, serviceName, componentSchemas);
1825
1828
  inlineModels.push(...opModels);
1826
1829
  const ops = serviceMap.get(serviceName) ?? [];
1827
1830
  ops.push(operation);
@@ -2064,14 +2067,14 @@ function extractBodyParameterGroups(op, operationContext) {
2064
2067
  }
2065
2068
  return groups.length > 0 ? groups : void 0;
2066
2069
  }
2067
- function buildOperation(method, path, op, pathLevelParams, operationIdTransform, serviceName) {
2070
+ function buildOperation(method, path, op, pathLevelParams, operationIdTransform, serviceName, componentSchemas) {
2068
2071
  const allParams = [...pathLevelParams, ...op.parameters ?? []];
2069
2072
  const hasIdempotencyHeader = allParams.some((p) => p.in === "header" && p.name.toLowerCase() === "idempotency-key");
2070
2073
  const opContext = serviceName;
2071
- const pathParams = extractParams(allParams, "path", opContext);
2072
- const queryParams = extractParams(allParams, "query", opContext);
2073
- const headerParams = extractParams(allParams, "header", opContext).filter((p) => p.name.toLowerCase() !== "idempotency-key");
2074
- 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);
2075
2078
  const reqBodyModels = [];
2076
2079
  const { body: requestBody, encoding: requestBodyEncoding } = extractRequestBody(op.requestBody, op, reqBodyModels);
2077
2080
  const { response, successResponses, errors, inlineModels, isPaginated, dataPath: responseDataPath, itemType: responseItemType } = extractResponses(op.responses, op, path, method);
@@ -2143,21 +2146,42 @@ function buildDescription(summary, description) {
2143
2146
  if (summary && description && summary !== description) return `${summary}\n\n${description}`;
2144
2147
  return description ?? summary;
2145
2148
  }
2146
- function extractParams(params, location, operationContext) {
2147
- return params.filter((p) => p.in === location).map((p) => ({
2148
- name: p.name,
2149
- type: p.schema ? schemaToTypeRef(p.schema, p.name, operationContext ? toPascalCase(operationContext) : void 0) : {
2150
- kind: "primitive",
2151
- type: "string"
2152
- },
2153
- required: p.required ?? false,
2154
- description: p.description,
2155
- deprecated: p.deprecated || p.schema?.deprecated || void 0,
2156
- default: p.schema?.default,
2157
- example: p.example ?? p.schema?.example,
2158
- style: p.style,
2159
- explode: p.explode
2160
- }));
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]];
2161
2185
  }
2162
2186
  function extractRequestBody(body, op, inlineModels) {
2163
2187
  if (!body?.content) return {};
@@ -2511,7 +2535,7 @@ async function parseSpec(specPath, options) {
2511
2535
  const version = spec.openapi ?? "";
2512
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}\`.`);
2513
2537
  const { models, enums } = extractSchemas(spec.components?.schemas, { schemaNameTransform: options?.schemaNameTransform });
2514
- const { services, inlineModels } = extractOperations(spec.paths, options?.operationIdTransform);
2538
+ const { services, inlineModels } = extractOperations(spec.paths, options?.operationIdTransform, spec.components?.schemas);
2515
2539
  clearSchemaNameTransform();
2516
2540
  const finalModels = collapseJsonSuffixModels(mergeFieldInlineModels(mergeInlineResponseModels(models, inlineModels), extractInlineModelsFromSchemas(spec.components?.schemas)), services);
2517
2541
  collectInlineEnumsFromModels(finalModels, enums);
@@ -6220,6 +6244,11 @@ function classifyFieldChange(kind, fieldName, isRequired) {
6220
6244
  fieldName,
6221
6245
  classification: "breaking"
6222
6246
  };
6247
+ case "field-format-changed": return {
6248
+ kind,
6249
+ fieldName,
6250
+ classification: "additive"
6251
+ };
6223
6252
  case "field-required-changed": return {
6224
6253
  kind,
6225
6254
  fieldName,
@@ -6250,11 +6279,21 @@ function classifyParamChange(kind, paramName, isRequired) {
6250
6279
  paramName,
6251
6280
  classification: "breaking"
6252
6281
  };
6282
+ case "param-format-changed": return {
6283
+ kind,
6284
+ paramName,
6285
+ classification: "additive"
6286
+ };
6253
6287
  case "param-required-changed": return {
6254
6288
  kind,
6255
6289
  paramName,
6256
6290
  classification: isRequired ? "breaking" : "additive"
6257
6291
  };
6292
+ case "param-default-changed": return {
6293
+ kind,
6294
+ paramName,
6295
+ classification: "breaking"
6296
+ };
6258
6297
  }
6259
6298
  }
6260
6299
  //#endregion
@@ -6298,7 +6337,11 @@ function diffFields(oldModel, newModel) {
6298
6337
  for (const [name, newField] of newByName) {
6299
6338
  const oldField = oldByName.get(name);
6300
6339
  if (!oldField) continue;
6301
- 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({
6302
6345
  kind: "field-type-changed",
6303
6346
  fieldName: name,
6304
6347
  classification: "breaking",
@@ -6309,6 +6352,23 @@ function diffFields(oldModel, newModel) {
6309
6352
  }
6310
6353
  return changes;
6311
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
+ }
6312
6372
  function typeRefsEqual(a, b) {
6313
6373
  if (a.kind !== b.kind) return false;
6314
6374
  switch (a.kind) {
@@ -6410,28 +6470,57 @@ function classifyErrorsChange(oldErrors, newErrors) {
6410
6470
  }
6411
6471
  function diffParams(oldOp, newOp) {
6412
6472
  const changes = [];
6473
+ const tagged = (loc, params) => params.map((p) => ({
6474
+ p,
6475
+ loc
6476
+ }));
6413
6477
  const allOldParams = [
6414
- ...oldOp.pathParams,
6415
- ...oldOp.queryParams,
6416
- ...oldOp.headerParams
6478
+ ...tagged("path", oldOp.pathParams),
6479
+ ...tagged("query", oldOp.queryParams),
6480
+ ...tagged("header", oldOp.headerParams)
6417
6481
  ];
6418
6482
  const allNewParams = [
6419
- ...newOp.pathParams,
6420
- ...newOp.queryParams,
6421
- ...newOp.headerParams
6483
+ ...tagged("path", newOp.pathParams),
6484
+ ...tagged("query", newOp.queryParams),
6485
+ ...tagged("header", newOp.headerParams)
6422
6486
  ];
6423
- const oldByName = new Map(allOldParams.map((p) => [p.name, p]));
6424
- const newByName = new Map(allNewParams.map((p) => [p.name, p]));
6425
- 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));
6426
6490
  for (const [name] of oldByName) if (!newByName.has(name)) changes.push(classifyParamChange("param-removed", name));
6427
- for (const [name, newParam] of newByName) {
6428
- const oldParam = oldByName.get(name);
6429
- if (!oldParam) continue;
6430
- if (!typeRefsEqual(oldParam.type, newParam.type)) changes.push(classifyParamChange("param-type-changed", name));
6431
- 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
+ }
6432
6510
  }
6433
6511
  return changes;
6434
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
+ }
6435
6524
  //#endregion
6436
6525
  //#region src/differ/services.ts
6437
6526
  function diffServices(oldServices, newServices) {
@@ -6463,13 +6552,38 @@ function diffSpecs(oldSpec, newSpec) {
6463
6552
  ...diffEnums(oldSpec.enums, newSpec.enums),
6464
6553
  ...diffServices(oldSpec.services, newSpec.services)
6465
6554
  ];
6555
+ const behaviorChanges = collectBehaviorChanges(changes);
6466
6556
  return {
6467
6557
  oldVersion: oldSpec.version,
6468
6558
  newVersion: newSpec.version,
6469
6559
  changes,
6470
- summary: summarize(changes)
6560
+ behaviorChanges,
6561
+ summary: {
6562
+ ...summarize(changes),
6563
+ behaviorChanges: behaviorChanges.length
6564
+ }
6471
6565
  };
6472
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
+ }
6473
6587
  function diffEnums(oldEnums, newEnums) {
6474
6588
  const changes = [];
6475
6589
  const oldByName = new Map(oldEnums.map((e) => [e.name, e]));