@workos/oagen-emitters 0.18.0 → 0.18.1

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.18.0"
2
+ ".": "0.18.1"
3
3
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.18.1](https://github.com/workos/oagen-emitters/compare/v0.18.0...v0.18.1) (2026-06-17)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **node:** support owning services with hand-owned generic types ([#152](https://github.com/workos/oagen-emitters/issues/152)) ([aa8223d](https://github.com/workos/oagen-emitters/commit/aa8223dbb6b56f5c2ab12ac42e5a1fc9f9e99943))
9
+
3
10
  ## [0.18.0](https://github.com/workos/oagen-emitters/compare/v0.17.0...v0.18.0) (2026-06-16)
4
11
 
5
12
 
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { A as fieldName$4, B as servicePropertyName, C as apiClassName, D as dotnetEmitter, E as propertyName, F as fieldName$3, H as fieldName$1, I as methodName, L as trimMountedResourceFromMethod, M as trimMountedResourceFromMethod$1, N as goEmitter, O as appendAsyncSuffix, P as className, R as phpEmitter, S as kotlinEmitter, T as packageSegment, U as safeParamName$1, V as pythonEmitter, W as nodeEmitter, _ as rubyEmitter, a as rustExtractor, b as resolveServiceTarget, c as pythonExtractor, d as rustEmitter, f as fieldName$5, g as typeName, h as resourceAccessorName, i as kotlinExtractor, j as methodName$1, k as className$1, l as rubyExtractor, m as moduleName, n as elixirExtractor, o as goExtractor, p as methodName$3, r as dotnetExtractor, s as phpExtractor, t as workosEmittersPlugin, u as nodeExtractor, v as buildExportedClassNameSet, w as methodName$2, x as safeParamName, y as fieldName, z as fieldName$2 } from "./plugin-DAa-HsN5.mjs";
1
+ import { A as fieldName$4, B as servicePropertyName, C as apiClassName, D as dotnetEmitter, E as propertyName, F as fieldName$3, H as fieldName$1, I as methodName, L as trimMountedResourceFromMethod, M as trimMountedResourceFromMethod$1, N as goEmitter, O as appendAsyncSuffix, P as className, R as phpEmitter, S as kotlinEmitter, T as packageSegment, U as safeParamName$1, V as pythonEmitter, W as nodeEmitter, _ as rubyEmitter, a as rustExtractor, b as resolveServiceTarget, c as pythonExtractor, d as rustEmitter, f as fieldName$5, g as typeName, h as resourceAccessorName, i as kotlinExtractor, j as methodName$1, k as className$1, l as rubyExtractor, m as moduleName, n as elixirExtractor, o as goExtractor, p as methodName$3, r as dotnetExtractor, s as phpExtractor, t as workosEmittersPlugin, u as nodeExtractor, v as buildExportedClassNameSet, w as methodName$2, x as safeParamName, y as fieldName, z as fieldName$2 } from "./plugin-CtU_wbid.mjs";
2
2
  import { collectSnippetArgs, collectWrapperArgs, toSnakeCase } from "@workos/oagen";
3
3
  //#region src/snippets/ruby.ts
4
4
  const INDENT$6 = " ";
@@ -3626,6 +3626,17 @@ function isNodeOwnedService(ctx, ...names) {
3626
3626
  const owned = new Set(configured.map(normalizeServiceName));
3627
3627
  return names.some((name) => name !== void 0 ? ownedLookupNames(name).some((candidate) => owned.has(normalizeServiceName(candidate))) : false);
3628
3628
  }
3629
+ /**
3630
+ * True when `name` is a hand-owned type (see {@link NodeEmitterOptions.handOwnedTypes}).
3631
+ * Hand-owned types are never generated; the emitter defers to the existing
3632
+ * hand-written declaration and routes imports/barrel exports to it.
3633
+ */
3634
+ function isHandOwnedType(ctx, name) {
3635
+ if (name === void 0) return false;
3636
+ const configured = nodeOptions(ctx).handOwnedTypes;
3637
+ if (!configured || configured.length === 0) return false;
3638
+ return configured.includes(name);
3639
+ }
3629
3640
  //#endregion
3630
3641
  //#region src/node/live-surface.ts
3631
3642
  const SRC_DIR = "src";
@@ -5582,9 +5593,15 @@ function optionsObjectInfo(service, method, op, plan, ctx, baselineMethod, resol
5582
5593
  generated: baselineTypeSourceFile(ctx, overrideType) === void 0
5583
5594
  };
5584
5595
  if (!operationHasOptionsInput(op, plan, resolvedOp)) return void 0;
5596
+ const resolvedService = resolveResourceClassName$3(service, ctx);
5597
+ let optionsType = methodOptionsName$1(method, resolvedService);
5598
+ if (method !== "list") {
5599
+ const baselineDir = baselineTypeSourceFile(ctx, optionsType)?.match(/^src\/([^/]+)\//)?.[1];
5600
+ if (baselineDir && baselineDir !== resolveResourceDir(service, ctx)) optionsType = `${toPascalCase(resolvedService)}${toPascalCase(method)}Options`;
5601
+ }
5585
5602
  return {
5586
5603
  name: "options",
5587
- type: methodOptionsName$1(method, resolveResourceClassName$3(service, ctx)),
5604
+ type: optionsType,
5588
5605
  optional: optionsObjectShouldBeOptional(op, plan, resolvedOp),
5589
5606
  generated: true
5590
5607
  };
@@ -6079,7 +6096,7 @@ function generateResourceClass(service, ctx) {
6079
6096
  const resolved = lookupResolved(op, resolvedLookup);
6080
6097
  const optionInfo = optionsObjectInfo(service, method, op, plan, ctx, baselineMethodFor$1(service, method, ctx), resolved);
6081
6098
  if (plan.isPaginated) {
6082
- const extraParams = op.queryParams.filter((p) => !PAGINATION_PARAM_NAMES.has(p.name));
6099
+ const extraParams = op.queryParams.filter((p) => !PAGINATION_PARAM_NAMES.has(p.name) && !p.deprecated);
6083
6100
  if (extraParams.length > 0) {
6084
6101
  const optionsName = optionInfo?.type ?? paginatedOptionsName(method, resolvedName);
6085
6102
  if (extraParams.some((p) => fieldName$6(p.name) !== wireFieldName(p.name))) {
@@ -7336,6 +7353,7 @@ function generateModels$7(models, ctx, shared) {
7336
7353
  if (isListMetadataModel(model) && !listMetadataNeeded.has(model.name)) continue;
7337
7354
  if (isListWrapperModel(model) && !nonPaginatedRefs.has(model.name)) continue;
7338
7355
  if (discriminatedSkip?.has(model.name)) continue;
7356
+ if (isHandOwnedType(ctx, model.name) || isHandOwnedType(ctx, resolveInterfaceName(model.name, ctx, { skipTypeAlias: true }))) continue;
7339
7357
  const service = modelToService.get(model.name);
7340
7358
  const isOwnedModel = isNodeOwnedService(ctx, service);
7341
7359
  if (!isOwnedModel && !modelHasNewFields(model, ctx) && !forceGenerate.has(model.name)) continue;
@@ -7410,6 +7428,7 @@ function generateModels$7(models, ctx, shared) {
7410
7428
  const crossServiceImports = /* @__PURE__ */ new Map();
7411
7429
  const unresolvableNames = /* @__PURE__ */ new Set();
7412
7430
  const enumToService = assignEnumsToServices(ctx.spec.enums, ctx.spec.services, ctx.spec.models, ctx);
7431
+ const serviceNameMap = buildServiceNameMap(ctx.spec.services, ctx);
7413
7432
  const resolvedEnumNames = /* @__PURE__ */ new Map();
7414
7433
  for (const e of ctx.spec.enums) resolvedEnumNames.set(resolveInterfaceName(e.name, ctx), e.name);
7415
7434
  for (const field of model.fields) {
@@ -7429,7 +7448,7 @@ function generateModels$7(models, ctx, shared) {
7429
7448
  const eDir = resolveDir(eService);
7430
7449
  const bEnum = ctx.apiSurface?.enums?.[irEnumName];
7431
7450
  const bAlias = ctx.apiSurface?.typeAliases?.[irEnumName];
7432
- const bSrc = isNodeOwnedService(ctx, eService) ? void 0 : bEnum?.sourceFile ?? bAlias?.sourceFile;
7451
+ const bSrc = isNodeOwnedService(ctx, eService, eService ? serviceNameMap.get(eService) : void 0) ? void 0 : bEnum?.sourceFile ?? bAlias?.sourceFile;
7433
7452
  const gPath = `src/${eDir}/interfaces/${fileName$3(irEnumName)}.interface.ts`;
7434
7453
  const cPath = `src/${dirName}/interfaces/${fileName$3(model.name)}.interface.ts`;
7435
7454
  if (bSrc === cPath) {
@@ -7474,7 +7493,7 @@ function generateModels$7(models, ctx, shared) {
7474
7493
  const baselineEnum = ctx.apiSurface?.enums?.[dep];
7475
7494
  const baselineAlias = ctx.apiSurface?.typeAliases?.[dep];
7476
7495
  const depService = enumToService.get(dep);
7477
- const baselineSrc = isNodeOwnedService(ctx, depService) ? void 0 : baselineEnum?.sourceFile ?? baselineAlias?.sourceFile ?? liveSurfaceInterfacePath(dep);
7496
+ const baselineSrc = isNodeOwnedService(ctx, depService, depService ? serviceNameMap.get(depService) : void 0) ? void 0 : baselineEnum?.sourceFile ?? baselineAlias?.sourceFile ?? liveSurfaceInterfacePath(dep);
7478
7497
  const depDir = resolveDir(depService);
7479
7498
  const generatedPath = `src/${depDir}/interfaces/${fileName$3(dep)}.interface.ts`;
7480
7499
  const currentFilePath = `src/${dirName}/interfaces/${fileName$3(model.name)}.interface.ts`;
@@ -7697,6 +7716,7 @@ function generateSerializers(models, ctx, shared) {
7697
7716
  if (isListMetadataModel(model) && !serializerListMetadataNeeded.has(model.name)) continue;
7698
7717
  if (isListWrapperModel(model) && !serializerNonPaginatedRefs.has(model.name)) continue;
7699
7718
  if (discriminatedSerializerSkip?.has(model.name)) continue;
7719
+ if (isHandOwnedType(ctx, model.name) || isHandOwnedType(ctx, resolveInterfaceName(model.name, ctx, { skipTypeAlias: true }))) continue;
7700
7720
  if (!isNodeOwnedService(ctx, modelToService.get(model.name)) && !modelHasNewFields(model, ctx) && !forceGenerateSerializer.has(model.name)) continue;
7701
7721
  eligibleModels.push(model);
7702
7722
  }
@@ -7780,12 +7800,20 @@ function generateSerializers(models, ctx, shared) {
7780
7800
  }
7781
7801
  const liveRootForBarrel = ctx.outputDir ?? ctx.targetDir;
7782
7802
  for (const [dir, stems] of serializersByDir) {
7783
- if (liveRootForBarrel && !isNodeOwnedService(ctx, dir)) {
7803
+ if (liveRootForBarrel) {
7804
+ const dirIsOwned = isNodeOwnedService(ctx, dir);
7784
7805
  const serializersDir = path.join(liveRootForBarrel, "src", dir, "serializers");
7785
7806
  try {
7786
7807
  for (const entry of fs.readdirSync(serializersDir)) {
7787
7808
  if (!entry.endsWith(".serializer.ts")) continue;
7788
- stems.add(entry.replace(/\.serializer\.ts$/, ""));
7809
+ const stem = entry.replace(/\.serializer\.ts$/, "");
7810
+ if (stems.has(stem)) continue;
7811
+ if (dirIsOwned) {
7812
+ const content = fs.readFileSync(path.join(serializersDir, entry), "utf-8");
7813
+ if (/auto-generated by oagen/i.test(content.slice(0, 400))) continue;
7814
+ if (![...content.matchAll(/export\s+(?:const|function)\s+((?:de)?serialize[A-Za-z0-9_]+)/g)].map((m) => m[1].replace(/^(?:de)?serialize/, "")).some((typeName) => isHandOwnedType(ctx, typeName))) continue;
7815
+ }
7816
+ stems.add(stem);
7789
7817
  }
7790
7818
  } catch {}
7791
7819
  }
@@ -8069,12 +8097,12 @@ function exportedNamesForSource(ctx, sourceFile) {
8069
8097
  */
8070
8098
  function generateServiceBarrels(spec, ctx) {
8071
8099
  const files = [];
8072
- const { modelToService, resolveDir } = createServiceDirResolver(spec.models, spec.services, ctx);
8100
+ const { modelToService, resolveDir, serviceNameMap } = createServiceDirResolver(spec.models, spec.services, ctx);
8073
8101
  const enumToService = assignEnumsToServices(spec.enums, spec.services, spec.models, ctx);
8074
8102
  const dirExports = /* @__PURE__ */ new Map();
8075
8103
  const dirSymbols = /* @__PURE__ */ new Map();
8076
8104
  const ownedDirNames = /* @__PURE__ */ new Set();
8077
- for (const service of spec.services) if (isNodeOwnedService(ctx, service.name)) {
8105
+ for (const service of spec.services) if (isNodeOwnedService(ctx, service.name, serviceNameMap.get(service.name))) {
8078
8106
  const dir = resolveDir(service.name);
8079
8107
  ownedDirNames.add(dir);
8080
8108
  if (!dirExports.has(dir)) {
@@ -8845,12 +8873,12 @@ function buildOptionsObjectTestArg(op, plan, baselineMethod, modelMap, ctx) {
8845
8873
  entries.push(`${optionField}: ${JSON.stringify(pathParamTestValue(param, localName))}`);
8846
8874
  }
8847
8875
  if (plan.isPaginated) entries.push("order: 'desc'");
8848
- const queryParams = plan.isPaginated ? op.queryParams.filter((param) => ![
8876
+ const queryParams = (plan.isPaginated ? op.queryParams.filter((param) => ![
8849
8877
  "limit",
8850
8878
  "before",
8851
8879
  "after",
8852
8880
  "order"
8853
- ].includes(param.name)) : op.queryParams;
8881
+ ].includes(param.name)) : op.queryParams).filter((param) => !param.deprecated);
8854
8882
  for (const param of queryParams) {
8855
8883
  const localName = fieldName$6(param.name);
8856
8884
  const value = queryParamTestValue(param, modelMap);
@@ -29869,4 +29897,4 @@ const workosEmittersPlugin = {
29869
29897
  //#endregion
29870
29898
  export { fieldName$2 as A, servicePropertyName$2 as B, apiClassName as C, dotnetEmitter as D, propertyName as E, fieldName$3 as F, fieldName$5 as H, methodName$3 as I, trimMountedResourceFromMethod$2 as L, trimMountedResourceFromMethod$1 as M, goEmitter as N, appendAsyncSuffix as O, className$3 as P, phpEmitter as R, kotlinEmitter as S, packageSegment as T, safeParamName$1 as U, pythonEmitter as V, nodeEmitter as W, rubyEmitter as _, rustExtractor as a, resolveServiceTarget as b, pythonExtractor as c, rustEmitter as d, fieldName as f, typeName as g, resourceAccessorName as h, kotlinExtractor as i, methodName$2 as j, className$2 as k, rubyExtractor as l, moduleName as m, elixirExtractor as n, goExtractor as o, methodName as p, dotnetExtractor as r, phpExtractor as s, workosEmittersPlugin as t, nodeExtractor as u, buildExportedClassNameSet as v, methodName$1 as w, safeParamName as x, fieldName$1 as y, fieldName$4 as z };
29871
29899
 
29872
- //# sourceMappingURL=plugin-DAa-HsN5.mjs.map
29900
+ //# sourceMappingURL=plugin-CtU_wbid.mjs.map