@workos/oagen-emitters 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/.github/workflows/ci.yml +20 -0
  2. package/.github/workflows/lint-pr-title.yml +16 -0
  3. package/.github/workflows/lint.yml +21 -0
  4. package/.github/workflows/release-please.yml +28 -0
  5. package/.github/workflows/release.yml +32 -0
  6. package/.husky/commit-msg +1 -0
  7. package/.husky/pre-commit +1 -0
  8. package/.husky/pre-push +1 -0
  9. package/.node-version +1 -0
  10. package/.oxfmtrc.json +10 -0
  11. package/.oxlintrc.json +29 -0
  12. package/.vscode/settings.json +11 -0
  13. package/LICENSE.txt +21 -0
  14. package/README.md +123 -0
  15. package/commitlint.config.ts +1 -0
  16. package/dist/index.d.ts +5 -0
  17. package/dist/index.js +2158 -0
  18. package/docs/endpoint-coverage.md +275 -0
  19. package/docs/sdk-architecture/node.md +355 -0
  20. package/oagen.config.ts +51 -0
  21. package/package.json +83 -0
  22. package/renovate.json +26 -0
  23. package/smoke/sdk-dotnet.ts +903 -0
  24. package/smoke/sdk-elixir.ts +771 -0
  25. package/smoke/sdk-go.ts +948 -0
  26. package/smoke/sdk-kotlin.ts +799 -0
  27. package/smoke/sdk-node.ts +516 -0
  28. package/smoke/sdk-php.ts +699 -0
  29. package/smoke/sdk-python.ts +738 -0
  30. package/smoke/sdk-ruby.ts +723 -0
  31. package/smoke/sdk-rust.ts +774 -0
  32. package/src/compat/extractors/dotnet.ts +8 -0
  33. package/src/compat/extractors/elixir.ts +8 -0
  34. package/src/compat/extractors/go.ts +8 -0
  35. package/src/compat/extractors/kotlin.ts +8 -0
  36. package/src/compat/extractors/node.ts +8 -0
  37. package/src/compat/extractors/php.ts +8 -0
  38. package/src/compat/extractors/python.ts +8 -0
  39. package/src/compat/extractors/ruby.ts +8 -0
  40. package/src/compat/extractors/rust.ts +8 -0
  41. package/src/index.ts +1 -0
  42. package/src/node/client.ts +356 -0
  43. package/src/node/common.ts +203 -0
  44. package/src/node/config.ts +70 -0
  45. package/src/node/enums.ts +87 -0
  46. package/src/node/errors.ts +205 -0
  47. package/src/node/fixtures.ts +139 -0
  48. package/src/node/index.ts +57 -0
  49. package/src/node/manifest.ts +23 -0
  50. package/src/node/models.ts +323 -0
  51. package/src/node/naming.ts +96 -0
  52. package/src/node/resources.ts +380 -0
  53. package/src/node/serializers.ts +286 -0
  54. package/src/node/tests.ts +336 -0
  55. package/src/node/type-map.ts +56 -0
  56. package/src/node/utils.ts +164 -0
  57. package/test/compat/extractors/node.test.ts +145 -0
  58. package/test/fixtures/sample-sdk-node/package.json +7 -0
  59. package/test/fixtures/sample-sdk-node/src/client.ts +24 -0
  60. package/test/fixtures/sample-sdk-node/src/index.ts +4 -0
  61. package/test/fixtures/sample-sdk-node/src/models.ts +28 -0
  62. package/test/fixtures/sample-sdk-node/tsconfig.json +13 -0
  63. package/test/node/client.test.ts +165 -0
  64. package/test/node/enums.test.ts +128 -0
  65. package/test/node/errors.test.ts +65 -0
  66. package/test/node/models.test.ts +301 -0
  67. package/test/node/naming.test.ts +212 -0
  68. package/test/node/resources.test.ts +260 -0
  69. package/test/node/serializers.test.ts +206 -0
  70. package/test/node/type-map.test.ts +127 -0
  71. package/tsconfig.json +20 -0
  72. package/tsup.config.ts +8 -0
  73. package/vitest.config.ts +4 -0
