ts-procedures 5.16.0 → 6.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/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 +87 -19
- package/agent_config/claude-code/skills/ts-procedures/api-reference.md +162 -16
- package/agent_config/claude-code/skills/ts-procedures/patterns.md +179 -16
- 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 +78 -12
- package/agent_config/cursor/cursorrules +78 -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 +17 -1
- package/build/implementations/http/doc-registry.js +47 -79
- package/build/implementations/http/doc-registry.js.map +1 -1
- package/build/implementations/http/doc-registry.test.js +149 -16
- package/build/implementations/http/doc-registry.test.js.map +1 -1
- package/build/implementations/http/error-taxonomy.d.ts +249 -0
- package/build/implementations/http/error-taxonomy.js +252 -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 +139 -0
- package/build/implementations/http/route-errors.test.js.map +1 -0
- package/build/implementations/types.d.ts +43 -3
- package/docs/client-and-codegen.md +105 -12
- package/docs/core.md +14 -5
- package/docs/http-integrations.md +138 -5
- package/docs/streaming.md +3 -1
- package/docs/superpowers/plans/2026-04-24-doc-registry-simplification.md +886 -0
- 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 +21 -7
- package/src/implementations/http/doc-registry.test.ts +164 -16
- package/src/implementations/http/doc-registry.ts +58 -82
- package/src/implementations/http/error-taxonomy.test.ts +438 -0
- package/src/implementations/http/error-taxonomy.ts +361 -0
- package/src/implementations/http/express-rpc/README.md +23 -24
- 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 +20 -21
- 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 +176 -0
- package/src/implementations/types.ts +43 -3
|
@@ -103,6 +103,173 @@ const { TransferFunds } = Create(
|
|
|
103
103
|
|
|
104
104
|
---
|
|
105
105
|
|
|
106
|
+
## Error Taxonomy (declarative error-to-response mapping)
|
|
107
|
+
|
|
108
|
+
`defineErrorTaxonomy` replaces `instanceof` ladders inside `onError`. Register error classes once with their status code and serializer, pass to any HTTP builder. Works across `hono-api`, `hono-rpc`, `express-rpc`, and `hono-stream` pre-stream (mid-stream still uses `onMidStreamError`).
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { defineErrorTaxonomy } from 'ts-procedures/http-errors'
|
|
112
|
+
import { HonoAPIAppBuilder } from 'ts-procedures/hono-api'
|
|
113
|
+
|
|
114
|
+
class UseCaseError extends Error {
|
|
115
|
+
constructor(readonly externalMsg: string, readonly internalMsg: string) {
|
|
116
|
+
super(externalMsg); this.name = 'UseCaseError'
|
|
117
|
+
Object.setPrototypeOf(this, UseCaseError.prototype)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const appErrors = defineErrorTaxonomy({
|
|
122
|
+
AuthError: { class: AuthError, statusCode: 401 }, // default toResponse → { name, message }
|
|
123
|
+
UseCaseError: {
|
|
124
|
+
class: UseCaseError,
|
|
125
|
+
statusCode: 422,
|
|
126
|
+
toResponse: (err) => ({ message: err.externalMsg }), // `name: 'UseCaseError'` auto-injected
|
|
127
|
+
onCatch: (err, { procedure }) => logger.error({ procedure: procedure.name, internal: err.internalMsg }),
|
|
128
|
+
},
|
|
129
|
+
// 3rd-party / non-subclassable errors — use `match:`
|
|
130
|
+
MongoDuplicateKey: {
|
|
131
|
+
match: (err): err is Error & { code: number } =>
|
|
132
|
+
err instanceof Error && (err as any).code === 11000,
|
|
133
|
+
statusCode: 409,
|
|
134
|
+
toResponse: () => ({ message: 'Resource already exists' }),
|
|
135
|
+
},
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
new HonoAPIAppBuilder({
|
|
139
|
+
errors: appErrors,
|
|
140
|
+
unknownError: {
|
|
141
|
+
toResponse: () => ({ name: 'InternalServerError', message: 'Unexpected error' }),
|
|
142
|
+
onCatch: (err, { procedure }) => logger.error({ procedure: procedure.name, err }),
|
|
143
|
+
},
|
|
144
|
+
}).register(API, (c) => ({ requestId: c.req.header('x-request-id') })).build()
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Handlers throw directly — no try/catch, no ladders:
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
API.Create('GetUser', { /* ... */ }, async (ctx, { pathParams }) => {
|
|
151
|
+
const user = await usersRepo.findById(pathParams.id)
|
|
152
|
+
if (!user) throw new UseCaseError('User not found', `repo returned null for ${pathParams.id}`)
|
|
153
|
+
return user
|
|
154
|
+
})
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Key rules:
|
|
158
|
+
|
|
159
|
+
- `defineErrorTaxonomy` topologically sorts `class:` entries so a subclass always resolves before its base — declaration order inside the helper no longer matters for inheritance chains. `match:` entries keep declared order.
|
|
160
|
+
- The core wraps any non-`ProcedureError` thrown from a handler into a `ProcedureError` with `.cause` set. The resolver unwraps this automatically so your taxonomy sees the real class.
|
|
161
|
+
- Two peer modes: declarative (`errors` + `unknownError`) or imperative (`onError`). Both are first-class. When neither is configured, the builder falls through to a hard default (`{ error: message }`, 500).
|
|
162
|
+
- `onCatch`'s `raw` is the framework request object (Hono `Context` / `{ req, res }` for Express) typed as `unknown` — cast at the use site.
|
|
163
|
+
|
|
164
|
+
### Per-route error declaration (typed)
|
|
165
|
+
|
|
166
|
+
Narrow `APIConfig` / `RPCConfig` to your taxonomy's keys for compile-time typo protection, and declare which errors each procedure may emit. These populate the DocEnvelope per-route so generated clients can type `catch` blocks precisely.
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
import { APIConfig } from 'ts-procedures/http'
|
|
170
|
+
import { appErrors } from './errors/taxonomy'
|
|
171
|
+
|
|
172
|
+
type MyAPIConfig = APIConfig<keyof typeof appErrors & string>
|
|
173
|
+
|
|
174
|
+
const API = Procedures<Ctx, MyAPIConfig>()
|
|
175
|
+
|
|
176
|
+
API.Create('GetUser', {
|
|
177
|
+
path: '/users/:id',
|
|
178
|
+
method: 'get',
|
|
179
|
+
errors: ['UseCaseError', 'AuthError'], // typed against taxonomy keys; typos are TS errors
|
|
180
|
+
schema: { /* ... */ },
|
|
181
|
+
}, async (ctx, params) => { /* ... */ })
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Seed the DocEnvelope with both taxonomy errors and framework defaults in one call:
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
import { DocRegistry } from 'ts-procedures/http-docs'
|
|
188
|
+
|
|
189
|
+
const envelope = new DocRegistry({ errors: appErrors, basePath: '/api' })
|
|
190
|
+
.from(apiApp)
|
|
191
|
+
.from(rpcApp)
|
|
192
|
+
.toJSON()
|
|
193
|
+
// envelope.errors: all registered + framework defaults (deduped)
|
|
194
|
+
// envelope.routes[i].errors: per-route subset declared on config.errors
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
// For errors outside your taxonomy (middleware, infrastructure, doc-only):
|
|
199
|
+
const docsWithExtras = new DocRegistry({ errors: appErrors, basePath: '/api' })
|
|
200
|
+
.from(apiBuilder)
|
|
201
|
+
.documentError({ name: 'RateLimitExceeded', statusCode: 429, description: 'too many requests' })
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Typed catch blocks on the client (via codegen)
|
|
205
|
+
|
|
206
|
+
The codegen emits runtime error classes (not just types) and a registry object. `createApiClient` wires the registry automatically so non-2xx responses arrive as typed class instances.
|
|
207
|
+
|
|
208
|
+
Generated client (simplified):
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
// Generated _errors.ts
|
|
212
|
+
export namespace ApiErrors {
|
|
213
|
+
export class ApiProcedureError<TBody> extends Error { /* ... */ }
|
|
214
|
+
export class UseCaseError extends ApiProcedureError<UseCaseErrorBody> {
|
|
215
|
+
static fromResponse(body, meta): UseCaseError { /* ... */ }
|
|
216
|
+
}
|
|
217
|
+
export const ApiErrorRegistry = { UseCaseError, AuthError, /* ... */ }
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Generated index.ts
|
|
221
|
+
export function createApiClient(config) {
|
|
222
|
+
return createClient({ ...config, errorRegistry: ApiErrors.ApiErrorRegistry, scopes: createApiBindings })
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Consumer code:
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
import { createApiClient, ApiErrors, createFetchAdapter } from './generated'
|
|
230
|
+
|
|
231
|
+
const api = createApiClient({
|
|
232
|
+
adapter: createFetchAdapter({ /* ... */ }),
|
|
233
|
+
basePath: 'https://api.example.com',
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
const user = await api.users.getUser({ pathParams: { id: 'u_123' } })
|
|
238
|
+
} catch (err) {
|
|
239
|
+
if (err instanceof ApiErrors.UseCaseError) {
|
|
240
|
+
// err.message is typed; err.body is typed to UseCaseErrorBody;
|
|
241
|
+
// err.status, err.procedureName, err.scope are available.
|
|
242
|
+
} else if (err instanceof ApiErrors.ApiProcedureError) {
|
|
243
|
+
// Catch-all for any generated service error.
|
|
244
|
+
} else {
|
|
245
|
+
// Transport error (network, non-JSON body) — still a ClientRequestError.
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Per-route error narrowing: when a route declares `errors: [...]`, the generated scope file emits an `Errors` type union the consumer can use to annotate their catch:
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
// Generated scope file includes:
|
|
254
|
+
export namespace Users {
|
|
255
|
+
export namespace GetUser {
|
|
256
|
+
export type Params = { /* ... */ }
|
|
257
|
+
export type Response = { /* ... */ }
|
|
258
|
+
export type Errors = ApiErrors.UseCaseError | ApiErrors.AuthError
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Consumer:
|
|
263
|
+
catch (err: unknown) {
|
|
264
|
+
// Cast is manual today (TS has no typed throws); the union documents
|
|
265
|
+
// which errors this specific route can throw.
|
|
266
|
+
const e = err as Users.GetUser.Errors | ClientRequestError
|
|
267
|
+
if (e instanceof ApiErrors.UseCaseError) { /* ... */ }
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
106
273
|
## Stream Procedures
|
|
107
274
|
|
|
108
275
|
```typescript
|
|
@@ -275,15 +442,13 @@ const { UpdateUser } = Create(
|
|
|
275
442
|
async (ctx, params) => updateUser(params)
|
|
276
443
|
)
|
|
277
444
|
|
|
278
|
-
// 2. Build Express app
|
|
445
|
+
// 2. Build Express app — the taxonomy handles framework errors (ProcedureValidationError
|
|
446
|
+
// maps to 400 by default) and anything the app throws; unknownError is the last resort.
|
|
279
447
|
const app = new ExpressRPCAppBuilder({
|
|
280
448
|
pathPrefix: '/api',
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
} else {
|
|
285
|
-
res.status(500).json({ error: error.message })
|
|
286
|
-
}
|
|
449
|
+
errors: appErrors, // from defineErrorTaxonomy({ ... })
|
|
450
|
+
unknownError: {
|
|
451
|
+
toResponse: (err) => ({ error: (err as Error).message }),
|
|
287
452
|
},
|
|
288
453
|
})
|
|
289
454
|
.register(
|
|
@@ -437,11 +602,9 @@ API.Create('DeleteUser', {
|
|
|
437
602
|
|
|
438
603
|
const app = new HonoAPIAppBuilder({
|
|
439
604
|
pathPrefix: '/api',
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
}
|
|
444
|
-
return c.json({ error: error.message }, 500)
|
|
605
|
+
errors: appErrors, // taxonomy maps error classes to statuses/bodies; ProcedureValidationError → 400 by default
|
|
606
|
+
unknownError: {
|
|
607
|
+
toResponse: (err) => ({ error: (err as Error).message }),
|
|
445
608
|
},
|
|
446
609
|
})
|
|
447
610
|
.register(API, (c) => ({
|
|
@@ -649,7 +812,7 @@ import { DocRegistry } from 'ts-procedures/http-docs'
|
|
|
649
812
|
const docs = new DocRegistry({
|
|
650
813
|
basePath: '/api',
|
|
651
814
|
headers: [{ name: 'Authorization', description: 'Bearer token', required: false }],
|
|
652
|
-
errors:
|
|
815
|
+
errors: appErrors, // your ErrorTaxonomy — framework defaults auto-merged and deduped
|
|
653
816
|
})
|
|
654
817
|
.from(rpcBuilder)
|
|
655
818
|
.from(apiBuilder)
|
|
@@ -668,7 +831,7 @@ app.get('/docs', (c) => c.json(docs.toJSON({
|
|
|
668
831
|
**Key points:**
|
|
669
832
|
- `from()` stores a reference — register builders before or after `.build()`
|
|
670
833
|
- `toJSON()` reads docs lazily, so late-registered procedures are included
|
|
671
|
-
- `
|
|
834
|
+
- Pass your `ErrorTaxonomy` directly to `errors` — the constructor auto-merges framework defaults and dedupes; opt out with `includeDefaults: false`
|
|
672
835
|
- Accepts any object satisfying `{ readonly docs: AnyHttpRouteDoc[] }` — not limited to built-in builders
|
|
673
836
|
|
|
674
837
|
---
|
|
@@ -754,7 +917,7 @@ onRequestStart → factoryContext() → handler() → onSuccess → onRequestEnd
|
|
|
754
917
|
### Streaming (HonoStreamAppBuilder)
|
|
755
918
|
```
|
|
756
919
|
onRequestStart → factoryContext() → params validation
|
|
757
|
-
→
|
|
920
|
+
→ onError (if invalid) → onRequestEnd
|
|
758
921
|
→ onStreamStart → handler yields → onStreamEnd → onRequestEnd
|
|
759
922
|
→ onMidStreamError (if throw) → onStreamEnd → onRequestEnd
|
|
760
923
|
```
|
|
@@ -770,7 +933,7 @@ import { DocRegistry } from 'ts-procedures/http-docs'
|
|
|
770
933
|
const docs = new DocRegistry({
|
|
771
934
|
basePath: '/api',
|
|
772
935
|
headers: [{ name: 'Authorization', description: 'Bearer token' }],
|
|
773
|
-
errors:
|
|
936
|
+
errors: appErrors, // your ErrorTaxonomy — framework defaults auto-merged
|
|
774
937
|
})
|
|
775
938
|
.from(rpcBuilder)
|
|
776
939
|
.from(apiBuilder)
|
|
@@ -14,7 +14,7 @@ Parse `$ARGUMENTS` as a file or directory path. If a directory, review all `.ts`
|
|
|
14
14
|
## Instructions
|
|
15
15
|
|
|
16
16
|
1. Read the target file(s).
|
|
17
|
-
2. Identify ts-procedures imports (`ts-procedures`, `ts-procedures/express-rpc`, `ts-procedures/hono-rpc`, `ts-procedures/hono-stream`, `ts-procedures/hono-api`, `ts-procedures/http`, `ts-procedures/http-docs`) to determine file types.
|
|
17
|
+
2. Identify ts-procedures imports (`ts-procedures`, `ts-procedures/express-rpc`, `ts-procedures/hono-rpc`, `ts-procedures/hono-stream`, `ts-procedures/hono-api`, `ts-procedures/http`, `ts-procedures/http-docs`, `ts-procedures/http-errors`, `ts-procedures/client`, `ts-procedures/codegen`) to determine file types.
|
|
18
18
|
3. Check each file against the categorized checklist in [checklist.md](checklist.md).
|
|
19
19
|
4. For detailed code examples of each violation pattern, reference [anti-patterns.md](../ts-procedures/anti-patterns.md) — it shows 20 common mistakes with before/after code fixes and severity ratings.
|
|
20
20
|
5. Output findings grouped by severity.
|
|
@@ -62,26 +62,29 @@
|
|
|
62
62
|
### CRITICAL
|
|
63
63
|
- [ ] Standard `Create` procedures registered with `ExpressRPCAppBuilder` or `HonoRPCAppBuilder`, NOT `HonoStreamAppBuilder`
|
|
64
64
|
- [ ] Stream `CreateStream` procedures registered with `HonoStreamAppBuilder`, NOT the RPC builders
|
|
65
|
-
- [ ] `
|
|
65
|
+
- [ ] Custom error classes are registered via `defineErrorTaxonomy` + builder's `errors` config — NOT hand-written `onError` instanceof ladders (anti-pattern #20)
|
|
66
|
+
- [ ] If a handler throws a non-`ProcedureError`, its class is either in the taxonomy or intentionally caught by `unknownError`
|
|
66
67
|
|
|
67
68
|
### WARNING
|
|
68
69
|
- [ ] `pathPrefix` set consistently across builders
|
|
69
70
|
- [ ] `scope` and `version` set on all procedures when using `RPCConfig`
|
|
70
71
|
- [ ] Uses `extendProcedureDoc` for documentation generation instead of manual doc building
|
|
71
72
|
- [ ] `onRequestEnd` used for cleanup/logging, not business logic
|
|
73
|
+
- [ ] `RPCConfig<keyof typeof appErrors & string>` used when the app wants compile-time typo protection on per-route `errors: [...]`
|
|
72
74
|
|
|
73
75
|
### SUGGESTION
|
|
74
76
|
- [ ] Route documentation accessed via `builder.docs` for OpenAPI generation
|
|
75
|
-
- [ ] Uses `DocRegistry` to compose docs from multiple builders
|
|
77
|
+
- [ ] Uses `new DocRegistry({ errors: appErrors })` to compose docs from multiple builders
|
|
76
78
|
- [ ] Lifecycle hooks used for observability (logging, metrics)
|
|
79
|
+
- [ ] Per-route `errors: [...]` declared so generated clients can narrow `catch` types
|
|
77
80
|
|
|
78
81
|
---
|
|
79
82
|
|
|
80
83
|
## Streaming Checks (HonoStreamAppBuilder)
|
|
81
84
|
|
|
82
85
|
### CRITICAL
|
|
83
|
-
- [ ] `
|
|
84
|
-
- [ ] `onMidStreamError` handles runtime errors during streaming (yields error event)
|
|
86
|
+
- [ ] Pre-stream errors handled via either peer mode — `errors` + `unknownError` taxonomy OR `onError` callback (the hono-stream `onPreStreamError` was renamed to `onError` in v6)
|
|
87
|
+
- [ ] `onMidStreamError` handles runtime errors during streaming (yields error event) — mid-stream is NOT covered by the taxonomy since HTTP status is already committed
|
|
85
88
|
- [ ] Stream handler does not assume `signal.reason` is always `'stream-completed'` — check for external abort
|
|
86
89
|
|
|
87
90
|
### WARNING
|
|
@@ -98,20 +101,21 @@
|
|
|
98
101
|
## API Builder Checks (HonoAPIAppBuilder)
|
|
99
102
|
|
|
100
103
|
### CRITICAL
|
|
101
|
-
- [ ] `build()` is
|
|
104
|
+
- [ ] `build()` is called synchronously — it returns `Hono`, NOT `Promise<Hono>`. Do not `await` it.
|
|
102
105
|
- [ ] Path param names in route template match `schema.input.pathParams` property names exactly
|
|
103
106
|
- [ ] Does not define both `schema.params` and `schema.input` on the same procedure
|
|
104
|
-
- [ ] `
|
|
107
|
+
- [ ] Custom error classes registered via `defineErrorTaxonomy` + builder `errors` config — not hand-written `onError` ladders
|
|
105
108
|
|
|
106
109
|
### WARNING
|
|
107
110
|
- [ ] Uses `schema.input` channels appropriate for the HTTP method (no `body` on GET/HEAD)
|
|
108
111
|
- [ ] `pathPrefix` set consistently
|
|
109
112
|
- [ ] `successStatus` overridden only when default (POST→201, DELETE→204) is wrong
|
|
110
113
|
- [ ] Uses `APIInput` type constraint (`satisfies APIInput`) to catch channel name typos
|
|
114
|
+
- [ ] `APIConfig<keyof typeof appErrors & string>` used when the app wants compile-time typo protection on per-route `errors: [...]`
|
|
111
115
|
|
|
112
116
|
### SUGGESTION
|
|
113
117
|
- [ ] Route documentation accessed via `builder.docs` for OpenAPI generation
|
|
114
|
-
- [ ] Uses `DocRegistry` to compose docs across builders instead of manual assembly
|
|
118
|
+
- [ ] Uses `new DocRegistry({ errors: appErrors })` to compose docs across builders instead of manual assembly
|
|
115
119
|
- [ ] Custom `queryParser` provided if complex query string formats needed
|
|
116
120
|
- [ ] Lifecycle hooks used for observability (logging, metrics)
|
|
117
121
|
|
|
@@ -120,18 +124,22 @@
|
|
|
120
124
|
## Error Handling Checks
|
|
121
125
|
|
|
122
126
|
### CRITICAL
|
|
123
|
-
- [ ] Handler errors use `ctx.error(message, meta?)` —
|
|
127
|
+
- [ ] Handler errors use `ctx.error(message, meta?)` OR throw a class registered in the app's `defineErrorTaxonomy` — never `throw new Error()` directly
|
|
124
128
|
- [ ] Does not catch and swallow errors without re-throwing (hides failures from caller)
|
|
125
|
-
- [ ] HTTP builder has `onError` callback — default
|
|
129
|
+
- [ ] HTTP builder has either an `errors` taxonomy or an `onError` callback configured — both are first-class peer modes; leaving both off uses the hard default `{ error: message }` 500, which may expose internal details
|
|
130
|
+
- [ ] `defineErrorTaxonomy` entries use `class:` for `instanceof` dispatch or `match:` for 3rd-party errors (e.g. MongoServerError) — never both on the same entry
|
|
126
131
|
|
|
127
132
|
### WARNING
|
|
128
|
-
- [ ] `ProcedureValidationError`
|
|
133
|
+
- [ ] Framework errors (`ProcedureValidationError` → 400, `ProcedureError` → 500) are covered by the default taxonomy automatically; don't re-declare them unless overriding
|
|
134
|
+
- [ ] `toResponse` output either includes a `name` field or lets the resolver auto-inject `{ name: key }` — required for typed client dispatch
|
|
129
135
|
- [ ] Error `meta` object contains useful context (error codes, IDs) — not sensitive data
|
|
136
|
+
- [ ] Stack traces and `internalMsg`-style fields are kept server-side via `onCatch`, never included in `toResponse` output
|
|
130
137
|
- [ ] Stack traces not exposed in production responses
|
|
131
138
|
|
|
132
139
|
### SUGGESTION
|
|
133
|
-
- [ ] Consistent error response shape across all procedures
|
|
134
|
-
- [ ] Error codes documented for API consumers
|
|
140
|
+
- [ ] Consistent error response shape across all procedures (guaranteed by the taxonomy's `name` field)
|
|
141
|
+
- [ ] Error codes documented for API consumers (feed `description` and `schema` on each taxonomy entry)
|
|
142
|
+
- [ ] Per-route `errors: [...]` declared on each procedure config so the generated client gets narrowed `Errors` union types
|
|
135
143
|
|
|
136
144
|
---
|
|
137
145
|
|
|
@@ -38,7 +38,8 @@ If either argument is missing, ask the user for `<type>` and `<Name>`.
|
|
|
38
38
|
|
|
39
39
|
## Rules
|
|
40
40
|
|
|
41
|
-
- Use `ctx.error()` for business
|
|
41
|
+
- Use `ctx.error()` for ad-hoc business errors; for structured errors with status codes and typed client dispatch, throw custom error classes and register them via `defineErrorTaxonomy` in the builder's `errors` config (see the HTTP builder templates).
|
|
42
|
+
- Never throw raw `Error` — either use `ctx.error()` or a typed class registered in the taxonomy.
|
|
42
43
|
- `schema.params` is validated at runtime; `schema.returnType` is documentation only.
|
|
43
44
|
- Pass `ctx.signal` to all downstream async calls.
|
|
44
45
|
- Stream handlers always have `ctx.signal` (guaranteed `AbortSignal`).
|
|
@@ -3,14 +3,13 @@
|
|
|
3
3
|
## Implementation — `{{Name}}.client.ts`
|
|
4
4
|
|
|
5
5
|
```typescript
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
// With --service-name {{Name}}: import { create{{Name}}
|
|
6
|
+
import { createFetchAdapter } from 'ts-procedures/client'
|
|
7
|
+
import { createApiClient, ApiErrors } from './generated/api'
|
|
8
|
+
// With --service-name {{Name}}: import { create{{Name}}Client, {{Name}}Errors } from './generated/api'
|
|
9
9
|
|
|
10
10
|
// --- Optional: type per-request meta end-to-end via declaration merging ---
|
|
11
|
-
// Self-contained clients augment './generated/_types'
|
|
12
|
-
|
|
13
|
-
declare module 'ts-procedures/client' {
|
|
11
|
+
// Self-contained clients (the default) augment './generated/_types':
|
|
12
|
+
declare module './generated/_types' {
|
|
14
13
|
interface RequestMeta {
|
|
15
14
|
// TODO: add your fields — typed in options.meta, ctx.request.meta, adapter req.meta
|
|
16
15
|
// traceId?: string
|
|
@@ -18,12 +17,12 @@ declare module 'ts-procedures/client' {
|
|
|
18
17
|
}
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
// Create the typed client
|
|
22
|
-
|
|
20
|
+
// Create the typed client — createApiClient wires the error registry
|
|
21
|
+
// automatically so non-2xx responses arrive as typed class instances you can
|
|
22
|
+
// catch with `instanceof ApiErrors.<ErrorName>`.
|
|
23
|
+
export const {{name}}Client = createApiClient({
|
|
23
24
|
adapter: createFetchAdapter(),
|
|
24
25
|
basePath: 'http://localhost:3000', // TODO: configure base URL
|
|
25
|
-
scopes: createApiBindings,
|
|
26
|
-
// With --service-name {{Name}}: scopes: create{{Name}}Bindings,
|
|
27
26
|
defaults: {
|
|
28
27
|
// TODO: set client-wide defaults (overridden by per-call options)
|
|
29
28
|
// timeout: 30_000,
|
|
@@ -45,6 +44,15 @@ export const {{name}}Client = createClient({
|
|
|
45
44
|
},
|
|
46
45
|
})
|
|
47
46
|
|
|
47
|
+
// --- Typed error handling ---
|
|
48
|
+
// try {
|
|
49
|
+
// const user = await {{name}}Client.users.GetUser({ pathParams: { id: '123' } })
|
|
50
|
+
// } catch (err) {
|
|
51
|
+
// if (err instanceof ApiErrors.UseCaseError) { /* err.body typed; err.status, procedureName, scope */ }
|
|
52
|
+
// else if (err instanceof ApiErrors.ApiProcedureError) { /* catch-all for service errors */ }
|
|
53
|
+
// else { /* transport error — ClientRequestError */ }
|
|
54
|
+
// }
|
|
55
|
+
|
|
48
56
|
// --- RPC call example ---
|
|
49
57
|
// const user = await {{name}}Client.users.GetUser({ userId: '123' })
|
|
50
58
|
|
|
@@ -105,11 +113,10 @@ describe('{{Name}} Client', () => {
|
|
|
105
113
|
// expect(result.id).toBe('test-id')
|
|
106
114
|
})
|
|
107
115
|
|
|
108
|
-
test('
|
|
109
|
-
// TODO: call with invalid params and verify error
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
// ).rejects.toThrow()
|
|
116
|
+
test('dispatches typed errors from the server via the registry', async () => {
|
|
117
|
+
// TODO: call with invalid params and verify typed error dispatch
|
|
118
|
+
// const call = {{name}}Client.users.GetUser({} as any)
|
|
119
|
+
// await expect(call).rejects.toBeInstanceOf(ApiErrors.ProcedureValidationError)
|
|
113
120
|
})
|
|
114
121
|
|
|
115
122
|
test('streams data with typed yields', async () => {
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
## Implementation — `{{Name}}.rpc.ts`
|
|
4
4
|
|
|
5
5
|
```typescript
|
|
6
|
-
import { Procedures
|
|
6
|
+
import { Procedures } from 'ts-procedures'
|
|
7
|
+
import { defineErrorTaxonomy } from 'ts-procedures/express-rpc'
|
|
7
8
|
import { ExpressRPCAppBuilder } from 'ts-procedures/express-rpc'
|
|
8
9
|
import type { RPCConfig } from 'ts-procedures/http'
|
|
9
10
|
import { Type } from 'typebox'
|
|
@@ -60,26 +61,28 @@ export const { ListItems } = RPC.Create(
|
|
|
60
61
|
}
|
|
61
62
|
)
|
|
62
63
|
|
|
64
|
+
// ─── Error Taxonomy ───────────────────────────────────────
|
|
65
|
+
// Declare the error classes this service throws. Framework errors
|
|
66
|
+
// (ProcedureValidationError → 400, ctx.error() → 500) are caught by the
|
|
67
|
+
// default taxonomy automatically. Add your own classes here — handlers just
|
|
68
|
+
// `throw` them and the builder serializes via this map. See
|
|
69
|
+
// docs/http-integrations.md#error-handling for the full contract.
|
|
70
|
+
|
|
71
|
+
const {{name}}Errors = defineErrorTaxonomy({
|
|
72
|
+
// Example — replace with your app's error classes:
|
|
73
|
+
// NotFoundError: { class: NotFoundError, statusCode: 404 },
|
|
74
|
+
// AuthError: { class: AuthError, statusCode: 401 },
|
|
75
|
+
})
|
|
76
|
+
|
|
63
77
|
// ─── Express App Builder ──────────────────────────────────
|
|
64
78
|
|
|
65
79
|
export const {{name}}App = new ExpressRPCAppBuilder({
|
|
66
80
|
pathPrefix: '/api',
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
procedure: error.procedureName,
|
|
73
|
-
})
|
|
74
|
-
} else if (error instanceof ProcedureError) {
|
|
75
|
-
res.status(422).json({
|
|
76
|
-
error: error.message,
|
|
77
|
-
meta: error.meta,
|
|
78
|
-
procedure: error.procedureName,
|
|
79
|
-
})
|
|
80
|
-
} else {
|
|
81
|
-
res.status(500).json({ error: 'Internal server error' })
|
|
82
|
-
}
|
|
81
|
+
errors: {{name}}Errors,
|
|
82
|
+
unknownError: {
|
|
83
|
+
statusCode: 500,
|
|
84
|
+
toResponse: () => ({ name: 'InternalServerError', message: 'Unexpected error' }),
|
|
85
|
+
onCatch: (err, { procedure }) => console.error(`[${procedure.name}]`, err),
|
|
83
86
|
},
|
|
84
87
|
})
|
|
85
88
|
.register(RPC, async (req) => ({
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
## Implementation — `{{Name}}.api.ts`
|
|
4
4
|
|
|
5
5
|
```typescript
|
|
6
|
-
import { Procedures
|
|
6
|
+
import { Procedures } from 'ts-procedures'
|
|
7
|
+
import { defineErrorTaxonomy } from 'ts-procedures/hono-api'
|
|
7
8
|
import { HonoAPIAppBuilder } from 'ts-procedures/hono-api'
|
|
8
9
|
import type { APIConfig, APIInput } from 'ts-procedures/http'
|
|
9
10
|
import { Type } from 'typebox'
|
|
@@ -81,25 +82,28 @@ export const { DeleteItem } = API.Create(
|
|
|
81
82
|
}
|
|
82
83
|
)
|
|
83
84
|
|
|
85
|
+
// ─── Error Taxonomy ───────────────────────────────────────
|
|
86
|
+
// Declare the error classes this service throws. Framework errors
|
|
87
|
+
// (ProcedureValidationError → 400, ctx.error() → 500) are caught by the
|
|
88
|
+
// default taxonomy automatically. Add your own classes here — handlers just
|
|
89
|
+
// `throw` them and the builder serializes via this map. See
|
|
90
|
+
// docs/http-integrations.md#error-handling for the full contract.
|
|
91
|
+
|
|
92
|
+
const {{name}}Errors = defineErrorTaxonomy({
|
|
93
|
+
// Example — replace with your app's error classes:
|
|
94
|
+
// NotFoundError: { class: NotFoundError, statusCode: 404 },
|
|
95
|
+
// AuthError: { class: AuthError, statusCode: 401 },
|
|
96
|
+
})
|
|
97
|
+
|
|
84
98
|
// ─── Hono App Builder ─────────────────────────────────────
|
|
85
99
|
|
|
86
100
|
export const {{name}}App = new HonoAPIAppBuilder({
|
|
87
101
|
pathPrefix: '/api',
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
procedure: error.procedureName,
|
|
94
|
-
}, 400)
|
|
95
|
-
} else if (error instanceof ProcedureError) {
|
|
96
|
-
return c.json({
|
|
97
|
-
error: error.message,
|
|
98
|
-
meta: error.meta,
|
|
99
|
-
procedure: error.procedureName,
|
|
100
|
-
}, 422)
|
|
101
|
-
}
|
|
102
|
-
return c.json({ error: 'Internal server error' }, 500)
|
|
102
|
+
errors: {{name}}Errors,
|
|
103
|
+
unknownError: {
|
|
104
|
+
statusCode: 500,
|
|
105
|
+
toResponse: () => ({ name: 'InternalServerError', message: 'Unexpected error' }),
|
|
106
|
+
onCatch: (err, { procedure }) => console.error(`[${procedure.name}]`, err),
|
|
103
107
|
},
|
|
104
108
|
})
|
|
105
109
|
.register(API, (c) => ({
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
## Implementation — `{{Name}}.rpc.ts`
|
|
4
4
|
|
|
5
5
|
```typescript
|
|
6
|
-
import { Procedures
|
|
7
|
-
import { HonoRPCAppBuilder } from 'ts-procedures/hono-rpc'
|
|
6
|
+
import { Procedures } from 'ts-procedures'
|
|
7
|
+
import { HonoRPCAppBuilder, defineErrorTaxonomy } from 'ts-procedures/hono-rpc'
|
|
8
8
|
import type { RPCConfig } from 'ts-procedures/http'
|
|
9
9
|
import { Type } from 'typebox'
|
|
10
10
|
|
|
@@ -60,25 +60,28 @@ export const { ListItems } = RPC.Create(
|
|
|
60
60
|
}
|
|
61
61
|
)
|
|
62
62
|
|
|
63
|
+
// ─── Error Taxonomy ───────────────────────────────────────
|
|
64
|
+
// Declare the error classes this service throws. Framework errors
|
|
65
|
+
// (ProcedureValidationError → 400, ctx.error() → 500) are caught by the
|
|
66
|
+
// default taxonomy automatically. Add your own classes here — handlers just
|
|
67
|
+
// `throw` them and the builder serializes via this map. See
|
|
68
|
+
// docs/http-integrations.md#error-handling for the full contract.
|
|
69
|
+
|
|
70
|
+
const {{name}}Errors = defineErrorTaxonomy({
|
|
71
|
+
// Example — replace with your app's error classes:
|
|
72
|
+
// NotFoundError: { class: NotFoundError, statusCode: 404 },
|
|
73
|
+
// AuthError: { class: AuthError, statusCode: 401 },
|
|
74
|
+
})
|
|
75
|
+
|
|
63
76
|
// ─── Hono App Builder ─────────────────────────────────────
|
|
64
77
|
|
|
65
78
|
export const {{name}}App = new HonoRPCAppBuilder({
|
|
66
79
|
pathPrefix: '/api',
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
procedure: error.procedureName,
|
|
73
|
-
}, 400)
|
|
74
|
-
} else if (error instanceof ProcedureError) {
|
|
75
|
-
return c.json({
|
|
76
|
-
error: error.message,
|
|
77
|
-
meta: error.meta,
|
|
78
|
-
procedure: error.procedureName,
|
|
79
|
-
}, 422)
|
|
80
|
-
}
|
|
81
|
-
return c.json({ error: 'Internal server error' }, 500)
|
|
80
|
+
errors: {{name}}Errors,
|
|
81
|
+
unknownError: {
|
|
82
|
+
statusCode: 500,
|
|
83
|
+
toResponse: () => ({ name: 'InternalServerError', message: 'Unexpected error' }),
|
|
84
|
+
onCatch: (err, { procedure }) => console.error(`[${procedure.name}]`, err),
|
|
82
85
|
},
|
|
83
86
|
})
|
|
84
87
|
.register(RPC, (c) => ({
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
```typescript
|
|
6
6
|
import { Procedures } from 'ts-procedures'
|
|
7
|
-
import { HonoStreamAppBuilder, sse } from 'ts-procedures/hono-stream'
|
|
7
|
+
import { HonoStreamAppBuilder, sse, defineErrorTaxonomy } from 'ts-procedures/hono-stream'
|
|
8
8
|
import type { RPCConfig } from 'ts-procedures/http'
|
|
9
9
|
import { Type } from 'typebox'
|
|
10
10
|
|
|
@@ -60,13 +60,26 @@ async function pollForEvents(channel: string, opts?: { signal?: AbortSignal }) {
|
|
|
60
60
|
return { type: 'update', data: { value: Math.random() } }
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
// ─── Error Taxonomy (pre-stream) ──────────────────────────
|
|
64
|
+
// Pre-stream errors (validation, context resolution) flow through the
|
|
65
|
+
// taxonomy — framework errors are caught by the default taxonomy automatically.
|
|
66
|
+
// Mid-stream errors (thrown after the first yield) still go through
|
|
67
|
+
// onMidStreamError since the HTTP status is already committed.
|
|
68
|
+
|
|
69
|
+
const {{name}}Errors = defineErrorTaxonomy({
|
|
70
|
+
// Example — replace with your app's error classes:
|
|
71
|
+
// AuthError: { class: AuthError, statusCode: 401 },
|
|
72
|
+
})
|
|
73
|
+
|
|
63
74
|
// ─── Hono Stream App Builder ──────────────────────────────
|
|
64
75
|
|
|
65
76
|
export const {{name}}StreamApp = new HonoStreamAppBuilder({
|
|
66
77
|
pathPrefix: '/api',
|
|
67
78
|
defaultStreamMode: 'sse', // or 'text' for newline-delimited JSON
|
|
68
|
-
|
|
69
|
-
|
|
79
|
+
errors: {{name}}Errors,
|
|
80
|
+
unknownError: {
|
|
81
|
+
statusCode: 500,
|
|
82
|
+
toResponse: () => ({ name: 'InternalServerError', message: 'Unexpected error' }),
|
|
70
83
|
},
|
|
71
84
|
onMidStreamError: (procedure, c, error) => ({
|
|
72
85
|
data: { type: 'error', payload: { message: error.message }, timestamp: Date.now() },
|