@workos/oagen-emitters 0.2.0 → 0.3.0

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.
Files changed (110) hide show
  1. package/.husky/pre-commit +1 -0
  2. package/.oxfmtrc.json +8 -1
  3. package/.release-please-manifest.json +1 -1
  4. package/CHANGELOG.md +15 -0
  5. package/README.md +129 -0
  6. package/dist/index.d.mts +10 -1
  7. package/dist/index.d.mts.map +1 -1
  8. package/dist/index.mjs +11943 -2728
  9. package/dist/index.mjs.map +1 -1
  10. package/docs/sdk-architecture/go.md +338 -0
  11. package/docs/sdk-architecture/php.md +315 -0
  12. package/docs/sdk-architecture/python.md +511 -0
  13. package/oagen.config.ts +298 -2
  14. package/package.json +9 -5
  15. package/scripts/generate-php.js +13 -0
  16. package/scripts/git-push-with-published-oagen.sh +21 -0
  17. package/smoke/sdk-dotnet.ts +17 -3
  18. package/smoke/sdk-elixir.ts +17 -3
  19. package/smoke/sdk-go.ts +137 -46
  20. package/smoke/sdk-kotlin.ts +23 -4
  21. package/smoke/sdk-node.ts +15 -3
  22. package/smoke/sdk-php.ts +28 -26
  23. package/smoke/sdk-python.ts +5 -2
  24. package/smoke/sdk-ruby.ts +17 -3
  25. package/smoke/sdk-rust.ts +16 -3
  26. package/src/go/client.ts +141 -0
  27. package/src/go/enums.ts +196 -0
  28. package/src/go/fixtures.ts +212 -0
  29. package/src/go/index.ts +81 -0
  30. package/src/go/manifest.ts +36 -0
  31. package/src/go/models.ts +254 -0
  32. package/src/go/naming.ts +191 -0
  33. package/src/go/resources.ts +827 -0
  34. package/src/go/tests.ts +751 -0
  35. package/src/go/type-map.ts +82 -0
  36. package/src/go/wrappers.ts +261 -0
  37. package/src/index.ts +3 -0
  38. package/src/node/client.ts +167 -122
  39. package/src/node/enums.ts +13 -4
  40. package/src/node/errors.ts +42 -233
  41. package/src/node/field-plan.ts +726 -0
  42. package/src/node/fixtures.ts +15 -5
  43. package/src/node/index.ts +65 -16
  44. package/src/node/models.ts +264 -96
  45. package/src/node/naming.ts +52 -25
  46. package/src/node/resources.ts +621 -172
  47. package/src/node/sdk-errors.ts +41 -0
  48. package/src/node/tests.ts +71 -27
  49. package/src/node/type-map.ts +4 -2
  50. package/src/node/utils.ts +56 -64
  51. package/src/node/wrappers.ts +151 -0
  52. package/src/php/client.ts +171 -0
  53. package/src/php/enums.ts +67 -0
  54. package/src/php/errors.ts +9 -0
  55. package/src/php/fixtures.ts +181 -0
  56. package/src/php/index.ts +96 -0
  57. package/src/php/manifest.ts +36 -0
  58. package/src/php/models.ts +310 -0
  59. package/src/php/naming.ts +298 -0
  60. package/src/php/resources.ts +561 -0
  61. package/src/php/tests.ts +533 -0
  62. package/src/php/type-map.ts +90 -0
  63. package/src/php/utils.ts +18 -0
  64. package/src/php/wrappers.ts +151 -0
  65. package/src/python/client.ts +337 -0
  66. package/src/python/enums.ts +313 -0
  67. package/src/python/fixtures.ts +196 -0
  68. package/src/python/index.ts +95 -0
  69. package/src/python/manifest.ts +38 -0
  70. package/src/python/models.ts +688 -0
  71. package/src/python/naming.ts +209 -0
  72. package/src/python/resources.ts +1322 -0
  73. package/src/python/tests.ts +1335 -0
  74. package/src/python/type-map.ts +93 -0
  75. package/src/python/wrappers.ts +191 -0
  76. package/src/shared/model-utils.ts +255 -0
  77. package/src/shared/naming-utils.ts +107 -0
  78. package/src/shared/non-spec-services.ts +54 -0
  79. package/src/shared/resolved-ops.ts +109 -0
  80. package/src/shared/wrapper-utils.ts +59 -0
  81. package/test/go/client.test.ts +92 -0
  82. package/test/go/enums.test.ts +132 -0
  83. package/test/go/errors.test.ts +9 -0
  84. package/test/go/models.test.ts +265 -0
  85. package/test/go/resources.test.ts +408 -0
  86. package/test/go/tests.test.ts +143 -0
  87. package/test/node/client.test.ts +199 -94
  88. package/test/node/enums.test.ts +75 -3
  89. package/test/node/errors.test.ts +2 -41
  90. package/test/node/models.test.ts +109 -20
  91. package/test/node/naming.test.ts +37 -4
  92. package/test/node/resources.test.ts +662 -30
  93. package/test/node/serializers.test.ts +36 -7
  94. package/test/node/type-map.test.ts +11 -0
  95. package/test/php/client.test.ts +94 -0
  96. package/test/php/enums.test.ts +173 -0
  97. package/test/php/errors.test.ts +9 -0
  98. package/test/php/models.test.ts +497 -0
  99. package/test/php/resources.test.ts +644 -0
  100. package/test/php/tests.test.ts +118 -0
  101. package/test/python/client.test.ts +200 -0
  102. package/test/python/enums.test.ts +228 -0
  103. package/test/python/errors.test.ts +16 -0
  104. package/test/python/manifest.test.ts +74 -0
  105. package/test/python/models.test.ts +716 -0
  106. package/test/python/resources.test.ts +617 -0
  107. package/test/python/tests.test.ts +202 -0
  108. package/src/node/common.ts +0 -273
  109. package/src/node/config.ts +0 -71
  110. package/src/node/serializers.ts +0 -744
