@workos/oagen-emitters 0.12.3 → 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.3"
2
+ ".": "0.12.5"
3
3
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
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
+
10
+ ## [0.12.4](https://github.com/workos/oagen-emitters/compare/v0.12.3...v0.12.4) (2026-05-19)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **node:** emit serializers/index.ts barrel for owned and adopted services ([#109](https://github.com/workos/oagen-emitters/issues/109)) ([e176552](https://github.com/workos/oagen-emitters/commit/e17655296de50c9df759b84273b104ae828cd08e))
16
+
3
17
  ## [0.12.3](https://github.com/workos/oagen-emitters/compare/v0.12.2...v0.12.3) (2026-05-18)
4
18
 
5
19
 
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-D2N2ZT5W.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,33 @@ function generateSerializers(models, ctx, shared) {
6312
6327
  });
6313
6328
  }
6314
6329
  ctx._skippedSerializeModels = skippedSerializeModels;
6330
+ ctx._responseReachableModels = responseReachableModels;
6331
+ const serializersByDir = /* @__PURE__ */ new Map();
6332
+ for (const f of files) {
6333
+ const match = f.path.match(/^src\/([^/]+)\/serializers\/(.+)\.serializer\.ts$/);
6334
+ if (!match) continue;
6335
+ const [, dir, stem] = match;
6336
+ if (!serializersByDir.has(dir)) serializersByDir.set(dir, /* @__PURE__ */ new Set());
6337
+ serializersByDir.get(dir).add(stem);
6338
+ }
6339
+ const liveRootForBarrel = ctx.outputDir ?? ctx.targetDir;
6340
+ for (const [dir, stems] of serializersByDir) {
6341
+ if (liveRootForBarrel) {
6342
+ const serializersDir = path.join(liveRootForBarrel, "src", dir, "serializers");
6343
+ try {
6344
+ for (const entry of fs.readdirSync(serializersDir)) {
6345
+ if (!entry.endsWith(".serializer.ts")) continue;
6346
+ stems.add(entry.replace(/\.serializer\.ts$/, ""));
6347
+ }
6348
+ } catch {}
6349
+ }
6350
+ const lines = [...stems].sort().map((stem) => `export * from './${stem}.serializer';`);
6351
+ files.push({
6352
+ path: `src/${dir}/serializers/index.ts`,
6353
+ content: lines.join("\n") + "\n",
6354
+ overwriteExisting: true
6355
+ });
6356
+ }
6315
6357
  return files;
6316
6358
  }
