@workos/oagen-emitters 0.12.4 → 0.12.5

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.
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.12.4"
2
+ ".": "0.12.5"
3
3
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.12.5](https://github.com/workos/oagen-emitters/compare/v0.12.4...v0.12.5) (2026-05-19)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **node:** skip dead deserializers and strengthen all-optional body tests ([#111](https://github.com/workos/oagen-emitters/issues/111)) ([632bd82](https://github.com/workos/oagen-emitters/commit/632bd8257d4139ab53895b5225fffd6913702e3b))
9
+
3
10
  ## [0.12.4](https://github.com/workos/oagen-emitters/compare/v0.12.3...v0.12.4) (2026-05-19)
4
11
 
5
12
 
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { _ as pythonEmitter, a as rustExtractor, c as pythonExtractor, d as rustEmitter, f as rubyEmitter, g as phpEmitter, h as goEmitter, i as kotlinExtractor, l as rubyExtractor, m as dotnetEmitter, n as elixirExtractor, o as goExtractor, p as kotlinEmitter, r as dotnetExtractor, s as phpExtractor, t as workosEmittersPlugin, u as nodeExtractor, v as nodeEmitter } from "./plugin-nmiHN7Ko.mjs";
1
+ import { _ as pythonEmitter, a as rustExtractor, c as pythonExtractor, d as rustEmitter, f as rubyEmitter, g as phpEmitter, h as goEmitter, i as kotlinExtractor, l as rubyExtractor, m as dotnetEmitter, n as elixirExtractor, o as goExtractor, p as kotlinEmitter, r as dotnetExtractor, s as phpExtractor, t as workosEmittersPlugin, u as nodeExtractor, v as nodeEmitter } from "./plugin-Ca9LUkWW.mjs";
2
2
  export { dotnetEmitter, dotnetExtractor, elixirExtractor, goEmitter, goExtractor, kotlinEmitter, kotlinExtractor, nodeEmitter, nodeExtractor, phpEmitter, phpExtractor, pythonEmitter, pythonExtractor, rubyEmitter, rubyExtractor, rustEmitter, rustExtractor, workosEmittersPlugin };
@@ -4073,6 +4073,7 @@ function buildSerializerImports(model, serializerPath, dirName, domainName, resp
4073
4073
  const rel = relativeImport(serializerPath, depSerializerPath);
4074
4074
  const canon = sctx.dedup.get(dep);
4075
4075
  const depSkipSerialize = sctx.skippedSerializeModels.has(dep) || canon != null && sctx.skippedSerializeModels.has(canon);
4076
+ const depSkipDeserialize = sctx.responseReachableModels !== void 0 && !sctx.responseReachableModels.has(dep) && (canon == null || !sctx.responseReachableModels.has(canon));
4076
4077
  const hasDeser = liveSurfaceHasFunction(`deserialize${depName}`);
4077
4078
  const hasSer = liveSurfaceHasFunction(`serialize${depName}`);
4078
4079
  const fileExists = liveSurfaceHasFile(depSerializerPath);
@@ -4093,7 +4094,9 @@ function buildSerializerImports(model, serializerPath, dirName, domainName, resp
4093
4094
  lines.push(`import { serialize${depName} } from '${rel}';`);
4094
4095
  continue;
4095
4096
  }
4097
+ if (depSkipSerialize && depSkipDeserialize) continue;
4096
4098
  if (depSkipSerialize) lines.push(`import { deserialize${depName} } from '${rel}';`);
4099
+ else if (depSkipDeserialize) lines.push(`import { serialize${depName} } from '${rel}';`);
4097
4100
  else lines.push(`import { deserialize${depName}, serialize${depName} } from '${rel}';`);
4098
4101
  }
4099
4102
  lines.push("");
@@ -4129,24 +4132,26 @@ function shouldSkipSerializeForModel(model, baselineResponse, baselineDomain, de
4129
4132
  }
4130
4133
  return shouldSkip;
4131
4134
  }
