@workos/oagen-emitters 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/.husky/pre-commit +1 -0
  2. package/.release-please-manifest.json +1 -1
  3. package/CHANGELOG.md +8 -0
  4. package/README.md +129 -0
  5. package/dist/index.d.mts +10 -1
  6. package/dist/index.d.mts.map +1 -1
  7. package/dist/index.mjs +11893 -3226
  8. package/dist/index.mjs.map +1 -1
  9. package/docs/sdk-architecture/go.md +338 -0
  10. package/docs/sdk-architecture/php.md +315 -0
  11. package/docs/sdk-architecture/python.md +511 -0
  12. package/oagen.config.ts +298 -2
  13. package/package.json +9 -5
  14. package/scripts/generate-php.js +13 -0
  15. package/scripts/git-push-with-published-oagen.sh +21 -0
  16. package/smoke/sdk-go.ts +116 -42
  17. package/smoke/sdk-php.ts +28 -26
  18. package/smoke/sdk-python.ts +5 -2
  19. package/src/go/client.ts +141 -0
  20. package/src/go/enums.ts +196 -0
  21. package/src/go/fixtures.ts +212 -0
  22. package/src/go/index.ts +81 -0
  23. package/src/go/manifest.ts +36 -0
  24. package/src/go/models.ts +254 -0
  25. package/src/go/naming.ts +191 -0
  26. package/src/go/resources.ts +827 -0
  27. package/src/go/tests.ts +751 -0
  28. package/src/go/type-map.ts +82 -0
  29. package/src/go/wrappers.ts +261 -0
  30. package/src/index.ts +3 -0
  31. package/src/node/client.ts +78 -115
  32. package/src/node/enums.ts +9 -0
  33. package/src/node/errors.ts +37 -232
  34. package/src/node/field-plan.ts +726 -0
  35. package/src/node/fixtures.ts +9 -1
  36. package/src/node/index.ts +2 -9
  37. package/src/node/models.ts +178 -21
  38. package/src/node/naming.ts +49 -111
  39. package/src/node/resources.ts +374 -364
  40. package/src/node/sdk-errors.ts +41 -0
  41. package/src/node/tests.ts +32 -12
  42. package/src/node/type-map.ts +4 -2
  43. package/src/node/utils.ts +13 -71
  44. package/src/node/wrappers.ts +151 -0
  45. package/src/php/client.ts +171 -0
  46. package/src/php/enums.ts +67 -0
  47. package/src/php/errors.ts +9 -0
  48. package/src/php/fixtures.ts +181 -0
  49. package/src/php/index.ts +96 -0
  50. package/src/php/manifest.ts +36 -0
  51. package/src/php/models.ts +310 -0
  52. package/src/php/naming.ts +298 -0
  53. package/src/php/resources.ts +561 -0
  54. package/src/php/tests.ts +533 -0
  55. package/src/php/type-map.ts +90 -0
  56. package/src/php/utils.ts +18 -0
  57. package/src/php/wrappers.ts +151 -0
  58. package/src/python/client.ts +337 -0
  59. package/src/python/enums.ts +313 -0
  60. package/src/python/fixtures.ts +196 -0
  61. package/src/python/index.ts +95 -0
  62. package/src/python/manifest.ts +38 -0
  63. package/src/python/models.ts +688 -0
  64. package/src/python/naming.ts +209 -0
  65. package/src/python/resources.ts +1322 -0
  66. package/src/python/tests.ts +1335 -0
  67. package/src/python/type-map.ts +93 -0
  68. package/src/python/wrappers.ts +191 -0
  69. package/src/shared/model-utils.ts +255 -0
  70. package/src/shared/naming-utils.ts +107 -0
  71. package/src/shared/non-spec-services.ts +54 -0
  72. package/src/shared/resolved-ops.ts +109 -0
  73. package/src/shared/wrapper-utils.ts +59 -0
  74. package/test/go/client.test.ts +92 -0
  75. package/test/go/enums.test.ts +132 -0
  76. package/test/go/errors.test.ts +9 -0
  77. package/test/go/models.test.ts +265 -0
  78. package/test/go/resources.test.ts +408 -0
  79. package/test/go/tests.test.ts +143 -0
  80. package/test/node/client.test.ts +18 -12
  81. package/test/node/enums.test.ts +2 -0
  82. package/test/node/errors.test.ts +2 -41
  83. package/test/node/models.test.ts +2 -0
  84. package/test/node/naming.test.ts +23 -0
  85. package/test/node/resources.test.ts +99 -69
  86. package/test/node/serializers.test.ts +3 -1
  87. package/test/node/type-map.test.ts +11 -0
  88. package/test/php/client.test.ts +94 -0
  89. package/test/php/enums.test.ts +173 -0
  90. package/test/php/errors.test.ts +9 -0
  91. package/test/php/models.test.ts +497 -0
  92. package/test/php/resources.test.ts +644 -0
  93. package/test/php/tests.test.ts +118 -0
  94. package/test/python/client.test.ts +200 -0
  95. package/test/python/enums.test.ts +228 -0
  96. package/test/python/errors.test.ts +16 -0
  97. package/test/python/manifest.test.ts +74 -0
  98. package/test/python/models.test.ts +716 -0
  99. package/test/python/resources.test.ts +617 -0
  100. package/test/python/tests.test.ts +202 -0
  101. package/src/node/common.ts +0 -273
  102. package/src/node/config.ts +0 -71
  103. package/src/node/serializers.ts +0 -746
