@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.
- package/.github/workflows/ci.yml +20 -0
- package/.github/workflows/lint-pr-title.yml +16 -0
- package/.github/workflows/lint.yml +21 -0
- package/.github/workflows/release-please.yml +28 -0
- package/.github/workflows/release.yml +32 -0
- package/.husky/commit-msg +1 -0
- package/.husky/pre-commit +1 -0
- package/.husky/pre-push +1 -0
- package/.node-version +1 -0
- package/.oxfmtrc.json +10 -0
- package/.oxlintrc.json +29 -0
- package/.vscode/settings.json +11 -0
- package/LICENSE.txt +21 -0
- package/README.md +123 -0
- package/commitlint.config.ts +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +2158 -0
- package/docs/endpoint-coverage.md +275 -0
- package/docs/sdk-architecture/node.md +355 -0
- package/oagen.config.ts +51 -0
- package/package.json +83 -0
- package/renovate.json +26 -0
- package/smoke/sdk-dotnet.ts +903 -0
- package/smoke/sdk-elixir.ts +771 -0
- package/smoke/sdk-go.ts +948 -0
- package/smoke/sdk-kotlin.ts +799 -0
- package/smoke/sdk-node.ts +516 -0
- package/smoke/sdk-php.ts +699 -0
- package/smoke/sdk-python.ts +738 -0
- package/smoke/sdk-ruby.ts +723 -0
- package/smoke/sdk-rust.ts +774 -0
- package/src/compat/extractors/dotnet.ts +8 -0
- package/src/compat/extractors/elixir.ts +8 -0
- package/src/compat/extractors/go.ts +8 -0
- package/src/compat/extractors/kotlin.ts +8 -0
- package/src/compat/extractors/node.ts +8 -0
- package/src/compat/extractors/php.ts +8 -0
- package/src/compat/extractors/python.ts +8 -0
- package/src/compat/extractors/ruby.ts +8 -0
- package/src/compat/extractors/rust.ts +8 -0
- package/src/index.ts +1 -0
- package/src/node/client.ts +356 -0
- package/src/node/common.ts +203 -0
- package/src/node/config.ts +70 -0
- package/src/node/enums.ts +87 -0
- package/src/node/errors.ts +205 -0
- package/src/node/fixtures.ts +139 -0
- package/src/node/index.ts +57 -0
- package/src/node/manifest.ts +23 -0
- package/src/node/models.ts +323 -0
- package/src/node/naming.ts +96 -0
- package/src/node/resources.ts +380 -0
- package/src/node/serializers.ts +286 -0
- package/src/node/tests.ts +336 -0
- package/src/node/type-map.ts +56 -0
- package/src/node/utils.ts +164 -0
- package/test/compat/extractors/node.test.ts +145 -0
- package/test/fixtures/sample-sdk-node/package.json +7 -0
- package/test/fixtures/sample-sdk-node/src/client.ts +24 -0
- package/test/fixtures/sample-sdk-node/src/index.ts +4 -0
- package/test/fixtures/sample-sdk-node/src/models.ts +28 -0
- package/test/fixtures/sample-sdk-node/tsconfig.json +13 -0
- package/test/node/client.test.ts +165 -0
- package/test/node/enums.test.ts +128 -0
- package/test/node/errors.test.ts +65 -0
- package/test/node/models.test.ts +301 -0
- package/test/node/naming.test.ts +212 -0
- package/test/node/resources.test.ts +260 -0
- package/test/node/serializers.test.ts +206 -0
- package/test/node/type-map.test.ts +127 -0
- package/tsconfig.json +20 -0
- package/tsup.config.ts +8 -0
- package/vitest.config.ts +4 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
import type { WorkOSOptions, Organization, ListResponse } from './models.js';
|
|
3
|
+
|
|
4
|
+
export class WorkOSClient {
|
|
5
|
+
readonly baseURL: string;
|
|
6
|
+
private readonly apiKey: string;
|
|
7
|
+
|
|
8
|
+
constructor(options: WorkOSOptions) {
|
|
9
|
+
this.apiKey = options.apiKey;
|
|
10
|
+
this.baseURL = options.baseUrl ?? 'https://api.workos.com';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async getOrganization(id: string): Promise<Organization> {
|
|
14
|
+
throw new Error('Not implemented');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async listOrganizations(limit?: number): Promise<ListResponse<Organization>> {
|
|
18
|
+
throw new Error('Not implemented');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async deleteOrganization(id: string): Promise<void> {
|
|
22
|
+
throw new Error('Not implemented');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface WorkOSOptions {
|
|
2
|
+
apiKey: string;
|
|
3
|
+
baseUrl?: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface Organization {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
createdAt: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface OrganizationResponse {
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
created_at: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ListResponse<T> {
|
|
19
|
+
data: T[];
|
|
20
|
+
hasMore: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type StatusType = 'active' | 'inactive';
|
|
24
|
+
|
|
25
|
+
export enum Status {
|
|
26
|
+
Active = 'active',
|
|
27
|
+
Inactive = 'inactive',
|
|
28
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { generateClient } from '../../src/node/client.js';
|
|
3
|
+
import type { EmitterContext, ApiSpec, Service, Model } from '@workos/oagen';
|
|
4
|
+
|
|
5
|
+
const service: Service = {
|
|
6
|
+
name: 'Organizations',
|
|
7
|
+
operations: [
|
|
8
|
+
{
|
|
9
|
+
name: 'getOrganization',
|
|
10
|
+
httpMethod: 'get',
|
|
11
|
+
path: '/organizations/{id}',
|
|
12
|
+
pathParams: [{ name: 'id', type: { kind: 'primitive', type: 'string' }, required: true }],
|
|
13
|
+
queryParams: [],
|
|
14
|
+
headerParams: [],
|
|
15
|
+
response: { kind: 'model', name: 'Organization' },
|
|
16
|
+
errors: [],
|
|
17
|
+
injectIdempotencyKey: false,
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const model: Model = {
|
|
23
|
+
name: 'Organization',
|
|
24
|
+
fields: [
|
|
25
|
+
{ name: 'id', type: { kind: 'primitive', type: 'string' }, required: true },
|
|
26
|
+
{ name: 'name', type: { kind: 'primitive', type: 'string' }, required: true },
|
|
27
|
+
],
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const spec: ApiSpec = {
|
|
31
|
+
name: 'Test',
|
|
32
|
+
version: '1.0.0',
|
|
33
|
+
baseUrl: 'https://api.example.com',
|
|
34
|
+
services: [service],
|
|
35
|
+
models: [model],
|
|
36
|
+
enums: [],
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const ctx: EmitterContext = {
|
|
40
|
+
namespace: 'workos',
|
|
41
|
+
namespacePascal: 'WorkOS',
|
|
42
|
+
spec,
|
|
43
|
+
irVersion: 6,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
describe('generateClient', () => {
|
|
47
|
+
it('generates WorkOS client with resource accessors', () => {
|
|
48
|
+
const files = generateClient(spec, ctx);
|
|
49
|
+
const workosFile = files.find((f) => f.path === 'src/workos.ts');
|
|
50
|
+
expect(workosFile).toBeDefined();
|
|
51
|
+
|
|
52
|
+
const content = workosFile!.content;
|
|
53
|
+
expect(content).toContain('export class WorkOS {');
|
|
54
|
+
expect(content).toContain('readonly organizations = new Organizations(this);');
|
|
55
|
+
expect(content).toContain('async get<Result');
|
|
56
|
+
expect(content).toContain('async post<Result');
|
|
57
|
+
expect(content).toContain('async delete(');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('generates barrel exports', () => {
|
|
61
|
+
const files = generateClient(spec, ctx);
|
|
62
|
+
const barrel = files.find((f) => f.path === 'src/index.ts');
|
|
63
|
+
expect(barrel).toBeDefined();
|
|
64
|
+
|
|
65
|
+
const content = barrel!.content;
|
|
66
|
+
expect(content).toContain("export * from './common/exceptions';");
|
|
67
|
+
expect(content).toContain("export { AutoPaginatable } from './common/utils/pagination';");
|
|
68
|
+
expect(content).toContain("export { WorkOS } from './workos';");
|
|
69
|
+
expect(content).toContain('export type { Organization, OrganizationResponse }');
|
|
70
|
+
expect(content).toContain("export { Organizations } from './organizations/organizations';");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('generates package.json and tsconfig.json', () => {
|
|
74
|
+
const files = generateClient(spec, ctx);
|
|
75
|
+
const pkg = files.find((f) => f.path === 'package.json');
|
|
76
|
+
const tsconfig = files.find((f) => f.path === 'tsconfig.json');
|
|
77
|
+
|
|
78
|
+
expect(pkg).toBeDefined();
|
|
79
|
+
expect(pkg!.skipIfExists).toBe(true);
|
|
80
|
+
|
|
81
|
+
expect(tsconfig).toBeDefined();
|
|
82
|
+
expect(tsconfig!.skipIfExists).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('uses overlay-resolved names for imports and accessors', () => {
|
|
86
|
+
const mfaService: Service = {
|
|
87
|
+
name: 'MultiFactorAuth',
|
|
88
|
+
operations: [
|
|
89
|
+
{
|
|
90
|
+
name: 'enrollFactor',
|
|
91
|
+
httpMethod: 'post',
|
|
92
|
+
path: '/auth/factors/enroll',
|
|
93
|
+
pathParams: [],
|
|
94
|
+
queryParams: [],
|
|
95
|
+
headerParams: [],
|
|
96
|
+
response: { kind: 'model', name: 'AuthenticationFactor' },
|
|
97
|
+
errors: [],
|
|
98
|
+
injectIdempotencyKey: true,
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const mfaModel: Model = {
|
|
104
|
+
name: 'AuthenticationFactor',
|
|
105
|
+
fields: [{ name: 'id', type: { kind: 'primitive', type: 'string' }, required: true }],
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const overlaySpec: ApiSpec = {
|
|
109
|
+
name: 'Test',
|
|
110
|
+
version: '1.0.0',
|
|
111
|
+
baseUrl: 'https://api.example.com',
|
|
112
|
+
services: [mfaService],
|
|
113
|
+
models: [mfaModel],
|
|
114
|
+
enums: [],
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const overlayCtx: EmitterContext = {
|
|
118
|
+
namespace: 'workos',
|
|
119
|
+
namespacePascal: 'WorkOS',
|
|
120
|
+
spec: overlaySpec,
|
|
121
|
+
irVersion: 6,
|
|
122
|
+
overlayLookup: {
|
|
123
|
+
methodByOperation: new Map([
|
|
124
|
+
[
|
|
125
|
+
'POST /auth/factors/enroll',
|
|
126
|
+
{ className: 'Mfa', methodName: 'enrollFactor', params: [], returnType: 'void' },
|
|
127
|
+
],
|
|
128
|
+
]),
|
|
129
|
+
httpKeyByMethod: new Map(),
|
|
130
|
+
interfaceByName: new Map(),
|
|
131
|
+
typeAliasByName: new Map(),
|
|
132
|
+
requiredExports: new Map(),
|
|
133
|
+
modelNameByIR: new Map(),
|
|
134
|
+
fileBySymbol: new Map(),
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const files = generateClient(overlaySpec, overlayCtx);
|
|
139
|
+
const workosFile = files.find((f) => f.path === 'src/workos.ts');
|
|
140
|
+
expect(workosFile).toBeDefined();
|
|
141
|
+
|
|
142
|
+
const content = workosFile!.content;
|
|
143
|
+
// Import path uses resolved name
|
|
144
|
+
expect(content).toContain("from './mfa/mfa'");
|
|
145
|
+
// Property uses resolved name
|
|
146
|
+
expect(content).toContain('readonly mfa = new Mfa(this);');
|
|
147
|
+
|
|
148
|
+
const barrel = files.find((f) => f.path === 'src/index.ts');
|
|
149
|
+
expect(barrel).toBeDefined();
|
|
150
|
+
// Barrel export uses resolved name
|
|
151
|
+
expect(barrel!.content).toContain("from './mfa/mfa'");
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('generates error handling in WorkOS client', () => {
|
|
155
|
+
const files = generateClient(spec, ctx);
|
|
156
|
+
const workosFile = files.find((f) => f.path === 'src/workos.ts')!;
|
|
157
|
+
const content = workosFile.content;
|
|
158
|
+
|
|
159
|
+
expect(content).toContain('case 401: throw new UnauthorizedException');
|
|
160
|
+
expect(content).toContain('case 404: throw new NotFoundException');
|
|
161
|
+
expect(content).toContain('case 422: throw new UnprocessableEntityException');
|
|
162
|
+
expect(content).toContain('case 429:');
|
|
163
|
+
expect(content).toContain('throw new RateLimitExceededException');
|
|
164
|
+
});
|
|
165
|
+
});
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { generateEnums } from '../../src/node/enums.js';
|
|
3
|
+
import type { EmitterContext, ApiSpec, Enum, 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('generateEnums', () => {
|
|
22
|
+
it('returns empty for no enums', () => {
|
|
23
|
+
expect(generateEnums([], ctx)).toEqual([]);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('generates string literal union type', () => {
|
|
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: {
|
|
38
|
+
kind: 'model',
|
|
39
|
+
name: 'Organization',
|
|
40
|
+
},
|
|
41
|
+
errors: [],
|
|
42
|
+
injectIdempotencyKey: false,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const enums: Enum[] = [
|
|
48
|
+
{
|
|
49
|
+
name: 'Status',
|
|
50
|
+
values: [
|
|
51
|
+
{ name: 'ACTIVE', value: 'active' },
|
|
52
|
+
{ name: 'INACTIVE', value: 'inactive' },
|
|
53
|
+
{ name: 'PENDING', value: 'pending' },
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
// Enum not referenced by any service → placed in common/
|
|
59
|
+
const files = generateEnums(enums, {
|
|
60
|
+
...ctx,
|
|
61
|
+
spec: { ...emptySpec, services: [service] },
|
|
62
|
+
});
|
|
63
|
+
expect(files.length).toBe(1);
|
|
64
|
+
expect(files[0].content).toMatchInlineSnapshot(`
|
|
65
|
+
"export type Status =
|
|
66
|
+
| 'active'
|
|
67
|
+
| 'inactive'
|
|
68
|
+
| 'pending';"
|
|
69
|
+
`);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('places enum in service directory when referenced', () => {
|
|
73
|
+
const service: Service = {
|
|
74
|
+
name: 'Organizations',
|
|
75
|
+
operations: [
|
|
76
|
+
{
|
|
77
|
+
name: 'getOrganization',
|
|
78
|
+
httpMethod: 'get',
|
|
79
|
+
path: '/organizations/{id}',
|
|
80
|
+
pathParams: [{ name: 'id', type: { kind: 'primitive', type: 'string' }, required: true }],
|
|
81
|
+
queryParams: [],
|
|
82
|
+
headerParams: [],
|
|
83
|
+
response: { kind: 'enum', name: 'OrgStatus' },
|
|
84
|
+
errors: [],
|
|
85
|
+
injectIdempotencyKey: false,
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const enums: Enum[] = [
|
|
91
|
+
{
|
|
92
|
+
name: 'OrgStatus',
|
|
93
|
+
values: [
|
|
94
|
+
{ name: 'ACTIVE', value: 'active' },
|
|
95
|
+
{ name: 'INACTIVE', value: 'inactive' },
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
const files = generateEnums(enums, {
|
|
101
|
+
...ctx,
|
|
102
|
+
spec: { ...emptySpec, services: [service] },
|
|
103
|
+
});
|
|
104
|
+
expect(files[0].path).toBe('src/organizations/interfaces/org-status.interface.ts');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('renders @deprecated on enum values', () => {
|
|
108
|
+
const enums: Enum[] = [
|
|
109
|
+
{
|
|
110
|
+
name: 'Status',
|
|
111
|
+
values: [
|
|
112
|
+
{ name: 'ACTIVE', value: 'active' },
|
|
113
|
+
{ name: 'LEGACY', value: 'legacy', description: 'No longer supported.', deprecated: true },
|
|
114
|
+
{ name: 'OLD', value: 'old', deprecated: true },
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
const files = generateEnums(enums, ctx);
|
|
120
|
+
const content = files[0].content;
|
|
121
|
+
|
|
122
|
+
// Value with description + deprecated gets multiline JSDoc
|
|
123
|
+
expect(content).toContain(' /**\n * No longer supported.\n * @deprecated\n */');
|
|
124
|
+
|
|
125
|
+
// Value with only deprecated gets single-line JSDoc
|
|
126
|
+
expect(content).toContain(' /** @deprecated */');
|
|
127
|
+
});
|
|
128
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { generateErrors } from '../../src/node/errors.js';
|
|
3
|
+
import type { EmitterContext, ApiSpec } 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('generateErrors', () => {
|
|
22
|
+
it('generates all exception classes', () => {
|
|
23
|
+
const files = generateErrors(ctx);
|
|
24
|
+
|
|
25
|
+
const names = files.map((f) => f.path);
|
|
26
|
+
expect(names).toContain('src/common/exceptions/bad-request.exception.ts');
|
|
27
|
+
expect(names).toContain('src/common/exceptions/unauthorized.exception.ts');
|
|
28
|
+
expect(names).toContain('src/common/exceptions/not-found.exception.ts');
|
|
29
|
+
expect(names).toContain('src/common/exceptions/conflict.exception.ts');
|
|
30
|
+
expect(names).toContain('src/common/exceptions/unprocessable-entity.exception.ts');
|
|
31
|
+
expect(names).toContain('src/common/exceptions/rate-limit-exceeded.exception.ts');
|
|
32
|
+
expect(names).toContain('src/common/exceptions/generic-server.exception.ts');
|
|
33
|
+
expect(names).toContain('src/common/exceptions/no-api-key-provided.exception.ts');
|
|
34
|
+
expect(names).toContain('src/common/exceptions/index.ts');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('generates NotFoundException with correct status', () => {
|
|
38
|
+
const files = generateErrors(ctx);
|
|
39
|
+
const notFoundFile = files.find((f) => f.path.includes('not-found.exception.ts'))!;
|
|
40
|
+
|
|
41
|
+
expect(notFoundFile.content).toContain('export class NotFoundException extends Error');
|
|
42
|
+
expect(notFoundFile.content).toContain('readonly status = 404;');
|
|
43
|
+
expect(notFoundFile.content).toContain('requestID: string');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('generates RateLimitExceededException with retryAfter', () => {
|
|
47
|
+
const files = generateErrors(ctx);
|
|
48
|
+
const rateLimitFile = files.find((f) => f.path.includes('rate-limit-exceeded.exception.ts'))!;
|
|
49
|
+
|
|
50
|
+
expect(rateLimitFile.content).toContain('export class RateLimitExceededException extends Error');
|
|
51
|
+
expect(rateLimitFile.content).toContain('readonly status = 429;');
|
|
52
|
+
expect(rateLimitFile.content).toContain('retryAfter?: number');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('generates exception barrel with all exports', () => {
|
|
56
|
+
const files = generateErrors(ctx);
|
|
57
|
+
const barrel = files.find((f) => f.path === 'src/common/exceptions/index.ts')!;
|
|
58
|
+
|
|
59
|
+
expect(barrel.content).toContain('export { BadRequestException }');
|
|
60
|
+
expect(barrel.content).toContain('export { UnauthorizedException }');
|
|
61
|
+
expect(barrel.content).toContain('export { NotFoundException }');
|
|
62
|
+
expect(barrel.content).toContain('export { RateLimitExceededException }');
|
|
63
|
+
expect(barrel.content).toContain('export { NoApiKeyProvidedException }');
|
|
64
|
+
});
|
|
65
|
+
});
|