@@ -0,0 +1,260 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { generateResources } from '../../src/node/resources.js';
3
+ import type { EmitterContext, ApiSpec, Service } from '@workos/oagen';
4
+
5
+ const emptySpec: ApiSpec = {
6
+ name: 'Test',
7
+ version: '1.0.0',
8
+ baseUrl: '',
9
+ services: [],
10
+ models: [],
11
+ enums: [],
12
+ };
13
+
14
+ const ctx: EmitterContext = {
15
+ namespace: 'workos',
16
+ namespacePascal: 'WorkOS',
17
+ spec: emptySpec,
18
+ irVersion: 6,
19
+ };
20
+
21
+ describe('generateResources', () => {
22
+ it('returns empty for no services', () => {
23
+ expect(generateResources([], ctx)).toEqual([]);
24
+ });
25
+
26
+ it('generates a resource class with GET method', () => {
27
+ const services: Service[] = [
28
+ {
29
+ name: 'Organizations',
30
+ operations: [
31
+ {
32
+ name: 'getOrganization',
33
+ httpMethod: 'get',
34
+ path: '/organizations/{id}',
35
+ pathParams: [
36
+ {
37
+ name: 'id',
38
+ type: { kind: 'primitive', type: 'string' },
39
+ required: true,
40
+ },
41
+ ],
42
+ queryParams: [],
43
+ headerParams: [],
44
+ response: { kind: 'model', name: 'Organization' },
45
+ errors: [],
46
+ injectIdempotencyKey: false,
47
+ },
48
+ ],
49
+ },
50
+ ];
51
+
52
+ const files = generateResources(services, ctx);
53
+ expect(files.length).toBe(1);
54
+ expect(files[0].path).toBe('src/organizations/organizations.ts');
55
+
56
+ const content = files[0].content;
57
+ expect(content).toContain('export class Organizations {');
58
+ expect(content).toContain('constructor(private readonly workos: WorkOS) {}');
59
+ expect(content).toContain('async getOrganization(id: string): Promise<Organization>');
60
+ expect(content).toContain('deserializeOrganization(data)');
61
+ });
62
+
63
+ it('generates paginated list method', () => {
64
+ const services: Service[] = [
65
+ {
66
+ name: 'Organizations',
67
+ operations: [
68
+ {
69
+ name: 'listOrganizations',
70
+ httpMethod: 'get',
71
+ path: '/organizations',
72
+ pathParams: [],
73
+ queryParams: [
74
+ {
75
+ name: 'domains',
76
+ type: { kind: 'array', items: { kind: 'primitive', type: 'string' } },
77
+ required: false,
78
+ },
79
+ ],
80
+ headerParams: [],
81
+ response: { kind: 'model', name: 'Organization' },
82
+ errors: [],
83
+ pagination: {
84
+ cursorParam: 'after',
85
+ dataPath: 'data',
86
+ itemType: { kind: 'model', name: 'Organization' },
87
+ },
88
+ injectIdempotencyKey: false,
89
+ },
90
+ ],
91
+ },
92
+ ];
93
+
94
+ const files = generateResources(services, ctx);
95
+ const content = files[0].content;
96
+
97
+ // Should have AutoPaginatable imports
98
+ expect(content).toContain('import { AutoPaginatable }');
99
+ expect(content).toContain('import { fetchAndDeserialize }');
100
+
101
+ // Should generate options interface
102
+ expect(content).toContain('export interface ListOrganizationsOptions extends PaginationOptions {');
103
+ expect(content).toContain('domains?: string[];');
104
+
105
+ // Should return AutoPaginatable
106
+ expect(content).toContain('Promise<AutoPaginatable<Organization, ListOrganizationsOptions>>');
107
+ });
108
+
109
+ it('generates DELETE method returning void', () => {
110
+ const services: Service[] = [
111
+ {
112
+ name: 'Organizations',
113
+ operations: [
114
+ {
115
+ name: 'deleteOrganization',
116
+ httpMethod: 'delete',
117
+ path: '/organizations/{id}',
118
+ pathParams: [
119
+ {
120
+ name: 'id',
121
+ type: { kind: 'primitive', type: 'string' },
122
+ required: true,
123
+ },
124
+ ],
125
+ queryParams: [],
126
+ headerParams: [],
127
+ response: { kind: 'primitive', type: 'unknown' },
128
+ errors: [],
129
+ injectIdempotencyKey: false,
130
+ },
131
+ ],
132
+ },
133
+ ];
134
+
135
+ const files = generateResources(services, ctx);
136
+ const content = files[0].content;
137
+ expect(content).toContain('async deleteOrganization(id: string): Promise<void>');
138
+ expect(content).toContain('await this.workos.delete(');
139
+ });
140
+
141
+ it('generates POST method with body and idempotency', () => {
142
+ const services: Service[] = [
143
+ {
144
+ name: 'Organizations',
145
+ operations: [
146
+ {
147
+ name: 'createOrganization',
148
+ httpMethod: 'post',
149
+ path: '/organizations',
150
+ pathParams: [],
151
+ queryParams: [],
152
+ headerParams: [],
153
+ requestBody: { kind: 'model', name: 'CreateOrganizationInput' },
154
+ response: { kind: 'model', name: 'Organization' },
155
+ errors: [],
156
+ injectIdempotencyKey: true,
157
+ },
158
+ ],
159
+ },
160
+ ];
161
+
162
+ const files = generateResources(services, ctx);
163
+ const content = files[0].content;
164
+ expect(content).toContain(
165
+ 'async createOrganization(payload: CreateOrganizationInput, requestOptions: PostOptions = {}): Promise<Organization>',
166
+ );
167
+ expect(content).toContain('serializeCreateOrganizationInput(payload)');
168
+ expect(content).toContain('requestOptions,');
169
+ });
170
+
171
+ it('uses overlay-resolved name for output path and class', () => {
172
+ const mfaService: Service = {
173
+ name: 'MultiFactorAuth',
174
+ operations: [
175
+ {
176
+ name: 'enrollFactor',
177
+ httpMethod: 'post',
178
+ path: '/auth/factors/enroll',
179
+ pathParams: [],
180
+ queryParams: [],
181
+ headerParams: [],
182
+ requestBody: { kind: 'model', name: 'EnrollFactorInput' },
183
+ response: { kind: 'model', name: 'AuthenticationFactor' },
184
+ errors: [],
185
+ injectIdempotencyKey: true,
186
+ },
187
+ ],
188
+ };
189
+
190
+ const overlayCtx: EmitterContext = {
191
+ namespace: 'workos',
192
+ namespacePascal: 'WorkOS',
193
+ spec: { ...emptySpec, services: [mfaService], models: [] },
194
+ irVersion: 6,
195
+ overlayLookup: {
196
+ methodByOperation: new Map([
197
+ [
198
+ 'POST /auth/factors/enroll',
199
+ { className: 'Mfa', methodName: 'enrollFactor', params: [], returnType: 'void' },
200
+ ],
201
+ ]),
202
+ httpKeyByMethod: new Map(),
203
+ interfaceByName: new Map(),
204
+ typeAliasByName: new Map(),
205
+ requiredExports: new Map(),
206
+ modelNameByIR: new Map(),
207
+ fileBySymbol: new Map(),
208
+ },
209
+ };
210
+
211
+ const files = generateResources([mfaService], overlayCtx);
212
+ expect(files.length).toBe(1);
213
+ expect(files[0].path).toBe('src/mfa/mfa.ts');
214
+
215
+ const content = files[0].content;
216
+ expect(content).toContain('export class Mfa {');
217
+ });
218
+
219
+ it('renders multiline description and @deprecated in method docstring', () => {
220
+ const services: Service[] = [
221
+ {
222
+ name: 'Radar',
223
+ operations: [
224
+ {
225
+ name: 'updateAttempt',
226
+ description: 'Update a Radar attempt\n\nYou may optionally inform Radar that an attempt was successful.',
227
+ httpMethod: 'put',
228
+ path: '/radar/attempts/{id}',
229
+ pathParams: [
230
+ {
231
+ name: 'id',
232
+ type: { kind: 'primitive', type: 'string' },
233
+ required: true,
234
+ description: 'The unique identifier of the attempt.',
235
+ },
236
+ ],
237
+ queryParams: [],
238
+ headerParams: [],
239
+ requestBody: { kind: 'model', name: 'UpdateAttemptInput' },
240
+ response: { kind: 'model', name: 'RadarAttempt' },
241
+ errors: [],
242
+ injectIdempotencyKey: false,
243
+ deprecated: true,
244
+ },
245
+ ],
246
+ },
247
+ ];
248
+
249
+ const files = generateResources(services, ctx);
250
+ const content = files[0].content;
251
+
252
+ expect(content).toContain(' /**');
253
+ expect(content).toContain(' * Update a Radar attempt');
254
+ expect(content).toContain(' *');
255
+ expect(content).toContain(' * You may optionally inform Radar that an attempt was successful.');
256
+ expect(content).toContain(' * @param id - The unique identifier of the attempt.');
257
+ expect(content).toContain(' * @deprecated');
258
+ expect(content).toContain(' */');
259
+ });
260
+ });
@@ -0,0 +1,206 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { generateSerializers } from '../../src/node/serializers.js';
3
+ import type { EmitterContext, ApiSpec, Model, Service } from '@workos/oagen';
4
+
5
+ const emptySpec: ApiSpec = {
6
+ name: 'Test',
7
+ version: '1.0.0',
8
+ baseUrl: '',
9
+ services: [],
10
+ models: [],
11
+ enums: [],
12
+ };
13
+
14
+ const ctx: EmitterContext = {
15
+ namespace: 'workos',
16
+ namespacePascal: 'WorkOS',
17
+ spec: emptySpec,
18
+ irVersion: 6,
19
+ };
20
+
21
+ describe('generateSerializers', () => {
22
+ it('returns empty for no models', () => {
23
+ expect(generateSerializers([], ctx)).toEqual([]);
24
+ });
25
+
26
+ it('generates deserializer with camelCase→snake_case mapping', () => {
27
+ const service: Service = {
28
+ name: 'Organizations',
29
+ operations: [
30
+ {
31
+ name: 'getOrganization',
32
+ httpMethod: 'get',
33
+ path: '/organizations/{id}',
34
+ pathParams: [{ name: 'id', type: { kind: 'primitive', type: 'string' }, required: true }],
35
+ queryParams: [],
36
+ headerParams: [],
37
+ response: { kind: 'model', name: 'Organization' },
38
+ errors: [],
39
+ injectIdempotencyKey: false,
40
+ },
41
+ ],
42
+ };
43
+
44
+ const models: Model[] = [
45
+ {
46
+ name: 'Organization',
47
+ fields: [
48
+ {
49
+ name: 'id',
50
+ type: { kind: 'primitive', type: 'string' },
51
+ required: true,
52
+ },
53
+ {
54
+ name: 'created_at',
55
+ type: { kind: 'primitive', type: 'string', format: 'date-time' },
56
+ required: true,
57
+ },
58
+ {
59
+ name: 'external_id',
60
+ type: {
61
+ kind: 'nullable',
62
+ inner: { kind: 'primitive', type: 'string' },
63
+ },
64
+ required: false,
65
+ },
66
+ ],
67
+ },
68
+ ];
69
+
70
+ const ctxWithServices: EmitterContext = {
71
+ ...ctx,
72
+ spec: { ...emptySpec, services: [service], models },
73
+ };
74
+
75
+ const files = generateSerializers(models, ctxWithServices);
76
+ expect(files.length).toBe(1);
77
+ expect(files[0].path).toBe('src/organizations/serializers/organization.serializer.ts');
78
+
79
+ const content = files[0].content;
80
+ expect(content).toContain('export const deserializeOrganization');
81
+ expect(content).toContain(" id: response.id ?? '',");
82
+ expect(content).toContain(" createdAt: response.created_at ?? '',");
83
+ expect(content).toContain(' externalId: response.external_id ?? null,');
84
+ });
85
+
86
+ it('generates nested model deserialization', () => {
87
+ const service: Service = {
88
+ name: 'Organizations',
89
+ operations: [
90
+ {
91
+ name: 'getOrganization',
92
+ httpMethod: 'get',
93
+ path: '/organizations/{id}',
94
+ pathParams: [{ name: 'id', type: { kind: 'primitive', type: 'string' }, required: true }],
95
+ queryParams: [],
96
+ headerParams: [],
97
+ response: { kind: 'model', name: 'Organization' },
98
+ errors: [],
99
+ injectIdempotencyKey: false,
100
+ },
101
+ ],
102
+ };
103
+
104
+ const models: Model[] = [
105
+ {
106
+ name: 'Organization',
107
+ fields: [
108
+ {
109
+ name: 'id',
110
+ type: { kind: 'primitive', type: 'string' },
111
+ required: true,
112
+ },
113
+ {
114
+ name: 'domains',
115
+ type: {
116
+ kind: 'array',
117
+ items: { kind: 'model', name: 'OrganizationDomain' },
118
+ },
119
+ required: true,
120
+ },
121
+ ],
122
+ },
123
+ {
124
+ name: 'OrganizationDomain',
125
+ fields: [
126
+ {
127
+ name: 'id',
128
+ type: { kind: 'primitive', type: 'string' },
129
+ required: true,
130
+ },
131
+ ],
132
+ },
133
+ ];
134
+
135
+ const ctxWithServices: EmitterContext = {
136
+ ...ctx,
137
+ spec: { ...emptySpec, services: [service], models },
138
+ };
139
+
140
+ const files = generateSerializers(models, ctxWithServices);
141
+ const orgSerializer = files.find((f) => f.path.includes('organization.serializer.ts'))!;
142
+
143
+ expect(orgSerializer.content).toContain('domains: response.domains.map(deserializeOrganizationDomain),');
144
+ expect(orgSerializer.content).toContain('import { deserializeOrganizationDomain, serializeOrganizationDomain }');
145
+ });
146
+
147
+ it('generates serialize function for request body models', () => {
148
+ const service: Service = {
149
+ name: 'Organizations',
150
+ operations: [
151
+ {
152
+ name: 'createOrganization',
153
+ httpMethod: 'post',
154
+ path: '/organizations',
155
+ pathParams: [],
156
+ queryParams: [],
157
+ headerParams: [],
158
+ requestBody: { kind: 'model', name: 'CreateOrganizationInput' },
159
+ response: { kind: 'model', name: 'Organization' },
160
+ errors: [],
161
+ injectIdempotencyKey: false,
162
+ },
163
+ ],
164
+ };
165
+
166
+ const models: Model[] = [
167
+ {
168
+ name: 'Organization',
169
+ fields: [
170
+ {
171
+ name: 'id',
172
+ type: { kind: 'primitive', type: 'string' },
173
+ required: true,
174
+ },
175
+ ],
176
+ },
177
+ {
178
+ name: 'CreateOrganizationInput',
179
+ fields: [
180
+ {
181
+ name: 'name',
182
+ type: { kind: 'primitive', type: 'string' },
183
+ required: true,
184
+ },
185
+ {
186
+ name: 'external_id',
187
+ type: { kind: 'primitive', type: 'string' },
188
+ required: false,
189
+ },
190
+ ],
191
+ },
192
+ ];
193
+
194
+ const ctxWithServices: EmitterContext = {
195
+ ...ctx,
196
+ spec: { ...emptySpec, services: [service], models },
197
+ };
198
+
199
+ const files = generateSerializers(models, ctxWithServices);
200
+ const inputSerializer = files.find((f) => f.path.includes('create-organization-input.serializer.ts'))!;
201
+
202
+ // Should have both deserialize AND serialize
203
+ expect(inputSerializer.content).toContain('export const deserializeCreateOrganizationInput');
204
+ expect(inputSerializer.content).toContain('export const serializeCreateOrganizationInput');
205
+ });
206
+ });
@@ -0,0 +1,127 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { mapTypeRef, mapWireTypeRef } from '../../src/node/type-map.js';
3
+ import type { TypeRef } from '@workos/oagen';
4
+
5
+ describe('mapTypeRef', () => {
6
+ it('maps primitive string', () => {
7
+ const ref: TypeRef = { kind: 'primitive', type: 'string' };
8
+ expect(mapTypeRef(ref)).toBe('string');
9
+ });
10
+
11
+ it('maps primitive integer to number', () => {
12
+ const ref: TypeRef = { kind: 'primitive', type: 'integer' };
13
+ expect(mapTypeRef(ref)).toBe('number');
14
+ });
15
+
16
+ it('maps primitive boolean', () => {
17
+ const ref: TypeRef = { kind: 'primitive', type: 'boolean' };
18
+ expect(mapTypeRef(ref)).toBe('boolean');
19
+ });
20
+
21
+ it('maps unknown to any', () => {
22
+ const ref: TypeRef = { kind: 'primitive', type: 'unknown' };
23
+ expect(mapTypeRef(ref)).toBe('any');
24
+ });
25
+
26
+ it('maps array of strings', () => {
27
+ const ref: TypeRef = {
28
+ kind: 'array',
29
+ items: { kind: 'primitive', type: 'string' },
30
+ };
31
+ expect(mapTypeRef(ref)).toBe('string[]');
32
+ });
33
+
34
+ it('maps model reference', () => {
35
+ const ref: TypeRef = { kind: 'model', name: 'Organization' };
36
+ expect(mapTypeRef(ref)).toBe('Organization');
37
+ });
38
+
39
+ it('maps enum reference', () => {
40
+ const ref: TypeRef = { kind: 'enum', name: 'Status' };
41
+ expect(mapTypeRef(ref)).toBe('Status');
42
+ });
43
+
44
+ it('maps nullable type', () => {
45
+ const ref: TypeRef = {
46
+ kind: 'nullable',
47
+ inner: { kind: 'primitive', type: 'string' },
48
+ };
49
+ expect(mapTypeRef(ref)).toBe('string | null');
50
+ });
51
+
52
+ it('maps union type', () => {
53
+ const ref: TypeRef = {
54
+ kind: 'union',
55
+ variants: [
56
+ { kind: 'primitive', type: 'string' },
57
+ { kind: 'primitive', type: 'number' },
58
+ ],
59
+ };
60
+ expect(mapTypeRef(ref)).toBe('string | number');
61
+ });
62
+
63
+ it('maps map type', () => {
64
+ const ref: TypeRef = {
65
+ kind: 'map',
66
+ valueType: { kind: 'primitive', type: 'string' },
67
+ };
68
+ expect(mapTypeRef(ref)).toBe('Record<string, string>');
69
+ });
70
+
71
+ it('maps literal string', () => {
72
+ const ref: TypeRef = { kind: 'literal', value: 'organization' };
73
+ expect(mapTypeRef(ref)).toBe("'organization'");
74
+ });
75
+
76
+ it('maps literal number', () => {
77
+ const ref: TypeRef = { kind: 'literal', value: 42 };
78
+ expect(mapTypeRef(ref)).toBe('42');
79
+ });
80
+
81
+ it('parenthesizes union in array', () => {
82
+ const ref: TypeRef = {
83
+ kind: 'array',
84
+ items: {
85
+ kind: 'union',
86
+ variants: [
87
+ { kind: 'primitive', type: 'string' },
88
+ { kind: 'primitive', type: 'number' },
89
+ ],
90
+ },
91
+ };
92
+ expect(mapTypeRef(ref)).toBe('(string | number)[]');
93
+ });
94
+ });
95
+
96
+ describe('mapWireTypeRef', () => {
97
+ it('maps model reference with Response suffix', () => {
98
+ const ref: TypeRef = { kind: 'model', name: 'Organization' };
99
+ expect(mapWireTypeRef(ref)).toBe('OrganizationResponse');
100
+ });
101
+
102
+ it('maps array of models with Response suffix', () => {
103
+ const ref: TypeRef = {
104
+ kind: 'array',
105
+ items: { kind: 'model', name: 'OrganizationDomain' },
106
+ };
107
+ expect(mapWireTypeRef(ref)).toBe('OrganizationDomainResponse[]');
108
+ });
109
+
110
+ it('keeps primitives unchanged', () => {
111
+ const ref: TypeRef = { kind: 'primitive', type: 'string' };
112
+ expect(mapWireTypeRef(ref)).toBe('string');
113
+ });
114
+
115
+ it('keeps enum references unchanged', () => {
116
+ const ref: TypeRef = { kind: 'enum', name: 'Status' };
117
+ expect(mapWireTypeRef(ref)).toBe('Status');
118
+ });
119
+
120
+ it('maps nullable model with Response suffix', () => {
121
+ const ref: TypeRef = {
122
+ kind: 'nullable',
123
+ inner: { kind: 'model', name: 'Organization' },
124
+ };
125
+ expect(mapWireTypeRef(ref)).toBe('OrganizationResponse | null');
126
+ });
127
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "declaration": true,
8
+ "declarationMap": true,
9
+ "sourceMap": true,
10
+ "outDir": "dist",
11
+ "rootDir": ".",
12
+ "esModuleInterop": true,
13
+ "skipLibCheck": true,
14
+ "forceConsistentCasingInFileNames": true,
15
+ "resolveJsonModule": true,
16
+ "isolatedModules": true
17
+ },
18
+ "include": ["src", "test"],
19
+ "exclude": ["node_modules", "dist"]
20
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,8 @@
1
+ import { defineConfig } from 'tsup';
2
+ export default defineConfig({
3
+ entry: { index: 'src/index.ts' },
4
+ format: ['esm'],
5
+ dts: true,
6
+ clean: true,
7
+ target: 'node20',
8
+ });
@@ -0,0 +1,4 @@
1
+ import { defineConfig } from 'vitest/config';
2
+ export default defineConfig({
3
+ test: { globals: true, include: ['test/**/*.test.ts'] },
4
+ });