@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.
- package/.husky/pre-commit +1 -0
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +8 -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 +11893 -3226
- 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-go.ts +116 -42
- package/smoke/sdk-php.ts +28 -26
- package/smoke/sdk-python.ts +5 -2
- 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 +78 -115
- package/src/node/enums.ts +9 -0
- package/src/node/errors.ts +37 -232
- package/src/node/field-plan.ts +726 -0
- package/src/node/fixtures.ts +9 -1
- package/src/node/index.ts +2 -9
- package/src/node/models.ts +178 -21
- package/src/node/naming.ts +49 -111
- package/src/node/resources.ts +374 -364
- package/src/node/sdk-errors.ts +41 -0
- package/src/node/tests.ts +32 -12
- package/src/node/type-map.ts +4 -2
- package/src/node/utils.ts +13 -71
- 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 +18 -12
- package/test/node/enums.test.ts +2 -0
- package/test/node/errors.test.ts +2 -41
- package/test/node/models.test.ts +2 -0
- package/test/node/naming.test.ts +23 -0
- package/test/node/resources.test.ts +99 -69
- package/test/node/serializers.test.ts +3 -1
- 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 -746
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,33 +38,31 @@ 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
67
|
const results: {
|
|
262
68
|
modelName: string;
|
|
@@ -272,8 +78,7 @@ function collectTypedErrors(
|
|
|
272
78
|
results.push({
|
|
273
79
|
modelName: err.type.name,
|
|
274
80
|
statusCode: err.statusCode,
|
|
275
|
-
baseException:
|
|
276
|
-
STATUS_TO_BASE_EXCEPTION[err.statusCode] ?? (err.statusCode >= 500 ? 'GenericServerException' : null),
|
|
81
|
+
baseException: statusToBase[err.statusCode] ?? (err.statusCode >= 500 ? 'GenericServerException' : null),
|
|
277
82
|
});
|
|
278
83
|
}
|
|
279
84
|
}
|