@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.
- package/.husky/pre-commit +1 -0
- package/.oxfmtrc.json +8 -1
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +15 -0
- package/README.md +129 -0
- package/dist/index.d.mts +10 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +11943 -2728
- package/dist/index.mjs.map +1 -1
- package/docs/sdk-architecture/go.md +338 -0
- package/docs/sdk-architecture/php.md +315 -0
- package/docs/sdk-architecture/python.md +511 -0
- package/oagen.config.ts +298 -2
- package/package.json +9 -5
- package/scripts/generate-php.js +13 -0
- package/scripts/git-push-with-published-oagen.sh +21 -0
- package/smoke/sdk-dotnet.ts +17 -3
- package/smoke/sdk-elixir.ts +17 -3
- package/smoke/sdk-go.ts +137 -46
- package/smoke/sdk-kotlin.ts +23 -4
- package/smoke/sdk-node.ts +15 -3
- package/smoke/sdk-php.ts +28 -26
- package/smoke/sdk-python.ts +5 -2
- package/smoke/sdk-ruby.ts +17 -3
- package/smoke/sdk-rust.ts +16 -3
- package/src/go/client.ts +141 -0
- package/src/go/enums.ts +196 -0
- package/src/go/fixtures.ts +212 -0
- package/src/go/index.ts +81 -0
- package/src/go/manifest.ts +36 -0
- package/src/go/models.ts +254 -0
- package/src/go/naming.ts +191 -0
- package/src/go/resources.ts +827 -0
- package/src/go/tests.ts +751 -0
- package/src/go/type-map.ts +82 -0
- package/src/go/wrappers.ts +261 -0
- package/src/index.ts +3 -0
- package/src/node/client.ts +167 -122
- package/src/node/enums.ts +13 -4
- package/src/node/errors.ts +42 -233
- package/src/node/field-plan.ts +726 -0
- package/src/node/fixtures.ts +15 -5
- package/src/node/index.ts +65 -16
- package/src/node/models.ts +264 -96
- package/src/node/naming.ts +52 -25
- package/src/node/resources.ts +621 -172
- package/src/node/sdk-errors.ts +41 -0
- package/src/node/tests.ts +71 -27
- package/src/node/type-map.ts +4 -2
- package/src/node/utils.ts +56 -64
- package/src/node/wrappers.ts +151 -0
- package/src/php/client.ts +171 -0
- package/src/php/enums.ts +67 -0
- package/src/php/errors.ts +9 -0
- package/src/php/fixtures.ts +181 -0
- package/src/php/index.ts +96 -0
- package/src/php/manifest.ts +36 -0
- package/src/php/models.ts +310 -0
- package/src/php/naming.ts +298 -0
- package/src/php/resources.ts +561 -0
- package/src/php/tests.ts +533 -0
- package/src/php/type-map.ts +90 -0
- package/src/php/utils.ts +18 -0
- package/src/php/wrappers.ts +151 -0
- package/src/python/client.ts +337 -0
- package/src/python/enums.ts +313 -0
- package/src/python/fixtures.ts +196 -0
- package/src/python/index.ts +95 -0
- package/src/python/manifest.ts +38 -0
- package/src/python/models.ts +688 -0
- package/src/python/naming.ts +209 -0
- package/src/python/resources.ts +1322 -0
- package/src/python/tests.ts +1335 -0
- package/src/python/type-map.ts +93 -0
- package/src/python/wrappers.ts +191 -0
- package/src/shared/model-utils.ts +255 -0
- package/src/shared/naming-utils.ts +107 -0
- package/src/shared/non-spec-services.ts +54 -0
- package/src/shared/resolved-ops.ts +109 -0
- package/src/shared/wrapper-utils.ts +59 -0
- package/test/go/client.test.ts +92 -0
- package/test/go/enums.test.ts +132 -0
- package/test/go/errors.test.ts +9 -0
- package/test/go/models.test.ts +265 -0
- package/test/go/resources.test.ts +408 -0
- package/test/go/tests.test.ts +143 -0
- package/test/node/client.test.ts +199 -94
- package/test/node/enums.test.ts +75 -3
- package/test/node/errors.test.ts +2 -41
- package/test/node/models.test.ts +109 -20
- package/test/node/naming.test.ts +37 -4
- package/test/node/resources.test.ts +662 -30
- package/test/node/serializers.test.ts +36 -7
- package/test/node/type-map.test.ts +11 -0
- package/test/php/client.test.ts +94 -0
- package/test/php/enums.test.ts +173 -0
- package/test/php/errors.test.ts +9 -0
- package/test/php/models.test.ts +497 -0
- package/test/php/resources.test.ts +644 -0
- package/test/php/tests.test.ts +118 -0
- package/test/python/client.test.ts +200 -0
- package/test/python/enums.test.ts +228 -0
- package/test/python/errors.test.ts +16 -0
- package/test/python/manifest.test.ts +74 -0
- package/test/python/models.test.ts +716 -0
- package/test/python/resources.test.ts +617 -0
- package/test/python/tests.test.ts +202 -0
- package/src/node/common.ts +0 -273
- package/src/node/config.ts +0 -71
- package/src/node/serializers.ts +0 -744
package/src/node/client.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ApiSpec, AuthScheme, EmitterContext, GeneratedFile, Service } from '@workos/oagen';
|
|
2
|
-
import {
|
|
2
|
+
import { collectReferencedNames } from '@workos/oagen';
|
|
3
|
+
import { fileName, resolveServiceDir, servicePropertyName, resolveInterfaceName, wireInterfaceName } from './naming.js';
|
|
3
4
|
import {
|
|
4
5
|
docComment,
|
|
5
6
|
createServiceDirResolver,
|
|
@@ -15,9 +16,7 @@ export function generateClient(spec: ApiSpec, ctx: EmitterContext): GeneratedFil
|
|
|
15
16
|
files.push(generateWorkOSClient(spec, ctx));
|
|
16
17
|
files.push(...generateServiceBarrels(spec, ctx));
|
|
17
18
|
files.push(generateBarrel(spec, ctx));
|
|
18
|
-
|
|
19
|
-
files.push(generatePackageJson(ctx));
|
|
20
|
-
files.push(generateTsConfig());
|
|
19
|
+
// worker barrel, package.json, tsconfig.json are now hand-maintained in the target SDK
|
|
21
20
|
|
|
22
21
|
return files;
|
|
23
22
|
}
|
|
@@ -46,7 +45,7 @@ function generateWorkOSClient(spec: ApiSpec, ctx: EmitterContext): GeneratedFile
|
|
|
46
45
|
for (const service of spec.services) {
|
|
47
46
|
if (coveredServices.has(service.name)) continue;
|
|
48
47
|
const resolvedName = resolveResourceClassName(service, ctx);
|
|
49
|
-
const serviceDir =
|
|
48
|
+
const serviceDir = resolveServiceDir(resolvedName);
|
|
50
49
|
lines.push(`import { ${resolvedName} } from './${serviceDir}/${fileName(resolvedName)}';`);
|
|
51
50
|
}
|
|
52
51
|
|
|
@@ -99,7 +98,11 @@ function generateWorkOSClient(spec: ApiSpec, ctx: EmitterContext): GeneratedFile
|
|
|
99
98
|
|
|
100
99
|
lines.push('}');
|
|
101
100
|
|
|
102
|
-
return {
|
|
101
|
+
return {
|
|
102
|
+
path: 'src/workos.ts',
|
|
103
|
+
content: lines.join('\n'),
|
|
104
|
+
skipIfExists: true,
|
|
105
|
+
};
|
|
103
106
|
}
|
|
104
107
|
|
|
105
108
|
/**
|
|
@@ -124,47 +127,36 @@ function generateServiceBarrels(spec: ApiSpec, ctx: EmitterContext): GeneratedFi
|
|
|
124
127
|
// exports a name (e.g., AuditLogSchema from create-audit-log-schema-options),
|
|
125
128
|
// the generated model with the same name must be skipped to prevent the
|
|
126
129
|
// merger from adding a duplicate `export *` that causes TS2308.
|
|
130
|
+
//
|
|
131
|
+
// Also track baseline file stems per directory so we can detect when the
|
|
132
|
+
// barrel needs updating with new export lines (see hasNewExports below).
|
|
133
|
+
const dirSymbolsFromBaseline = new Map<string, Set<string>>();
|
|
134
|
+
const seedFromBaseline = (sourceFile: string, name: string) => {
|
|
135
|
+
const match = sourceFile.match(/^src\/([^/]+)\/interfaces\/(.+)\.ts$/);
|
|
136
|
+
if (!match) return;
|
|
137
|
+
const dirName = match[1];
|
|
138
|
+
const fileStem = match[2];
|
|
139
|
+
if (!dirSymbols.has(dirName)) dirSymbols.set(dirName, new Set());
|
|
140
|
+
dirSymbols.get(dirName)!.add(name);
|
|
141
|
+
if (!dirSymbolsFromBaseline.has(dirName)) dirSymbolsFromBaseline.set(dirName, new Set());
|
|
142
|
+
dirSymbolsFromBaseline.get(dirName)!.add(fileStem);
|
|
143
|
+
};
|
|
127
144
|
if (ctx.apiSurface?.interfaces) {
|
|
128
145
|
for (const [name, iface] of Object.entries(ctx.apiSurface.interfaces)) {
|
|
129
146
|
const sourceFile = (iface as any).sourceFile as string | undefined;
|
|
130
|
-
if (
|
|
131
|
-
// Match paths like "src/audit-logs/interfaces/foo.interface.ts" to directory "audit-logs"
|
|
132
|
-
const match = sourceFile.match(/^src\/([^/]+)\/interfaces\//);
|
|
133
|
-
if (match) {
|
|
134
|
-
const dirName = match[1];
|
|
135
|
-
if (!dirSymbols.has(dirName)) {
|
|
136
|
-
dirSymbols.set(dirName, new Set());
|
|
137
|
-
}
|
|
138
|
-
dirSymbols.get(dirName)!.add(name);
|
|
139
|
-
}
|
|
147
|
+
if (sourceFile) seedFromBaseline(sourceFile, name);
|
|
140
148
|
}
|
|
141
149
|
}
|
|
142
150
|
if (ctx.apiSurface?.enums) {
|
|
143
151
|
for (const [name, enumDef] of Object.entries(ctx.apiSurface.enums)) {
|
|
144
152
|
const sourceFile = (enumDef as any).sourceFile as string | undefined;
|
|
145
|
-
if (
|
|
146
|
-
const match = sourceFile.match(/^src\/([^/]+)\/interfaces\//);
|
|
147
|
-
if (match) {
|
|
148
|
-
const dirName = match[1];
|
|
149
|
-
if (!dirSymbols.has(dirName)) {
|
|
150
|
-
dirSymbols.set(dirName, new Set());
|
|
151
|
-
}
|
|
152
|
-
dirSymbols.get(dirName)!.add(name);
|
|
153
|
-
}
|
|
153
|
+
if (sourceFile) seedFromBaseline(sourceFile, name);
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
156
|
if (ctx.apiSurface?.typeAliases) {
|
|
157
157
|
for (const [name, alias] of Object.entries(ctx.apiSurface.typeAliases)) {
|
|
158
158
|
const sourceFile = (alias as any).sourceFile as string | undefined;
|
|
159
|
-
if (
|
|
160
|
-
const match = sourceFile.match(/^src\/([^/]+)\/interfaces\//);
|
|
161
|
-
if (match) {
|
|
162
|
-
const dirName = match[1];
|
|
163
|
-
if (!dirSymbols.has(dirName)) {
|
|
164
|
-
dirSymbols.set(dirName, new Set());
|
|
165
|
-
}
|
|
166
|
-
dirSymbols.get(dirName)!.add(name);
|
|
167
|
-
}
|
|
159
|
+
if (sourceFile) seedFromBaseline(sourceFile, name);
|
|
168
160
|
}
|
|
169
161
|
}
|
|
170
162
|
|
|
@@ -183,8 +175,12 @@ function generateServiceBarrels(spec: ApiSpec, ctx: EmitterContext): GeneratedFi
|
|
|
183
175
|
// Models -> service directories
|
|
184
176
|
// Skip list wrapper and list metadata models — they use shared List<T>/ListMetadata
|
|
185
177
|
// from common utils, so no per-resource interface file is generated.
|
|
178
|
+
// Also skip unreachable models — oagen only passes service-referenced models
|
|
179
|
+
// to generateModels, so unreachable models have no interface file to export.
|
|
180
|
+
const barrelReachable = collectReferencedNames(spec.services, spec.models);
|
|
186
181
|
for (const model of spec.models) {
|
|
187
182
|
if (isListMetadataModel(model) || isListWrapperModel(model)) continue;
|
|
183
|
+
if (!barrelReachable.models.has(model.name)) continue;
|
|
188
184
|
const service = modelToService.get(model.name);
|
|
189
185
|
const dirName = resolveDir(service);
|
|
190
186
|
if (!dirExports.has(dirName)) {
|
|
@@ -236,14 +232,57 @@ function generateServiceBarrels(spec: ApiSpec, ctx: EmitterContext): GeneratedFi
|
|
|
236
232
|
}
|
|
237
233
|
|
|
238
234
|
for (const [dirName, exports] of dirExports) {
|
|
239
|
-
|
|
240
|
-
|
|
235
|
+
const exportSet = new Set(exports);
|
|
236
|
+
|
|
237
|
+
// When integrating into an existing SDK, include baseline exports from
|
|
238
|
+
// the api-surface so the barrel is comprehensive. This ensures stale
|
|
239
|
+
// entries (e.g., renamed files from previous generations) are removed
|
|
240
|
+
// when overwriteExisting replaces the barrel.
|
|
241
|
+
if (ctx.apiSurface) {
|
|
242
|
+
const addBaselineExports = (items: Record<string, any> | undefined) => {
|
|
243
|
+
if (!items) return;
|
|
244
|
+
for (const item of Object.values(items)) {
|
|
245
|
+
const sourceFile = (item as any).sourceFile as string | undefined;
|
|
246
|
+
if (!sourceFile) continue;
|
|
247
|
+
const match = sourceFile.match(/^src\/([^/]+)\/interfaces\/(.+)\.ts$/);
|
|
248
|
+
if (match && match[1] === dirName) {
|
|
249
|
+
exportSet.add(`export * from './${match[2].replace(/\.ts$/, '')}';`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
addBaselineExports(ctx.apiSurface.interfaces);
|
|
254
|
+
addBaselineExports(ctx.apiSurface.typeAliases);
|
|
255
|
+
addBaselineExports(ctx.apiSurface.enums);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Deduplicate and sort
|
|
259
|
+
const uniqueExports = [...exportSet];
|
|
241
260
|
uniqueExports.sort();
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
261
|
+
|
|
262
|
+
if (ctx.apiSurface) {
|
|
263
|
+
// Integration mode: overwrite the barrel so stale entries are removed.
|
|
264
|
+
files.push({
|
|
265
|
+
path: `src/${dirName}/interfaces/index.ts`,
|
|
266
|
+
content: uniqueExports.join('\n'),
|
|
267
|
+
overwriteExisting: true,
|
|
268
|
+
});
|
|
269
|
+
} else {
|
|
270
|
+
// Standalone generation: only update if there are new exports.
|
|
271
|
+
const baselineSymbols = dirSymbolsFromBaseline.get(dirName);
|
|
272
|
+
const hasNewExports = baselineSymbols
|
|
273
|
+
? uniqueExports.some((exp) => {
|
|
274
|
+
const match = exp.match(/from '\.\/(.*?)'/);
|
|
275
|
+
if (!match) return false;
|
|
276
|
+
return !baselineSymbols.has(match[1]);
|
|
277
|
+
})
|
|
278
|
+
: false;
|
|
279
|
+
|
|
280
|
+
files.push({
|
|
281
|
+
path: `src/${dirName}/interfaces/index.ts`,
|
|
282
|
+
content: uniqueExports.join('\n'),
|
|
283
|
+
skipIfExists: !hasNewExports,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
247
286
|
}
|
|
248
287
|
|
|
249
288
|
return files;
|
|
@@ -315,10 +354,68 @@ function generateBarrel(spec: ApiSpec, ctx: EmitterContext): GeneratedFile {
|
|
|
315
354
|
// Track directories that have already been wildcard-exported
|
|
316
355
|
const exportedDirs = new Set<string>();
|
|
317
356
|
|
|
357
|
+
// Pre-compute all names per interfaces directory (generated + baseline) to detect
|
|
358
|
+
// cross-directory conflicts before emitting any star exports. A star export is
|
|
359
|
+
// unsafe when two different directories export the same name (e.g., Factor in
|
|
360
|
+
// both mfa/interfaces and user-management/interfaces).
|
|
361
|
+
const dirAllNames = new Map<string, Set<string>>();
|
|
362
|
+
for (const service of spec.services) {
|
|
363
|
+
const iDir = resolveDir(service.name);
|
|
364
|
+
if (!dirAllNames.has(iDir)) dirAllNames.set(iDir, new Set());
|
|
365
|
+
const names = dirAllNames.get(iDir)!;
|
|
366
|
+
for (const model of spec.models) {
|
|
367
|
+
if (modelToService.get(model.name) !== service.name) continue;
|
|
368
|
+
if (isListMetadataModel(model) || isListWrapperModel(model)) continue;
|
|
369
|
+
names.add(resolveInterfaceName(model.name, ctx));
|
|
370
|
+
names.add(wireInterfaceName(resolveInterfaceName(model.name, ctx)));
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
// Add baseline names per directory
|
|
374
|
+
if (ctx.apiSurface?.interfaces) {
|
|
375
|
+
for (const [name, iface] of Object.entries(ctx.apiSurface.interfaces)) {
|
|
376
|
+
const sourceFile = (iface as any).sourceFile as string | undefined;
|
|
377
|
+
if (!sourceFile) continue;
|
|
378
|
+
const match = sourceFile.match(/^src\/([^/]+)\/interfaces\//);
|
|
379
|
+
if (match) {
|
|
380
|
+
const dirName = match[1];
|
|
381
|
+
if (!dirAllNames.has(dirName)) dirAllNames.set(dirName, new Set());
|
|
382
|
+
dirAllNames.get(dirName)!.add(name);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
if (ctx.apiSurface?.typeAliases) {
|
|
387
|
+
for (const [name, alias] of Object.entries(ctx.apiSurface.typeAliases)) {
|
|
388
|
+
const sourceFile = (alias as any).sourceFile as string | undefined;
|
|
389
|
+
if (!sourceFile) continue;
|
|
390
|
+
const match = sourceFile.match(/^src\/([^/]+)\/interfaces\//);
|
|
391
|
+
if (match) {
|
|
392
|
+
const dirName = match[1];
|
|
393
|
+
if (!dirAllNames.has(dirName)) dirAllNames.set(dirName, new Set());
|
|
394
|
+
dirAllNames.get(dirName)!.add(name);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
// Detect directories with cross-directory name conflicts
|
|
399
|
+
const unsafeStarDirs = new Set<string>();
|
|
400
|
+
const allDirEntries = [...dirAllNames.entries()];
|
|
401
|
+
for (let i = 0; i < allDirEntries.length; i++) {
|
|
402
|
+
for (let j = i + 1; j < allDirEntries.length; j++) {
|
|
403
|
+
const [dirA, namesA] = allDirEntries[i];
|
|
404
|
+
const [dirB, namesB] = allDirEntries[j];
|
|
405
|
+
for (const name of namesA) {
|
|
406
|
+
if (namesB.has(name)) {
|
|
407
|
+
unsafeStarDirs.add(dirA);
|
|
408
|
+
unsafeStarDirs.add(dirB);
|
|
409
|
+
break;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
318
415
|
// Per-service exports: service barrel + resource class
|
|
319
416
|
for (const service of spec.services) {
|
|
320
417
|
const resolvedName = resolveResourceClassName(service, ctx);
|
|
321
|
-
const serviceDir =
|
|
418
|
+
const serviceDir = resolveServiceDir(resolvedName);
|
|
322
419
|
// The interfaces directory may differ from the resource class directory when
|
|
323
420
|
// a service's class name is remapped (e.g., WebhooksEndpoints class lives in
|
|
324
421
|
// webhooks-endpoints/ but its model interfaces live in webhooks/).
|
|
@@ -338,13 +435,25 @@ function generateBarrel(spec: ApiSpec, ctx: EmitterContext): GeneratedFile {
|
|
|
338
435
|
return enumService === service.name;
|
|
339
436
|
});
|
|
340
437
|
|
|
341
|
-
// Check whether any model or enum in this service conflicts with
|
|
342
|
-
//
|
|
438
|
+
// Check whether any model or enum in this service conflicts with names already
|
|
439
|
+
// exported (from earlier star exports or the existing SDK baseline). If so, fall
|
|
440
|
+
// back to individual named exports to avoid duplicate-export TS2308 errors.
|
|
343
441
|
const hasConflict =
|
|
344
|
-
serviceModels.some((m) =>
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
442
|
+
serviceModels.some((m) => {
|
|
443
|
+
const name = resolveInterfaceName(m.name, ctx);
|
|
444
|
+
return existingSdkExports.has(name) || exportedNames.has(name) || exportedNames.has(wireInterfaceName(name));
|
|
445
|
+
}) || serviceEnums.some((e) => existingSdkExports.has(e.name) || exportedNames.has(e.name));
|
|
446
|
+
|
|
447
|
+
// Skip star export for covered services — their directory may have hand-written types
|
|
448
|
+
// (e.g., Factor in mfa/) that conflict with types in the covering service's directory.
|
|
449
|
+
const isCovered = coveredServicesBarrel.has(service.name);
|
|
450
|
+
if (
|
|
451
|
+
(serviceModels.length > 0 || serviceEnums.length > 0) &&
|
|
452
|
+
!exportedDirs.has(interfacesDir) &&
|
|
453
|
+
!hasConflict &&
|
|
454
|
+
!unsafeStarDirs.has(interfacesDir) &&
|
|
455
|
+
!isCovered
|
|
456
|
+
) {
|
|
348
457
|
exportedDirs.add(interfacesDir);
|
|
349
458
|
lines.push(`export * from './${interfacesDir}/interfaces';`);
|
|
350
459
|
// Track the individual names so they don't get re-exported below
|
|
@@ -383,7 +492,11 @@ function generateBarrel(spec: ApiSpec, ctx: EmitterContext): GeneratedFile {
|
|
|
383
492
|
}
|
|
384
493
|
|
|
385
494
|
// Unassigned models (common) — use barrel if any exist
|
|
386
|
-
|
|
495
|
+
// Filter to reachable models only: oagen's generateAllFiles passes only
|
|
496
|
+
// service-referenced models to generateModels, so unreachable models
|
|
497
|
+
// never get interface files. Exporting them here would create broken imports.
|
|
498
|
+
const reachable = collectReferencedNames(spec.services, spec.models);
|
|
499
|
+
const unassignedModels = spec.models.filter((m) => !modelToService.has(m.name) && reachable.models.has(m.name));
|
|
387
500
|
const commonEnums = spec.enums.filter((e) => {
|
|
388
501
|
const enumService = findEnumService(e.name, spec.services);
|
|
389
502
|
return !enumService;
|
|
@@ -446,20 +559,11 @@ function generateBarrel(spec: ApiSpec, ctx: EmitterContext): GeneratedFile {
|
|
|
446
559
|
lines.push("export { WorkOS } from './workos';");
|
|
447
560
|
}
|
|
448
561
|
|
|
449
|
-
return {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
* the main barrel. This keeps type exports in sync automatically.
|
|
455
|
-
*/
|
|
456
|
-
function generateWorkerBarrel(_spec: ApiSpec, _ctx: EmitterContext): GeneratedFile {
|
|
457
|
-
const lines: string[] = [];
|
|
458
|
-
|
|
459
|
-
// Re-export everything from the main index — keeps type exports in sync
|
|
460
|
-
lines.push("export * from './index';");
|
|
461
|
-
|
|
462
|
-
return { path: 'src/index.worker.ts', content: lines.join('\n'), skipIfExists: true };
|
|
562
|
+
return {
|
|
563
|
+
path: 'src/index.ts',
|
|
564
|
+
content: lines.join('\n'),
|
|
565
|
+
skipIfExists: true,
|
|
566
|
+
};
|
|
463
567
|
}
|
|
464
568
|
|
|
465
569
|
function findEnumService(enumName: string, services: Service[]): string | undefined {
|
|
@@ -528,62 +632,3 @@ function serverConstName(description: string): string {
|
|
|
528
632
|
.toUpperCase()
|
|
529
633
|
);
|
|
530
634
|
}
|
|
531
|
-
|
|
532
|
-
function generatePackageJson(ctx: EmitterContext): GeneratedFile {
|
|
533
|
-
const pkg = {
|
|
534
|
-
name: `@${ctx.namespace}/sdk`,
|
|
535
|
-
version: '0.0.0',
|
|
536
|
-
type: 'module',
|
|
537
|
-
main: 'src/index.ts',
|
|
538
|
-
types: 'src/index.ts',
|
|
539
|
-
exports: {
|
|
540
|
-
'.': './src/index.ts',
|
|
541
|
-
},
|
|
542
|
-
scripts: {
|
|
543
|
-
test: 'jest',
|
|
544
|
-
build: 'tsc',
|
|
545
|
-
},
|
|
546
|
-
devDependencies: {
|
|
547
|
-
typescript: '^5.0.0',
|
|
548
|
-
jest: '^29.0.0',
|
|
549
|
-
'jest-fetch-mock': '^3.0.0',
|
|
550
|
-
'@types/jest': '^29.0.0',
|
|
551
|
-
'ts-jest': '^29.0.0',
|
|
552
|
-
},
|
|
553
|
-
};
|
|
554
|
-
|
|
555
|
-
return {
|
|
556
|
-
path: 'package.json',
|
|
557
|
-
content: JSON.stringify(pkg, null, 2),
|
|
558
|
-
skipIfExists: true,
|
|
559
|
-
integrateTarget: false,
|
|
560
|
-
};
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
function generateTsConfig(): GeneratedFile {
|
|
564
|
-
const config = {
|
|
565
|
-
compilerOptions: {
|
|
566
|
-
target: 'ES2020',
|
|
567
|
-
module: 'CommonJS',
|
|
568
|
-
lib: ['ES2020'],
|
|
569
|
-
declaration: true,
|
|
570
|
-
strict: true,
|
|
571
|
-
exactOptionalPropertyTypes: true,
|
|
572
|
-
esModuleInterop: true,
|
|
573
|
-
skipLibCheck: true,
|
|
574
|
-
forceConsistentCasingInFileNames: true,
|
|
575
|
-
resolveJsonModule: true,
|
|
576
|
-
outDir: './lib',
|
|
577
|
-
rootDir: './src',
|
|
578
|
-
},
|
|
579
|
-
include: ['src/**/*'],
|
|
580
|
-
exclude: ['node_modules', 'lib', '**/*.spec.ts'],
|
|
581
|
-
};
|
|
582
|
-
|
|
583
|
-
return {
|
|
584
|
-
path: 'tsconfig.json',
|
|
585
|
-
content: JSON.stringify(config, null, 2),
|
|
586
|
-
skipIfExists: true,
|
|
587
|
-
integrateTarget: false,
|
|
588
|
-
};
|
|
589
|
-
}
|
package/src/node/enums.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Enum, EmitterContext, GeneratedFile, Service } from '@workos/oagen';
|
|
2
|
-
import { walkTypeRef } from '@workos/oagen';
|
|
3
|
-
import { fileName,
|
|
2
|
+
import { toPascalCase, walkTypeRef } from '@workos/oagen';
|
|
3
|
+
import { fileName, resolveServiceDir, buildServiceNameMap } from './naming.js';
|
|
4
4
|
import { docComment } from './utils.js';
|
|
5
5
|
|
|
6
6
|
export function generateEnums(enums: Enum[], ctx: EmitterContext): GeneratedFile[] {
|
|
@@ -9,7 +9,7 @@ export function generateEnums(enums: Enum[], ctx: EmitterContext): GeneratedFile
|
|
|
9
9
|
const enumToService = assignEnumsToServices(enums, ctx.spec.services);
|
|
10
10
|
const serviceNameMap = buildServiceNameMap(ctx.spec.services, ctx);
|
|
11
11
|
const resolveDir = (irService: string | undefined) =>
|
|
12
|
-
irService ?
|
|
12
|
+
irService ? resolveServiceDir(serviceNameMap.get(irService) ?? irService) : 'common';
|
|
13
13
|
const files: GeneratedFile[] = [];
|
|
14
14
|
|
|
15
15
|
for (const enumDef of enums) {
|
|
@@ -19,6 +19,15 @@ export function generateEnums(enums: Enum[], ctx: EmitterContext): GeneratedFile
|
|
|
19
19
|
// Check baseline surface for representation and values
|
|
20
20
|
const baselineEnum = ctx.apiSurface?.enums?.[enumDef.name];
|
|
21
21
|
const baselineAlias = ctx.apiSurface?.typeAliases?.[enumDef.name];
|
|
22
|
+
const generatedPath = `src/${dirName}/interfaces/${fileName(enumDef.name)}.interface.ts`;
|
|
23
|
+
|
|
24
|
+
// If the baseline already provides this enum from a different file (e.g., `.enum.ts`),
|
|
25
|
+
// skip generation to avoid duplicate exports from the same barrel.
|
|
26
|
+
const baselineSourceFile = (baselineEnum as any)?.sourceFile ?? (baselineAlias as any)?.sourceFile;
|
|
27
|
+
if (baselineSourceFile && baselineSourceFile !== generatedPath) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
|
|
22
31
|
const lines: string[] = [];
|
|
23
32
|
|
|
24
33
|
// Track whether the generated content has new values not in the baseline.
|
|
@@ -41,7 +50,7 @@ export function generateEnums(enums: Enum[], ctx: EmitterContext): GeneratedFile
|
|
|
41
50
|
// Append new values from the spec that the baseline is missing
|
|
42
51
|
for (const val of missingValues) {
|
|
43
52
|
// Derive a PascalCase member name from the value
|
|
44
|
-
const memberName = val
|
|
53
|
+
const memberName = toPascalCase(val);
|
|
45
54
|
lines.push(` ${memberName} = '${val}',`);
|
|
46
55
|
}
|
|
47
56
|
lines.push('}');
|