@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.
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.12.2"
2
+ ".": "0.12.3"
3
3
  }
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
 
@@ -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":";;;;;cAuVa,WAAA,EAAa,OAwFzB;;;cCxYY,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"}
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-eCuvoL1T.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-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 optionField = resolveOptionsObjectField$1(localName, optionType, ctx);
5205
- return optionField === localName ? localName : `${optionField}: ${localName}`;
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(${fixture});`);
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}(result);`);
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, "result", modelMap);
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(${fixture});`);
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}(result);`);
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, "result", modelMap);
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(${accessor}.${domainField}).toBe(${exampleLiteral});`);
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
- const domainField = fieldName$6(field.name);
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 (!ownedPath && !surface.autogenFiles.has(f.path)) continue;
7705
- if (ownedPath && !policy.regenerateOwnedTests) continue;
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-eCuvoL1T.mjs.map
26562
+ //# sourceMappingURL=plugin-D2N2ZT5W.mjs.map