@@ -1,14 +1,21 @@
1
1
  import type { Operation, Service, EmitterContext } from '@workos/oagen';
2
2
  import { toPascalCase, toCamelCase, toKebabCase, toSnakeCase } from '@workos/oagen';
3
+ import { buildResolvedLookup, lookupMethodName } from '../shared/resolved-ops.js';
4
+ import { stripUrnPrefix } from '../shared/naming-utils.js';
5
+
6
+ /** Strip spec-noise suffixes (e.g., "Dto") from an IR name. */
7
+ export function stripNoiseSuffixes(name: string): string {
8
+ return name.replace(/Dto$/i, '');
9
+ }
3
10
 
4
11
  /** PascalCase class/interface name. */
5
12
  export function className(name: string): string {
6
- return toPascalCase(name);
13
+ return toPascalCase(stripUrnPrefix(name));
7
14
  }
8
15
 
9
16
  /** kebab-case file name (without extension). */
10
17
  export function fileName(name: string): string {
11
- return toKebabCase(name);
18
+ return toKebabCase(stripUrnPrefix(name));
12
19
  }
13
20
 
14
21
  /** camelCase method name. */
@@ -66,33 +73,33 @@ export function buildServiceNameMap(services: Service[], ctx: EmitterContext): M
66
73
  return map;
67
74
  }
68
75
 
