@workos/oagen-emitters 0.12.1 → 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.
Files changed (45) hide show
  1. package/.github/workflows/ci.yml +1 -1
  2. package/.github/workflows/lint-pr-title.yml +1 -1
  3. package/.github/workflows/lint.yml +1 -1
  4. package/.github/workflows/release-please.yml +2 -2
  5. package/.github/workflows/release.yml +1 -1
  6. package/.node-version +1 -1
  7. package/.release-please-manifest.json +1 -1
  8. package/CHANGELOG.md +14 -0
  9. package/dist/index.d.mts.map +1 -1
  10. package/dist/index.mjs +1 -1
  11. package/dist/{plugin-CmfzawTp.mjs → plugin-D2N2ZT5W.mjs} +2566 -1493
  12. package/dist/plugin-D2N2ZT5W.mjs.map +1 -0
  13. package/dist/plugin.mjs +1 -1
  14. package/package.json +6 -6
  15. package/renovate.json +46 -6
  16. package/src/node/client.ts +19 -32
  17. package/src/node/enums.ts +67 -30
  18. package/src/node/errors.ts +2 -8
  19. package/src/node/field-plan.ts +188 -52
  20. package/src/node/fixtures.ts +11 -33
  21. package/src/node/index.ts +354 -20
  22. package/src/node/live-surface.ts +378 -0
  23. package/src/node/models.ts +547 -351
  24. package/src/node/naming.ts +122 -25
  25. package/src/node/node-overrides.ts +77 -0
  26. package/src/node/options.ts +41 -0
  27. package/src/node/path-expression.ts +11 -4
  28. package/src/node/resources.ts +473 -48
  29. package/src/node/sdk-errors.ts +0 -16
  30. package/src/node/tests.ts +152 -93
  31. package/src/node/type-map.ts +40 -18
  32. package/src/node/utils.ts +89 -102
  33. package/src/node/wrappers.ts +0 -20
  34. package/test/node/client.test.ts +106 -1201
  35. package/test/node/enums.test.ts +59 -130
  36. package/test/node/errors.test.ts +2 -3
  37. package/test/node/live-surface.test.ts +240 -0
  38. package/test/node/models.test.ts +396 -765
  39. package/test/node/naming.test.ts +69 -234
  40. package/test/node/resources.test.ts +435 -2025
  41. package/test/node/tests.test.ts +214 -0
  42. package/test/node/type-map.test.ts +49 -54
  43. package/test/node/utils.test.ts +29 -80
  44. package/dist/plugin-CmfzawTp.mjs.map +0 -1
  45. package/test/node/serializers.test.ts +0 -444
@@ -1,51 +1,15 @@
1
1
  import { describe, it, expect } from 'vitest';
2
+ import type { EmitterContext, ApiSpec, Service } from '@workos/oagen';
3
+ import { defaultSdkBehavior } from '@workos/oagen';
2
4
  import { generateClient } from '../../src/node/client.js';
3
5
  import { isServiceCoveredByExisting } from '../../src/node/utils.js';
4
- import type { EmitterContext, ApiSpec, Service, Model, Enum } from '@workos/oagen';
5
- import { defaultSdkBehavior } from '@workos/oagen';
6
- import type { ApiSurface } from '@workos/oagen/compat';
7
-
8
- const service: Service = {
9
- name: 'Organizations',
10
- operations: [
11
- {
12
- name: 'getOrganization',
13
- httpMethod: 'get',
14
- path: '/organizations/{id}',
15
- pathParams: [
16
- {
17
- name: 'id',
18
- type: { kind: 'primitive', type: 'string' },
19
- required: true,
20
- },
21
- ],
22
- queryParams: [],
23
- headerParams: [],
24
- response: { kind: 'model', name: 'Organization' },
25
- errors: [],
26
- injectIdempotencyKey: false,
27
- },
28
- ],
29
- };
30
6
 
