@workos/oagen-emitters 0.12.2 → 0.12.3
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.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{plugin-eCuvoL1T.mjs → plugin-D2N2ZT5W.mjs} +63 -24
- package/dist/plugin-D2N2ZT5W.mjs.map +1 -0
- package/dist/plugin.mjs +1 -1
- package/package.json +4 -4
- package/renovate.json +1 -1
- package/src/node/index.ts +11 -2
- package/src/node/models.ts +7 -0
- package/src/node/naming.ts +3 -0
- package/src/node/path-expression.ts +11 -4
- package/src/node/resources.ts +23 -7
- package/src/node/tests.ts +44 -10
- package/test/node/resources.test.ts +70 -0
- package/test/node/tests.test.ts +95 -0
- package/dist/plugin-eCuvoL1T.mjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.12.3](https://github.com/workos/oagen-emitters/compare/v0.12.2...v0.12.3) (2026-05-18)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **node:** generate tests for adopted services and fix related bugs ([#107](https://github.com/workos/oagen-emitters/issues/107)) ([abe877b](https://github.com/workos/oagen-emitters/commit/abe877bd6489365d0f145a1739a7c449b15760c2))
|
|
9
|
+
|
|
3
10
|
## [0.12.2](https://github.com/workos/oagen-emitters/compare/v0.12.1...v0.12.2) (2026-05-15)
|
|
4
11
|
|
|
5
12
|
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/node/index.ts","../src/python/index.ts","../src/php/index.ts","../src/go/index.ts","../src/dotnet/index.ts","../src/kotlin/index.ts","../src/ruby/index.ts","../src/rust/index.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/node/index.ts","../src/python/index.ts","../src/php/index.ts","../src/go/index.ts","../src/dotnet/index.ts","../src/kotlin/index.ts","../src/ruby/index.ts","../src/rust/index.ts"],"mappings":";;;;;cAgWa,WAAA,EAAa,OAwFzB;;;cCjZY,aAAA,EAAe,OA+D3B;;;cClEY,UAAA,EAAY,OA2DxB;;;cClEY,SAAA,EAAW,OAyEvB;;;cCnCY,aAAA,EAAe,OAiR3B;;;cCrTY,aAAA,EAAe,OA6E3B;;;cChFY,WAAA,EAAa,OAkEzB;;;cC1DY,WAAA,EAAa,OA0DzB"}
|
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-D2N2ZT5W.mjs";
|
|
2
2
|
export { dotnetEmitter, dotnetExtractor, elixirExtractor, goEmitter, goExtractor, kotlinEmitter, kotlinExtractor, nodeEmitter, nodeExtractor, phpEmitter, phpExtractor, pythonEmitter, pythonExtractor, rubyEmitter, rubyExtractor, rustEmitter, rustExtractor, workosEmittersPlugin };
|
|
@@ -338,6 +338,9 @@ let adoptedModelNames = /* @__PURE__ */ new Set();
|
|
|
338
338
|
function setAdoptedModelNames(names) {
|
|
339
339
|
adoptedModelNames = names;
|
|
340
340
|
}
|
|
341
|
+
function isAdoptedModelName(name) {
|
|
342
|
+
return adoptedModelNames.has(name);
|
|
343
|
+
}
|
|
341
344
|
/**
|
|
342
345
|
* Wire/response interface name.
|
|
343
346
|
*
|
|
@@ -4291,17 +4294,23 @@ function hasPathParams(segments) {
|
|
|
4291
4294
|
* "/orgs" → `'orgs'`
|
|
4292
4295
|
* "/orgs/{id}" → `` `orgs/${encodeURIComponent(id)}` ``
|
|
4293
4296
|
* "/orgs/{id}/foo" → `` `orgs/${encodeURIComponent(id)}/foo` ``
|
|
4297
|
+
*
|
|
4298
|
+
* `paramNameMap` lets a caller override the local variable name used for a
|
|
4299
|
+
* spec parameter — used by the options-object code path so the URL template
|
|
4300
|
+
* references the SDK's public field name (e.g. `organizationMembershipId`)
|
|
4301
|
+
* instead of the spec's path-param name (e.g. `omId`), avoiding a
|
|
4302
|
+
* destructure rename in the method body.
|
|
4294
4303
|
*/
|
|
4295
|
-
function buildNodePathExpression(rawPath) {
|
|
4304
|
+
function buildNodePathExpression(rawPath, paramNameMap) {
|
|
4296
4305
|
const segments = parsePathTemplate(rawPath);
|
|
4297
4306
|
if (!hasPathParams(segments)) return `'${rawPath}'`;
|
|
4298
4307
|
let body = "";
|
|
4299
|
-
for (const seg of segments) body += renderSegment(seg);
|
|
4308
|
+
for (const seg of segments) body += renderSegment(seg, paramNameMap);
|
|
4300
4309
|
return `\`${body}\``;
|
|
4301
4310
|
}
|
|
4302
|
-
function renderSegment(seg) {
|
|
4311
|
+
function renderSegment(seg, paramNameMap) {
|
|
4303
4312
|
if (seg.kind === "literal") return seg.value.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
4304
|
-
return `\${encodeURIComponent(${fieldName$6(seg.name)})}`;
|
|
4313
|
+
return `\${encodeURIComponent(${paramNameMap?.get(seg.name) ?? fieldName$6(seg.name)})}`;
|
|
4305
4314
|
}
|
|
4306
4315
|
//#endregion
|
|
4307
4316
|
//#region src/node/wrappers.ts
|
|
@@ -5092,8 +5101,8 @@ function renderOptionsObjectMethod(lines, op, plan, method, service, ctx, modelM
|
|
|
5092
5101
|
const optionParam = optionsObjectParam$1(baselineMethod);
|
|
5093
5102
|
if (!optionParam) return false;
|
|
5094
5103
|
const responseModel = plan.responseModelName ? resolveInterfaceName(plan.responseModelName, ctx) : null;
|
|
5095
|
-
const pathStr = buildPathStr(op);
|
|
5096
5104
|
const pathBindings = buildOptionsObjectPathBindings(op, optionParam.type, ctx);
|
|
5105
|
+
const pathStr = buildPathStr(op, buildOptionsObjectPathParamMap(op, optionParam.type, ctx));
|
|
5097
5106
|
if (plan.isPaginated && op.pagination && op.httpMethod === "get") {
|
|
5098
5107
|
let itemRawName = op.pagination.itemType.kind === "model" ? op.pagination.itemType.name : null;
|
|
5099
5108
|
if (itemRawName) {
|
|
@@ -5199,11 +5208,23 @@ function renderOptionsObjectDestructure(lines, pathBindings, restName) {
|
|
|
5199
5208
|
else if (restName) lines.push(` const ${restName} = options;`);
|
|
5200
5209
|
}
|
|
5201
5210
|
function buildOptionsObjectPathBindings(op, optionType, ctx) {
|
|
5202
|
-
return op.pathParams.map((param) =>
|
|
5211
|
+
return op.pathParams.map((param) => resolveOptionsObjectField$1(fieldName$6(param.name), optionType, ctx));
|
|
5212
|
+
}
|
|
5213
|
+
/**
|
|
5214
|
+
* Map spec path-param names (e.g. `omId`) to the SDK field name exposed on
|
|
5215
|
+
* the options interface (e.g. `organizationMembershipId`). Empty when every
|
|
5216
|
+
* path param's spec name already matches the SDK field. Consumed by
|
|
5217
|
+
* `buildNodePathExpression` so the URL template binds to the same identifier
|
|
5218
|
+
* the destructure does.
|
|
5219
|
+
*/
|
|
5220
|
+
function buildOptionsObjectPathParamMap(op, optionType, ctx) {
|
|
5221
|
+
const map = /* @__PURE__ */ new Map();
|
|
5222
|
+
for (const param of op.pathParams) {
|
|
5203
5223
|
const localName = fieldName$6(param.name);
|
|
5204
|
-
const
|
|
5205
|
-
|
|
5206
|
-
}
|
|
5224
|
+
const sdkField = resolveOptionsObjectField$1(localName, optionType, ctx);
|
|
5225
|
+
if (sdkField !== localName) map.set(param.name, sdkField);
|
|
5226
|
+
}
|
|
5227
|
+
return map;
|
|
5207
5228
|
}
|
|
5208
5229
|
function resolveOptionsObjectField$1(localName, optionType, ctx) {
|
|
5209
5230
|
const fields = ctx.apiSurface?.interfaces?.[optionType]?.fields;
|
|
@@ -5464,8 +5485,8 @@ function renderQueryExpr(queryParams) {
|
|
|
5464
5485
|
}
|
|
5465
5486
|
return `options ? { ${parts.join(", ")} } : undefined`;
|
|
5466
5487
|
}
|
|
5467
|
-
function buildPathStr(op) {
|
|
5468
|
-
return buildNodePathExpression(op.path);
|
|
5488
|
+
function buildPathStr(op, paramNameMap) {
|
|
5489
|
+
return buildNodePathExpression(op.path, paramNameMap);
|
|
5469
5490
|
}
|
|
5470
5491
|
function buildPathParams(op, specEnumNames) {
|
|
5471
5492
|
const declaredNames = new Set(op.pathParams.map((p) => fieldName$6(p.name)));
|
|
@@ -5842,6 +5863,7 @@ function isSupportedFieldType(ref, ownerModelName, shared, ctx) {
|
|
|
5842
5863
|
if (ref.name === ownerModelName) return true;
|
|
5843
5864
|
const resolvedName = resolveInterfaceName(ref.name, ctx);
|
|
5844
5865
|
if (ctx.apiSurface?.interfaces?.[resolvedName] || ctx.apiSurface?.typeAliases?.[resolvedName]) return true;
|
|
5866
|
+
if (isAdoptedModelName(ref.name)) return true;
|
|
5845
5867
|
return liveSurfaceHasManagedFile(`src/${shared.resolveDir(shared.modelToService.get(ref.name))}/interfaces/${fileName$3(ref.name)}.interface.ts`);
|
|
5846
5868
|
}
|
|
5847
5869
|
case "enum": {
|
|
@@ -6901,6 +6923,7 @@ function generateTests$7(spec, ctx) {
|
|
|
6901
6923
|
if (ops.length === 0) continue;
|
|
6902
6924
|
const propName = mountAccessors.get(mountName) ?? servicePropertyName$4(mountName);
|
|
6903
6925
|
if (ctx.apiSurface && baselineWorkOSProps.size > 0 && !baselineWorkOSProps.has(propName)) continue;
|
|
6926
|
+
if (resolveResourceClassName$3(mergedService, ctx) !== mountName) continue;
|
|
6904
6927
|
const testService = ops.length < operations.length ? {
|
|
6905
6928
|
...mergedService,
|
|
6906
6929
|
operations: ops
|
|
@@ -7076,8 +7099,11 @@ function renderBodyTest(lines, op, plan, method, serviceProp, modelMap, ctx, ent
|
|
|
7076
7099
|
const payload = buildTestPayload(op, modelMap);
|
|
7077
7100
|
const payloadArg = payload ? payload.camelCaseObj : fallbackBodyArg(op, modelMap);
|
|
7078
7101
|
const allArgs = buildOptionsObjectTestArg(op, plan, baselineMethod, modelMap, ctx) ?? (pathArgs ? `${pathArgs}, ${payloadArg}` : payloadArg);
|
|
7102
|
+
const isArrayResponse = !!plan.isArrayResponse;
|
|
7103
|
+
const fixtureExpr = isArrayResponse ? `[${fixture}]` : fixture;
|
|
7104
|
+
const accessor = isArrayResponse ? "result[0]" : "result";
|
|
7079
7105
|
lines.push(" it('sends the correct request and returns result', async () => {");
|
|
7080
|
-
lines.push(` fetchOnce(${
|
|
7106
|
+
lines.push(` fetchOnce(${fixtureExpr});`);
|
|
7081
7107
|
lines.push("");
|
|
7082
7108
|
lines.push(` const result = await workos.${serviceProp}.${method}(${allArgs});`);
|
|
7083
7109
|
lines.push("");
|
|
@@ -7086,12 +7112,13 @@ function renderBodyTest(lines, op, plan, method, serviceProp, modelMap, ctx, ent
|
|
|
7086
7112
|
lines.push(` expect(new URL(String(fetchURL())).pathname).toBe('${expectedPath}');`);
|
|
7087
7113
|
if (payload) lines.push(` expect(fetchBody()).toEqual(expect.objectContaining(${payload.snakeCaseObj}));`);
|
|
7088
7114
|
else lines.push(" expect(fetchBody()).toBeDefined();");
|
|
7115
|
+
if (isArrayResponse) lines.push(" expect(Array.isArray(result)).toBe(true);");
|
|
7089
7116
|
const bodyHelperName = ctx ? `expect${resolveInterfaceName(responseModelName, ctx)}` : null;
|
|
7090
|
-
if (bodyHelperName && entityHelpers?.has(bodyHelperName)) lines.push(` ${bodyHelperName}(
|
|
7117
|
+
if (bodyHelperName && entityHelpers?.has(bodyHelperName)) lines.push(` ${bodyHelperName}(${accessor});`);
|
|
7091
7118
|
else {
|
|
7092
7119
|
const responseModel = modelMap.get(responseModelName);
|
|
7093
7120
|
if (responseModel) {
|
|
7094
|
-
const assertions = buildFieldAssertions(responseModel,
|
|
7121
|
+
const assertions = buildFieldAssertions(responseModel, accessor, modelMap);
|
|
7095
7122
|
if (assertions.length > 0) for (const assertion of assertions) lines.push(` ${assertion}`);
|
|
7096
7123
|
else lines.push(" expect(result).toBeDefined();");
|
|
7097
7124
|
} else lines.push(" expect(result).toBeDefined();");
|
|
@@ -7103,20 +7130,24 @@ function renderGetTest(lines, op, plan, method, serviceProp, modelMap, ctx, enti
|
|
|
7103
7130
|
const fixture = `${toCamelCase(responseModelName)}Fixture`;
|
|
7104
7131
|
const pathArgs = buildTestPathArgs(op);
|
|
7105
7132
|
const optionsArg = buildOptionsObjectTestArg(op, plan, baselineMethod, modelMap, ctx);
|
|
7133
|
+
const isArrayResponse = !!plan.isArrayResponse;
|
|
7134
|
+
const fixtureExpr = isArrayResponse ? `[${fixture}]` : fixture;
|
|
7135
|
+
const accessor = isArrayResponse ? "result[0]" : "result";
|
|
7106
7136
|
lines.push(" it('returns the expected result', async () => {");
|
|
7107
|
-
lines.push(` fetchOnce(${
|
|
7137
|
+
lines.push(` fetchOnce(${fixtureExpr});`);
|
|
7108
7138
|
lines.push("");
|
|
7109
7139
|
lines.push(` const result = await workos.${serviceProp}.${method}(${optionsArg ?? pathArgs});`);
|
|
7110
7140
|
lines.push("");
|
|
7111
7141
|
lines.push(` expect(fetchMethod()).toBe('${op.httpMethod.toUpperCase()}');`);
|
|
7112
7142
|
const expectedPathGet = buildExpectedPath$4(op);
|
|
7113
7143
|
lines.push(` expect(new URL(String(fetchURL())).pathname).toBe('${expectedPathGet}');`);
|
|
7144
|
+
if (isArrayResponse) lines.push(" expect(Array.isArray(result)).toBe(true);");
|
|
7114
7145
|
const helperName = ctx ? `expect${resolveInterfaceName(responseModelName, ctx)}` : null;
|
|
7115
|
-
if (helperName && entityHelpers?.has(helperName)) lines.push(` ${helperName}(
|
|
7146
|
+
if (helperName && entityHelpers?.has(helperName)) lines.push(` ${helperName}(${accessor});`);
|
|
7116
7147
|
else {
|
|
7117
7148
|
const responseModel = modelMap.get(responseModelName);
|
|
7118
7149
|
if (responseModel) {
|
|
7119
|
-
const assertions = buildFieldAssertions(responseModel,
|
|
7150
|
+
const assertions = buildFieldAssertions(responseModel, accessor, modelMap);
|
|
7120
7151
|
if (assertions.length > 0) for (const assertion of assertions) lines.push(` ${assertion}`);
|
|
7121
7152
|
else lines.push(" expect(result).toBeDefined();");
|
|
7122
7153
|
} else lines.push(" expect(result).toBeDefined();");
|
|
@@ -7219,23 +7250,28 @@ function generateEntityHelpers(plans, modelMap, ctx) {
|
|
|
7219
7250
|
* nested models so we still get meaningful assertions instead of a bare
|
|
7220
7251
|
* `toBeDefined()`.
|
|
7221
7252
|
*/
|
|
7253
|
+
function isDateTimeFieldType(type) {
|
|
7254
|
+
if (type.kind === "primitive") return type.format === "date-time";
|
|
7255
|
+
if (type.kind === "nullable") return isDateTimeFieldType(type.inner);
|
|
7256
|
+
return false;
|
|
7257
|
+
}
|
|
7222
7258
|
function buildFieldAssertions(model, accessor, modelMap) {
|
|
7223
7259
|
const assertions = [];
|
|
7224
7260
|
for (const field of model.fields) {
|
|
7225
7261
|
if (!field.required) continue;
|
|
7262
|
+
const domainField = fieldName$6(field.name);
|
|
7263
|
+
const fieldAccessor = isDateTimeFieldType(field.type) ? `${accessor}.${domainField}.toISOString()` : `${accessor}.${domainField}`;
|
|
7226
7264
|
if (field.example !== void 0) {
|
|
7227
|
-
const domainField = fieldName$6(field.name);
|
|
7228
7265
|
if (typeof field.example === "object" && field.example !== null) assertions.push(`expect(${accessor}.${domainField}).toEqual(${JSON.stringify(field.example)});`);
|
|
7229
7266
|
else {
|
|
7230
7267
|
const exampleLiteral = typeof field.example === "string" ? `'${field.example}'` : String(field.example);
|
|
7231
|
-
assertions.push(`expect(${
|
|
7268
|
+
assertions.push(`expect(${fieldAccessor}).toBe(${exampleLiteral});`);
|
|
7232
7269
|
}
|
|
7233
7270
|
continue;
|
|
7234
7271
|
}
|
|
7235
7272
|
const value = fixtureValueForType(field.type, field.name, model.name);
|
|
7236
7273
|
if (value === null) continue;
|
|
7237
|
-
|
|
7238
|
-
assertions.push(`expect(${accessor}.${domainField}).toBe(${value});`);
|
|
7274
|
+
assertions.push(`expect(${fieldAccessor}).toBe(${value});`);
|
|
7239
7275
|
}
|
|
7240
7276
|
if (assertions.length === 0 && modelMap) for (const field of model.fields) {
|
|
7241
7277
|
if (!field.required) continue;
|
|
@@ -7700,9 +7736,12 @@ function applyLiveSurface(files, ctx, surface) {
|
|
|
7700
7736
|
if (policy.hasExistingSdk && !policy.managedPaths.has(f.path) && !canCreateNewPath(f.path, policy)) continue;
|
|
7701
7737
|
if (surface.files.has(f.path) && !surface.autogenFiles.has(f.path) && !ownedPath) continue;
|
|
7702
7738
|
if (isUserOwnedAfterFirstEmit(f.path)) {
|
|
7739
|
+
const dir = topLevelDir(f.path);
|
|
7740
|
+
const isAdoptedDir = dir !== void 0 && policy.adoptedServiceDirs.has(dir);
|
|
7741
|
+
const isManagedDir = ownedPath || isAdoptedDir;
|
|
7703
7742
|
if (surface.files.has(f.path) && !surface.autogenFiles.has(f.path)) continue;
|
|
7704
|
-
if (!
|
|
7705
|
-
if (
|
|
7743
|
+
if (!isManagedDir && !surface.autogenFiles.has(f.path)) continue;
|
|
7744
|
+
if (isManagedDir && !policy.regenerateOwnedTests) continue;
|
|
7706
7745
|
}
|
|
7707
7746
|
if (surface.autogenFiles.has(f.path) || ownedPath) {
|
|
7708
7747
|
f.overwriteExisting = true;
|
|
@@ -26520,4 +26559,4 @@ const workosEmittersPlugin = {
|
|
|
26520
26559
|
//#endregion
|
|
26521
26560
|
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 };
|
|
26522
26561
|
|
|
26523
|
-
//# sourceMappingURL=plugin-
|
|
26562
|
+
//# sourceMappingURL=plugin-D2N2ZT5W.mjs.map
|