6317
6359
  function generateModelsAndSerializers(models, ctx) {
@@ -6331,6 +6373,8 @@ function buildGeneratedResourceModelUsage(models, ctx) {
6331
6373
  const modelMap = new Map(models.map((model) => [model.name, model]));
6332
6374
  const interfaceRoots = /* @__PURE__ */ new Set();
6333
6375
  const serializerRoots = /* @__PURE__ */ new Set();
6376
+ const requestRoots = /* @__PURE__ */ new Set();
6377
+ const responseRoots = /* @__PURE__ */ new Set();
6334
6378
  const resolvedLookup = buildResolvedLookup(ctx);
6335
6379
  const mountGroups = groupByMount(ctx);
6336
6380
  const services = mountGroups.size > 0 ? [...mountGroups].map(([name, group]) => ({
@@ -6359,15 +6403,18 @@ function buildGeneratedResourceModelUsage(models, ctx) {
6359
6403
  if (unwrapped) itemName = unwrapped.name;
6360
6404
  interfaceRoots.add(itemName);
6361
6405
  serializerRoots.add(itemName);
6406
+ responseRoots.add(itemName);
6362
6407
  }
6363
6408
  } else if (plan.responseModelName) {
6364
6409
  interfaceRoots.add(plan.responseModelName);
6365
6410
  serializerRoots.add(plan.responseModelName);
6411
+ responseRoots.add(plan.responseModelName);
6366
6412
  }
6367
6413
  const bodyInfo = extractRequestBodyModels(op, ctx);
6368
6414
  for (const name of bodyInfo) {
6369
6415
  interfaceRoots.add(name);
6370
6416
  serializerRoots.add(name);
6417
+ requestRoots.add(name);
6371
6418
  }
6372
6419
  for (const param of [
6373
6420
  ...op.pathParams,
@@ -6379,6 +6426,7 @@ function buildGeneratedResourceModelUsage(models, ctx) {
6379
6426
  for (const name of collectWrapperResponseModels$1(resolved)) {
6380
6427
  interfaceRoots.add(name);
6381
6428
  serializerRoots.add(name);
6429
+ responseRoots.add(name);
6382
6430
  }
6383
6431
  for (const wrapper of resolved.wrappers ?? []) for (const { field } of resolveWrapperParams(wrapper, ctx)) if (field) collectTypeRefModels(field.type, interfaceRoots);
6384
6432
  }
@@ -6386,7 +6434,9 @@ function buildGeneratedResourceModelUsage(models, ctx) {
6386
6434
  }
6387
6435
  return {
6388
6436
  interfaceRoots,
6389
- serializerRoots
6437
+ serializerRoots,
6438
+ requestRoots,
6439
+ responseRoots
6390
6440
  };
6391
6441
  }
6392
6442
  function extractRequestBodyModels(op, ctx) {
@@ -7367,12 +7417,20 @@ function buildTestPayload(op, modelMap) {
7367
7417
  model = modelMap.get(firstVariant.name);
7368
7418
  } else if (op.requestBody.kind === "model") model = modelMap.get(op.requestBody.name);
7369
7419
  if (!model) return null;
7370
- const fields = model.fields.filter((f) => f.required);
7371
- const usableFields = fields.filter((f) => fixtureValueForType(f.type, f.name, model.name, modelMap) !== null);
7372
- 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
+ }
7373
7431
  const camelEntries = [];
7374
7432
  const snakeEntries = [];
7375
- for (const field of usableFields) {
7433
+ for (const field of chosenFields) {
7376
7434
  const camelValue = fixtureValueForType(field.type, field.name, model.name, modelMap);
7377
7435
  const wireValue = fixtureValueForType(field.type, field.name, model.name, modelMap, true);
7378
7436
  const camelKey = fieldName$6(field.name);
@@ -7432,6 +7490,7 @@ function generateSerializerTests(spec, ctx) {
7432
7490
  });
7433
7491
  if (eligibleModels.length === 0) return files;
7434
7492
  const serializeSkipped = ctx._skippedSerializeModels ?? /* @__PURE__ */ new Set();
7493
+ const responseReachableModels = ctx._responseReachableModels;
7435
7494
  const modelsByDir = /* @__PURE__ */ new Map();
7436
7495
  for (const model of eligibleModels) {
7437
7496
  const dirName = resolveDir(modelToService.get(model.name));
@@ -7446,15 +7505,20 @@ function generateSerializerTests(spec, ctx) {
7446
7505
  const interfaceImports = [];
7447
7506
  const fixtureImports = [];
7448
7507
  const deserializeOnlyModels = /* @__PURE__ */ new Set();
7508
+ const serializeOnlyModels = /* @__PURE__ */ new Set();
7449
7509
  for (const model of models) {
7450
7510
  const domainName = resolveInterfaceName(model.name, ctx);
7451
7511
  const modelDir = resolveDir(modelToService.get(model.name));
7452
7512
  const serializerPath = `src/${modelDir}/serializers/${fileName$3(model.name)}.serializer.ts`;
7453
7513
  const interfacePath = `src/${modelDir}/interfaces/${fileName$3(model.name)}.interface.ts`;
7454
7514
  const fixturePath = `src/${modelDir}/fixtures/${fileName$3(model.name)}.json`;
7455
- 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;
7456
7518
  if (deserializeOnly) deserializeOnlyModels.add(model.name);
7519
+ if (serializeOnly) serializeOnlyModels.add(model.name);
7457
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)}';`);
7458
7522
  else serializerImports.push(`import { deserialize${domainName}, serialize${domainName} } from '${relativeImport(testPath, serializerPath)}';`);
7459
7523
  const wireName = wireInterfaceName(domainName);
7460
7524
  interfaceImports.push(`import type { ${wireName} } from '${relativeImport(testPath, interfacePath)}';`);
@@ -7477,6 +7541,14 @@ function generateSerializerTests(spec, ctx) {
7477
7541
  lines.push(" expect(deserialized).toBeDefined();");
7478
7542
  lines.push(" });");
7479
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("});");
7480
7552
  } else {
7481
7553
  lines.push(`describe('${domainName}Serializer', () => {`);
7482
7554
  lines.push(" it('round-trips through serialize/deserialize', () => {");
@@ -26559,4 +26631,4 @@ const workosEmittersPlugin = {
26559
26631
  //#endregion
26560
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 };
26561
26633
 
26562
- //# sourceMappingURL=plugin-D2N2ZT5W.mjs.map
26634
+ //# sourceMappingURL=plugin-Ca9LUkWW.mjs.map