69
- /** Resolve the SDK method name for an operation, checking overlay first. */
76
+ /**
77
+ * Resolve the output directory for a service.
78
+ * Mount rules already handle directory placement, so this is a simple kebab-case conversion.
79
+ */
80
+ export function resolveServiceDir(resolvedServiceName: string): string {
81
+ return serviceDirName(resolvedServiceName);
82
+ }
83
+
84
+ /** Resolve the SDK method name for an operation, using resolved operations first. */
70
85
  export function resolveMethodName(op: Operation, _service: Service, ctx: EmitterContext): string {
86
+ const lookup = buildResolvedLookup(ctx);
87
+ const resolved = lookupMethodName(op, lookup);
88
+ if (resolved) return toCamelCase(resolved);
89
+ // Fallback to overlay, then spec-derived
71
90
  const httpKey = `${op.httpMethod.toUpperCase()} ${op.path}`;
72
91
  const existing = ctx.overlayLookup?.methodByOperation?.get(httpKey);
73
- if (existing) {
74
- // Fix: when the path ends with a path parameter (single-resource operation)
75
- // and the overlay method name is plural, prefer the singular form.
76
- // E.g., getUsers → getUser when path is /user_management/users/{id}
77
- const isSingleResource = /\/\{[^}]+\}$/.test(op.path);
78
- if (isSingleResource && existing.methodName.endsWith('s') && !existing.methodName.endsWith('ss')) {
79
- const singular = existing.methodName.slice(0, -1);
80
- // Only singularize if it looks like a typical pluralization (ends in 's')
81
- // and the spec-derived name agrees it should be singular
82
- const specDerived = toCamelCase(op.name);
83
- if (specDerived === singular || specDerived.endsWith(singular.slice(singular.length - 4))) {
84
- return singular;
85
- }
86
- }
87
- return existing.methodName;
88
- }
92
+ if (existing) return existing.methodName;
89
93
  return toCamelCase(op.name);
90
94
  }
91
95
 
92
- /** Resolve the SDK class name for a service, checking overlay for existing names. */
96
+ /** Resolve the SDK class name for a service, using resolved ops mountOn as canonical. */
93
97
  export function resolveClassName(service: Service, ctx: EmitterContext): string {
94
- // Check overlay's methodByOperation for any operation in this service
95
- // to find the existing class name
98
+ // Use resolved ops mountOn as canonical class name
99
+ for (const r of ctx.resolvedOperations ?? []) {
100
+ if (r.service.name === service.name) return r.mountOn;
101
+ }
102
+ // Fallback to overlay
96
103
  if (ctx.overlayLookup?.methodByOperation) {
97
104
  for (const op of service.operations) {
98
105
  const httpKey = `${op.httpMethod.toUpperCase()} ${op.path}`;
@@ -103,9 +110,29 @@ export function resolveClassName(service: Service, ctx: EmitterContext): string
103
110
  return toPascalCase(service.name);
104
111
  }
105
112
 
106
- /** Resolve the interface name for a model, checking overlay first. */
107
- export function resolveInterfaceName(name: string, ctx: EmitterContext): string {
113
+ /** Resolve the interface name for a model, checking overlay first.
114
+ *
115
+ * @param opts.skipTypeAlias - When true, skip apiSurface typeAlias resolution.
116
+ * Use this for dedup models to ensure the file exports match the import
117
+ * names (preserved files export the raw name, not the resolved alias).
118
+ */
119
+ export function resolveInterfaceName(name: string, ctx: EmitterContext, opts?: { skipTypeAlias?: boolean }): string {
108
120
  const existing = ctx.overlayLookup?.interfaceByName?.get(name);
109
121
  if (existing) return existing;
110
- return toPascalCase(name);
122
+
123
+ // If the model name is a type alias that points to a canonical interface,
124
+ // use the canonical name. This prevents the merger from generating unused
125
+ // backward-compat aliases (e.g., `type FlagOwner = FeatureFlagOwner`).
126
+ if (!opts?.skipTypeAlias && ctx.apiSurface?.typeAliases) {
127
+ const alias = ctx.apiSurface.typeAliases[name] as { value?: string } | undefined;
128
+ if (alias?.value && ctx.apiSurface.interfaces?.[alias.value]) {
129
+ return alias.value;
130
+ }
131
+ }
132
+
133
+ // Strip spec-noise suffixes (e.g., "Dto") only for models without a
134
+ // baseline. When an overlay exists (Scenario A), the overlay check above
135
+ // handles existing models. New models (no overlay entry) get clean names.
136
+ const cleaned = ctx.apiSurface ? name : stripNoiseSuffixes(name);
137
+ return toPascalCase(stripUrnPrefix(cleaned));
111
138
  }