@@ -0,0 +1,408 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import type { EmitterContext, ApiSpec, Service, Operation, Model } from '@workos/oagen';
3
+ import { defaultSdkBehavior } from '@workos/oagen';
4
+ import { generateResources } from '../../src/go/resources.js';
5
+
6
+ function makeSpec(services: Service[], models: Model[] = []): ApiSpec {
7
+ return {
8
+ name: 'Test',
9
+ version: '1.0.0',
10
+ baseUrl: '',
11
+ services,
12
+ models,
13
+ enums: [],
14
+ sdk: defaultSdkBehavior(),
15
+ };
16
+ }
17
+
18
+ function makeCtx(spec: ApiSpec): EmitterContext {
19
+ return {
20
+ namespace: 'workos',
21
+ namespacePascal: 'WorkOS',
22
+ spec,
23
+ };
24
+ }
25
+
26
+ function makeOp(overrides: Partial<Operation>): Operation {
27
+ return {
28
+ name: 'listOrganizations',
29
+ httpMethod: 'get',
30
+ path: '/organizations',
31
+ pathParams: [],
32
+ queryParams: [],
33
+ headerParams: [],
34
+ requestBody: undefined,
35
+ response: { kind: 'model', name: 'Organization' },
36
+ errors: [],
37
+ injectIdempotencyKey: false,
38
+ ...overrides,
39
+ };
40
+ }
41
+
42
+ describe('go/resources', () => {
43
+ it('returns empty for no services', () => {
44
+ const spec = makeSpec([]);
45
+ expect(generateResources([], makeCtx(spec))).toEqual([]);
46
+ });
47
+
48
+ it('generates a service file with methods', () => {
49
+ const services: Service[] = [
50
+ {
51
+ name: 'Organizations',
52
+ operations: [
53
+ makeOp({
54
+ name: 'listOrganizations',
55
+ httpMethod: 'get',
56
+ path: '/organizations',
57
+ queryParams: [
58
+ {
59
+ name: 'limit',
60
+ type: { kind: 'primitive', type: 'integer' },
61
+ required: false,
62
+ },
63
+ ],
64
+ pagination: {
65
+ strategy: 'cursor',
66
+ param: 'after',
67
+ dataPath: 'data',
68
+ itemType: { kind: 'model', name: 'Organization' },
69
+ },
70
+ }),
71
+ makeOp({
72
+ name: 'getOrganization',
73
+ httpMethod: 'get',
74
+ path: '/organizations/{id}',
75
+ pathParams: [
76
+ {
77
+ name: 'id',
78
+ type: { kind: 'primitive', type: 'string' },
79
+ required: true,
80
+ },
81
+ ],
82
+ }),
83
+ makeOp({
84
+ name: 'createOrganization',
85
+ httpMethod: 'post',
86
+ path: '/organizations',
87
+ requestBody: { kind: 'model', name: 'CreateOrganizationRequest' },
88
+ }),
89
+ makeOp({
90
+ name: 'deleteOrganization',
91
+ httpMethod: 'delete',
92
+ path: '/organizations/{id}',
93
+ pathParams: [
94
+ {
95
+ name: 'id',
96
+ type: { kind: 'primitive', type: 'string' },
97
+ required: true,
98
+ },
99
+ ],
100
+ response: { kind: 'primitive', type: 'unknown' },
101
+ }),
102
+ ],
103
+ },
104
+ ];
105
+ const spec = makeSpec(services, [
106
+ {
107
+ name: 'CreateOrganizationRequest',
108
+ fields: [{ name: 'name', type: { kind: 'primitive', type: 'string' }, required: true }],
109
+ },
110
+ ]);
111
+ const ctx = makeCtx(spec);
112
+ const files = generateResources(services, ctx);
113
+
114
+ expect(files.length).toBeGreaterThanOrEqual(1);
115
+ const content = files[0].content;
116
+ expect(content).toContain('package workos');
117
+ expect(content).toContain('type organizationService struct {');
118
+ expect(content).toContain('Limit *int `url:"limit,omitempty" json:"-"`');
119
+ expect(content).toContain('func (s *organizationService) List(');
120
+ expect(content).toContain('func (s *organizationService) Get(');
121
+ expect(content).toContain('func (s *organizationService) Create(');
122
+ expect(content).toContain('func (s *organizationService) Delete(');
123
+ });
124
+
125
+ it('generates path interpolation with fmt.Sprintf', () => {
126
+ const services: Service[] = [
127
+ {
128
+ name: 'Users',
129
+ operations: [
130
+ makeOp({
131
+ name: 'getUser',
132
+ httpMethod: 'get',
133
+ path: '/users/{id}',
134
+ pathParams: [{ name: 'id', type: { kind: 'primitive', type: 'string' }, required: true }],
135
+ }),
136
+ ],
137
+ },
138
+ ];
139
+ const spec = makeSpec(services);
140
+ const files = generateResources(services, makeCtx(spec));
141
+ const content = files[0].content;
142
+ expect(content).toContain('fmt.Sprintf("/users/%s", id)');
143
+ });
144
+
145
+ it('generates paginated methods returning Iterator', () => {
146
+ const services: Service[] = [
147
+ {
148
+ name: 'Users',
149
+ operations: [
150
+ makeOp({
151
+ name: 'listUsers',
152
+ httpMethod: 'get',
153
+ path: '/users',
154
+ pagination: {
155
+ strategy: 'cursor',
156
+ param: 'after',
157
+ dataPath: 'data',
158
+ itemType: { kind: 'model', name: 'User' },
159
+ },
160
+ }),
161
+ ],
162
+ },
163
+ ];
164
+ const spec = makeSpec(services);
165
+ const files = generateResources(services, makeCtx(spec));
166
+ const content = files[0].content;
167
+ expect(content).toContain('*Iterator[User]');
168
+ expect(content).toContain('newIterator[User](ctx, s.client, "GET", "/users", nil, "after", "data", opts)');
169
+ });
170
+
171
+ it('generates delete methods returning error', () => {
172
+ const services: Service[] = [
173
+ {
174
+ name: 'Items',
175
+ operations: [
176
+ makeOp({
177
+ name: 'deleteItem',
178
+ httpMethod: 'delete',
179
+ path: '/items/{id}',
180
+ pathParams: [{ name: 'id', type: { kind: 'primitive', type: 'string' }, required: true }],
181
+ response: { kind: 'primitive', type: 'unknown' },
182
+ }),
183
+ ],
184
+ },
185
+ ];
186
+ const spec = makeSpec(services);
187
+ const files = generateResources(services, makeCtx(spec));
188
+ const content = files[0].content;
189
+ expect(content).toContain(') error {');
190
+ expect(content).toContain('return err');
191
+ });
192
+
193
+ it('generates params struct for body + query params', () => {
194
+ const services: Service[] = [
195
+ {
196
+ name: 'Users',
197
+ operations: [
198
+ makeOp({
199
+ name: 'createUser',
200
+ httpMethod: 'post',
201
+ path: '/users',
202
+ requestBody: { kind: 'model', name: 'CreateUserRequest' },
203
+ queryParams: [
204
+ {
205
+ name: 'notify',
206
+ type: { kind: 'primitive', type: 'boolean' },
207
+ required: false,
208
+ },
209
+ ],
210
+ }),
211
+ ],
212
+ },
213
+ ];
214
+ const spec = makeSpec(services, [
215
+ {
216
+ name: 'CreateUserRequest',
217
+ fields: [{ name: 'email', type: { kind: 'primitive', type: 'string' }, required: true }],
218
+ },
219
+ ]);
220
+ const files = generateResources(services, makeCtx(spec));
221
+ const content = files[0].content;
222
+ expect(content).toContain('type UsersCreateParams struct {');
223
+ expect(content).toContain('Email string `json:"email"`');
224
+ expect(content).toContain('Notify *bool `url:"notify,omitempty" json:"-"`');
225
+ expect(content).toContain('request(ctx, "POST", "/users", params, params, &result, opts)');
226
+ });
227
+
228
+ it('emits Deprecated comment for deprecated body field in params struct', () => {
229
+ const services: Service[] = [
230
+ {
231
+ name: 'Users',
232
+ operations: [
233
+ makeOp({
234
+ name: 'createUser',
235
+ httpMethod: 'post',
236
+ path: '/users',
237
+ requestBody: { kind: 'model', name: 'CreateUserRequest' },
238
+ }),
239
+ ],
240
+ },
241
+ ];
242
+ const spec = makeSpec(services, [
243
+ {
244
+ name: 'CreateUserRequest',
245
+ fields: [
246
+ {
247
+ name: 'email',
248
+ type: { kind: 'primitive', type: 'string' },
249
+ required: true,
250
+ description: 'The user email.',
251
+ deprecated: true,
252
+ },
253
+ {
254
+ name: 'name',
255
+ type: { kind: 'primitive', type: 'string' },
256
+ required: true,
257
+ },
258
+ ],
259
+ },
260
+ ]);
261
+ const files = generateResources(services, makeCtx(spec));
262
+ const content = files[0].content;
263
+ expect(content).toContain('\t// Email is the user email.\n\t//\n\t// Deprecated: this field is deprecated.');
264
+ expect(content).not.toMatch(/Deprecated.*\n\tName/);
265
+ });
266
+
267
+ it('emits Deprecated comment for deprecated query param in params struct', () => {
268
+ const services: Service[] = [
269
+ {
270
+ name: 'Users',
271
+ operations: [
272
+ makeOp({
273
+ name: 'listUsers',
274
+ httpMethod: 'get',
275
+ path: '/users',
276
+ queryParams: [
277
+ {
278
+ name: 'old_filter',
279
+ type: { kind: 'primitive', type: 'string' },
280
+ required: false,
281
+ description: 'A legacy filter.',
282
+ deprecated: true,
283
+ },
284
+ {
285
+ name: 'limit',
286
+ type: { kind: 'primitive', type: 'integer' },
287
+ required: false,
288
+ },
289
+ ],
290
+ }),
291
+ ],
292
+ },
293
+ ];
294
+ const spec = makeSpec(services);
295
+ const files = generateResources(services, makeCtx(spec));
296
+ const content = files[0].content;
297
+ expect(content).toContain(
298
+ '\t// OldFilter is a legacy filter.\n\t//\n\t// Deprecated: this parameter is deprecated.',
299
+ );
300
+ expect(content).not.toMatch(/Deprecated.*\n\tLimit/);
301
+ });
302
+
303
+ it('emits Deprecated note in godoc for deprecated path param', () => {
304
+ const services: Service[] = [
305
+ {
306
+ name: 'Items',
307
+ operations: [
308
+ makeOp({
309
+ name: 'getItem',
310
+ httpMethod: 'get',
311
+ path: '/items/{old_id}',
312
+ pathParams: [
313
+ {
314
+ name: 'old_id',
315
+ type: { kind: 'primitive', type: 'string' },
316
+ required: true,
317
+ deprecated: true,
318
+ description: 'use new_id instead',
319
+ },
320
+ ],
321
+ }),
322
+ ],
323
+ },
324
+ ];
325
+ const spec = makeSpec(services);
326
+ const files = generateResources(services, makeCtx(spec));
327
+ const content = files[0].content;
328
+ expect(content).toContain('// Deprecated parameter OldID: use new_id instead');
329
+ });
330
+
331
+ it('emits Deprecated note in godoc for deprecated path param without description', () => {
332
+ const services: Service[] = [
333
+ {
334
+ name: 'Items',
335
+ operations: [
336
+ makeOp({
337
+ name: 'getItem',
338
+ httpMethod: 'get',
339
+ path: '/items/{old_id}',
340
+ pathParams: [
341
+ {
342
+ name: 'old_id',
343
+ type: { kind: 'primitive', type: 'string' },
344
+ required: true,
345
+ deprecated: true,
346
+ },
347
+ ],
348
+ }),
349
+ ],
350
+ },
351
+ ];
352
+ const spec = makeSpec(services);
353
+ const files = generateResources(services, makeCtx(spec));
354
+ const content = files[0].content;
355
+ expect(content).toContain('// Deprecated parameter OldID.');
356
+ });
357
+
358
+ it('emits Deprecated in godoc for deprecated operation', () => {
359
+ const services: Service[] = [
360
+ {
361
+ name: 'Items',
362
+ operations: [
363
+ makeOp({
364
+ name: 'getItem',
365
+ httpMethod: 'get',
366
+ path: '/items/{id}',
367
+ pathParams: [
368
+ {
369
+ name: 'id',
370
+ type: { kind: 'primitive', type: 'string' },
371
+ required: true,
372
+ },
373
+ ],
374
+ deprecated: true,
375
+ description: 'Get an item.',
376
+ }),
377
+ ],
378
+ },
379
+ ];
380
+ const spec = makeSpec(services);
381
+ const files = generateResources(services, makeCtx(spec));
382
+ const content = files[0].content;
383
+ expect(content).toContain('// Deprecated: this operation is deprecated.');
384
+ });
385
+
386
+ it('uses params.Body for non-model request bodies', () => {
387
+ const services: Service[] = [
388
+ {
389
+ name: 'Connect',
390
+ operations: [
391
+ makeOp({
392
+ name: 'createApplications',
393
+ httpMethod: 'post',
394
+ path: '/connect/applications',
395
+ requestBody: { kind: 'primitive', type: 'unknown' },
396
+ response: { kind: 'model', name: 'ConnectApplication' },
397
+ }),
398
+ ],
399
+ },
400
+ ];
401
+ const spec = makeSpec(services);
402
+ const files = generateResources(services, makeCtx(spec));
403
+ const content = files[0].content;
404
+ expect(content).toContain('type ConnectCreateApplicationsParams struct {');
405
+ expect(content).toContain('Body interface{} `json:"-"`');
406
+ expect(content).toContain('request(ctx, "POST", "/connect/applications", nil, params.Body, &result, opts)');
407
+ });
408
+ });
@@ -0,0 +1,143 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import type { EmitterContext, ApiSpec, Service, Operation, Model } from '@workos/oagen';
3
+ import { defaultSdkBehavior } from '@workos/oagen';
4
+ import { generateTests } from '../../src/go/tests.js';
5
+
6
+ function makeOp(overrides: Partial<Operation>): Operation {
7
+ return {
8
+ name: 'getOrganization',
9
+ httpMethod: 'get',
10
+ path: '/organizations/{id}',
11
+ pathParams: [{ name: 'id', type: { kind: 'primitive', type: 'string' }, required: true }],
12
+ queryParams: [],
13
+ headerParams: [],
14
+ requestBody: undefined,
15
+ response: { kind: 'model', name: 'Organization' },
16
+ errors: [],
17
+ injectIdempotencyKey: false,
18
+ ...overrides,
19
+ };
20
+ }
21
+
22
+ function makeSpec(services: Service[], models: Model[] = []): ApiSpec {
23
+ return {
24
+ name: 'Test',
25
+ version: '1.0.0',
26
+ baseUrl: '',
27
+ services,
28
+ models: [
29
+ {
30
+ name: 'Organization',
31
+ fields: [
32
+ { name: 'id', type: { kind: 'primitive', type: 'string' }, required: true },
33
+ { name: 'name', type: { kind: 'primitive', type: 'string' }, required: true },
34
+ ],
35
+ },
36
+ ...models,
37
+ ],
38
+ enums: [],
39
+ sdk: defaultSdkBehavior(),
40
+ };
41
+ }
42
+
43
+ function makeCtx(spec: ApiSpec): EmitterContext {
44
+ return {
45
+ namespace: 'workos',
46
+ namespacePascal: 'WorkOS',
47
+ spec,
48
+ };
49
+ }
50
+
51
+ describe('go/tests', () => {
52
+ it('generates test files and fixtures', () => {
53
+ const services: Service[] = [
54
+ {
55
+ name: 'Organizations',
56
+ operations: [makeOp({})],
57
+ },
58
+ ];
59
+ const spec = makeSpec(services);
60
+ const files = generateTests(spec, makeCtx(spec));
61
+
62
+ // Should have fixture files and test files
63
+ const testFiles = files.filter((f) => f.path.endsWith('_test.go'));
64
+ const fixtureFiles = files.filter((f) => f.path.startsWith('testdata/'));
65
+
66
+ expect(testFiles.length).toBeGreaterThanOrEqual(1);
67
+ expect(fixtureFiles.length).toBeGreaterThanOrEqual(1);
68
+ });
69
+
70
+ it('generates httptest-based tests', () => {
71
+ const services: Service[] = [
72
+ {
73
+ name: 'Organizations',
74
+ operations: [makeOp({})],
75
+ },
76
+ ];
77
+ const spec = makeSpec(services);
78
+ const files = generateTests(spec, makeCtx(spec));
79
+ const testFile = files.find((f) => f.path.endsWith('_test.go') && !f.path.includes('helpers'))!;
80
+ const content = testFile.content;
81
+
82
+ expect(content).toContain('package workos_test');
83
+ expect(content).toContain('httptest.NewServer');
84
+ expect(content).toContain('require.Equal');
85
+ expect(content).toContain('workos.NewClient');
86
+ });
87
+
88
+ it('generates shared test helpers file', () => {
89
+ const services: Service[] = [
90
+ {
91
+ name: 'Organizations',
92
+ operations: [makeOp({})],
93
+ },
94
+ ];
95
+ const spec = makeSpec(services);
96
+ const files = generateTests(spec, makeCtx(spec));
97
+ const helperFile = files.find((f) => f.path === 'helpers_test.go')!;
98
+ expect(helperFile).toBeDefined();
99
+ expect(helperFile.content).toContain('ptrString');
100
+ expect(helperFile.content).toContain('ptrInt');
101
+ expect(helperFile.content).toContain('setupTestServer');
102
+ });
103
+
104
+ it('generates error test for 404 and 422', () => {
105
+ const services: Service[] = [
106
+ {
107
+ name: 'Organizations',
108
+ operations: [makeOp({})],
109
+ },
110
+ ];
111
+ const spec = makeSpec(services);
112
+ const files = generateTests(spec, makeCtx(spec));
113
+ const testFile = files.find((f) => f.path.endsWith('_test.go') && !f.path.includes('helpers'))!;
114
+ const content = testFile.content;
115
+
116
+ expect(content).toContain('Error404');
117
+ expect(content).toContain('NotFoundError');
118
+ expect(content).toContain('Error422');
119
+ expect(content).toContain('UnprocessableEntityError');
120
+ });
121
+
122
+ it('generates delete operation tests with no response assertion', () => {
123
+ const services: Service[] = [
124
+ {
125
+ name: 'Organizations',
126
+ operations: [
127
+ makeOp({
128
+ name: 'deleteOrganization',
129
+ httpMethod: 'delete',
130
+ response: { kind: 'primitive', type: 'unknown' },
131
+ }),
132
+ ],
133
+ },
134
+ ];
135
+ const spec = makeSpec(services);
136
+ const files = generateTests(spec, makeCtx(spec));
137
+ const testFile = files.find((f) => f.path.endsWith('_test.go') && !f.path.includes('helpers'))!;
138
+ const content = testFile.content;
139
+
140
+ expect(content).toContain('StatusNoContent');
141
+ expect(content).toContain('require.NoError(t, err)');
142
+ });
143
+ });
@@ -2,6 +2,7 @@ import { describe, it, expect } from 'vitest';
2
2
  import { generateClient } from '../../src/node/client.js';