31
- const model: Model = {
32
- name: 'Organization',
33
- fields: [
34
- { name: 'id', type: { kind: 'primitive', type: 'string' }, required: true },
35
- {
36
- name: 'name',
37
- type: { kind: 'primitive', type: 'string' },
38
- required: true,
39
- },
40
- ],
41
- };
42
-
43
- const spec: ApiSpec = {
7
+ const emptySpec: ApiSpec = {
44
8
  name: 'Test',
45
9
  version: '1.0.0',
46
- baseUrl: 'https://api.example.com',
47
- services: [service],
48
- models: [model],
10
+ baseUrl: 'https://api.workos.com',
11
+ services: [],
12
+ models: [],
49
13
  enums: [],
50
14
  sdk: defaultSdkBehavior(),
51
15
  };
@@ -53,940 +17,140 @@ const spec: ApiSpec = {
53
17
  const ctx: EmitterContext = {
54
18
  namespace: 'workos',
55
19
  namespacePascal: 'WorkOS',
56
- spec,
20
+ spec: emptySpec,
57
21
  };
58
22
 
59
23
  describe('generateClient', () => {
60
24
  it('generates WorkOS client with resource accessors', () => {
61
- const files = generateClient(spec, ctx);
62
- const workosFile = files.find((f) => f.path === 'src/workos.ts');
63
- expect(workosFile).toBeDefined();
64
-
65
- const content = workosFile!.content;
66
- expect(content).toContain('export class WorkOS extends WorkOSBase {');
67
- expect(content).toContain('readonly organizations = new Organizations(this);');
68
- expect(content).toContain("import { WorkOSBase } from './common/workos-base';");
69
- });
70
-
71
- it('allows workos.ts to participate in integration (no integrateTarget: false)', () => {
72
- const files = generateClient(spec, ctx);
73
- const workosFile = files.find((f) => f.path === 'src/workos.ts');
74
- expect(workosFile).toBeDefined();
75
- expect(workosFile!.integrateTarget).not.toBe(false);
76
- });
77
-
78
- it('generates barrel exports', () => {
79
- const files = generateClient(spec, ctx);
80
- const barrel = files.find((f) => f.path === 'src/index.ts');
81
- expect(barrel).toBeDefined();
82
-
83
- const content = barrel!.content;
84
- expect(content).toContain("export * from './common/exceptions';");
85
- expect(content).toContain("export { AutoPaginatable } from './common/utils/pagination';");
86
- expect(content).toContain("export { WorkOS } from './workos';");
87
- // Service types are now re-exported via the service barrel
88
- expect(content).toContain("export * from './organizations/interfaces';");
89
- expect(content).not.toContain('export type { Organization, OrganizationResponse }');
90
- expect(content).toContain("export { Organizations } from './organizations/organizations';");
91
- });
92
-
93
- it('generates per-service barrel files', () => {
94
- const files = generateClient(spec, ctx);
95
- const serviceBarrel = files.find((f) => f.path === 'src/organizations/interfaces/index.ts');
96
- expect(serviceBarrel).toBeDefined();
97
-
98
- const content = serviceBarrel!.content;
99
- expect(content).toContain("export * from './organization.interface';");
100
- expect(serviceBarrel!.skipIfExists).toBe(true);
101
- });
102
-
103
- it('does not generate package.json, tsconfig.json, or worker barrel (now hand-maintained)', () => {
104
- const files = generateClient(spec, ctx);
105
- expect(files.find((f) => f.path === 'package.json')).toBeUndefined();
106
- expect(files.find((f) => f.path === 'tsconfig.json')).toBeUndefined();
107
- expect(files.find((f) => f.path === 'src/index.worker.ts')).toBeUndefined();
108
- });
109
-
110
- it('uses overlay-resolved names for imports and accessors', () => {
111
- const mfaService: Service = {
112
- name: 'Billing',
113
- operations: [
114
- {
115
- name: 'enrollFactor',
116
- httpMethod: 'post',
117
- path: '/auth/factors/enroll',
118
- pathParams: [],
119
- queryParams: [],
120
- headerParams: [],
121
- response: { kind: 'model', name: 'AuthenticationFactor' },
122
- errors: [],
123
- injectIdempotencyKey: true,
124
- },
125
- ],
126
- };
127
-
128
- const mfaModel: Model = {
129
- name: 'AuthenticationFactor',
130
- fields: [
131
- {
132
- name: 'id',
133
- type: { kind: 'primitive', type: 'string' },
134
- required: true,
135
- },
136
- ],
137
- };
138
-
139
- const overlaySpec: ApiSpec = {
140
- name: 'Test',
141
- version: '1.0.0',
142
- baseUrl: 'https://api.example.com',
143
- services: [mfaService],
144
- models: [mfaModel],
145
- enums: [],
146
- sdk: defaultSdkBehavior(),
147
- };
148
-
149
- const overlayCtx: EmitterContext = {
150
- namespace: 'workos',
151
- namespacePascal: 'WorkOS',
152
- spec: overlaySpec,
153
- overlayLookup: {
154
- methodByOperation: new Map([
155
- [
156
- 'POST /auth/factors/enroll',
157
- {
158
- className: 'Mfa',
159
- methodName: 'enrollFactor',
160
- params: [],
161
- returnType: 'void',
162
- },
163
- ],
164
- ]),
165
- httpKeyByMethod: new Map(),
166
- interfaceByName: new Map(),
167
- typeAliasByName: new Map(),
168
- requiredExports: new Map(),
169
- modelNameByIR: new Map(),
170
- fileBySymbol: new Map(),
171
- },
172
- };
173
-
174
- const files = generateClient(overlaySpec, overlayCtx);
175
- const workosFile = files.find((f) => f.path === 'src/workos.ts');
176
- expect(workosFile).toBeDefined();
177
-
178
- const content = workosFile!.content;
179
- // Import path uses resolved name
180
- expect(content).toContain("from './mfa/mfa'");
181
- // Property uses resolved name
182
- expect(content).toContain('readonly mfa = new Mfa(this);');
183
-
184
- const barrel = files.find((f) => f.path === 'src/index.ts');
185
- expect(barrel).toBeDefined();
186
- // Barrel export uses resolved name for resource class
187
- expect(barrel!.content).toContain("from './mfa/mfa'");
188
- // Service barrel uses resolved directory name
189
- expect(barrel!.content).toContain("export * from './mfa/interfaces'");
190
-
191
- // Per-service barrel is generated with resolved directory
192
- const serviceBarrel = files.find((f) => f.path === 'src/mfa/interfaces/index.ts');
193
- expect(serviceBarrel).toBeDefined();
194
- expect(serviceBarrel!.content).toContain("export * from './authentication-factor.interface';");
195
- });
196
-
197
- it('propagates @deprecated from baseline service class to the property declaration', () => {
198
- // Regression test for PR #1535 reviewer comment r3075509969: when a
199
- // service class has `@deprecated` in its JSDoc, TS's deprecation-lint
200
- // only fires at `new X()` call sites — not at `workos.x` access unless
201
- // the property itself is annotated. The emitter propagates the class
202
- // deprecation to the property JSDoc so IDEs surface the strikethrough
203
- // on every access.
204
- const deprecatedCtx: EmitterContext = {
205
- ...ctx,
206
- apiSurface: {
207
- language: 'node',
208
- extractedFrom: 'test',
209
- extractedAt: '2026-01-01',
210
- classes: {
211
- Organizations: {
212
- name: 'Organizations',
213
- methods: {},
214
- properties: {},
215
- constructorParams: [{ name: 'workos', type: 'WorkOS', optional: false }],
216
- deprecationMessage: 'Use `workos.connect` instead.',
25
+ const services: Service[] = [
26
+ {
27
+ name: 'Organizations',
28
+ operations: [
29
+ {
30
+ name: 'listOrganizations',
31
+ httpMethod: 'get',
32
+ path: '/organizations',
33
+ pathParams: [],
34
+ queryParams: [],
35
+ headerParams: [],
36
+ response: { kind: 'primitive', type: 'unknown' },
37
+ errors: [],
38
+ injectIdempotencyKey: false,
217
39
  },
218
- },
219
- interfaces: {},
220
- typeAliases: {},
221
- enums: {},
222
- exports: {},
40
+ ],
223
41
  },
224
- };
42
+ ];
225
43
 
226
- const files = generateClient(spec, deprecatedCtx);
227
- const workosFile = files.find((f) => f.path === 'src/workos.ts')!;
228
- const content = workosFile.content;
44
+ const spec: ApiSpec = { ...emptySpec, services, models: [] };
45
+ const ctxWithServices: EmitterContext = { ...ctx, spec };
46
+ const result = generateClient(spec, ctxWithServices);
229
47
 
230
- // Property JSDoc carries the deprecation and the directive is preserved
231
- // on the line immediately above the accessor.
232
- expect(content).toMatch(
233
- /\/\*\* @deprecated Use `workos\.connect` instead\. \*\/\s+readonly organizations = new Organizations\(this\);/,
234
- );
48
+ const workosFile = result.find((f) => f.path === 'src/workos.ts');
49
+ expect(workosFile).toBeDefined();
50
+ expect(workosFile!.content).toContain('export class WorkOS');
51
+ expect(workosFile!.content).toContain('readonly organizations = new Organizations(this)');
235
52
  });
236
53
 
237
- it('emits a bare @deprecated when the baseline class deprecation has no message', () => {
238
- const deprecatedCtx: EmitterContext = {
239
- ...ctx,
240
- apiSurface: {
241
- language: 'node',
242
- extractedFrom: 'test',
243
- extractedAt: '2026-01-01',
244
- classes: {
245
- Organizations: {
246
- name: 'Organizations',
247
- methods: {},
248
- properties: {},
249
- constructorParams: [{ name: 'workos', type: 'WorkOS', optional: false }],
250
- deprecationMessage: '',
54
+ it('generates barrel exports', () => {
55
+ const services: Service[] = [
56
+ {
57
+ name: 'Organizations',
58
+ operations: [
59
+ {
60
+ name: 'listOrganizations',
61
+ httpMethod: 'get',
62
+ path: '/organizations',
63
+ pathParams: [],
64
+ queryParams: [],
65
+ headerParams: [],
66
+ response: { kind: 'primitive', type: 'unknown' },
67
+ errors: [],
68
+ injectIdempotencyKey: false,
251
69
  },
252
- },
253
- interfaces: {},
254
- typeAliases: {},
255
- enums: {},
256
- exports: {},
257
- },
258
- };
259
-
260
- const files = generateClient(spec, deprecatedCtx);
261
- const workosFile = files.find((f) => f.path === 'src/workos.ts')!;
262
- expect(workosFile.content).toMatch(/\/\*\* @deprecated \*\/\s+readonly organizations = new Organizations\(this\);/);
263
- });
264
-
265
- it('does not emit @deprecated when the baseline class has no deprecationMessage', () => {
266
- const files = generateClient(spec, ctx);
267
- const workosFile = files.find((f) => f.path === 'src/workos.ts')!;
268
- expect(workosFile.content).not.toContain('@deprecated');
269
- });
270
-
271
- it('does not generate error handling in WorkOS client (lives in WorkOSBase)', () => {
272
- const files = generateClient(spec, ctx);
273
- const workosFile = files.find((f) => f.path === 'src/workos.ts')!;
274
- const content = workosFile.content;
275
-
276
- expect(content).not.toContain('handleHttpError');
277
- expect(content).not.toContain('UnauthorizedException');
278
- expect(content).not.toContain('NotFoundException');
279
- });
280
-
281
- it('skips explicit model export when name is already in apiSurface.exports', () => {
282
- // Simulates the Event shadowing bug: the existing SDK already exports "Event"
283
- // via a wildcard re-export (e.g., a hand-written 60+ member discriminated union).
284
- // The barrel must not emit an explicit `export type { Event }` that would shadow it.
285
- const eventService: Service = {
286
- name: 'Events',
287
- operations: [
288
- {
289
- name: 'listEvents',
290
- httpMethod: 'get',
291
- path: '/events',
292
- pathParams: [],
293
- queryParams: [],
294
- headerParams: [],
295
- response: { kind: 'model', name: 'Event' },
296
- errors: [],
297
- injectIdempotencyKey: false,
298
- },
299
- ],
300
- };
301
-
302
- const eventModel: Model = {
303
- name: 'Event',
304
- fields: [
305
- {
306
- name: 'id',
307
- type: { kind: 'primitive', type: 'string' },
308
- required: true,
309
- },
310
- {
311
- name: 'event',
312
- type: { kind: 'primitive', type: 'string' },
313
- required: true,
314
- },
315
- ],
316
- };
317
-
318
- const otherModel: Model = {
319
- name: 'EventCursor',
320
- fields: [
321
- {
322
- name: 'cursor',
323
- type: { kind: 'primitive', type: 'string' },
324
- required: true,
325
- },
326
- ],
327
- };
328
-
329
- const eventSpec: ApiSpec = {
330
- name: 'Test',
331
- version: '1.0.0',
332
- baseUrl: 'https://api.example.com',
333
- services: [eventService],
334
- models: [eventModel, otherModel],
335
- enums: [],
336
- sdk: defaultSdkBehavior(),
337
- };
338
-
339
- const surface: ApiSurface = {
340
- language: 'node',
341
- extractedFrom: '/tmp/test-sdk',
342
- extractedAt: '2025-01-01T00:00:00Z',
343
- classes: {},
344
- interfaces: {
345
- Event: {
346
- name: 'Event',
347
- sourceFile: 'src/common/interfaces/event.interface.ts',
348
- fields: {},
349
- extends: [],
350
- },
351
- },
352
- typeAliases: {},
353
- enums: {},
354
- // The existing SDK's barrel re-exports "Event" via a wildcard chain
355
- exports: {
356
- 'src/common/interfaces/event.interface.ts': ['Event'],
357
- 'src/index.ts': ['Event', 'WorkOS', 'Events'],
358
- },
359
- };
360
-
361
- const eventCtx: EmitterContext = {
362
- namespace: 'workos',
363
- namespacePascal: 'WorkOS',
364
- spec: eventSpec,
365
- apiSurface: surface,
366
- };
367
-
368
- const files = generateClient(eventSpec, eventCtx);
369
- const barrel = files.find((f) => f.path === 'src/index.ts')!;
370
- const content = barrel.content;
371
-
372
- // Event must NOT appear as an explicit named export — it would shadow the wildcard
373
- expect(content).not.toContain('export type { Event,');
374
- expect(content).not.toContain('export type { Event }');
375
-
376
- // EventCursor is unreachable (not referenced by any service), so it should
377
- // NOT be exported — oagen only generates interface files for reachable models
378
- expect(content).not.toContain("export * from './common/interfaces'");
379
- expect(content).not.toContain('EventCursor');
380
-
381
- // The resource class export should still be present
382
- expect(content).toContain("export { Events } from './events/events'");
383
- });
384
-
385
- it('skips explicit enum export when name is already in apiSurface.exports', () => {
386
- const enumSpec: ApiSpec = {
387
- name: 'Test',
388
- version: '1.0.0',
389
- baseUrl: 'https://api.example.com',
390
- services: [service],
391
- models: [model],
392
- enums: [
393
- {
394
- name: 'EventType',
395
- values: [
396
- { name: 'CONNECTION_ACTIVATED', value: 'connection.activated' },
397
- { name: 'CONNECTION_DELETED', value: 'connection.deleted' },
398
- ],
399
- },
400
- ],
401
- sdk: defaultSdkBehavior(),
402
- };
403
-
404
- const surface: ApiSurface = {
405
- language: 'node',
406
- extractedFrom: '/tmp/test-sdk',
407
- extractedAt: '2025-01-01T00:00:00Z',
408
- classes: {},
409
- interfaces: {},
410
- typeAliases: {},
411
- enums: {},
412
- exports: {
413
- 'src/common/interfaces/event-type.interface.ts': ['EventType'],
70
+ ],
414
71
  },
415
- };
416
-
417
- const enumCtx: EmitterContext = {
418
- namespace: 'workos',
419
- namespacePascal: 'WorkOS',
420
- spec: enumSpec,
421
- apiSurface: surface,
422
- };
423
-
424
- const files = generateClient(enumSpec, enumCtx);
425
- const barrel = files.find((f) => f.path === 'src/index.ts')!;
426
-
427
- // EventType should NOT appear as an explicit export — already covered by wildcard
428
- expect(barrel.content).not.toContain('export type { EventType }');
429
- });
430
-
431
- it('emits model exports normally when no apiSurface is present', () => {
432
- // Without apiSurface, all models should be exported via service barrel
433
- const files = generateClient(spec, ctx);
434
- const barrel = files.find((f) => f.path === 'src/index.ts')!;
435
- expect(barrel.content).toContain("export * from './organizations/interfaces'");
436
- });
437
-
438
- it('renders spec.description as JSDoc on WorkOS class', () => {
439
- const specWithDesc: ApiSpec = {
440
- ...spec,
441
- description: 'The WorkOS API provides a unified interface for enterprise features.',
442
- };
443
-
444
- const descCtx: EmitterContext = {
445
- namespace: 'workos',
446
- namespacePascal: 'WorkOS',
447
- spec: specWithDesc,
448
- };
449
-
450
- const files = generateClient(specWithDesc, descCtx);
451
- const workosFile = files.find((f) => f.path === 'src/workos.ts')!;
452
- const content = workosFile.content;
453
-
454
- expect(content).toContain('/** The WorkOS API provides a unified interface for enterprise features. */');
455
- expect(content).toContain('export class WorkOS extends WorkOSBase {');
456
- });
72
+ ];
457
73
 
458
- it('uses value export for baseline TS enums and type export for type aliases', () => {
459
- const enumDef: Enum = {
460
- name: 'ConnectionType',
461
- values: [
462
- { name: 'ADFSSAML', value: 'ADFSSAML' },
463
- { name: 'GoogleOAuth', value: 'GoogleOAuth' },
464
- ],
465
- };
466
- const aliasEnumDef: Enum = {
467
- name: 'DirectoryState',
468
- values: [
469
- { name: 'active', value: 'active' },
470
- { name: 'inactive', value: 'inactive' },
471
- ],
472
- };
473
- const enumService: Service = {
474
- name: 'Payments',
475
- operations: [
476
- {
477
- name: 'listPayments',
478
- httpMethod: 'get',
479
- path: '/payments',
480
- pathParams: [],
481
- queryParams: [
482
- {
483
- name: 'type',
484
- type: {
485
- kind: 'enum',
486
- name: 'ConnectionType',
487
- values: ['ADFSSAML', 'GoogleOAuth'],
488
- },
489
- required: false,
490
- },
491
- ],
492
- headerParams: [],
493
- response: { kind: 'model', name: 'Organization' },
494
- errors: [],
495
- injectIdempotencyKey: false,
496
- },
497
- ],
498
- };
499
- const dirService: Service = {
500
- name: 'Invoices',
501
- operations: [
502
- {
503
- name: 'listInvoices',
504
- httpMethod: 'get',
505
- path: '/invoices',
506
- pathParams: [],
507
- queryParams: [
508
- {
509
- name: 'state',
510
- type: {
511
- kind: 'enum',
512
- name: 'DirectoryState',
513
- values: ['active', 'inactive'],
514
- },
515
- required: false,
516
- },
517
- ],
518
- headerParams: [],
519
- response: { kind: 'model', name: 'Organization' },
520
- errors: [],
521
- injectIdempotencyKey: false,
522
- },
523
- ],
524
- };
525
- const enumSpec: ApiSpec = {
526
- name: 'Test',
527
- version: '1.0.0',
528
- baseUrl: 'https://api.example.com',
529
- services: [service, enumService, dirService],
530
- models: [model],
531
- enums: [enumDef, aliasEnumDef],
532
- sdk: defaultSdkBehavior(),
533
- };
534
- const enumCtx: EmitterContext = {
535
- namespace: 'workos',
536
- namespacePascal: 'WorkOS',
537
- spec: enumSpec,
538
- apiSurface: {
539
- language: 'node',
540
- extractedFrom: 'test',
541
- extractedAt: '2024-01-01',
542
- interfaces: {},
543
- classes: {},
544
- enums: {
545
- ConnectionType: {
546
- name: 'ConnectionType',
547
- members: { ADFSSAML: 'ADFSSAML', GoogleOAuth: 'GoogleOAuth' },
548
- },
549
- },
550
- typeAliases: {},
551
- exports: {},
552
- },
553
- };
74
+ const spec: ApiSpec = { ...emptySpec, services, models: [] };
75
+ const ctxWithServices: EmitterContext = { ...ctx, spec };
76
+ const result = generateClient(spec, ctxWithServices);
554
77
 
555
- const files = generateClient(enumSpec, enumCtx);
556
- const barrel = files.find((f) => f.path === 'src/index.ts');
78
+ const barrel = result.find((f) => f.path === 'src/index.ts');
557
79
  expect(barrel).toBeDefined();
558
-
559
- const content = barrel!.content;
560
- // Both enums are now re-exported via per-service barrel wildcards
561
- expect(content).toContain("export * from './payments/interfaces'");
562
- expect(content).toContain("export * from './invoices/interfaces'");
563
- // Individual enum exports should NOT appear (covered by wildcard)
564
- expect(content).not.toContain('export { ConnectionType }');
565
- expect(content).not.toContain('export type { InvoiceState }');
566
- });
567
-
568
- it('skips services whose endpoints are fully covered by existing hand-written services', () => {
569
- const connectionsService: Service = {
570
- name: 'Payments',
571
- operations: [
572
- {
573
- name: 'listPayments',
574
- httpMethod: 'get',
575
- path: '/payments',
576
- pathParams: [],
577
- queryParams: [],
578
- headerParams: [],
579
- response: { kind: 'model', name: 'ConnectionList' },
580
- errors: [],
581
- injectIdempotencyKey: false,
582
- },
583
- {
584
- name: 'getConnection',
585
- httpMethod: 'get',
586
- path: '/payments/{id}',
587
- pathParams: [
588
- {
589
- name: 'id',
590
- type: { kind: 'primitive', type: 'string' },
591
- required: true,
592
- },
593
- ],
594
- queryParams: [],
595
- headerParams: [],
596
- response: { kind: 'model', name: 'Connection' },
597
- errors: [],
598
- injectIdempotencyKey: false,
599
- },
600
- ],
601
- };
602
-
603
- const connectionModel: Model = {
604
- name: 'Connection',
605
- fields: [
606
- {
607
- name: 'id',
608
- type: { kind: 'primitive', type: 'string' },
609
- required: true,
610
- },
611
- {
612
- name: 'name',
613
- type: { kind: 'primitive', type: 'string' },
614
- required: true,
615
- },
616
- ],
617
- };
618
-
619
- const radarService: Service = {
620
- name: 'Radar',
621
- operations: [
622
- {
623
- name: 'assess',
624
- httpMethod: 'post',
625
- path: '/radar/assess',
626
- pathParams: [],
627
- queryParams: [],
628
- headerParams: [],
629
- response: { kind: 'model', name: 'RadarResult' },
630
- errors: [],
631
- injectIdempotencyKey: false,
632
- },
633
- ],
634
- };
635
-
636
- const radarModel: Model = {
637
- name: 'RadarResult',
638
- fields: [
639
- {
640
- name: 'score',
641
- type: { kind: 'primitive', type: 'number' },
642
- required: true,
643
- },
644
- ],
645
- };
646
-
647
- const coveredSpec: ApiSpec = {
648
- name: 'Test',
649
- version: '1.0.0',
650
- baseUrl: 'https://api.example.com',
651
- services: [connectionsService, radarService],
652
- models: [connectionModel, radarModel],
653
- enums: [],
654
- sdk: defaultSdkBehavior(),
655
- };
656
-
657
- const coveredCtx: EmitterContext = {
658
- namespace: 'workos',
659
- namespacePascal: 'WorkOS',
660
- spec: coveredSpec,
661
- apiSurface: {
662
- language: 'node',
663
- extractedFrom: 'test',
664
- extractedAt: '2024-01-01',
665
- interfaces: {},
666
- classes: {
667
- Sso: {
668
- name: 'Sso',
669
- methods: {
670
- listConnections: [
671
- {
672
- name: 'listConnections',
673
- params: [],
674
- returnType: 'Promise<AutoPaginatable<Connection>>',
675
- async: true,
676
- },
677
- ],
678
- getConnection: [
679
- {
680
- name: 'getConnection',
681
- params: [{ name: 'id', type: 'string', optional: false }],
682
- returnType: 'Promise<Connection>',
683
- async: true,
684
- },
685
- ],
686
- },
687
- properties: {},
688
- constructorParams: [],
689
- },
690
- },
691
- enums: {},
692
- typeAliases: {},
693
- exports: {},
694
- },
695
- overlayLookup: {
696
- methodByOperation: new Map([
697
- [
698
- 'GET /connections',
699
- {
700
- className: 'Sso',
701
- methodName: 'listConnections',
702
- params: [],
703
- returnType: 'Promise<AutoPaginatable<Connection>>',
704
- },
705
- ],
706
- [
707
- 'GET /connections/{id}',
708
- {
709
- className: 'Sso',
710
- methodName: 'getConnection',
711
- params: [{ name: 'id', type: 'string', optional: false }],
712
- returnType: 'Promise<Connection>',
713
- },
714
- ],
715
- ]),
716
- httpKeyByMethod: new Map(),
717
- interfaceByName: new Map(),
718
- typeAliasByName: new Map(),
719
- requiredExports: new Map(),
720
- modelNameByIR: new Map(),
721
- fileBySymbol: new Map(),
722
- },
723
- };
724
-
725
- const files = generateClient(coveredSpec, coveredCtx);
726
- const workosFile = files.find((f) => f.path === 'src/workos.ts')!;
727
- const content = workosFile.content;
728
-
729
- // Connections service should NOT appear (fully covered by Sso in baseline)
730
- expect(content).not.toContain('Connections');
731
- expect(content).not.toContain("from './sso/sso'");
732
-
733
- // Radar service should still appear (not covered)
734
- expect(content).toContain('readonly radar = new Radar(this);');
735
- expect(content).toContain("import { Radar } from './radar/radar';");
736
-
737
- // Barrel should also skip the Connections resource class export
738
- const barrel = files.find((f) => f.path === 'src/index.ts')!;
739
- const barrelContent = barrel.content;
740
- expect(barrelContent).not.toContain('export { Sso }');
741
- expect(barrelContent).not.toContain('export { Connections }');
742
-
743
- // Covered services don't generate barrel exports — their types are
744
- // already exported by the hand-written service's own barrel.
745
- expect(barrelContent).not.toContain("export * from './sso/interfaces'");
746
- });
747
-
748
- it('does not skip services when only some operations are covered', () => {
749
- const partialService: Service = {
750
- name: 'Invoices',
751
- operations: [
752
- {
753
- name: 'listInvoices',
754
- httpMethod: 'get',
755
- path: '/invoices',
756
- pathParams: [],
757
- queryParams: [],
758
- headerParams: [],
759
- response: { kind: 'model', name: 'DirectoryList' },
760
- errors: [],
761
- injectIdempotencyKey: false,
762
- },
763
- {
764
- name: 'createInvoice',
765
- httpMethod: 'post',
766
- path: '/invoices',
767
- pathParams: [],
768
- queryParams: [],
769
- headerParams: [],
770
- response: { kind: 'model', name: 'Invoice' },
771
- errors: [],
772
- injectIdempotencyKey: false,
773
- },
774
- ],
775
- };
776
-
777
- const dirModel: Model = {
778
- name: 'Invoice',
779
- fields: [
780
- {
781
- name: 'id',
782
- type: { kind: 'primitive', type: 'string' },
783
- required: true,
784
- },
785
- ],
786
- };
787
-
788
- const partialSpec: ApiSpec = {
789
- name: 'Test',
790
- version: '1.0.0',
791
- baseUrl: 'https://api.example.com',
792
- services: [partialService],
793
- models: [dirModel],
794
- enums: [],
795
- sdk: defaultSdkBehavior(),
796
- };
797
-
798
- const partialCtx: EmitterContext = {
799
- namespace: 'workos',
800
- namespacePascal: 'WorkOS',
801
- spec: partialSpec,
802
- apiSurface: {
803
- language: 'node',
804
- extractedFrom: 'test',
805
- extractedAt: '2024-01-01',
806
- interfaces: {},
807
- classes: {
808
- Billing: {
809
- name: 'Billing',
810
- methods: {
811
- listInvoices: [
812
- {
813
- name: 'listInvoices',
814
- params: [],
815
- returnType: 'Promise<AutoPaginatable<Invoice>>',
816
- async: true,
817
- },
818
- ],
819
- },
820
- properties: {},
821
- constructorParams: [],
80
+ expect(barrel!.content).toContain("export * from './common/exceptions';");
81
+ expect(barrel!.content).toContain("export { AutoPaginatable } from './common/utils/pagination';");
82
+ expect(barrel!.content).toContain("export { WorkOS } from './workos';");
83
+ });
84
+
85
+ it('does not generate package.json or tsconfig.json', () => {
86
+ const services: Service[] = [
87
+ {
88
+ name: 'Organizations',
89
+ operations: [
90
+ {
91
+ name: 'listOrganizations',
92
+ httpMethod: 'get',
93
+ path: '/organizations',
94
+ pathParams: [],
95
+ queryParams: [],
96
+ headerParams: [],
97
+ response: { kind: 'primitive', type: 'unknown' },
98
+ errors: [],
99
+ injectIdempotencyKey: false,
822
100
  },
823
- },
824
- enums: {},
825
- typeAliases: {},
826
- exports: {},
827
- },
828
- overlayLookup: {
829
- methodByOperation: new Map([
830
- [
831
- 'GET /invoices',
832
- {
833
- className: 'Billing',
834
- methodName: 'listInvoices',
835
- params: [],
836
- returnType: 'Promise<AutoPaginatable<Invoice>>',
837
- },
838
- ],
839
- ]),
840
- httpKeyByMethod: new Map(),
841
- interfaceByName: new Map(),
842
- typeAliasByName: new Map(),
843
- requiredExports: new Map(),
844
- modelNameByIR: new Map(),
845
- fileBySymbol: new Map(),
101
+ ],
846
102
  },
847
- };
848
-
849
- const files = generateClient(partialSpec, partialCtx);
850
- const workosFile = files.find((f) => f.path === 'src/workos.ts')!;
851
- const content = workosFile.content;
852
-
853
- // Service should still be generated because it has an uncovered operation
854
- expect(content).toContain('Billing');
855
- });
856
-
857
- it('does not skip services when no overlay is provided', () => {
858
- const files = generateClient(spec, ctx);
859
- const workosFile = files.find((f) => f.path === 'src/workos.ts')!;
860
- expect(workosFile.content).toContain('readonly organizations = new Organizations(this);');
861
- });
862
-
863
- it('does not skip services when overlay exists but no apiSurface baseline', () => {
864
- const mfaService: Service = {
865
- name: 'Analytics',
866
- operations: [
867
- {
868
- name: 'enrollFactor',
869
- httpMethod: 'post',
870
- path: '/auth/factors/enroll',
871
- pathParams: [],
872
- queryParams: [],
873
- headerParams: [],
874
- response: { kind: 'model', name: 'AuthenticationFactor' },
875
- errors: [],
876
- injectIdempotencyKey: true,
877
- },
878
- ],
879
- };
880
-
881
- const mfaModel: Model = {
882
- name: 'AuthenticationFactor',
883
- fields: [
884
- {
885
- name: 'id',
886
- type: { kind: 'primitive', type: 'string' },
887
- required: true,
888
- },
889
- ],
890
- };
103
+ ];
891
104
 
892
- const mfaSpec: ApiSpec = {
893
- name: 'Test',
894
- version: '1.0.0',
895
- baseUrl: 'https://api.example.com',
896
- services: [mfaService],
897
- models: [mfaModel],
898
- enums: [],
899
- sdk: defaultSdkBehavior(),
900
- };
901
-
902
- const namingOnlyCtx: EmitterContext = {
903
- namespace: 'workos',
904
- namespacePascal: 'WorkOS',
905
- spec: mfaSpec,
906
- overlayLookup: {
907
- methodByOperation: new Map([
908
- [
909
- 'POST /auth/factors/enroll',
910
- {
911
- className: 'Analytics',
912
- methodName: 'enrollFactor',
913
- params: [],
914
- returnType: 'void',
915
- },
916
- ],
917
- ]),
918
- httpKeyByMethod: new Map(),
919
- interfaceByName: new Map(),
920
- typeAliasByName: new Map(),
921
- requiredExports: new Map(),
922
- modelNameByIR: new Map(),
923
- fileBySymbol: new Map(),
924
- },
925
- };
105
+ const spec: ApiSpec = { ...emptySpec, services, models: [] };
106
+ const ctxWithServices: EmitterContext = { ...ctx, spec };
107
+ const result = generateClient(spec, ctxWithServices);
926
108
 
927
- const files = generateClient(mfaSpec, namingOnlyCtx);
928
- const workosFile = files.find((f) => f.path === 'src/workos.ts')!;
929
- expect(workosFile.content).toContain('readonly analytics = new Analytics(this);');
109
+ expect(result.every((f) => !f.path.includes('package.json'))).toBe(true);
110
+ expect(result.every((f) => !f.path.includes('tsconfig.json'))).toBe(true);
930
111
  });
931
112
  });
932
113
 
933
114
  describe('isServiceCoveredByExisting', () => {
934
- const emptySpec: ApiSpec = {
935
- name: 'Test',
936
- version: '1.0.0',
937
- baseUrl: '',
938
- services: [],
939
- models: [],
940
- enums: [],
941
- sdk: defaultSdkBehavior(),
942
- };
943
-
944
115
  it('returns false when no overlay is provided', () => {
945
- const svc: Service = {
946
- name: 'Payments',
116
+ const service: Service = {
117
+ name: 'Organizations',
947
118
  operations: [
948
119
  {
949
- name: 'listPayments',
120
+ name: 'listOrganizations',
950
121
  httpMethod: 'get',
951
- path: '/payments',
122
+ path: '/organizations',
952
123
  pathParams: [],
953
124
  queryParams: [],
954
125
  headerParams: [],
955
- response: { kind: 'model', name: 'ConnectionList' },
126
+ response: { kind: 'primitive', type: 'unknown' },
956
127
  errors: [],
957
128
  injectIdempotencyKey: false,
958
129
  },
959
130
  ],
960
131
  };
961
- const noOverlayCtx: EmitterContext = {
962
- namespace: 'workos',
963
- namespacePascal: 'WorkOS',
964
- spec: emptySpec,
965
- };
966
- expect(isServiceCoveredByExisting(svc, noOverlayCtx)).toBe(false);
132
+ expect(isServiceCoveredByExisting(service, ctx)).toBe(false);
967
133
  });
968
134
 
969
135
  it('returns false when overlay is empty', () => {
970
- const svc: Service = {
971
- name: 'Payments',
136
+ const service: Service = {
137
+ name: 'Organizations',
972
138
  operations: [
973
139
  {
974
- name: 'listPayments',
140
+ name: 'listOrganizations',
975
141
  httpMethod: 'get',
976
- path: '/payments',
142
+ path: '/organizations',
977
143
  pathParams: [],
978
144
  queryParams: [],
979
145
  headerParams: [],
980
- response: { kind: 'model', name: 'ConnectionList' },
146
+ response: { kind: 'primitive', type: 'unknown' },
981
147
  errors: [],
982
148
  injectIdempotencyKey: false,
983
149
  },
984
150
  ],
985
151
  };
986
- const emptyOverlayCtx: EmitterContext = {
987
- namespace: 'workos',
988
- namespacePascal: 'WorkOS',
989
- spec: emptySpec,
152
+ const ctxWithOverlay: EmitterContext = {
153
+ ...ctx,
990
154
  overlayLookup: {
991
155
  methodByOperation: new Map(),
992
156
  httpKeyByMethod: new Map(),
@@ -997,301 +161,42 @@ describe('isServiceCoveredByExisting', () => {
997
161
  fileBySymbol: new Map(),
998
162
  },
999
163
  };
1000
- expect(isServiceCoveredByExisting(svc, emptyOverlayCtx)).toBe(false);
1001
- });
1002
-
1003
- it('returns true when all operations are covered by overlay and class exists in baseline', () => {
1004
- const svc: Service = {
1005
- name: 'Connections',
1006
- operations: [
1007
- {
1008
- name: 'listConnections',
1009
- httpMethod: 'get',
1010
- path: '/connections',
1011
- pathParams: [],
1012
- queryParams: [],
1013
- headerParams: [],
1014
- response: { kind: 'model', name: 'ConnectionList' },
1015
- errors: [],
1016
- injectIdempotencyKey: false,
1017
- },
1018
- {
1019
- name: 'getConnection',
1020
- httpMethod: 'get',
1021
- path: '/connections/{id}',
1022
- pathParams: [
1023
- {
1024
- name: 'id',
1025
- type: { kind: 'primitive', type: 'string' },
1026
- required: true,
1027
- },
1028
- ],
1029
- queryParams: [],
1030
- headerParams: [],
1031
- response: { kind: 'model', name: 'Connection' },
1032
- errors: [],
1033
- injectIdempotencyKey: false,
1034
- },
1035
- ],
1036
- };
1037
- const fullCoverageCtx: EmitterContext = {
1038
- namespace: 'workos',
1039
- namespacePascal: 'WorkOS',
1040
- spec: emptySpec,
1041
- apiSurface: {
1042
- language: 'node',
1043
- extractedFrom: 'test',
1044
- extractedAt: '2024-01-01',
1045
- interfaces: {},
1046
- classes: {
1047
- Sso: {
1048
- name: 'Sso',
1049
- methods: {},
1050
- properties: {},
1051
- constructorParams: [],
1052
- },
1053
- },
1054
- enums: {},
1055
- typeAliases: {},
1056
- exports: {},
1057
- },
1058
- overlayLookup: {
1059
- methodByOperation: new Map([
1060
- [
1061
- 'GET /connections',
1062
- {
1063
- className: 'Sso',
1064
- methodName: 'listConnections',
1065
- params: [],
1066
- returnType: 'Promise<AutoPaginatable<Connection>>',
1067
- },
1068
- ],
1069
- [
1070
- 'GET /connections/{id}',
1071
- {
1072
- className: 'Sso',
1073
- methodName: 'getConnection',
1074
- params: [{ name: 'id', type: 'string', optional: false }],
1075
- returnType: 'Promise<Connection>',
1076
- },
1077
- ],
1078
- ]),
1079
- httpKeyByMethod: new Map(),
1080
- interfaceByName: new Map(),
1081
- typeAliasByName: new Map(),
1082
- requiredExports: new Map(),
1083
- modelNameByIR: new Map(),
1084
- fileBySymbol: new Map(),
1085
- },
1086
- };
1087
- expect(isServiceCoveredByExisting(svc, fullCoverageCtx)).toBe(true);
1088
- });
1089
-
1090
- it('returns false when only some operations are covered', () => {
1091
- const svc: Service = {
1092
- name: 'Invoices',
1093
- operations: [
1094
- {
1095
- name: 'listInvoices',
1096
- httpMethod: 'get',
1097
- path: '/invoices',
1098
- pathParams: [],
1099
- queryParams: [],
1100
- headerParams: [],
1101
- response: { kind: 'model', name: 'DirectoryList' },
1102
- errors: [],
1103
- injectIdempotencyKey: false,
1104
- },
1105
- {
1106
- name: 'createInvoice',
1107
- httpMethod: 'post',
1108
- path: '/invoices',
1109
- pathParams: [],
1110
- queryParams: [],
1111
- headerParams: [],
1112
- response: { kind: 'model', name: 'Invoice' },
1113
- errors: [],
1114
- injectIdempotencyKey: false,
1115
- },
1116
- ],
1117
- };
1118
- const partialCtx: EmitterContext = {
1119
- namespace: 'workos',
1120
- namespacePascal: 'WorkOS',
1121
- spec: emptySpec,
1122
- apiSurface: {
1123
- language: 'node',
1124
- extractedFrom: 'test',
1125
- extractedAt: '2024-01-01',
1126
- interfaces: {},
1127
- classes: {
1128
- Billing: {
1129
- name: 'Billing',
1130
- methods: {},
1131
- properties: {},
1132
- constructorParams: [],
1133
- },
1134
- },
1135
- enums: {},
1136
- typeAliases: {},
1137
- exports: {},
1138
- },
1139
- overlayLookup: {
1140
- methodByOperation: new Map([
1141
- [
1142
- 'GET /invoices',
1143
- {
1144
- className: 'Billing',
1145
- methodName: 'listInvoices',
1146
- params: [],
1147
- returnType: 'Promise<AutoPaginatable<Directory>>',
1148
- },
1149
- ],
1150
- ]),
1151
- httpKeyByMethod: new Map(),
1152
- interfaceByName: new Map(),
1153
- typeAliasByName: new Map(),
1154
- requiredExports: new Map(),
1155
- modelNameByIR: new Map(),
1156
- fileBySymbol: new Map(),
1157
- },
1158
- };
1159
- expect(isServiceCoveredByExisting(svc, partialCtx)).toBe(false);
164
+ expect(isServiceCoveredByExisting(service, ctxWithOverlay)).toBe(false);
1160
165
  });
1161
166
 
1162
167
  it('returns false for services with zero operations', () => {
1163
- const emptySvc: Service = {
1164
- name: 'Empty',
1165
- operations: [],
1166
- };
1167
- const overlayCtx: EmitterContext = {
1168
- namespace: 'workos',
1169
- namespacePascal: 'WorkOS',
1170
- spec: emptySpec,
1171
- apiSurface: {
1172
- language: 'node',
1173
- extractedFrom: 'test',
1174
- extractedAt: '2024-01-01',
1175
- interfaces: {},
1176
- classes: {
1177
- Other: {
1178
- name: 'Other',
1179
- methods: {},
1180
- properties: {},
1181
- constructorParams: [],
1182
- },
1183
- },
1184
- enums: {},
1185
- typeAliases: {},
1186
- exports: {},
1187
- },
1188
- overlayLookup: {
1189
- methodByOperation: new Map([
1190
- [
1191
- 'GET /something',
1192
- {
1193
- className: 'Other',
1194
- methodName: 'doSomething',
1195
- params: [],
1196
- returnType: 'void',
1197
- },
1198
- ],
1199
- ]),
1200
- httpKeyByMethod: new Map(),
1201
- interfaceByName: new Map(),
1202
- typeAliasByName: new Map(),
1203
- requiredExports: new Map(),
1204
- modelNameByIR: new Map(),
1205
- fileBySymbol: new Map(),
1206
- },
1207
- };
1208
- expect(isServiceCoveredByExisting(emptySvc, overlayCtx)).toBe(false);
1209
- });
1210
-
1211
- it('returns false when overlay covers operations but target class is not in baseline', () => {
1212
- const svc: Service = {
1213
- name: 'Payments',
1214
- operations: [
1215
- {
1216
- name: 'listPayments',
1217
- httpMethod: 'get',
1218
- path: '/payments',
1219
- pathParams: [],
1220
- queryParams: [],
1221
- headerParams: [],
1222
- response: { kind: 'model', name: 'ConnectionList' },
1223
- errors: [],
1224
- injectIdempotencyKey: false,
1225
- },
1226
- ],
1227
- };
1228
- const missingClassCtx: EmitterContext = {
1229
- namespace: 'workos',
1230
- namespacePascal: 'WorkOS',
1231
- spec: emptySpec,
1232
- apiSurface: {
1233
- language: 'node',
1234
- extractedFrom: 'test',
1235
- extractedAt: '2024-01-01',
1236
- interfaces: {},
1237
- classes: {},
1238
- enums: {},
1239
- typeAliases: {},
1240
- exports: {},
1241
- },
1242
- overlayLookup: {
1243
- methodByOperation: new Map([
1244
- [
1245
- 'GET /payments',
1246
- {
1247
- className: 'Sso',
1248
- methodName: 'listPayments',
1249
- params: [],
1250
- returnType: 'Promise<AutoPaginatable<Connection>>',
1251
- },
1252
- ],
1253
- ]),
1254
- httpKeyByMethod: new Map(),
1255
- interfaceByName: new Map(),
1256
- typeAliasByName: new Map(),
1257
- requiredExports: new Map(),
1258
- modelNameByIR: new Map(),
1259
- fileBySymbol: new Map(),
1260
- },
1261
- };
1262
- expect(isServiceCoveredByExisting(svc, missingClassCtx)).toBe(false);
168
+ const service: Service = { name: 'Empty', operations: [] };
169
+ expect(isServiceCoveredByExisting(service, ctx)).toBe(false);
1263
170
  });
1264
171
 
1265
172
  it('returns false when no apiSurface is provided', () => {
1266
- const svc: Service = {
1267
- name: 'Payments',
173
+ const service: Service = {
174
+ name: 'Organizations',
1268
175
  operations: [
1269
176
  {
1270
- name: 'listPayments',
177
+ name: 'listOrganizations',
1271
178
  httpMethod: 'get',
1272
- path: '/payments',
179
+ path: '/organizations',
1273
180
  pathParams: [],
1274
181
  queryParams: [],
1275
182
  headerParams: [],
1276
- response: { kind: 'model', name: 'ConnectionList' },
183
+ response: { kind: 'primitive', type: 'unknown' },
1277
184
  errors: [],
1278
185
  injectIdempotencyKey: false,
1279
186
  },
1280
187
  ],
1281
188
  };
1282
- const noSurfaceCtx: EmitterContext = {
1283
- namespace: 'workos',
1284
- namespacePascal: 'WorkOS',
1285
- spec: emptySpec,
189
+ const ctxWithOverlayNoSurface: EmitterContext = {
190
+ ...ctx,
1286
191
  overlayLookup: {
1287
192
  methodByOperation: new Map([
1288
193
  [
1289
- 'GET /payments',
194
+ 'GET /organizations',
1290
195
  {
1291
- className: 'Sso',
1292
- methodName: 'listPayments',
196
+ className: 'Organizations',
197
+ methodName: 'listOrganizations',
1293
198
  params: [],
1294
- returnType: 'Promise<AutoPaginatable<Connection>>',
199
+ returnType: 'Promise<AutoPaginatable<Organization>>',
1295
200
  },
1296
201
  ],
1297
202
  ]),
@@ -1303,6 +208,6 @@ describe('isServiceCoveredByExisting', () => {
1303
208
  fileBySymbol: new Map(),
1304
209
  },
1305
210
  };
1306
- expect(isServiceCoveredByExisting(svc, noSurfaceCtx)).toBe(false);
211
+ expect(isServiceCoveredByExisting(service, ctxWithOverlayNoSurface)).toBe(false);
1307
212
  });
1308
213
  });