4132
- function emitSerializerBody(model, domainName, responseName, typeParams, baselineDomain, baselineResponse, skipFormatFields, shouldSkipSerialize, ctx) {
4135
+ function emitSerializerBody(model, domainName, responseName, typeParams, baselineDomain, baselineResponse, skipFormatFields, shouldSkipSerialize, shouldSkipDeserialize, ctx) {
4133
4136
  const lines = [];
4134
- const seenDeserFields = /* @__PURE__ */ new Set();
4135
- const deserParamPrefix = model.fields.length === 0 ? "_" : "";
4136
- lines.push(`export const deserialize${domainName} = ${typeParams.decl}(`);
4137
- lines.push(` ${deserParamPrefix}response: ${responseName}${typeParams.usage},`);
4138
- lines.push(`): ${domainName}${typeParams.usage} => ({`);
4139
- for (const field of model.fields) {
4140
- const domain = fieldName$6(field.name);
4141
- if (seenDeserFields.has(domain)) continue;
4142
- seenDeserFields.add(domain);
4143
- const plan = planDeserializeField(field, baselineDomain, baselineResponse, skipFormatFields, ctx);
4144
- if (!plan.skip) lines.push(plan.line);
4137
+ if (!shouldSkipDeserialize) {
4138
+ const seenDeserFields = /* @__PURE__ */ new Set();
4139
+ const deserParamPrefix = model.fields.length === 0 ? "_" : "";
4140
+ lines.push(`export const deserialize${domainName} = ${typeParams.decl}(`);
4141
+ lines.push(` ${deserParamPrefix}response: ${responseName}${typeParams.usage},`);
4142
+ lines.push(`): ${domainName}${typeParams.usage} => ({`);
4143
+ for (const field of model.fields) {
4144
+ const domain = fieldName$6(field.name);
4145
+ if (seenDeserFields.has(domain)) continue;
4146
+ seenDeserFields.add(domain);
4147
+ const plan = planDeserializeField(field, baselineDomain, baselineResponse, skipFormatFields, ctx);
4148
+ if (!plan.skip) lines.push(plan.line);
4149
+ }
4150
+ lines.push("});");
4145
4151
  }
4146
- lines.push("});");
4147
4152
  if (!shouldSkipSerialize) {
4153
+ if (!shouldSkipDeserialize) lines.push("");
4148
4154
  const serParamPrefix = model.fields.length === 0 ? "_" : "";
4149
- lines.push("");
4150
4155
  lines.push(`export const serialize${domainName} = ${typeParams.decl}(`);
4151
4156
  lines.push(` ${serParamPrefix}model: ${domainName}${typeParams.usage},`);
4152
4157
  lines.push(`): ${responseName}${typeParams.usage} => ({`);
@@ -6217,6 +6222,7 @@ function generateSerializers(models, ctx, shared) {
6217
6222
  const projectedByName = new Map(projectedModels.map((model) => [model.name, model]));
6218
6223
  const resourceUsage = buildGeneratedResourceModelUsage(models, ctx);
6219
6224
  const serializerEligibleModels = resourceUsage ? expandModelRoots(resourceUsage.serializerRoots, projectedByName) : void 0;
6225
+ const responseReachableModels = resourceUsage ? expandModelRoots(resourceUsage.responseRoots, projectedByName) : void 0;
6220
6226
  const serializerReachable = computeNonEventReachable(ctx.spec.services, models);
6221
6227
  const liveRoot = ctx.targetDir ?? ctx.outputDir;
6222
6228
  if (liveRoot) for (const originalModel of models) {
@@ -6280,7 +6286,13 @@ function generateSerializers(models, ctx, shared) {
6280
6286
  if (serializerPath === canonSerializerPath) continue;
6281
6287
  if (domainName === canonDomainName) continue;
6282
6288
  const rel = relativeImport(serializerPath, canonSerializerPath);
6283
- const reexportContent = skippedSerializeModels.has(canonicalName) || skippedSerializeModels.has(model.name) ? `export { deserialize${canonDomainName} as deserialize${domainName} } from '${rel}';` : `export { deserialize${canonDomainName} as deserialize${domainName}, serialize${canonDomainName} as serialize${domainName} } from '${rel}';`;
6289
+ const canonSkipSerialize = skippedSerializeModels.has(canonicalName) || skippedSerializeModels.has(model.name);
6290
+ const canonSkipDeserialize = responseReachableModels !== void 0 && !responseReachableModels.has(canonicalName) && !responseReachableModels.has(model.name);
6291
+ if (canonSkipSerialize && canonSkipDeserialize) continue;
6292
+ const parts = [];
6293
+ if (!canonSkipDeserialize) parts.push(`deserialize${canonDomainName} as deserialize${domainName}`);
6294
+ if (!canonSkipSerialize) parts.push(`serialize${canonDomainName} as serialize${domainName}`);
6295
+ const reexportContent = `export { ${parts.join(", ")} } from '${rel}';`;
6284
6296
  files.push({
6285
6297
  path: serializerPath,
6286
6298
  content: reexportContent,
@@ -6298,13 +6310,16 @@ function generateSerializers(models, ctx, shared) {
6298
6310
  const baselineDomain = ctx.apiSurface?.interfaces?.[domainName];
6299
6311
  const skipFormatFields = buildSkipFormatFields(model, baselineDomain);
6300
6312
  const shouldSkipSerialize = skippedSerializeModels.has(model.name);
6313
+ const shouldSkipDeserialize = responseReachableModels !== void 0 && !responseReachableModels.has(model.name);
6314
+ if (shouldSkipSerialize && shouldSkipDeserialize) continue;
6301
6315
  const lines = [...buildSerializerImports(model, serializerPath, dirName, domainName, responseName, {
6302
6316
  modelToService,
6303
6317
  resolveDir,
6304
6318
  dedup,
6305
6319
  skippedSerializeModels,
6320
+ responseReachableModels,
6306
6321
  ctx
6307
- }), ...emitSerializerBody(model, domainName, responseName, typeParams, baselineDomain, baselineResponse, skipFormatFields, shouldSkipSerialize, ctx)];
6322
+ }), ...emitSerializerBody(model, domainName, responseName, typeParams, baselineDomain, baselineResponse, skipFormatFields, shouldSkipSerialize, shouldSkipDeserialize, ctx)];
6308
6323
  files.push({
6309
6324
  path: serializerPath,
6310
6325
  content: pruneUnusedImports(lines).join("\n"),
@@ -6312,6 +6327,7 @@ function generateSerializers(models, ctx, shared) {
6312
6327
  });
6313
6328
  }
6314
6329
  ctx._skippedSerializeModels = skippedSerializeModels;
6330
+ ctx._responseReachableModels = responseReachableModels;
6315
6331
  const serializersByDir = /* @__PURE__ */ new Map();
6316
6332
  for (const f of files) {
6317
6333
  const match = f.path.match(/^src\/([^/]+)\/serializers\/(.+)\.serializer\.ts$/);
@@ -6357,6 +6373,8 @@ function buildGeneratedResourceModelUsage(models, ctx) {
6357
6373
  const modelMap = new Map(models.map((model) => [model.name, model]));
6358
6374
  const interfaceRoots = /* @__PURE__ */ new Set();
6359
6375
  const serializerRoots = /* @__PURE__ */ new Set();
6376
+ const requestRoots = /* @__PURE__ */ new Set();
6377
+ const responseRoots = /* @__PURE__ */ new Set();
6360
6378
  const resolvedLookup = buildResolvedLookup(ctx);
6361
6379
  const mountGroups = groupByMount(ctx);
6362
6380
  const services = mountGroups.size > 0 ? [...mountGroups].map(([name, group]) => ({
@@ -6385,15 +6403,18 @@ function buildGeneratedResourceModelUsage(models, ctx) {
6385
6403
  if (unwrapped) itemName = unwrapped.name;
6386
6404
  interfaceRoots.add(itemName);
6387
6405
  serializerRoots.add(itemName);
6406
+ responseRoots.add(itemName);
6388
6407
  }
6389
6408
  } else if (plan.responseModelName) {
6390
6409
  interfaceRoots.add(plan.responseModelName);
6391
6410
  serializerRoots.add(plan.responseModelName);
6411
+ responseRoots.add(plan.responseModelName);
6392
6412
  }
6393
6413
  const bodyInfo = extractRequestBodyModels(op, ctx);
6394
6414
  for (const name of bodyInfo) {
6395
6415
  interfaceRoots.add(name);
6396
6416
  serializerRoots.add(name);
6417
+ requestRoots.add(name);
6397
6418
  }
6398
6419
  for (const param of [
6399
6420
  ...op.pathParams,
@@ -6405,6 +6426,7 @@ function buildGeneratedResourceModelUsage(models, ctx) {
6405
6426
  for (const name of collectWrapperResponseModels$1(resolved)) {
6406
6427
  interfaceRoots.add(name);
6407
6428
  serializerRoots.add(name);
6429
+ responseRoots.add(name);
6408
6430
  }
6409
6431
  for (const wrapper of resolved.wrappers ?? []) for (const { field } of resolveWrapperParams(wrapper, ctx)) if (field) collectTypeRefModels(field.type, interfaceRoots);
6410
6432
  }
@@ -6412,7 +6434,9 @@ function buildGeneratedResourceModelUsage(models, ctx) {
6412
6434
  }
6413
6435
  return {
6414
6436
  interfaceRoots,
6415
- serializerRoots
6437
+ serializerRoots,
6438
+ requestRoots,
6439
+ responseRoots
6416
6440
  };
6417
6441
  }
6418
6442
  function extractRequestBodyModels(op, ctx) {
@@ -7393,12 +7417,20 @@ function buildTestPayload(op, modelMap) {
7393
7417
  model = modelMap.get(firstVariant.name);
7394
7418
  } else if (op.requestBody.kind === "model") model = modelMap.get(op.requestBody.name);
7395
7419
  if (!model) return null;
7396
- const fields = model.fields.filter((f) => f.required);
7397
- const usableFields = fields.filter((f) => fixtureValueForType(f.type, f.name, model.name, modelMap) !== null);
7398
- if (usableFields.length === 0 || usableFields.length < fields.length) return null;
7420
+ const requiredFields = model.fields.filter((f) => f.required);
7421
+ const usableRequired = requiredFields.filter((f) => fixtureValueForType(f.type, f.name, model.name, modelMap) !== null);
7422
+ let chosenFields;
7423
+ if (requiredFields.length > 0) {
7424
+ if (usableRequired.length < requiredFields.length) return null;
7425
+ chosenFields = usableRequired;
7426
+ } else {
7427
+ const usableOptional = model.fields.filter((f) => !f.required && fixtureValueForType(f.type, f.name, model.name, modelMap) !== null);
7428
+ if (usableOptional.length === 0) return null;
7429
+ chosenFields = usableOptional.slice(0, 2);
7430
+ }
7399
7431
  const camelEntries = [];
7400
7432
  const snakeEntries = [];
7401
- for (const field of usableFields) {
7433
+ for (const field of chosenFields) {
7402
7434
  const camelValue = fixtureValueForType(field.type, field.name, model.name, modelMap);
7403
7435
  const wireValue = fixtureValueForType(field.type, field.name, model.name, modelMap, true);
7404
7436
  const camelKey = fieldName$6(field.name);
@@ -7458,6 +7490,7 @@ function generateSerializerTests(spec, ctx) {
7458
7490
  });
7459
7491
  if (eligibleModels.length === 0) return files;
7460
7492
  const serializeSkipped = ctx._skippedSerializeModels ?? /* @__PURE__ */ new Set();
7493
+ const responseReachableModels = ctx._responseReachableModels;
7461
7494
  const modelsByDir = /* @__PURE__ */ new Map();
7462
7495
  for (const model of eligibleModels) {
7463
7496
  const dirName = resolveDir(modelToService.get(model.name));
@@ -7472,15 +7505,20 @@ function generateSerializerTests(spec, ctx) {
7472
7505
  const interfaceImports = [];
7473
7506
  const fixtureImports = [];
7474
7507
  const deserializeOnlyModels = /* @__PURE__ */ new Set();
7508
+ const serializeOnlyModels = /* @__PURE__ */ new Set();
7475
7509
  for (const model of models) {
7476
7510
  const domainName = resolveInterfaceName(model.name, ctx);
7477
7511
  const modelDir = resolveDir(modelToService.get(model.name));
7478
7512
  const serializerPath = `src/${modelDir}/serializers/${fileName$3(model.name)}.serializer.ts`;
7479
7513
  const interfacePath = `src/${modelDir}/interfaces/${fileName$3(model.name)}.interface.ts`;
7480
7514
  const fixturePath = `src/${modelDir}/fixtures/${fileName$3(model.name)}.json`;
7481
- const deserializeOnly = serializeSkipped.has(model.name) || fixtureIsHandOwned(fixturePath, ctx);
7515
+ const isRequestOnly = responseReachableModels !== void 0 && !responseReachableModels.has(model.name);
7516
+ const deserializeOnly = !isRequestOnly && (serializeSkipped.has(model.name) || fixtureIsHandOwned(fixturePath, ctx));
7517
+ const serializeOnly = isRequestOnly;
7482
7518
  if (deserializeOnly) deserializeOnlyModels.add(model.name);
7519
+ if (serializeOnly) serializeOnlyModels.add(model.name);
7483
7520
  if (deserializeOnly) serializerImports.push(`import { deserialize${domainName} } from '${relativeImport(testPath, serializerPath)}';`);
7521
+ else if (serializeOnly) serializerImports.push(`import { serialize${domainName} } from '${relativeImport(testPath, serializerPath)}';`);
7484
7522
  else serializerImports.push(`import { deserialize${domainName}, serialize${domainName} } from '${relativeImport(testPath, serializerPath)}';`);
7485
7523
  const wireName = wireInterfaceName(domainName);
7486
7524
  interfaceImports.push(`import type { ${wireName} } from '${relativeImport(testPath, interfacePath)}';`);
@@ -7503,6 +7541,14 @@ function generateSerializerTests(spec, ctx) {
7503
7541
  lines.push(" expect(deserialized).toBeDefined();");
7504
7542
  lines.push(" });");
7505
7543
  lines.push("});");
7544
+ } else if (serializeOnlyModels.has(model.name)) {
7545
+ lines.push(`describe('${domainName}Serializer', () => {`);
7546
+ lines.push(" it('serializes correctly', () => {");
7547
+ lines.push(` const fixture = ${fixtureName} as ${wireName};`);
7548
+ lines.push(` const serialized = serialize${domainName}(fixture as any);`);
7549
+ lines.push(" expect(serialized).toBeDefined();");
7550
+ lines.push(" });");
7551
+ lines.push("});");
7506
7552
  } else {
7507
7553
  lines.push(`describe('${domainName}Serializer', () => {`);
7508
7554
  lines.push(" it('round-trips through serialize/deserialize', () => {");
@@ -26585,4 +26631,4 @@ const workosEmittersPlugin = {
26585
26631
  //#endregion
26586
26632
  export { pythonEmitter as _, rustExtractor as a, pythonExtractor as c, rustEmitter as d, rubyEmitter as f, phpEmitter as g, goEmitter as h, kotlinExtractor as i, rubyExtractor as l, dotnetEmitter as m, elixirExtractor as n, goExtractor as o, kotlinEmitter as p, dotnetExtractor as r, phpExtractor as s, workosEmittersPlugin as t, nodeExtractor as u, nodeEmitter as v };
26587
26633
 
26588
- //# sourceMappingURL=plugin-nmiHN7Ko.mjs.map
26634
+ //# sourceMappingURL=plugin-Ca9LUkWW.mjs.map