@workos/oagen-emitters 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.husky/pre-commit +1 -0
- package/.oxfmtrc.json +8 -1
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +15 -0
- package/README.md +129 -0
- package/dist/index.d.mts +10 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +11943 -2728
- package/dist/index.mjs.map +1 -1
- package/docs/sdk-architecture/go.md +338 -0
- package/docs/sdk-architecture/php.md +315 -0
- package/docs/sdk-architecture/python.md +511 -0
- package/oagen.config.ts +298 -2
- package/package.json +9 -5
- package/scripts/generate-php.js +13 -0
- package/scripts/git-push-with-published-oagen.sh +21 -0
- package/smoke/sdk-dotnet.ts +17 -3
- package/smoke/sdk-elixir.ts +17 -3
- package/smoke/sdk-go.ts +137 -46
- package/smoke/sdk-kotlin.ts +23 -4
- package/smoke/sdk-node.ts +15 -3
- package/smoke/sdk-php.ts +28 -26
- package/smoke/sdk-python.ts +5 -2
- package/smoke/sdk-ruby.ts +17 -3
- package/smoke/sdk-rust.ts +16 -3
- package/src/go/client.ts +141 -0
- package/src/go/enums.ts +196 -0
- package/src/go/fixtures.ts +212 -0
- package/src/go/index.ts +81 -0
- package/src/go/manifest.ts +36 -0
- package/src/go/models.ts +254 -0
- package/src/go/naming.ts +191 -0
- package/src/go/resources.ts +827 -0
- package/src/go/tests.ts +751 -0
- package/src/go/type-map.ts +82 -0
- package/src/go/wrappers.ts +261 -0
- package/src/index.ts +3 -0
- package/src/node/client.ts +167 -122
- package/src/node/enums.ts +13 -4
- package/src/node/errors.ts +42 -233
- package/src/node/field-plan.ts +726 -0
- package/src/node/fixtures.ts +15 -5
- package/src/node/index.ts +65 -16
- package/src/node/models.ts +264 -96
- package/src/node/naming.ts +52 -25
- package/src/node/resources.ts +621 -172
- package/src/node/sdk-errors.ts +41 -0
- package/src/node/tests.ts +71 -27
- package/src/node/type-map.ts +4 -2
- package/src/node/utils.ts +56 -64
- package/src/node/wrappers.ts +151 -0
- package/src/php/client.ts +171 -0
- package/src/php/enums.ts +67 -0
- package/src/php/errors.ts +9 -0
- package/src/php/fixtures.ts +181 -0
- package/src/php/index.ts +96 -0
- package/src/php/manifest.ts +36 -0
- package/src/php/models.ts +310 -0
- package/src/php/naming.ts +298 -0
- package/src/php/resources.ts +561 -0
- package/src/php/tests.ts +533 -0
- package/src/php/type-map.ts +90 -0
- package/src/php/utils.ts +18 -0
- package/src/php/wrappers.ts +151 -0
- package/src/python/client.ts +337 -0
- package/src/python/enums.ts +313 -0
- package/src/python/fixtures.ts +196 -0
- package/src/python/index.ts +95 -0
- package/src/python/manifest.ts +38 -0
- package/src/python/models.ts +688 -0
- package/src/python/naming.ts +209 -0
- package/src/python/resources.ts +1322 -0
- package/src/python/tests.ts +1335 -0
- package/src/python/type-map.ts +93 -0
- package/src/python/wrappers.ts +191 -0
- package/src/shared/model-utils.ts +255 -0
- package/src/shared/naming-utils.ts +107 -0
- package/src/shared/non-spec-services.ts +54 -0
- package/src/shared/resolved-ops.ts +109 -0
- package/src/shared/wrapper-utils.ts +59 -0
- package/test/go/client.test.ts +92 -0
- package/test/go/enums.test.ts +132 -0
- package/test/go/errors.test.ts +9 -0
- package/test/go/models.test.ts +265 -0
- package/test/go/resources.test.ts +408 -0
- package/test/go/tests.test.ts +143 -0
- package/test/node/client.test.ts +199 -94
- package/test/node/enums.test.ts +75 -3
- package/test/node/errors.test.ts +2 -41
- package/test/node/models.test.ts +109 -20
- package/test/node/naming.test.ts +37 -4
- package/test/node/resources.test.ts +662 -30
- package/test/node/serializers.test.ts +36 -7
- package/test/node/type-map.test.ts +11 -0
- package/test/php/client.test.ts +94 -0
- package/test/php/enums.test.ts +173 -0
- package/test/php/errors.test.ts +9 -0
- package/test/php/models.test.ts +497 -0
- package/test/php/resources.test.ts +644 -0
- package/test/php/tests.test.ts +118 -0
- package/test/python/client.test.ts +200 -0
- package/test/python/enums.test.ts +228 -0
- package/test/python/errors.test.ts +16 -0
- package/test/python/manifest.test.ts +74 -0
- package/test/python/models.test.ts +716 -0
- package/test/python/resources.test.ts +617 -0
- package/test/python/tests.test.ts +202 -0
- package/src/node/common.ts +0 -273
- package/src/node/config.ts +0 -71
- package/src/node/serializers.ts +0 -744
package/src/node/errors.ts
CHANGED
|
@@ -1,223 +1,31 @@
|
|
|
1
1
|
import type { EmitterContext, GeneratedFile } from '@workos/oagen';
|
|
2
2
|
import { fileName } from './naming.js';
|
|
3
|
+
import { buildNodeStatusExceptions } from './sdk-errors.js';
|
|
3
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Static exception classes are now hand-maintained in the target SDK.
|
|
7
|
+
* Only typed exceptions derived from spec error models are generated here.
|
|
8
|
+
*/
|
|
4
9
|
export function generateErrors(ctx?: EmitterContext): GeneratedFile[] {
|
|
5
|
-
|
|
6
|
-
{
|
|
7
|
-
path: 'src/common/exceptions/bad-request.exception.ts',
|
|
8
|
-
content: `export class BadRequestException extends Error {
|
|
9
|
-
readonly status = 400;
|
|
10
|
-
readonly name = 'BadRequestException';
|
|
11
|
-
readonly requestID: string;
|
|
12
|
-
readonly code?: string;
|
|
10
|
+
if (!ctx?.spec) return [];
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
message,
|
|
17
|
-
requestID,
|
|
18
|
-
}: {
|
|
19
|
-
code?: string;
|
|
20
|
-
message?: string;
|
|
21
|
-
requestID: string;
|
|
22
|
-
}) {
|
|
23
|
-
super();
|
|
24
|
-
this.message = message ?? 'Bad request';
|
|
25
|
-
this.requestID = requestID;
|
|
26
|
-
if (code) this.code = code;
|
|
27
|
-
}
|
|
28
|
-
}`,
|
|
29
|
-
skipIfExists: true,
|
|
30
|
-
integrateTarget: false,
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
path: 'src/common/exceptions/unauthorized.exception.ts',
|
|
34
|
-
content: `export class UnauthorizedException extends Error {
|
|
35
|
-
readonly status = 401;
|
|
36
|
-
readonly name = 'UnauthorizedException';
|
|
37
|
-
readonly requestID: string;
|
|
38
|
-
|
|
39
|
-
constructor(requestID: string) {
|
|
40
|
-
super();
|
|
41
|
-
this.message = 'Unauthorized';
|
|
42
|
-
this.requestID = requestID;
|
|
43
|
-
}
|
|
44
|
-
}`,
|
|
45
|
-
skipIfExists: true,
|
|
46
|
-
integrateTarget: false,
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
path: 'src/common/exceptions/not-found.exception.ts',
|
|
50
|
-
content: `export class NotFoundException extends Error {
|
|
51
|
-
readonly status = 404;
|
|
52
|
-
readonly name = 'NotFoundException';
|
|
53
|
-
readonly requestID: string;
|
|
54
|
-
readonly code?: string;
|
|
55
|
-
|
|
56
|
-
constructor({
|
|
57
|
-
code,
|
|
58
|
-
message,
|
|
59
|
-
path,
|
|
60
|
-
requestID,
|
|
61
|
-
}: {
|
|
62
|
-
code?: string;
|
|
63
|
-
message?: string;
|
|
64
|
-
path: string;
|
|
65
|
-
requestID: string;
|
|
66
|
-
}) {
|
|
67
|
-
super();
|
|
68
|
-
this.message =
|
|
69
|
-
message ?? \`The requested path '\${path}' could not be found.\`;
|
|
70
|
-
this.requestID = requestID;
|
|
71
|
-
if (code) this.code = code;
|
|
72
|
-
}
|
|
73
|
-
}`,
|
|
74
|
-
skipIfExists: true,
|
|
75
|
-
integrateTarget: false,
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
path: 'src/common/exceptions/conflict.exception.ts',
|
|
79
|
-
content: `export class ConflictException extends Error {
|
|
80
|
-
readonly status = 409;
|
|
81
|
-
readonly name = 'ConflictException';
|
|
82
|
-
readonly requestID: string;
|
|
12
|
+
const typedErrors = collectTypedErrors(ctx);
|
|
13
|
+
if (typedErrors.length === 0) return [];
|
|
83
14
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
requestID,
|
|
87
|
-
}: {
|
|
88
|
-
message?: string;
|
|
89
|
-
error?: string;
|
|
90
|
-
requestID: string;
|
|
91
|
-
}) {
|
|
92
|
-
super();
|
|
93
|
-
this.message = message ?? 'Conflict';
|
|
94
|
-
this.requestID = requestID;
|
|
95
|
-
}
|
|
96
|
-
}`,
|
|
97
|
-
skipIfExists: true,
|
|
98
|
-
integrateTarget: false,
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
path: 'src/common/exceptions/unprocessable-entity.exception.ts',
|
|
102
|
-
content: `export interface UnprocessableEntityError {
|
|
103
|
-
code: string;
|
|
104
|
-
}
|
|
15
|
+
const files: GeneratedFile[] = [];
|
|
16
|
+
const exportLines: string[] = [];
|
|
105
17
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
18
|
+
for (const { modelName, statusCode, baseException } of typedErrors) {
|
|
19
|
+
const exceptionClassName = `${modelName}Exception`;
|
|
20
|
+
const filePath = `src/common/exceptions/${fileName(modelName)}.exception.ts`;
|
|
21
|
+
const baseImport = baseException
|
|
22
|
+
? `import { ${baseException} } from './${fileName(baseException.replace(/Exception$/, '')).replace(/^/, '')}.exception';\n\n`
|
|
23
|
+
: '';
|
|
24
|
+
const baseClass = baseException ?? 'Error';
|
|
111
25
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
message,
|
|
116
|
-
requestID,
|
|
117
|
-
}: {
|
|
118
|
-
code?: string;
|
|
119
|
-
errors?: UnprocessableEntityError[];
|
|
120
|
-
message?: string;
|
|
121
|
-
requestID: string;
|
|
122
|
-
}) {
|
|
123
|
-
super();
|
|
124
|
-
this.requestID = requestID;
|
|
125
|
-
this.message = message ?? 'Unprocessable entity';
|
|
126
|
-
if (code) this.code = code;
|
|
127
|
-
if (errors) {
|
|
128
|
-
const requirement =
|
|
129
|
-
errors.length === 1 ? 'requirement' : 'requirements';
|
|
130
|
-
this.message = \`The following \${requirement} must be met:\\n\`;
|
|
131
|
-
for (const { code: errCode } of errors) {
|
|
132
|
-
this.message = this.message.concat(\`\\t\${errCode}\\n\`);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}`,
|
|
137
|
-
skipIfExists: true,
|
|
138
|
-
integrateTarget: false,
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
path: 'src/common/exceptions/rate-limit-exceeded.exception.ts',
|
|
142
|
-
content: `export class RateLimitExceededException extends Error {
|
|
143
|
-
readonly status = 429;
|
|
144
|
-
readonly name = 'RateLimitExceededException';
|
|
145
|
-
readonly requestID: string;
|
|
146
|
-
readonly retryAfter?: number;
|
|
147
|
-
|
|
148
|
-
constructor(message: string, requestID: string, retryAfter?: number) {
|
|
149
|
-
super();
|
|
150
|
-
this.message = message ?? 'Too many requests';
|
|
151
|
-
this.requestID = requestID;
|
|
152
|
-
this.retryAfter = retryAfter;
|
|
153
|
-
}
|
|
154
|
-
}`,
|
|
155
|
-
skipIfExists: true,
|
|
156
|
-
integrateTarget: false,
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
path: 'src/common/exceptions/generic-server.exception.ts',
|
|
160
|
-
content: `export class GenericServerException extends Error {
|
|
161
|
-
readonly status: number;
|
|
162
|
-
readonly name = 'GenericServerException';
|
|
163
|
-
readonly requestID: string;
|
|
164
|
-
|
|
165
|
-
constructor(status: number, message: string, requestID: string) {
|
|
166
|
-
super();
|
|
167
|
-
this.status = status;
|
|
168
|
-
this.message = message ?? 'Server error';
|
|
169
|
-
this.requestID = requestID;
|
|
170
|
-
}
|
|
171
|
-
}`,
|
|
172
|
-
skipIfExists: true,
|
|
173
|
-
integrateTarget: false,
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
path: 'src/common/exceptions/no-api-key-provided.exception.ts',
|
|
177
|
-
content: `export class NoApiKeyProvidedException extends Error {
|
|
178
|
-
readonly name = 'NoApiKeyProvidedException';
|
|
179
|
-
|
|
180
|
-
constructor() {
|
|
181
|
-
super();
|
|
182
|
-
this.message =
|
|
183
|
-
'No API key provided. Pass it to the WorkOS constructor or set the WORKOS_API_KEY environment variable.';
|
|
184
|
-
}
|
|
185
|
-
}`,
|
|
186
|
-
skipIfExists: true,
|
|
187
|
-
integrateTarget: false,
|
|
188
|
-
},
|
|
189
|
-
{
|
|
190
|
-
path: 'src/common/exceptions/index.ts',
|
|
191
|
-
content: `export { BadRequestException } from './bad-request.exception';
|
|
192
|
-
export { UnauthorizedException } from './unauthorized.exception';
|
|
193
|
-
export { NotFoundException } from './not-found.exception';
|
|
194
|
-
export { ConflictException } from './conflict.exception';
|
|
195
|
-
export {
|
|
196
|
-
UnprocessableEntityException,
|
|
197
|
-
type UnprocessableEntityError,
|
|
198
|
-
} from './unprocessable-entity.exception';
|
|
199
|
-
export { RateLimitExceededException } from './rate-limit-exceeded.exception';
|
|
200
|
-
export { GenericServerException } from './generic-server.exception';
|
|
201
|
-
export { NoApiKeyProvidedException } from './no-api-key-provided.exception';`,
|
|
202
|
-
skipIfExists: true,
|
|
203
|
-
integrateTarget: false,
|
|
204
|
-
},
|
|
205
|
-
];
|
|
206
|
-
|
|
207
|
-
// Fix 6: Generate typed exception classes from ErrorResponse.type
|
|
208
|
-
if (ctx?.spec) {
|
|
209
|
-
const typedErrors = collectTypedErrors(ctx);
|
|
210
|
-
for (const { modelName, statusCode, baseException } of typedErrors) {
|
|
211
|
-
const exceptionClassName = `${modelName}Exception`;
|
|
212
|
-
const filePath = `src/common/exceptions/${fileName(modelName)}.exception.ts`;
|
|
213
|
-
const baseImport = baseException
|
|
214
|
-
? `import { ${baseException} } from './${fileName(baseException.replace(/Exception$/, '')).replace(/^/, '')}.exception';\n\n`
|
|
215
|
-
: '';
|
|
216
|
-
const baseClass = baseException ?? 'Error';
|
|
217
|
-
|
|
218
|
-
files.push({
|
|
219
|
-
path: filePath,
|
|
220
|
-
content: `${baseImport}export class ${exceptionClassName} extends ${baseClass} {
|
|
26
|
+
files.push({
|
|
27
|
+
path: filePath,
|
|
28
|
+
content: `${baseImport}export class ${exceptionClassName} extends ${baseClass} {
|
|
221
29
|
readonly status = ${statusCode};
|
|
222
30
|
readonly name = '${exceptionClassName}';
|
|
223
31
|
readonly requestID: string;
|
|
@@ -230,35 +38,37 @@ export { NoApiKeyProvidedException } from './no-api-key-provided.exception';`,
|
|
|
230
38
|
if (data) this.data = data;
|
|
231
39
|
}
|
|
232
40
|
}`,
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
41
|
+
skipIfExists: true,
|
|
42
|
+
integrateTarget: false,
|
|
43
|
+
});
|
|
236
44
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
45
|
+
exportLines.push(`export { ${exceptionClassName} } from './${fileName(modelName)}.exception';`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Generate a barrel for typed errors only (appended to existing exceptions/index.ts
|
|
49
|
+
// via region preservation if needed)
|
|
50
|
+
if (exportLines.length > 0) {
|
|
51
|
+
files.push({
|
|
52
|
+
path: 'src/common/exceptions/typed-errors.ts',
|
|
53
|
+
content: exportLines.join('\n'),
|
|
54
|
+
skipIfExists: true,
|
|
55
|
+
integrateTarget: false,
|
|
56
|
+
});
|
|
243
57
|
}
|
|
244
58
|
|
|
245
59
|
return files;
|
|
246
60
|
}
|
|
247
61
|
|
|
248
|
-
const STATUS_TO_BASE_EXCEPTION: Record<number, string> = {
|
|
249
|
-
400: 'BadRequestException',
|
|
250
|
-
401: 'UnauthorizedException',
|
|
251
|
-
404: 'NotFoundException',
|
|
252
|
-
409: 'ConflictException',
|
|
253
|
-
422: 'UnprocessableEntityException',
|
|
254
|
-
429: 'RateLimitExceededException',
|
|
255
|
-
};
|
|
256
|
-
|
|
257
62
|
function collectTypedErrors(
|
|
258
63
|
ctx: EmitterContext,
|
|
259
64
|
): { modelName: string; statusCode: number; baseException: string | null }[] {
|
|
65
|
+
const statusToBase = buildNodeStatusExceptions(ctx.spec.sdk);
|
|
260
66
|
const seen = new Set<string>();
|
|
261
|
-
const results: {
|
|
67
|
+
const results: {
|
|
68
|
+
modelName: string;
|
|
69
|
+
statusCode: number;
|
|
70
|
+
baseException: string | null;
|
|
71
|
+
}[] = [];
|
|
262
72
|
|
|
263
73
|
for (const service of ctx.spec.services) {
|
|
264
74
|
for (const op of service.operations) {
|
|
@@ -268,8 +78,7 @@ function collectTypedErrors(
|
|
|
268
78
|
results.push({
|
|
269
79
|
modelName: err.type.name,
|
|
270
80
|
statusCode: err.statusCode,
|
|
271
|
-
baseException:
|
|
272
|
-
STATUS_TO_BASE_EXCEPTION[err.statusCode] ?? (err.statusCode >= 500 ? 'GenericServerException' : null),
|
|
81
|
+
baseException: statusToBase[err.statusCode] ?? (err.statusCode >= 500 ? 'GenericServerException' : null),
|
|
273
82
|
});
|
|
274
83
|
}
|
|
275
84
|
}
|