@workos/oagen-emitters 0.5.0 → 0.6.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.
package/dist/plugin.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { t as workosEmittersPlugin } from "./plugin-BSop9f9z.mjs";
1
+ import { t as workosEmittersPlugin } from "./plugin-CZc7Teko.mjs";
2
2
  export { workosEmittersPlugin };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workos/oagen-emitters",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "WorkOS' oagen emitters",
5
5
  "license": "MIT",
6
6
  "author": "WorkOS",
@@ -40,20 +40,20 @@
40
40
  "devDependencies": {
41
41
  "@commitlint/cli": "^20.5.0",
42
42
  "@commitlint/config-conventional": "^20.5.0",
43
- "@types/node": "^25.3.3",
43
+ "@types/node": "^25.6.0",
44
44
  "husky": "^9.1.7",
45
- "oxfmt": "^0.45.0",
46
- "oxlint": "^1.51.0",
47
- "prettier": "^3.8.1",
48
- "tsdown": "^0.21.5",
49
- "tsx": "^4.19.0",
50
- "typescript": "^6.0.0-dev.20260324",
51
- "vitest": "^3.0.0"
45
+ "oxfmt": "^0.46.0",
46
+ "oxlint": "^1.61.0",
47
+ "prettier": "^3.8.3",
48
+ "tsdown": "^0.21.10",
49
+ "tsx": "^4.21.0",
50
+ "typescript": "^6.0.3",
51
+ "vitest": "^4.1.5"
52
52
  },
53
53
  "engines": {
54
54
  "node": ">=24.10.0"
55
55
  },
56
56
  "dependencies": {
57
- "@workos/oagen": "^0.7.0"
57
+ "@workos/oagen": "^0.9.0"
58
58
  }
59
59
  }
