@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.
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +14 -0
- package/dist/index.mjs +1 -1
- package/dist/{plugin-D2N2ZT5W.mjs → plugin-Ca9LUkWW.mjs} +95 -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 +87 -6
- package/src/node/tests.ts +48 -8
- package/test/node/models.test.ts +88 -0
- package/test/node/tests.test.ts +57 -0
- package/dist/plugin-D2N2ZT5W.mjs.map +0 -1
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-
|
|
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,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
|
|
7371
|
-
const
|
|
7372
|
-
|
|
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
|
|
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
|
|
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-
|
|
26634
|
+
//# sourceMappingURL=plugin-Ca9LUkWW.mjs.map
|