@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.
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +7 -0
- package/dist/index.mjs +1 -1
- package/dist/{plugin-nmiHN7Ko.mjs → plugin-Ca9LUkWW.mjs} +69 -23
- package/dist/plugin-Ca9LUkWW.mjs.map +1 -0
- package/dist/plugin.mjs +1 -1
- package/package.json +1 -1
- package/src/node/field-plan.ts +27 -13
- package/src/node/models.ts +52 -6
- package/src/node/tests.ts +48 -8
- package/test/node/models.test.ts +56 -0
- package/test/node/tests.test.ts +57 -0
- package/dist/plugin-nmiHN7Ko.mjs.map +0 -1
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-
|
|
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
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
const
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
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
|
|
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
|
|
7397
|
-
const
|
|
7398
|
-
|
|
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
|
|
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
|
|
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-
|
|
26634
|
+
//# sourceMappingURL=plugin-Ca9LUkWW.mjs.map
|