3
3
  import { isServiceCoveredByExisting } from '../../src/node/utils.js';
4
4
  import type { EmitterContext, ApiSpec, Service, Model, Enum } from '@workos/oagen';
5
+ import { defaultSdkBehavior } from '@workos/oagen';
5
6
  import type { ApiSurface } from '@workos/oagen/compat';
6
7
 
7
8
  const service: Service = {
@@ -46,6 +47,7 @@ const spec: ApiSpec = {
46
47
  services: [service],
47
48
  models: [model],
48
49
  enums: [],
50
+ sdk: defaultSdkBehavior(),
49
51
  };
50
52
 
51
53
  const ctx: EmitterContext = {
@@ -98,16 +100,11 @@ describe('generateClient', () => {
98
100
  expect(serviceBarrel!.skipIfExists).toBe(true);
99
101
  });
100
102
 
101
- it('generates package.json and tsconfig.json', () => {
103
+ it('does not generate package.json, tsconfig.json, or worker barrel (now hand-maintained)', () => {
102
104
  const files = generateClient(spec, ctx);
103
- const pkg = files.find((f) => f.path === 'package.json');
104
- const tsconfig = files.find((f) => f.path === 'tsconfig.json');
105
-
106
- expect(pkg).toBeDefined();
107
- expect(pkg!.skipIfExists).toBe(true);
108
-
109
- expect(tsconfig).toBeDefined();
110
- expect(tsconfig!.skipIfExists).toBe(true);
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();
111
108
  });
112
109
 
113
110
  it('uses overlay-resolved names for imports and accessors', () => {
@@ -146,6 +143,7 @@ describe('generateClient', () => {
146
143
  services: [mfaService],
147
144
  models: [mfaModel],
148
145
  enums: [],
146
+ sdk: defaultSdkBehavior(),
149
147
  };
150
148
 
151
149
  const overlayCtx: EmitterContext = {
@@ -261,6 +259,7 @@ describe('generateClient', () => {
261
259
  services: [eventService],
262
260
  models: [eventModel, otherModel],
263
261
  enums: [],
262
+ sdk: defaultSdkBehavior(),
264
263
  };
265
264
 
266
265
  const surface: ApiSurface = {
@@ -300,9 +299,10 @@ describe('generateClient', () => {
300
299
  expect(content).not.toContain('export type { Event,');
301
300
  expect(content).not.toContain('export type { Event }');
302
301
 
303
- // EventCursor is NOT in apiSurface.exports, so it should still be exported
304
- // (via common barrel wildcard since it's unassigned to any service)
305
- expect(content).toContain("export * from './common/interfaces'");
302
+ // EventCursor is unreachable (not referenced by any service), so it should
303
+ // NOT be exported oagen only generates interface files for reachable models
304
+ expect(content).not.toContain("export * from './common/interfaces'");
305
+ expect(content).not.toContain('EventCursor');
306
306
 
307
307
  // The resource class export should still be present
308
308
  expect(content).toContain("export { Events } from './events/events'");
@@ -324,6 +324,7 @@ describe('generateClient', () => {
324
324
  ],
325
325
  },
326
326
  ],
327
+ sdk: defaultSdkBehavior(),
327
328
  };
328
329
 
329
330
  const surface: ApiSurface = {
@@ -454,6 +455,7 @@ describe('generateClient', () => {
454
455
  services: [service, enumService, dirService],
455
456
  models: [model],
456
457
  enums: [enumDef, aliasEnumDef],
458
+ sdk: defaultSdkBehavior(),
457
459
  };
458
460
  const enumCtx: EmitterContext = {
459
461
  namespace: 'workos',
@@ -575,6 +577,7 @@ describe('generateClient', () => {
575
577
  services: [connectionsService, radarService],
576
578
  models: [connectionModel, radarModel],
577
579
  enums: [],
580
+ sdk: defaultSdkBehavior(),
578
581
  };
579
582
 
580
583
  const coveredCtx: EmitterContext = {
@@ -715,6 +718,7 @@ describe('generateClient', () => {
715
718
  services: [partialService],
716
719
  models: [dirModel],
717
720
  enums: [],
721
+ sdk: defaultSdkBehavior(),
718
722
  };
719
723
 
720
724
  const partialCtx: EmitterContext = {
@@ -818,6 +822,7 @@ describe('generateClient', () => {
818
822
  services: [mfaService],
819
823
  models: [mfaModel],
820
824
  enums: [],
825
+ sdk: defaultSdkBehavior(),
821
826
  };
822
827
 
823
828
  const namingOnlyCtx: EmitterContext = {
@@ -859,6 +864,7 @@ describe('isServiceCoveredByExisting', () => {
859
864
  services: [],
860
865
  models: [],
861
866
  enums: [],
867
+ sdk: defaultSdkBehavior(),
862
868
  };
863
869
 
864
870
  it('returns false when no overlay is provided', () => {
@@ -1,6 +1,7 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import { generateEnums } from '../../src/node/enums.js';
3
3
  import type { EmitterContext, ApiSpec, Enum, Service } from '@workos/oagen';
4
+ import { defaultSdkBehavior } from '@workos/oagen';
4
5
 
5
6
  const emptySpec: ApiSpec = {
6
7
  name: 'Test',
@@ -9,6 +10,7 @@ const emptySpec: ApiSpec = {
9
10
  services: [],
10
11
  models: [],
11
12
  enums: [],
13
+ sdk: defaultSdkBehavior(),
12
14
  };
13
15
 
14
16
  const ctx: EmitterContext = {