ts-procedures 5.16.0 → 6.0.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/README.md +2 -0
- package/agent_config/claude-code/agents/ts-procedures-architect.md +13 -6
- package/agent_config/claude-code/skills/ts-procedures/SKILL.md +26 -4
- package/agent_config/claude-code/skills/ts-procedures/anti-patterns.md +85 -17
- package/agent_config/claude-code/skills/ts-procedures/api-reference.md +163 -5
- package/agent_config/claude-code/skills/ts-procedures/patterns.md +169 -13
- package/agent_config/claude-code/skills/ts-procedures-review/SKILL.md +1 -1
- package/agent_config/claude-code/skills/ts-procedures-review/checklist.md +20 -12
- package/agent_config/claude-code/skills/ts-procedures-scaffold/SKILL.md +2 -1
- package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/client.md +22 -15
- package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/express-rpc.md +20 -17
- package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono-api.md +20 -16
- package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono-rpc.md +20 -17
- package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono-stream.md +16 -3
- package/agent_config/copilot/copilot-instructions.md +77 -12
- package/agent_config/cursor/cursorrules +77 -12
- package/build/client/call.d.ts +2 -1
- package/build/client/call.js +9 -1
- package/build/client/call.js.map +1 -1
- package/build/client/error-dispatch.d.ts +13 -0
- package/build/client/error-dispatch.js +26 -0
- package/build/client/error-dispatch.js.map +1 -0
- package/build/client/error-dispatch.test.d.ts +1 -0
- package/build/client/error-dispatch.test.js +56 -0
- package/build/client/error-dispatch.test.js.map +1 -0
- package/build/client/fetch-adapter.js +10 -4
- package/build/client/fetch-adapter.js.map +1 -1
- package/build/client/index.d.ts +2 -1
- package/build/client/index.js +5 -1
- package/build/client/index.js.map +1 -1
- package/build/client/stream.d.ts +2 -1
- package/build/client/stream.js +13 -3
- package/build/client/stream.js.map +1 -1
- package/build/client/typed-error-dispatch.test.d.ts +1 -0
- package/build/client/typed-error-dispatch.test.js +168 -0
- package/build/client/typed-error-dispatch.test.js.map +1 -0
- package/build/client/types.d.ts +37 -0
- package/build/codegen/e2e.test.js +9 -4
- package/build/codegen/e2e.test.js.map +1 -1
- package/build/codegen/emit-client-runtime.js +4 -0
- package/build/codegen/emit-client-runtime.js.map +1 -1
- package/build/codegen/emit-errors.d.ts +17 -6
- package/build/codegen/emit-errors.integration.test.d.ts +1 -0
- package/build/codegen/emit-errors.integration.test.js +162 -0
- package/build/codegen/emit-errors.integration.test.js.map +1 -0
- package/build/codegen/emit-errors.js +50 -39
- package/build/codegen/emit-errors.js.map +1 -1
- package/build/codegen/emit-errors.test.js +75 -78
- package/build/codegen/emit-errors.test.js.map +1 -1
- package/build/codegen/emit-index.d.ts +7 -0
- package/build/codegen/emit-index.js +26 -4
- package/build/codegen/emit-index.js.map +1 -1
- package/build/codegen/emit-index.test.js +55 -23
- package/build/codegen/emit-index.test.js.map +1 -1
- package/build/codegen/emit-scope.d.ts +8 -0
- package/build/codegen/emit-scope.js +82 -7
- package/build/codegen/emit-scope.js.map +1 -1
- package/build/codegen/pipeline.js +22 -2
- package/build/codegen/pipeline.js.map +1 -1
- package/build/implementations/http/doc-registry.d.ts +21 -0
- package/build/implementations/http/doc-registry.js +51 -78
- package/build/implementations/http/doc-registry.js.map +1 -1
- package/build/implementations/http/doc-registry.test.js +8 -6
- package/build/implementations/http/doc-registry.test.js.map +1 -1
- package/build/implementations/http/error-taxonomy.d.ts +240 -0
- package/build/implementations/http/error-taxonomy.js +230 -0
- package/build/implementations/http/error-taxonomy.js.map +1 -0
- package/build/implementations/http/error-taxonomy.test.d.ts +1 -0
- package/build/implementations/http/error-taxonomy.test.js +399 -0
- package/build/implementations/http/error-taxonomy.test.js.map +1 -0
- package/build/implementations/http/express-rpc/error-taxonomy.test.d.ts +1 -0
- package/build/implementations/http/express-rpc/error-taxonomy.test.js +83 -0
- package/build/implementations/http/express-rpc/error-taxonomy.test.js.map +1 -0
- package/build/implementations/http/express-rpc/index.d.ts +39 -8
- package/build/implementations/http/express-rpc/index.js +39 -8
- package/build/implementations/http/express-rpc/index.js.map +1 -1
- package/build/implementations/http/hono-api/error-taxonomy.test.d.ts +1 -0
- package/build/implementations/http/hono-api/error-taxonomy.test.js +137 -0
- package/build/implementations/http/hono-api/error-taxonomy.test.js.map +1 -0
- package/build/implementations/http/hono-api/index.d.ts +38 -1
- package/build/implementations/http/hono-api/index.js +32 -0
- package/build/implementations/http/hono-api/index.js.map +1 -1
- package/build/implementations/http/hono-rpc/error-taxonomy.test.d.ts +1 -0
- package/build/implementations/http/hono-rpc/error-taxonomy.test.js +64 -0
- package/build/implementations/http/hono-rpc/error-taxonomy.test.js.map +1 -0
- package/build/implementations/http/hono-rpc/index.d.ts +34 -7
- package/build/implementations/http/hono-rpc/index.js +31 -4
- package/build/implementations/http/hono-rpc/index.js.map +1 -1
- package/build/implementations/http/hono-stream/error-taxonomy.test.d.ts +1 -0
- package/build/implementations/http/hono-stream/error-taxonomy.test.js +87 -0
- package/build/implementations/http/hono-stream/error-taxonomy.test.js.map +1 -0
- package/build/implementations/http/hono-stream/index.d.ts +40 -3
- package/build/implementations/http/hono-stream/index.js +37 -10
- package/build/implementations/http/hono-stream/index.js.map +1 -1
- package/build/implementations/http/hono-stream/index.test.js +45 -18
- package/build/implementations/http/hono-stream/index.test.js.map +1 -1
- package/build/implementations/http/on-request-error.test.d.ts +1 -0
- package/build/implementations/http/on-request-error.test.js +173 -0
- package/build/implementations/http/on-request-error.test.js.map +1 -0
- package/build/implementations/http/route-errors.test.d.ts +1 -0
- package/build/implementations/http/route-errors.test.js +140 -0
- package/build/implementations/http/route-errors.test.js.map +1 -0
- package/build/implementations/types.d.ts +30 -2
- package/docs/client-and-codegen.md +105 -12
- package/docs/core.md +14 -5
- package/docs/http-integrations.md +135 -4
- package/docs/streaming.md +3 -1
- package/package.json +7 -2
- package/src/client/call.ts +10 -1
- package/src/client/error-dispatch.test.ts +72 -0
- package/src/client/error-dispatch.ts +27 -0
- package/src/client/fetch-adapter.ts +11 -5
- package/src/client/index.ts +9 -0
- package/src/client/stream.ts +14 -3
- package/src/client/typed-error-dispatch.test.ts +211 -0
- package/src/client/types.ts +42 -0
- package/src/codegen/e2e.test.ts +9 -4
- package/src/codegen/emit-client-runtime.ts +4 -0
- package/src/codegen/emit-errors.integration.test.ts +183 -0
- package/src/codegen/emit-errors.test.ts +91 -87
- package/src/codegen/emit-errors.ts +123 -41
- package/src/codegen/emit-index.test.ts +68 -24
- package/src/codegen/emit-index.ts +66 -4
- package/src/codegen/emit-scope.ts +124 -7
- package/src/codegen/pipeline.ts +25 -2
- package/src/implementations/http/README.md +19 -4
- package/src/implementations/http/doc-registry.test.ts +10 -6
- package/src/implementations/http/doc-registry.ts +63 -80
- package/src/implementations/http/error-taxonomy.test.ts +438 -0
- package/src/implementations/http/error-taxonomy.ts +337 -0
- package/src/implementations/http/express-rpc/README.md +21 -22
- package/src/implementations/http/express-rpc/error-taxonomy.test.ts +103 -0
- package/src/implementations/http/express-rpc/index.ts +75 -14
- package/src/implementations/http/hono-api/README.md +284 -0
- package/src/implementations/http/hono-api/error-taxonomy.test.ts +179 -0
- package/src/implementations/http/hono-api/index.ts +76 -1
- package/src/implementations/http/hono-rpc/README.md +18 -19
- package/src/implementations/http/hono-rpc/error-taxonomy.test.ts +82 -0
- package/src/implementations/http/hono-rpc/index.ts +65 -9
- package/src/implementations/http/hono-stream/README.md +44 -25
- package/src/implementations/http/hono-stream/error-taxonomy.test.ts +98 -0
- package/src/implementations/http/hono-stream/index.test.ts +54 -18
- package/src/implementations/http/hono-stream/index.ts +83 -13
- package/src/implementations/http/on-request-error.test.ts +201 -0
- package/src/implementations/http/route-errors.test.ts +177 -0
- package/src/implementations/types.ts +30 -2
|
@@ -1,59 +1,70 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsonSchemaToTypeBody } from './emit-types.js';
|
|
2
2
|
import { CODEGEN_HEADER } from './constants.js';
|
|
3
3
|
import { toPascalCase } from './naming.js';
|
|
4
4
|
/**
|
|
5
|
-
* Generates a TypeScript file with error
|
|
5
|
+
* Generates a TypeScript file with runtime error classes (plus a registry
|
|
6
|
+
* object for dispatch) derived from `DocEnvelope.errors`.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
8
|
+
* For each error with a schema, emits:
|
|
9
|
+
* - `interface <Name>Body { ... }` — typed response body
|
|
10
|
+
* - `class <Name> extends <ServiceName>ProcedureError<<Name>Body>` with
|
|
11
|
+
* constructor + static `fromResponse(body, meta)`
|
|
9
12
|
*
|
|
10
|
-
*
|
|
11
|
-
* (
|
|
12
|
-
*
|
|
13
|
+
* The shared base `<ServiceName>ProcedureError` is emitted so consumers can
|
|
14
|
+
* `catch (e) { if (e instanceof ApiErrors.ApiProcedureError) ... }` to handle
|
|
15
|
+
* any service error in one block. `instanceof` works across bundler boundaries
|
|
16
|
+
* because the generated file holds the sole class definition at runtime.
|
|
17
|
+
*
|
|
18
|
+
* The registry `<ServiceName>ErrorRegistry` maps body `name` values to
|
|
19
|
+
* classes, consumed by the client's `dispatchTypedError` to produce typed
|
|
20
|
+
* errors instead of generic `ClientRequestError` instances.
|
|
21
|
+
*
|
|
22
|
+
* When `namespaceTypes` is on, everything is wrapped in `export namespace
|
|
23
|
+
* <ServiceName>Errors { ... }`. Returns `undefined` if no errors have schemas.
|
|
13
24
|
*/
|
|
14
25
|
export async function emitErrorsFile(errors, options) {
|
|
15
26
|
const { ajsc: ajscOpts, namespaceTypes = false, serviceName } = options ?? {};
|
|
16
27
|
const servicePrefix = serviceName ? toPascalCase(serviceName) : '';
|
|
17
28
|
const namespaceName = servicePrefix ? `${servicePrefix}Errors` : 'Errors';
|
|
29
|
+
const baseClassName = servicePrefix ? `${servicePrefix}ProcedureError` : 'ProcedureErrorBase';
|
|
18
30
|
const unionName = servicePrefix ? `${servicePrefix}ProcedureErrorUnion` : 'ProcedureErrorUnion';
|
|
19
|
-
|
|
31
|
+
const registryName = servicePrefix ? `${servicePrefix}ErrorRegistry` : 'ErrorRegistry';
|
|
20
32
|
const errorsWithSchema = errors.filter((e) => e.schema != null);
|
|
21
33
|
if (errorsWithSchema.length === 0) {
|
|
22
34
|
return undefined;
|
|
23
35
|
}
|
|
24
|
-
|
|
36
|
+
// Compute the typed body for each error by converting its schema to a TS type.
|
|
37
|
+
const entries = [];
|
|
38
|
+
for (const doc of errorsWithSchema) {
|
|
39
|
+
const bodyType = await jsonSchemaToTypeBody(doc.schema, ajscOpts);
|
|
40
|
+
entries.push({ doc, bodyType: bodyType ?? 'unknown' });
|
|
41
|
+
}
|
|
42
|
+
const lines = [];
|
|
43
|
+
const indent = namespaceTypes ? ' ' : '';
|
|
25
44
|
if (namespaceTypes) {
|
|
26
|
-
|
|
27
|
-
for (const error of errorsWithSchema) {
|
|
28
|
-
const result = await jsonSchemaToExtractedTypes(error.schema, ajscOpts);
|
|
29
|
-
if (result != null) {
|
|
30
|
-
typeLines.push(` /** ${error.description} (HTTP ${error.statusCode}) */`);
|
|
31
|
-
for (const decl of result.declarations) {
|
|
32
|
-
typeLines.push(` ${decl}`);
|
|
33
|
-
}
|
|
34
|
-
typeLines.push(` export type ${error.name} = ${result.body}`);
|
|
35
|
-
typeLines.push('');
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
const unionMembers = errorsWithSchema.map((e) => e.name).join(' | ');
|
|
39
|
-
typeLines.push(` export type ${unionName} = ${unionMembers}`);
|
|
40
|
-
typeLines.push('}');
|
|
41
|
-
typeLines.push('');
|
|
45
|
+
lines.push(`export namespace ${namespaceName} {`);
|
|
42
46
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
47
|
+
// Shared base class — lets consumers catch any service error with one check.
|
|
48
|
+
lines.push(`${indent}/** Base class for every generated error in this service. Catch with \`instanceof\`. */`, `${indent}export class ${baseClassName}<TBody = unknown> extends Error {`, `${indent} readonly status: number`, `${indent} readonly procedureName: string`, `${indent} readonly scope: string`, `${indent} readonly body: TBody`, `${indent} constructor(args: {`, `${indent} name: string`, `${indent} message: string`, `${indent} status: number`, `${indent} procedureName: string`, `${indent} scope: string`, `${indent} body: TBody`, `${indent} }) {`, `${indent} super(args.message)`, `${indent} this.name = args.name`, `${indent} this.status = args.status`, `${indent} this.procedureName = args.procedureName`, `${indent} this.scope = args.scope`, `${indent} this.body = args.body`, `${indent} Object.setPrototypeOf(this, new.target.prototype)`, `${indent} }`, `${indent}}`, '');
|
|
49
|
+
// Per-error body interface + class with fromResponse static factory.
|
|
50
|
+
for (const { doc, bodyType } of entries) {
|
|
51
|
+
const bodyInterfaceName = `${doc.name}Body`;
|
|
52
|
+
lines.push(`${indent}/** Response body for ${doc.name}. */`, `${indent}export type ${bodyInterfaceName} = ${bodyType}`, '');
|
|
53
|
+
const statusLiteral = doc.statusCode;
|
|
54
|
+
const descLine = doc.description
|
|
55
|
+
? `${indent}/** ${doc.description} (HTTP ${statusLiteral}) */`
|
|
56
|
+
: `${indent}/** HTTP ${statusLiteral} */`;
|
|
57
|
+
lines.push(descLine, `${indent}export class ${doc.name} extends ${baseClassName}<${bodyInterfaceName}> {`, `${indent} static readonly errorName = '${doc.name}' as const`, `${indent} static readonly statusCode = ${statusLiteral}`, `${indent} static fromResponse(`, `${indent} body: ${bodyInterfaceName},`, `${indent} meta: { status: number; procedureName: string; scope: string }`, `${indent} ): ${doc.name} {`, `${indent} const message =`, `${indent} body && typeof (body as { message?: unknown }).message === 'string'`, `${indent} ? (body as { message: string }).message`, `${indent} : '${doc.name}'`, `${indent} return new ${doc.name}({`, `${indent} name: '${doc.name}',`, `${indent} message,`, `${indent} status: meta.status,`, `${indent} procedureName: meta.procedureName,`, `${indent} scope: meta.scope,`, `${indent} body,`, `${indent} })`, `${indent} }`, `${indent}}`, '');
|
|
58
|
+
}
|
|
59
|
+
// Union type — every generated error class instance.
|
|
60
|
+
const unionMembers = entries.map((e) => e.doc.name).join(' | ');
|
|
61
|
+
lines.push(`${indent}/** Union of every generated error in this service. */`, `${indent}export type ${unionName} = ${unionMembers}`, '');
|
|
62
|
+
// Registry for runtime dispatch — keyed by body.name.
|
|
63
|
+
lines.push(`${indent}/** Runtime registry consumed by the client to dispatch by \`body.name\`. */`, `${indent}export const ${registryName} = {`, ...entries.map((e) => `${indent} ${e.doc.name},`), `${indent}} as const`, '');
|
|
64
|
+
if (namespaceTypes) {
|
|
65
|
+
lines.push('}');
|
|
66
|
+
lines.push('');
|
|
55
67
|
}
|
|
56
|
-
|
|
57
|
-
return [CODEGEN_HEADER, '', body].join('\n');
|
|
68
|
+
return [CODEGEN_HEADER, '', lines.join('\n')].join('\n');
|
|
58
69
|
}
|
|
59
70
|
//# sourceMappingURL=emit-errors.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emit-errors.js","sourceRoot":"","sources":["../../src/codegen/emit-errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"emit-errors.js","sourceRoot":"","sources":["../../src/codegen/emit-errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -42,133 +42,130 @@ const errorDocWithoutSchema = {
|
|
|
42
42
|
// Tests
|
|
43
43
|
// ---------------------------------------------------------------------------
|
|
44
44
|
describe('emitErrorsFile', () => {
|
|
45
|
-
it('generates
|
|
45
|
+
it('generates a runtime class per error with schema', async () => {
|
|
46
46
|
const result = await emitErrorsFile([procedureErrorDoc, validationErrorDoc]);
|
|
47
47
|
expect(result).toBeDefined();
|
|
48
|
-
expect(result).toContain('export
|
|
49
|
-
expect(result).toContain('export
|
|
48
|
+
expect(result).toContain('export class ProcedureError');
|
|
49
|
+
expect(result).toContain('export class ProcedureValidationError');
|
|
50
50
|
});
|
|
51
|
-
it('
|
|
52
|
-
const result = await emitErrorsFile([procedureErrorDoc
|
|
51
|
+
it('emits a body type for each class', async () => {
|
|
52
|
+
const result = await emitErrorsFile([procedureErrorDoc]);
|
|
53
|
+
expect(result).toContain('export type ProcedureErrorBody =');
|
|
54
|
+
});
|
|
55
|
+
it('each class has a static fromResponse factory', async () => {
|
|
56
|
+
const result = await emitErrorsFile([procedureErrorDoc]);
|
|
57
|
+
expect(result).toContain('static fromResponse(');
|
|
58
|
+
expect(result).toContain("name: 'ProcedureError'");
|
|
59
|
+
});
|
|
60
|
+
it('emits a shared base class the errors extend', async () => {
|
|
61
|
+
const result = await emitErrorsFile([procedureErrorDoc]);
|
|
53
62
|
expect(result).toBeDefined();
|
|
63
|
+
expect(result).toMatch(/export class ProcedureErrorBase<TBody = unknown> extends Error/);
|
|
64
|
+
expect(result).toContain('extends ProcedureErrorBase<ProcedureErrorBody>');
|
|
65
|
+
});
|
|
66
|
+
it('emits a discriminated union type', async () => {
|
|
67
|
+
const result = await emitErrorsFile([procedureErrorDoc, validationErrorDoc]);
|
|
54
68
|
expect(result).toContain('export type ProcedureErrorUnion = ProcedureError | ProcedureValidationError');
|
|
55
69
|
});
|
|
70
|
+
it('emits a runtime registry keyed by class name', async () => {
|
|
71
|
+
const result = await emitErrorsFile([procedureErrorDoc, validationErrorDoc]);
|
|
72
|
+
expect(result).toContain('export const ErrorRegistry = {');
|
|
73
|
+
expect(result).toMatch(/ProcedureError,/);
|
|
74
|
+
expect(result).toMatch(/ProcedureValidationError,/);
|
|
75
|
+
});
|
|
56
76
|
it('includes JSDoc with statusCode and description', async () => {
|
|
57
77
|
const result = await emitErrorsFile([procedureErrorDoc]);
|
|
58
|
-
expect(result).toBeDefined();
|
|
59
78
|
expect(result).toContain('/** An error thrown from within a procedure handler via ctx.error(). (HTTP 500) */');
|
|
60
79
|
});
|
|
61
|
-
it('includes JSDoc before the
|
|
80
|
+
it('includes JSDoc before the concrete class declaration', async () => {
|
|
62
81
|
const result = await emitErrorsFile([procedureErrorDoc]);
|
|
63
|
-
expect(result).toBeDefined();
|
|
64
82
|
const jsdocIdx = result.indexOf('/** An error thrown from within a procedure handler via ctx.error().');
|
|
65
|
-
|
|
66
|
-
|
|
83
|
+
// Match only the concrete class (ProcedureError), not the base class
|
|
84
|
+
// (ProcedureErrorBase which is a prefix-collision).
|
|
85
|
+
const concreteClassIdx = result.indexOf('export class ProcedureError extends');
|
|
86
|
+
expect(jsdocIdx).toBeGreaterThan(-1);
|
|
87
|
+
expect(concreteClassIdx).toBeGreaterThan(jsdocIdx);
|
|
67
88
|
});
|
|
68
89
|
it('includes the auto-generated header comment', async () => {
|
|
69
90
|
const result = await emitErrorsFile([procedureErrorDoc]);
|
|
70
|
-
expect(result).toBeDefined();
|
|
71
91
|
expect(result).toContain('// Auto-generated by ts-procedures-codegen — do not edit');
|
|
72
92
|
});
|
|
73
93
|
it('returns undefined when no errors have schemas', async () => {
|
|
74
94
|
const result = await emitErrorsFile([errorDocWithoutSchema]);
|
|
75
95
|
expect(result).toBeUndefined();
|
|
76
96
|
});
|
|
77
|
-
it('returns undefined for empty errors array', async () => {
|
|
97
|
+
it('returns undefined for an empty errors array', async () => {
|
|
78
98
|
const result = await emitErrorsFile([]);
|
|
79
99
|
expect(result).toBeUndefined();
|
|
80
100
|
});
|
|
81
|
-
it('skips errors without schema,
|
|
82
|
-
const result = await emitErrorsFile([
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
expect(result).
|
|
88
|
-
|
|
89
|
-
expect(result).toContain('
|
|
90
|
-
expect(result).not.toContain('UnknownError');
|
|
91
|
-
});
|
|
92
|
-
it('union type has single type when only one error has schema', async () => {
|
|
93
|
-
const result = await emitErrorsFile([procedureErrorDoc]);
|
|
94
|
-
expect(result).toBeDefined();
|
|
95
|
-
expect(result).toContain('export type ProcedureErrorUnion = ProcedureError');
|
|
96
|
-
});
|
|
97
|
-
it('union type does not include errros without schemas', async () => {
|
|
98
|
-
const result = await emitErrorsFile([procedureErrorDoc, errorDocWithoutSchema]);
|
|
99
|
-
expect(result).toBeDefined();
|
|
101
|
+
it('skips errors without schema, emits classes for the rest', async () => {
|
|
102
|
+
const result = await emitErrorsFile([
|
|
103
|
+
procedureErrorDoc,
|
|
104
|
+
errorDocWithoutSchema,
|
|
105
|
+
validationErrorDoc,
|
|
106
|
+
]);
|
|
107
|
+
expect(result).toContain('export class ProcedureError');
|
|
108
|
+
expect(result).toContain('export class ProcedureValidationError');
|
|
109
|
+
expect(result).not.toContain('export class UnknownError');
|
|
100
110
|
expect(result).not.toContain('UnknownError');
|
|
101
|
-
expect(result).toContain('export type ProcedureErrorUnion = ProcedureError');
|
|
102
111
|
});
|
|
103
|
-
it('
|
|
104
|
-
// Just verify it does not crash when options are passed — behavior is tested in emit-types.test.ts
|
|
112
|
+
it('accepts ajsc options without crashing', async () => {
|
|
105
113
|
const result = await emitErrorsFile([procedureErrorDoc], { ajsc: { enumStyle: 'union' } });
|
|
106
114
|
expect(result).toBeDefined();
|
|
107
|
-
expect(result).toContain('export
|
|
115
|
+
expect(result).toContain('export class ProcedureError');
|
|
108
116
|
});
|
|
109
117
|
describe('serviceName', () => {
|
|
110
|
-
it('uses Errors namespace by default', async () => {
|
|
111
|
-
const result = await emitErrorsFile([procedureErrorDoc
|
|
118
|
+
it('uses Errors namespace by default in namespace mode', async () => {
|
|
119
|
+
const result = await emitErrorsFile([procedureErrorDoc], { namespaceTypes: true });
|
|
112
120
|
expect(result).toContain('export namespace Errors {');
|
|
113
121
|
});
|
|
114
|
-
it('prefixes namespace with serviceName
|
|
115
|
-
const result = await emitErrorsFile([procedureErrorDoc
|
|
122
|
+
it('prefixes namespace with serviceName', async () => {
|
|
123
|
+
const result = await emitErrorsFile([procedureErrorDoc], {
|
|
124
|
+
namespaceTypes: true,
|
|
125
|
+
serviceName: 'Auth',
|
|
126
|
+
});
|
|
116
127
|
expect(result).toContain('export namespace AuthErrors {');
|
|
117
128
|
expect(result).not.toContain('export namespace Errors {');
|
|
118
129
|
});
|
|
119
|
-
it('PascalCases a kebab-case serviceName
|
|
120
|
-
const result = await emitErrorsFile([procedureErrorDoc], {
|
|
130
|
+
it('PascalCases a kebab-case serviceName', async () => {
|
|
131
|
+
const result = await emitErrorsFile([procedureErrorDoc], {
|
|
132
|
+
namespaceTypes: true,
|
|
133
|
+
serviceName: 'user-service',
|
|
134
|
+
});
|
|
121
135
|
expect(result).toContain('export namespace UserServiceErrors {');
|
|
122
136
|
});
|
|
123
|
-
it('
|
|
124
|
-
const result = await emitErrorsFile([procedureErrorDoc], {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
it('prefixes ProcedureErrorUnion in flat mode when serviceName is set', async () => {
|
|
129
|
-
const result = await emitErrorsFile([procedureErrorDoc, validationErrorDoc], { namespaceTypes: false, serviceName: 'Auth' });
|
|
130
|
-
expect(result).toBeDefined();
|
|
137
|
+
it('prefixes base class, union, and registry when serviceName is set', async () => {
|
|
138
|
+
const result = await emitErrorsFile([procedureErrorDoc, validationErrorDoc], {
|
|
139
|
+
serviceName: 'Auth',
|
|
140
|
+
});
|
|
141
|
+
expect(result).toMatch(/export class AuthProcedureError<TBody = unknown> extends Error/);
|
|
131
142
|
expect(result).toContain('export type AuthProcedureErrorUnion = ProcedureError | ProcedureValidationError');
|
|
132
|
-
expect(result).
|
|
143
|
+
expect(result).toContain('export const AuthErrorRegistry = {');
|
|
133
144
|
});
|
|
134
|
-
it('
|
|
135
|
-
const result = await emitErrorsFile([procedureErrorDoc]
|
|
136
|
-
expect(result).
|
|
137
|
-
expect(result).toContain('AuthProcedureErrorUnion');
|
|
138
|
-
expect(result).not.toMatch(/\bexport type ProcedureErrorUnion\b/);
|
|
139
|
-
});
|
|
140
|
-
it('leaves ProcedureErrorUnion unprefixed when no serviceName', async () => {
|
|
141
|
-
const result = await emitErrorsFile([procedureErrorDoc], { namespaceTypes: false });
|
|
142
|
-
expect(result).toBeDefined();
|
|
145
|
+
it('leaves names unprefixed when no serviceName', async () => {
|
|
146
|
+
const result = await emitErrorsFile([procedureErrorDoc]);
|
|
147
|
+
expect(result).toMatch(/export class ProcedureErrorBase<TBody = unknown> extends Error/);
|
|
143
148
|
expect(result).toContain('export type ProcedureErrorUnion = ProcedureError');
|
|
149
|
+
expect(result).toContain('export const ErrorRegistry = {');
|
|
144
150
|
});
|
|
145
151
|
});
|
|
146
152
|
describe('namespaceTypes', () => {
|
|
147
|
-
it('wraps
|
|
148
|
-
const result = await emitErrorsFile([procedureErrorDoc, validationErrorDoc], {
|
|
149
|
-
|
|
153
|
+
it('wraps classes and registry in export namespace', async () => {
|
|
154
|
+
const result = await emitErrorsFile([procedureErrorDoc, validationErrorDoc], {
|
|
155
|
+
namespaceTypes: true,
|
|
156
|
+
});
|
|
150
157
|
expect(result).toContain('export namespace Errors {');
|
|
151
|
-
expect(result).toContain('export
|
|
152
|
-
expect(result).toContain('export
|
|
153
|
-
});
|
|
154
|
-
it('places union inside the namespace', async () => {
|
|
155
|
-
const result = await emitErrorsFile([procedureErrorDoc, validationErrorDoc], { namespaceTypes: true });
|
|
156
|
-
expect(result).toBeDefined();
|
|
157
|
-
expect(result).toContain('export type ProcedureErrorUnion = ProcedureError | ProcedureValidationError');
|
|
158
|
-
// Union should be inside the namespace (indented)
|
|
159
|
-
const lines = result.split('\n');
|
|
160
|
-
const unionLine = lines.find((l) => l.includes('ProcedureErrorUnion'));
|
|
161
|
-
expect(unionLine).toMatch(/^\s+export type ProcedureErrorUnion/);
|
|
158
|
+
expect(result).toContain('export class ProcedureError');
|
|
159
|
+
expect(result).toContain('export const ErrorRegistry = {');
|
|
162
160
|
});
|
|
163
|
-
it('
|
|
161
|
+
it('contents are indented inside the namespace block', async () => {
|
|
164
162
|
const result = await emitErrorsFile([procedureErrorDoc], { namespaceTypes: true });
|
|
165
|
-
|
|
166
|
-
expect(
|
|
163
|
+
const classLine = result.split('\n').find((l) => l.includes('export class ProcedureError'));
|
|
164
|
+
expect(classLine).toMatch(/^\s{2}export class ProcedureError/);
|
|
167
165
|
});
|
|
168
166
|
it('flat mode does not wrap in namespace', async () => {
|
|
169
167
|
const result = await emitErrorsFile([procedureErrorDoc], { namespaceTypes: false });
|
|
170
|
-
expect(result).
|
|
171
|
-
expect(result).not.toContain('export namespace Errors');
|
|
168
|
+
expect(result).not.toContain('export namespace');
|
|
172
169
|
});
|
|
173
170
|
});
|
|
174
171
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emit-errors.test.js","sourceRoot":"","sources":["../../src/codegen/emit-errors.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAGjD,8EAA8E;AAC9E,WAAW;AACX,8EAA8E;AAE9E,MAAM,iBAAiB,GAAa;IAClC,IAAI,EAAE,gBAAgB;IACtB,UAAU,EAAE,GAAG;IACf,WAAW,EAAE,kEAAkE;IAC/E,MAAM,EAAE;QACN,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,gBAAgB,EAAE;YACjD,aAAa,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACjC,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC3B,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;SACzB;QACD,QAAQ,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE,SAAS,CAAC;KAC/C;CACF,CAAA;AAED,MAAM,kBAAkB,GAAa;IACnC,IAAI,EAAE,0BAA0B;IAChC,UAAU,EAAE,GAAG;IACf,WAAW,EAAE,8DAA8D;IAC3E,MAAM,EAAE;QACN,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,0BAA0B,EAAE;YAC3D,aAAa,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACjC,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;SAC5B;QACD,QAAQ,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE,SAAS,CAAC;KAC/C;CACF,CAAA;AAED,MAAM,qBAAqB,GAAa;IACtC,IAAI,EAAE,cAAc;IACpB,UAAU,EAAE,GAAG;IACf,WAAW,EAAE,4BAA4B;IACzC,YAAY;CACb,CAAA;AAED,8EAA8E;AAC9E,QAAQ;AACR,8EAA8E;AAE9E,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"emit-errors.test.js","sourceRoot":"","sources":["../../src/codegen/emit-errors.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import type { ScopeGroup } from './group-routes.js';
|
|
2
2
|
export interface EmitIndexOptions {
|
|
3
|
+
/** Import specifier for types (`ClientInstance`, `CreateClientConfig`). */
|
|
3
4
|
clientImportPath?: string;
|
|
5
|
+
/**
|
|
6
|
+
* Import specifier for runtime values (`createClient`). Defaults to
|
|
7
|
+
* `clientImportPath`. In self-contained mode the pipeline sets this to
|
|
8
|
+
* `./_client` while `clientImportPath` points at `./_types`.
|
|
9
|
+
*/
|
|
10
|
+
clientRuntimeImportPath?: string;
|
|
4
11
|
hasErrors?: boolean;
|
|
5
12
|
namespaceTypes?: boolean;
|
|
6
13
|
serviceName?: string;
|
|
@@ -11,16 +11,21 @@ const ERRORS_MODULE_ALIAS = '_errorsModule';
|
|
|
11
11
|
*/
|
|
12
12
|
export function emitIndexFile(groups, options) {
|
|
13
13
|
const { clientImportPath = 'ts-procedures/client', hasErrors = false, namespaceTypes = false, serviceName = 'Api', } = options ?? {};
|
|
14
|
+
const clientRuntimeImportPath = options?.clientRuntimeImportPath ?? clientImportPath;
|
|
14
15
|
const servicePascal = toPascalCase(serviceName);
|
|
15
16
|
const factoryName = `create${servicePascal}Bindings`;
|
|
17
|
+
const clientFactoryName = `create${servicePascal}Client`;
|
|
16
18
|
const errorsExportedName = `${servicePascal}Errors`;
|
|
19
|
+
const registryName = `${servicePascal}ErrorRegistry`;
|
|
17
20
|
// Underscore-prefix the local module alias so scope names that collide with
|
|
18
21
|
// TypeScript reserved words (e.g. `public`, `default`, `class`) compile.
|
|
19
22
|
const localAlias = (camelCase) => `_${camelCase}`;
|
|
20
23
|
const scopeImports = groups
|
|
21
24
|
.map((g) => `import * as ${localAlias(g.camelCase)} from './${g.scopeKey}'`)
|
|
22
25
|
.join('\n');
|
|
23
|
-
|
|
26
|
+
// `_errors` is imported as a value (not `import type`) when errors exist, so
|
|
27
|
+
// the runtime registry is reachable for `createApiClient`.
|
|
28
|
+
const errorsImport = hasErrors
|
|
24
29
|
? `import * as ${ERRORS_MODULE_ALIAS} from './_errors'`
|
|
25
30
|
: '';
|
|
26
31
|
const importsBlock = [scopeImports, errorsImport].filter(Boolean).join('\n');
|
|
@@ -40,9 +45,10 @@ export function emitIndexFile(groups, options) {
|
|
|
40
45
|
const scopeBindings = groups
|
|
41
46
|
.map((g) => ` ${g.camelCase}: ${localAlias(g.camelCase)}.bind${toPascalCase(g.camelCase)}Scope(client),`)
|
|
42
47
|
.join('\n');
|
|
43
|
-
|
|
48
|
+
const pieces = [
|
|
44
49
|
CODEGEN_HEADER,
|
|
45
|
-
`import
|
|
50
|
+
`import { createClient } from '${clientRuntimeImportPath}'`,
|
|
51
|
+
`import type { ClientInstance, CreateClientConfig } from '${clientImportPath}'`,
|
|
46
52
|
importsBlock,
|
|
47
53
|
'',
|
|
48
54
|
namespaceBlock,
|
|
@@ -52,6 +58,22 @@ export function emitIndexFile(groups, options) {
|
|
|
52
58
|
' }',
|
|
53
59
|
'}',
|
|
54
60
|
'',
|
|
55
|
-
]
|
|
61
|
+
];
|
|
62
|
+
// `createApiClient` is a convenience wrapper that wires `createClient` with
|
|
63
|
+
// the generated error registry baked in, then invokes the bindings factory.
|
|
64
|
+
// Consumers that want manual control over `createClient` still use the
|
|
65
|
+
// factory above.
|
|
66
|
+
if (hasErrors) {
|
|
67
|
+
// In namespace mode, classes + registry live inside `${Service}Errors`;
|
|
68
|
+
// in flat mode they're at the module's top level.
|
|
69
|
+
const registryPath = namespaceTypes
|
|
70
|
+
? `${ERRORS_MODULE_ALIAS}.${errorsExportedName}.${registryName}`
|
|
71
|
+
: `${ERRORS_MODULE_ALIAS}.${registryName}`;
|
|
72
|
+
pieces.push(`/**`, ` * Creates a typed client for this service with the generated error`, ` * registry pre-configured. Non-2xx responses whose body \`name\` matches`, ` * a registered error are thrown as typed class instances instead of`, ` * generic \`ClientRequestError\`s.`, ` */`, `export function ${clientFactoryName}(`, ` config: Omit<CreateClientConfig<ReturnType<typeof ${factoryName}>>, 'scopes' | 'errorRegistry'>`, `) {`, ` return createClient({`, ` ...config,`, ` errorRegistry: ${registryPath},`, ` scopes: (client) => ${factoryName}(client),`, ` })`, `}`, '');
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
pieces.push(`/**`, ` * Creates a typed client for this service. No error registry is`, ` * attached because the DocEnvelope contained no typed errors.`, ` */`, `export function ${clientFactoryName}(`, ` config: Omit<CreateClientConfig<ReturnType<typeof ${factoryName}>>, 'scopes'>`, `) {`, ` return createClient({`, ` ...config,`, ` scopes: (client) => ${factoryName}(client),`, ` })`, `}`, '');
|
|
76
|
+
}
|
|
77
|
+
return pieces.join('\n');
|
|
56
78
|
}
|
|
57
79
|
//# sourceMappingURL=emit-index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emit-index.js","sourceRoot":"","sources":["../../src/codegen/emit-index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,MAAM,mBAAmB,GAAG,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"emit-index.js","sourceRoot":"","sources":["../../src/codegen/emit-index.ts"],"names":[],"mappings":"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"}
|