@@ -152,15 +152,21 @@ function createProxyServer(apiKey: string, captures: ProxyCapture[]): Promise<{
152
152
  // ---------------------------------------------------------------------------
153
153
 
154
154
  function loadManifest(sdkPath: string): Map<string, ManifestEntry> | null {
155
- const manifestPath = resolve(sdkPath, 'smoke-manifest.json');
155
+ const manifestPath = resolve(sdkPath, '.oagen-manifest.json');
156
156
  if (!existsSync(manifestPath)) {
157
- console.warn(`Warning: No smoke-manifest.json found at ${manifestPath}`);
158
- console.warn(' Method resolution will rely on heuristic tiers most operations may be skipped.');
157
+ console.warn(`Warning: No .oagen-manifest.json found at ${manifestPath}`);
158
+ console.warn(' Method resolution will rely on heuristic tiers -- most operations may be skipped.');
159
+ return null;
160
+ }
161
+ const parsed = JSON.parse(readFileSync(manifestPath, 'utf-8'));
162
+ const operations = parsed?.operations;
163
+ if (!operations || typeof operations !== 'object') {
164
+ console.warn('Warning: .oagen-manifest.json has no operations field');
165
+ console.warn(' Method resolution will rely on heuristic tiers -- most operations may be skipped.');
159
166
  return null;
160
167
  }
161
- const raw = JSON.parse(readFileSync(manifestPath, 'utf-8'));
162
168
  const manifest = new Map<string, ManifestEntry>();
163
- for (const [httpKey, entry] of Object.entries(raw)) {
169
+ for (const [httpKey, entry] of Object.entries(operations)) {
164
170
  manifest.set(httpKey, entry as ManifestEntry);
165
171
  }
166
172
  return manifest;
@@ -196,15 +196,21 @@ function startProxy(
196
196
  // ---------------------------------------------------------------------------
197
197
 
198
198
  function loadManifest(sdkPath: string): Map<string, ManifestEntry> | null {
199
- const manifestPath = resolve(sdkPath, 'smoke-manifest.json');
199
+ const manifestPath = resolve(sdkPath, '.oagen-manifest.json');
200
200
  if (!existsSync(manifestPath)) {
201
- console.warn(`Warning: No smoke-manifest.json found at ${manifestPath}`);
202
- console.warn(' Method resolution will rely on heuristic tiers most operations may be skipped.');
201
+ console.warn(`Warning: No .oagen-manifest.json found at ${manifestPath}`);
202
+ console.warn(' Method resolution will rely on heuristic tiers -- most operations may be skipped.');
203
+ return null;
204
+ }
205
+ const parsed = JSON.parse(readFileSync(manifestPath, 'utf-8'));
206
+ const operations = parsed?.operations;
207
+ if (!operations || typeof operations !== 'object') {
208
+ console.warn('Warning: .oagen-manifest.json has no operations field');
209
+ console.warn(' Method resolution will rely on heuristic tiers -- most operations may be skipped.');
203
210
  return null;
204
211
  }
205
- const raw = JSON.parse(readFileSync(manifestPath, 'utf-8'));
206
212
  const manifest = new Map<string, ManifestEntry>();
207
- for (const [httpKey, entry] of Object.entries(raw)) {
213
+ for (const [httpKey, entry] of Object.entries(operations)) {
208
214
  manifest.set(httpKey, entry as ManifestEntry);
209
215
  }
210
216
  return manifest;
package/smoke/sdk-go.ts CHANGED
@@ -126,15 +126,21 @@ function goFieldName(name: string): string {
126
126
  // ---------------------------------------------------------------------------
127
127
 
128
128
  function loadManifest(sdkPath: string): Map<string, ManifestEntry> | null {
129
- const manifestPath = resolve(sdkPath, 'smoke-manifest.json');
129
+ const manifestPath = resolve(sdkPath, '.oagen-manifest.json');
130
130
  if (!existsSync(manifestPath)) {
131
- console.warn(`Warning: No smoke-manifest.json found at ${manifestPath}`);
131
+ console.warn(`Warning: No .oagen-manifest.json found at ${manifestPath}`);
132
+ console.warn(' Method resolution will rely on heuristic tiers -- most operations may be skipped.');
133
+ return null;
134
+ }
135
+ const parsed = JSON.parse(readFileSync(manifestPath, 'utf-8'));
136
+ const operations = parsed?.operations;
137
+ if (!operations || typeof operations !== 'object') {
138
+ console.warn('Warning: .oagen-manifest.json has no operations field');
132
139
  console.warn(' Method resolution will rely on heuristic tiers -- most operations may be skipped.');
133
140
  return null;
134
141
  }
135
- const raw = JSON.parse(readFileSync(manifestPath, 'utf-8'));
136
142
  const manifest = new Map<string, ManifestEntry>();
137
- for (const [httpKey, entry] of Object.entries(raw)) {
143
+ for (const [httpKey, entry] of Object.entries(operations)) {
138
144
  manifest.set(httpKey, entry as ManifestEntry);
139
145
  }
140
146
  return manifest;
@@ -175,15 +175,21 @@ function createProxyServer(
175
175
  // ---------------------------------------------------------------------------
176
176
 
177
177
  function loadManifest(sdkPath: string): Map<string, ManifestEntry> | null {
178
- const manifestPath = resolve(sdkPath, 'smoke-manifest.json');
178
+ const manifestPath = resolve(sdkPath, '.oagen-manifest.json');
179
179
  if (!existsSync(manifestPath)) {
180
- console.warn(`Warning: No smoke-manifest.json found at ${manifestPath}`);
181
- console.warn(' Method resolution will rely on heuristic tiers most operations may be skipped.');
180
+ console.warn(`Warning: No .oagen-manifest.json found at ${manifestPath}`);
181
+ console.warn(' Method resolution will rely on heuristic tiers -- most operations may be skipped.');
182
+ return null;
183
+ }
184
+ const parsed = JSON.parse(readFileSync(manifestPath, 'utf-8'));
185
+ const operations = parsed?.operations;
186
+ if (!operations || typeof operations !== 'object') {
187
+ console.warn('Warning: .oagen-manifest.json has no operations field');
188
+ console.warn(' Method resolution will rely on heuristic tiers -- most operations may be skipped.');
182
189
  return null;
183
190
  }
184
- const raw = JSON.parse(readFileSync(manifestPath, 'utf-8'));
185
191
  const manifest = new Map<string, ManifestEntry>();
186
- for (const [httpKey, entry] of Object.entries(raw)) {
192
+ for (const [httpKey, entry] of Object.entries(operations)) {
187
193
  manifest.set(httpKey, entry as ManifestEntry);
188
194
  }
189
195
  return manifest;
package/smoke/sdk-node.ts CHANGED
@@ -107,15 +107,21 @@ function restoreFetch(): void {
107
107
  // ---------------------------------------------------------------------------
108
108
 
109
109
  function loadManifest(sdkPath: string): Map<string, ManifestEntry> | null {
110
- const manifestPath = resolve(sdkPath, 'smoke-manifest.json');
110
+ const manifestPath = resolve(sdkPath, '.oagen-manifest.json');
111
111
  if (!existsSync(manifestPath)) {
112
- console.warn(`⚠ No smoke-manifest.json found at ${manifestPath}`);
113
- console.warn(' Method resolution will rely on heuristic tiers most operations may be skipped.');
112
+ console.warn(`Warning: No .oagen-manifest.json found at ${manifestPath}`);
113
+ console.warn(' Method resolution will rely on heuristic tiers -- most operations may be skipped.');
114
+ return null;
115
+ }
116
+ const parsed = JSON.parse(readFileSync(manifestPath, 'utf-8'));
117
+ const operations = parsed?.operations;
118
+ if (!operations || typeof operations !== 'object') {
119
+ console.warn('Warning: .oagen-manifest.json has no operations field');
120
+ console.warn(' Method resolution will rely on heuristic tiers -- most operations may be skipped.');
114
121
  return null;
115
122
  }
116
- const raw = JSON.parse(readFileSync(manifestPath, 'utf-8'));
117
123
  const manifest = new Map<string, ManifestEntry>();
118
- for (const [httpKey, entry] of Object.entries(raw)) {
124
+ for (const [httpKey, entry] of Object.entries(operations)) {
119
125
  manifest.set(httpKey, entry as ManifestEntry);
120
126
  }
121
127
  return manifest;
package/smoke/sdk-php.ts CHANGED
@@ -166,14 +166,19 @@ class CaptureProxy {
166
166
  // ---------------------------------------------------------------------------
167
167
 
168
168
  function loadManifest(sdkPath: string): Map<string, ManifestEntry> | null {
169
- const manifestPath = resolve(sdkPath, 'smoke-manifest.json');
169
+ const manifestPath = resolve(sdkPath, '.oagen-manifest.json');
170
170
  if (!existsSync(manifestPath)) {
171
- console.warn(`Warning: No smoke-manifest.json found at ${manifestPath}`);
171
+ console.warn(`Warning: No .oagen-manifest.json found at ${manifestPath}`);
172
+ return null;
173
+ }
174
+ const parsed = JSON.parse(readFileSync(manifestPath, 'utf-8'));
175
+ const operations = parsed?.operations;
176
+ if (!operations || typeof operations !== 'object') {
177
+ console.warn('Warning: .oagen-manifest.json has no operations field');
172
178
  return null;
173
179
  }
174
- const raw = JSON.parse(readFileSync(manifestPath, 'utf-8'));
175
180
  const manifest = new Map<string, ManifestEntry>();
176
- for (const [httpKey, entry] of Object.entries(raw)) {
181
+ for (const [httpKey, entry] of Object.entries(operations)) {
177
182
  manifest.set(httpKey, entry as ManifestEntry);
178
183
  }
179
184
  return manifest;
@@ -191,15 +191,21 @@ function createProxyServer(
191
191
  // ---------------------------------------------------------------------------
192
192
 
193
193
  function loadManifest(sdkPath: string): Map<string, ManifestEntry> | null {
194
- const manifestPath = resolve(sdkPath, 'smoke-manifest.json');
194
+ const manifestPath = resolve(sdkPath, '.oagen-manifest.json');
195
195
  if (!existsSync(manifestPath)) {
196
- console.warn(`Warning: No smoke-manifest.json found at ${manifestPath}`);
196
+ console.warn(`Warning: No .oagen-manifest.json found at ${manifestPath}`);
197
+ console.warn(' Method resolution will rely on heuristic tiers -- most operations may be skipped.');
198
+ return null;
199
+ }
200
+ const parsed = JSON.parse(readFileSync(manifestPath, 'utf-8'));
201
+ const operations = parsed?.operations;
202
+ if (!operations || typeof operations !== 'object') {
203
+ console.warn('Warning: .oagen-manifest.json has no operations field');
197
204
  console.warn(' Method resolution will rely on heuristic tiers -- most operations may be skipped.');
198
205
  return null;
199
206
  }
200
- const raw = JSON.parse(readFileSync(manifestPath, 'utf-8'));
201
207
  const manifest = new Map<string, ManifestEntry>();
202
- for (const [httpKey, entry] of Object.entries(raw)) {
208
+ for (const [httpKey, entry] of Object.entries(operations)) {
203
209
  manifest.set(httpKey, entry as ManifestEntry);
204
210
  }
205
211
  return manifest;
package/smoke/sdk-ruby.ts CHANGED
@@ -78,15 +78,21 @@ interface MethodResolution {
78
78
  // ---------------------------------------------------------------------------
79
79
 
80
80
  function loadManifest(sdkPath: string): Map<string, ManifestEntry> | null {
81
- const manifestPath = resolve(sdkPath, 'smoke-manifest.json');
81
+ const manifestPath = resolve(sdkPath, '.oagen-manifest.json');
82
82
  if (!existsSync(manifestPath)) {
83
- console.warn(`Warning: No smoke-manifest.json found at ${manifestPath}`);
83
+ console.warn(`Warning: No .oagen-manifest.json found at ${manifestPath}`);
84
+ console.warn(' Method resolution will rely on heuristic tiers -- most operations may be skipped.');
85
+ return null;
86
+ }
87
+ const parsed = JSON.parse(readFileSync(manifestPath, 'utf-8'));
88
+ const operations = parsed?.operations;
89
+ if (!operations || typeof operations !== 'object') {
90
+ console.warn('Warning: .oagen-manifest.json has no operations field');
84
91
  console.warn(' Method resolution will rely on heuristic tiers -- most operations may be skipped.');
85
92
  return null;
86
93
  }
87
- const raw = JSON.parse(readFileSync(manifestPath, 'utf-8'));
88
94
  const manifest = new Map<string, ManifestEntry>();
89
- for (const [httpKey, entry] of Object.entries(raw)) {
95
+ for (const [httpKey, entry] of Object.entries(operations)) {
90
96
  manifest.set(httpKey, entry as ManifestEntry);
91
97
  }
92
98
  return manifest;
package/smoke/sdk-rust.ts CHANGED
@@ -167,15 +167,21 @@ function createProxyServer(
167
167
  // ---------------------------------------------------------------------------
168
168
 
169
169
  function loadManifest(sdkPath: string): Map<string, ManifestEntry> | null {
170
- const manifestPath = resolve(sdkPath, 'smoke-manifest.json');
170
+ const manifestPath = resolve(sdkPath, '.oagen-manifest.json');
171
171
  if (!existsSync(manifestPath)) {
172
- console.warn(`Warning: No smoke-manifest.json found at ${manifestPath}`);
173
- console.warn(' Method resolution will rely on heuristic tiers most operations may be skipped.');
172
+ console.warn(`Warning: No .oagen-manifest.json found at ${manifestPath}`);
173
+ console.warn(' Method resolution will rely on heuristic tiers -- most operations may be skipped.');
174
+ return null;
175
+ }
176
+ const parsed = JSON.parse(readFileSync(manifestPath, 'utf-8'));
177
+ const operations = parsed?.operations;
178
+ if (!operations || typeof operations !== 'object') {
179
+ console.warn('Warning: .oagen-manifest.json has no operations field');
180
+ console.warn(' Method resolution will rely on heuristic tiers -- most operations may be skipped.');
174
181
  return null;
175
182
  }
176
- const raw = JSON.parse(readFileSync(manifestPath, 'utf-8'));
177
183
  const manifest = new Map<string, ManifestEntry>();
178
- for (const [httpKey, entry] of Object.entries(raw)) {
184
+ for (const [httpKey, entry] of Object.entries(operations)) {
179
185
  manifest.set(httpKey, entry as ManifestEntry);
180
186
  }
181
187
  return manifest;
@@ -17,7 +17,7 @@ import { generateEnums, primeEnumAliases } from './enums.js';
17
17
  import { generateResources } from './resources.js';
18
18
  import { generateClient } from './client.js';
19
19
  import { generateTests } from './tests.js';
20
- import { generateManifest } from './manifest.js';
20
+ import { buildOperationsMap } from './manifest.js';
21
21
  import { generateWrapperOptionsClasses } from './wrappers.js';
22
22
  import { groupByMount } from '../shared/resolved-ops.js';
23
23
  import { discriminatedUnions } from './type-map.js';
@@ -206,8 +206,8 @@ export const dotnetEmitter: Emitter = {
206
206
  return prefixTestPaths(ensureTrailingNewlines(generateTests(spec, c)));
207
207
  },
208
208
 
209
- generateManifest(spec: ApiSpec, ctx: EmitterContext): GeneratedFile[] {
210
- return ensureTrailingNewlines(generateManifest(spec, fixNamespace(ctx)));
209
+ buildOperationsMap(spec: ApiSpec, ctx: EmitterContext) {
210
+ return buildOperationsMap(spec, fixNamespace(ctx));
211
211
  },
212
212
 
213
213
  fileHeader(): string {
@@ -1,13 +1,13 @@
1
- import type { ApiSpec, EmitterContext, GeneratedFile } from '@workos/oagen';
1
+ import type { ApiSpec, EmitterContext, OperationsMap } from '@workos/oagen';
2
2
  import { resolveMethodName } from './naming.js';
3
3
  import { buildServiceAccessPaths } from './client.js';
4
4
  import { getMountTarget } from '../shared/resolved-ops.js';
5
5
 
6
6
  /**
7
- * Generate smoke test manifest mapping HTTP operations to SDK methods.
7
+ * Build operation-to-SDK-method mapping for the manifest.
8
8
  */
9
- export function generateManifest(spec: ApiSpec, ctx: EmitterContext): GeneratedFile[] {
10
- const manifest: Record<string, { sdkMethod: string; service: string }> = {};
9
+ export function buildOperationsMap(spec: ApiSpec, ctx: EmitterContext): OperationsMap {
10
+ const manifest: OperationsMap = {};
11
11
  const accessPaths = buildServiceAccessPaths(spec.services, ctx);
12
12
 
13
13
  for (const service of spec.services) {
@@ -26,11 +26,5 @@ export function generateManifest(spec: ApiSpec, ctx: EmitterContext): GeneratedF
26
26
  }
27
27
  }
28
28
 
29
- return [
30
- {
31
- path: 'smoke-manifest.json',
32
- content: JSON.stringify(manifest, null, 2),
33
- integrateTarget: false,
34
- },
35
- ];
29
+ return manifest;
36
30
  }
package/src/go/index.ts CHANGED
@@ -15,7 +15,7 @@ import { generateEnums } from './enums.js';
15
15
  import { generateResources } from './resources.js';
16
16
  import { generateClient } from './client.js';
17
17
  import { generateTests } from './tests.js';
18
- import { generateManifest } from './manifest.js';
18
+ import { buildOperationsMap } from './manifest.js';
19
19
 
20
20
  /** Ensure every generated file's content ends with a trailing newline. */
21
21
  function ensureTrailingNewlines(files: GeneratedFile[]): GeneratedFile[] {
@@ -33,7 +33,21 @@ export const goEmitter: Emitter = {
33
33
  generateModels(models: Model[], ctx: EmitterContext): GeneratedFile[] {
34
34
  // Enrich models by flattening oneOf/allOf+oneOf variant fields from the raw spec
35
35
  const enriched = enrichModelsFromSpec(models);
36
- return ensureTrailingNewlines(generateModels(enriched, ctx));
36
+ // Go has no sum types, so discriminated union base models (e.g. EventSchema)
37
+ // need their base fields preserved. enrichModelsFromSpec clears fields for
38
+ // discriminated models so dispatcher-capable languages can generate sealed
39
+ // classes; restore the original fields here since Go just emits flat structs.
40
+ const originalByName = new Map(models.map((m) => [m.name, m]));
41
+ const goModels = enriched.map((m) => {
42
+ if ((m as any).discriminator && m.fields.length === 0) {
43
+ const original = originalByName.get(m.name);
44
+ if (original && original.fields.length > 0) {
45
+ return { ...m, fields: original.fields };
46
+ }
47
+ }
48
+ return m;
49
+ });
50
+ return ensureTrailingNewlines(generateModels(goModels, ctx));
37
51
  },
38
52
 
39
53
  generateEnums(enums: Enum[], ctx: EmitterContext): GeneratedFile[] {
@@ -64,8 +78,8 @@ export const goEmitter: Emitter = {
64
78
  return ensureTrailingNewlines(generateTests(spec, ctx));
65
79
  },
66
80
 
67
- generateManifest(spec: ApiSpec, ctx: EmitterContext): GeneratedFile[] {
68
- return ensureTrailingNewlines(generateManifest(spec, ctx));
81
+ buildOperationsMap(spec: ApiSpec, ctx: EmitterContext) {
82
+ return buildOperationsMap(spec, ctx);
69
83
  },
70
84
 
71
85
  fileHeader(): string {
@@ -1,13 +1,13 @@
1
- import type { ApiSpec, EmitterContext, GeneratedFile } from '@workos/oagen';
1
+ import type { ApiSpec, EmitterContext, OperationsMap } from '@workos/oagen';
2
2
  import { resolveMethodName } from './naming.js';
3
3
  import { buildServiceAccessPaths } from './client.js';
4
4
  import { getMountTarget } from '../shared/resolved-ops.js';
5
5
 
6
6
  /**
7
- * Generate smoke test manifest mapping HTTP operations to SDK methods.
7
+ * Build operation-to-SDK-method mapping for the manifest.
8
8
  */
9
- export function generateManifest(spec: ApiSpec, ctx: EmitterContext): GeneratedFile[] {
10
- const manifest: Record<string, { sdkMethod: string; service: string }> = {};
9
+ export function buildOperationsMap(spec: ApiSpec, ctx: EmitterContext): OperationsMap {
10
+ const manifest: OperationsMap = {};
11
11
  const accessPaths = buildServiceAccessPaths(spec.services, ctx);
12
12
 
13
13
  for (const service of spec.services) {
@@ -26,11 +26,5 @@ export function generateManifest(spec: ApiSpec, ctx: EmitterContext): GeneratedF
26
26
  }
27
27
  }
28
28
 
29
- return [
30
- {
31
- path: 'smoke-manifest.json',
32
- content: JSON.stringify(manifest, null, 2),
33
- integrateTarget: false,
34
- },
35
- ];
29
+ return manifest;
36
30
  }
@@ -16,7 +16,7 @@ import { generateEnums } from './enums.js';
16
16
  import { generateResources } from './resources.js';
17
17
  import { generateClient } from './client.js';
18
18
  import { generateTests } from './tests.js';
19
- import { generateManifest } from './manifest.js';
19
+ import { buildOperationsMap } from './manifest.js';
20
20
  import { enrichModelsFromSpec, getSyntheticEnums } from '../shared/model-utils.js';
21
21
 
22
22
  /** Ensure every generated file ends with a trailing newline. */
@@ -69,8 +69,8 @@ export const kotlinEmitter: Emitter = {
69
69
  return ensureTrailingNewlines(generateTests(enrichedSpec, { ...ctx, spec: enrichedSpec }));
70
70
  },
71
71
 
72
- generateManifest(spec: ApiSpec, ctx: EmitterContext): GeneratedFile[] {
73
- return ensureTrailingNewlines(generateManifest(spec, ctx));
72
+ buildOperationsMap(spec: ApiSpec, ctx: EmitterContext) {
73
+ return buildOperationsMap(spec, ctx);
74
74
  },
75
75
 
76
76
  fileHeader(): string {
@@ -1,20 +1,20 @@
1
- import type { ApiSpec, EmitterContext, GeneratedFile } from '@workos/oagen';
1
+ import type { ApiSpec, EmitterContext, OperationsMap } from '@workos/oagen';
2
2
  import { resolveMethodName, servicePropertyName, resolveClassName } from './naming.js';
3
3
  import { buildResolvedLookup, lookupResolved, getMountTarget } from '../shared/resolved-ops.js';
4
4
  import { propertyName } from './naming.js';
5
5
  import { isHandwrittenOverride } from './overrides.js';
6
6
 
7
7
  /**
8
- * Generate the smoke-test manifest mapping `"HTTP_METHOD /path"` to
9
- * `{ sdkMethod, service }`. The `service` is the camelCase accessor property
10
- * on the main `WorkOS` client (e.g., `organizations`).
8
+ * Build the operation-to-SDK-method mapping for the manifest.
9
+ *
10
+ * The `service` is the camelCase accessor property on the main `WorkOS`
11
+ * client (e.g., `organizations`).
11
12
  *
12
13
  * For polymorphic/split operations (e.g., authenticate -> 8 methods), the
13
- * manifest emits one entry per wrapper method so each variant is addressable.
14
+ * manifest emits an array of methods so each variant is addressable.
14
15
  */
15
- export function generateManifest(spec: ApiSpec, ctx: EmitterContext): GeneratedFile[] {
16
- const manifest: Record<string, { sdkMethod: string; service: string } | { sdkMethod: string; service: string }[]> =
17
- {};
16
+ export function buildOperationsMap(spec: ApiSpec, ctx: EmitterContext): OperationsMap {
17
+ const manifest: OperationsMap = {};
18
18
  const resolvedLookup = buildResolvedLookup(ctx);
19
19
 
20
20
  for (const service of spec.services) {
@@ -45,11 +45,5 @@ export function generateManifest(spec: ApiSpec, ctx: EmitterContext): GeneratedF
45
45
  }
46
46
  }
47
47
 
48
- return [
49
- {
50
- path: 'smoke-manifest.json',
51
- content: JSON.stringify(manifest, null, 2),
52
- integrateTarget: false,
53
- },
54
- ];
48
+ return manifest;
55
49
  }
package/src/node/index.ts CHANGED
@@ -18,7 +18,7 @@ import { generateClient } from './client.js';
18
18
  import { generateErrors } from './errors.js';
19
19
 
20
20
  import { generateTests } from './tests.js';
21
- import { generateManifest } from './manifest.js';
21
+ import { buildOperationsMap } from './manifest.js';
22
22
 
23
23
  /** Ensure every generated file's content ends with a trailing newline. */
24
24
  function ensureTrailingNewlines(files: GeneratedFile[]): GeneratedFile[] {
@@ -62,8 +62,8 @@ export const nodeEmitter: Emitter = {
62
62
  return ensureTrailingNewlines(generateTests(spec, ctx));
63
63
  },
64
64
 
65
- generateManifest(spec: ApiSpec, ctx: EmitterContext): GeneratedFile[] {
66
- return ensureTrailingNewlines(generateManifest(spec, ctx));
65
+ buildOperationsMap(spec: ApiSpec, ctx: EmitterContext) {
66
+ return buildOperationsMap(spec, ctx);
67
67
  },
68
68
 
69
69
  fileHeader(): string {
@@ -1,9 +1,9 @@
1
- import type { ApiSpec, EmitterContext, GeneratedFile } from '@workos/oagen';
1
+ import type { ApiSpec, EmitterContext, OperationsMap } from '@workos/oagen';
2
2
  import { resolveMethodName, servicePropertyName } from './naming.js';
3
3
  import { resolveResourceClassName } from './resources.js';
4
4
 
5
- export function generateManifest(spec: ApiSpec, ctx: EmitterContext): GeneratedFile[] {
6
- const manifest: Record<string, { sdkMethod: string; service: string }> = {};
5
+ export function buildOperationsMap(spec: ApiSpec, ctx: EmitterContext): OperationsMap {
6
+ const manifest: OperationsMap = {};
7
7
 
8
8
  for (const service of spec.services) {
9
9
  const propName = servicePropertyName(resolveResourceClassName(service, ctx));
@@ -14,12 +14,5 @@ export function generateManifest(spec: ApiSpec, ctx: EmitterContext): GeneratedF
14
14
  }
15
15
  }
16
16
 
17
- return [
18
- {
19
- path: 'smoke-manifest.json',
20
- content: JSON.stringify(manifest, null, 2),
21
- integrateTarget: false,
22
- overwriteExisting: true,
23
- },
24
- ];
17
+ return manifest;
25
18
  }
package/src/php/index.ts CHANGED
@@ -16,7 +16,7 @@ import { generateEnums } from './enums.js';
16
16
  import { generateResources } from './resources.js';
17
17
  import { generateClient } from './client.js';
18
18
  import { generateTests } from './tests.js';
19
- import { generateManifest } from './manifest.js';
19
+ import { buildOperationsMap } from './manifest.js';
20
20
  import { initializeEnumDedup } from './naming.js';
21
21
 
22
22
  /** Initialize enum deduplication from spec data. */
@@ -71,9 +71,9 @@ export const phpEmitter: Emitter = {
71
71
  return ensureTrailingNewlines(generateTests(spec, ctx));
72
72
  },
73
73
 
74
- generateManifest(spec: ApiSpec, ctx: EmitterContext): GeneratedFile[] {
74
+ buildOperationsMap(spec: ApiSpec, ctx: EmitterContext) {
75
75
  ensureNamingInitialized(ctx);
76
- return ensureTrailingNewlines(generateManifest(spec, ctx));
76
+ return buildOperationsMap(spec, ctx);
77
77
  },
78
78
 
79
79
  fileHeader(): string {
@@ -1,13 +1,13 @@
1
- import type { ApiSpec, EmitterContext, GeneratedFile } from '@workos/oagen';
1
+ import type { ApiSpec, EmitterContext, OperationsMap } from '@workos/oagen';
2
2
  import { resolveMethodName } from './naming.js';
3
3
  import { buildServiceAccessPaths } from './client.js';
4
4
  import { getMountTarget } from '../shared/resolved-ops.js';
5
5
 
6
6
  /**
7
- * Generate smoke test manifest mapping HTTP operations to SDK methods.
7
+ * Build operation-to-SDK-method mapping for the manifest.
8
8
  */
9
- export function generateManifest(spec: ApiSpec, ctx: EmitterContext): GeneratedFile[] {
10
- const manifest: Record<string, { sdkMethod: string; service: string }> = {};
9
+ export function buildOperationsMap(spec: ApiSpec, ctx: EmitterContext): OperationsMap {
10
+ const manifest: OperationsMap = {};
11
11
  const accessPaths = buildServiceAccessPaths(spec.services, ctx);
12
12
 
13
13
  for (const service of spec.services) {
@@ -26,11 +26,5 @@ export function generateManifest(spec: ApiSpec, ctx: EmitterContext): GeneratedF
26
26
  }
27
27
  }
28
28
 
29
- return [
30
- {
31
- path: 'smoke-manifest.json',
32
- content: JSON.stringify(manifest, null, 2),
33
- integrateTarget: false,
34
- },
35
- ];
29
+ return manifest;
36
30
  }
@@ -261,12 +261,14 @@ function generateMethod(
261
261
  }
262
262
 
263
263
  // @param for body fields
264
+ const groupedParamNames = collectGroupedParamNames(op);
264
265
  if (plan.hasBody && op.requestBody?.kind === 'model') {
265
266
  const bodyModel = modelMap.get(op.requestBody.name);
266
267
  if (bodyModel) {
267
268
  const bodyParamMap = buildBodyParamMap(op, bodyModel);
268
269
  for (const field of bodyModel.fields) {
269
270
  if (hiddenParams.has(field.name)) continue;
271
+ if (groupedParamNames.has(field.name)) continue;
270
272
  const docType = mapTypeRefForPHPDoc(field.type);
271
273
  const phpName = bodyParamMap.get(field.name) ?? fieldName(field.name);
272
274
  if (seenDocParams.has(phpName)) continue;
@@ -280,7 +282,6 @@ function generateMethod(
280
282
  }
281
283
 
282
284
  // @param for parameter groups (union-typed)
283
- const groupedParamNames = collectGroupedParamNames(op);
284
285
  for (const group of op.parameterGroups ?? []) {
285
286
  const phpName = fieldName(group.name);
286
287
  if (seenDocParams.has(phpName)) continue;
@@ -438,7 +439,9 @@ function generateMethod(
438
439
  if (plan.hasBody) {
439
440
  const bodyModel = op.requestBody?.kind === 'model' ? modelMap.get(op.requestBody.name) : null;
440
441
  const bodyParamMap = buildBodyParamMap(op, bodyModel ?? null);
441
- const visibleFields = bodyModel?.fields.filter((f) => !hiddenParams.has(f.name)) ?? [];
442
+ const deleteGroupedParams = collectGroupedParamNames(op);
443
+ const visibleFields =
444
+ bodyModel?.fields.filter((f) => !hiddenParams.has(f.name) && !deleteGroupedParams.has(f.name)) ?? [];
442
445
  const hasOptionalFields = visibleFields.some((f) => !f.required);
443
446
  if (hasOptionalFields) {
444
447
  lines.push(' $body = array_filter([');
@@ -493,7 +496,9 @@ function generateMethod(
493
496
  } else if (plan.hasBody) {
494
497
  const bodyModel = op.requestBody?.kind === 'model' ? modelMap.get(op.requestBody.name) : null;
495
498
  const bodyParamMap = buildBodyParamMap(op, bodyModel ?? null);
496
- const visibleFields = bodyModel?.fields.filter((f) => !hiddenParams.has(f.name)) ?? [];
499
+ const bodyGroupedParams = collectGroupedParamNames(op);
500
+ const visibleFields =
501
+ bodyModel?.fields.filter((f) => !hiddenParams.has(f.name) && !bodyGroupedParams.has(f.name)) ?? [];
497
502
  const hasOptionalFields = visibleFields.some((f) => !f.required);
498
503
  if (hasOptionalFields) {
499
504
  lines.push(' $body = array_filter([');
@@ -617,7 +622,6 @@ function buildMethodParams(
617
622
  const usedNames = new Set<string>();
618
623
  const hidden = hiddenParams ?? new Set();
619
624
  const groupedParams = collectGroupedParamNames(op);
620
-
621
625
  // Path params (always required)
622
626
  for (const p of op.pathParams) {
623
627
  const phpType = mapTypeRef(p.type, { qualified: true });
@@ -633,6 +637,7 @@ function buildMethodParams(
633
637
  if (bodyModel) {
634
638
  for (const field of bodyModel.fields) {
635
639
  if (hidden.has(field.name)) continue;
640
+ if (groupedParams.has(field.name)) continue;
636
641
  const phpType = mapTypeRef(field.type, { qualified: true });
637
642
  let phpName = fieldName(field.name);
638
643
  if (usedNames.has(phpName)) {