ts-procedures 7.2.0 → 8.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 +65 -3
- package/agent_config/claude-code/agents/ts-procedures-architect.md +6 -8
- package/agent_config/claude-code/skills/ts-procedures/SKILL.md +30 -33
- package/agent_config/claude-code/skills/ts-procedures/anti-patterns.md +139 -53
- package/agent_config/claude-code/skills/ts-procedures/api-reference.md +208 -231
- package/agent_config/claude-code/skills/ts-procedures/patterns.md +80 -153
- package/agent_config/claude-code/skills/ts-procedures-review/SKILL.md +1 -1
- package/agent_config/claude-code/skills/ts-procedures-review/checklist.md +4 -5
- package/agent_config/claude-code/skills/ts-procedures-scaffold/SKILL.md +4 -7
- package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono.md +223 -0
- package/agent_config/copilot/copilot-instructions.md +36 -48
- package/agent_config/cursor/cursorrules +36 -48
- package/build/client/call.js +4 -1
- package/build/client/call.js.map +1 -1
- package/build/client/call.test.js +23 -0
- package/build/client/call.test.js.map +1 -1
- package/build/client/fetch-adapter.js +3 -1
- package/build/client/fetch-adapter.js.map +1 -1
- package/build/client/fetch-adapter.test.js +11 -1
- package/build/client/fetch-adapter.test.js.map +1 -1
- package/build/client/index.test.js +7 -7
- package/build/client/index.test.js.map +1 -1
- package/build/client/request-builder.d.ts +1 -1
- package/build/client/request-builder.js +2 -2
- package/build/client/request-builder.js.map +1 -1
- package/build/client/stream.js +13 -2
- package/build/client/stream.js.map +1 -1
- package/build/client/stream.test.js +32 -7
- package/build/client/stream.test.js.map +1 -1
- package/build/client/typed-error-dispatch.test.js +8 -92
- package/build/client/typed-error-dispatch.test.js.map +1 -1
- package/build/client/types.d.ts +21 -3
- package/build/codegen/bin/cli.js +0 -0
- package/build/codegen/e2e.test.js +87 -23
- package/build/codegen/e2e.test.js.map +1 -1
- package/build/codegen/emit-errors.integration.test.js +1 -1
- package/build/codegen/emit-errors.integration.test.js.map +1 -1
- package/build/codegen/emit-scope.js +308 -47
- package/build/codegen/emit-scope.js.map +1 -1
- package/build/codegen/emit-scope.test.js +363 -110
- package/build/codegen/emit-scope.test.js.map +1 -1
- package/build/codegen/pipeline.test.js +7 -7
- package/build/codegen/pipeline.test.js.map +1 -1
- package/build/codegen/resolve-envelope.js +1 -1
- package/build/codegen/resolve-envelope.js.map +1 -1
- package/build/codegen/resolve-envelope.test.js +5 -5
- package/build/codegen/resolve-envelope.test.js.map +1 -1
- package/build/codegen/targets/_shared/route-slots.d.ts +8 -3
- package/build/codegen/targets/_shared/route-slots.js +49 -8
- package/build/codegen/targets/_shared/route-slots.js.map +1 -1
- package/build/codegen/targets/_shared/route-slots.test.js +99 -26
- package/build/codegen/targets/_shared/route-slots.test.js.map +1 -1
- package/build/codegen/targets/kotlin/emit-route-kotlin.test.js +88 -17
- package/build/codegen/targets/kotlin/emit-route-kotlin.test.js.map +1 -1
- package/build/codegen/targets/kotlin/emit-scope-kotlin.test.js +9 -6
- package/build/codegen/targets/kotlin/emit-scope-kotlin.test.js.map +1 -1
- package/build/codegen/targets/kotlin/integration.test.js +6 -0
- package/build/codegen/targets/kotlin/integration.test.js.map +1 -1
- package/build/codegen/targets/swift/access-level.test.js +8 -11
- package/build/codegen/targets/swift/access-level.test.js.map +1 -1
- package/build/codegen/targets/swift/emit-route-swift.test.js +91 -20
- package/build/codegen/targets/swift/emit-route-swift.test.js.map +1 -1
- package/build/codegen/targets/swift/emit-scope-swift.test.js +12 -9
- package/build/codegen/targets/swift/emit-scope-swift.test.js.map +1 -1
- package/build/codegen/targets/swift/integration.test.js +6 -0
- package/build/codegen/targets/swift/integration.test.js.map +1 -1
- package/build/create-http-stream.d.ts +58 -0
- package/build/create-http-stream.js +122 -0
- package/build/create-http-stream.js.map +1 -0
- package/build/create-http-stream.test.js +88 -0
- package/build/create-http-stream.test.js.map +1 -0
- package/build/create-http.d.ts +49 -0
- package/build/create-http.js +108 -0
- package/build/create-http.js.map +1 -0
- package/build/create-http.test.js +137 -0
- package/build/create-http.test.js.map +1 -0
- package/build/create-stream.d.ts +35 -0
- package/build/create-stream.js +123 -0
- package/build/create-stream.js.map +1 -0
- package/build/create-stream.test.js +428 -0
- package/build/create-stream.test.js.map +1 -0
- package/build/create.d.ts +28 -0
- package/build/create.js +82 -0
- package/build/create.js.map +1 -0
- package/build/create.test.js +483 -0
- package/build/create.test.js.map +1 -0
- package/build/exports.d.ts +2 -0
- package/build/implementations/http/astro/index.test.js +20 -12
- package/build/implementations/http/astro/index.test.js.map +1 -1
- package/build/implementations/http/doc-registry.js +1 -1
- package/build/implementations/http/doc-registry.js.map +1 -1
- package/build/implementations/http/doc-registry.test.js +36 -5
- package/build/implementations/http/doc-registry.test.js.map +1 -1
- package/build/implementations/http/error-dispatch.d.ts +76 -0
- package/build/implementations/http/error-dispatch.js +77 -0
- package/build/implementations/http/error-dispatch.js.map +1 -0
- package/build/implementations/http/error-dispatch.test.js +254 -0
- package/build/implementations/http/error-dispatch.test.js.map +1 -0
- package/build/implementations/http/error-taxonomy.d.ts +5 -5
- package/build/implementations/http/hono/docs/http-doc.d.ts +6 -0
- package/build/implementations/http/hono/docs/http-doc.js +42 -0
- package/build/implementations/http/hono/docs/http-doc.js.map +1 -0
- package/build/implementations/http/hono/docs/http-stream-doc.d.ts +6 -0
- package/build/implementations/http/hono/docs/http-stream-doc.js +40 -0
- package/build/implementations/http/hono/docs/http-stream-doc.js.map +1 -0
- package/build/implementations/http/hono/docs/rpc-doc.d.ts +6 -0
- package/build/implementations/http/hono/docs/rpc-doc.js +24 -0
- package/build/implementations/http/hono/docs/rpc-doc.js.map +1 -0
- package/build/implementations/http/hono/docs/stream-doc.d.ts +6 -0
- package/build/implementations/http/hono/docs/stream-doc.js +42 -0
- package/build/implementations/http/hono/docs/stream-doc.js.map +1 -0
- package/build/implementations/http/hono/handlers/http-stream.d.ts +10 -0
- package/build/implementations/http/hono/handlers/http-stream.js +123 -0
- package/build/implementations/http/hono/handlers/http-stream.js.map +1 -0
- package/build/implementations/http/hono/handlers/http-stream.test.js +128 -0
- package/build/implementations/http/hono/handlers/http-stream.test.js.map +1 -0
- package/build/implementations/http/hono/handlers/http.d.ts +10 -0
- package/build/implementations/http/hono/handlers/http.js +115 -0
- package/build/implementations/http/hono/handlers/http.js.map +1 -0
- package/build/implementations/http/hono/handlers/http.test.js +118 -0
- package/build/implementations/http/hono/handlers/http.test.js.map +1 -0
- package/build/implementations/http/hono/handlers/rpc.d.ts +11 -0
- package/build/implementations/http/hono/handlers/rpc.js +32 -0
- package/build/implementations/http/hono/handlers/rpc.js.map +1 -0
- package/build/implementations/http/hono/handlers/rpc.test.js +73 -0
- package/build/implementations/http/hono/handlers/rpc.test.js.map +1 -0
- package/build/implementations/http/hono/handlers/stream.d.ts +23 -0
- package/build/implementations/http/hono/handlers/stream.js +147 -0
- package/build/implementations/http/hono/handlers/stream.js.map +1 -0
- package/build/implementations/http/hono/handlers/stream.test.d.ts +1 -0
- package/build/implementations/http/hono/handlers/stream.test.js +177 -0
- package/build/implementations/http/hono/handlers/stream.test.js.map +1 -0
- package/build/implementations/http/hono/index.d.ts +57 -0
- package/build/implementations/http/hono/index.js +149 -0
- package/build/implementations/http/hono/index.js.map +1 -0
- package/build/implementations/http/hono/index.test.d.ts +1 -0
- package/build/implementations/http/hono/index.test.js +274 -0
- package/build/implementations/http/hono/index.test.js.map +1 -0
- package/build/implementations/http/hono/path.d.ts +17 -0
- package/build/implementations/http/hono/path.js +39 -0
- package/build/implementations/http/hono/path.js.map +1 -0
- package/build/implementations/http/hono/path.test.d.ts +1 -0
- package/build/implementations/http/hono/path.test.js +83 -0
- package/build/implementations/http/hono/path.test.js.map +1 -0
- package/build/implementations/http/hono/types.d.ts +51 -0
- package/build/implementations/http/hono/types.js.map +1 -0
- package/build/implementations/http/on-request-error.test.js +6 -96
- package/build/implementations/http/on-request-error.test.js.map +1 -1
- package/build/implementations/http/route-errors.test.js +11 -59
- package/build/implementations/http/route-errors.test.js.map +1 -1
- package/build/implementations/types.d.ts +43 -9
- package/build/index.d.ts +125 -115
- package/build/index.js +10 -222
- package/build/index.js.map +1 -1
- package/build/index.test.js +30 -822
- package/build/index.test.js.map +1 -1
- package/build/migration.test.d.ts +1 -0
- package/build/migration.test.js +34 -0
- package/build/migration.test.js.map +1 -0
- package/build/schema/compute-schema.d.ts +11 -3
- package/build/schema/compute-schema.js +13 -7
- package/build/schema/compute-schema.js.map +1 -1
- package/build/schema/parser.d.ts +11 -3
- package/build/schema/parser.js +49 -9
- package/build/schema/parser.js.map +1 -1
- package/build/stack-utils.js +8 -0
- package/build/stack-utils.js.map +1 -1
- package/build/types.d.ts +142 -0
- package/build/types.js.map +1 -0
- package/docs/astro-adapter.md +5 -5
- package/docs/core.md +34 -17
- package/docs/http-integrations.md +83 -170
- package/docs/streaming.md +3 -60
- package/docs/superpowers/plans/2026-05-07-astro-adapter.md +2 -7
- package/docs/superpowers/plans/2026-05-08-create-http.md +3355 -0
- package/docs/superpowers/plans/2026-05-08-hono-app-builder-convergence.md +3365 -0
- package/docs/superpowers/specs/2026-05-07-astro-adapter-design.md +1 -3
- package/docs/superpowers/specs/2026-05-08-create-http-design.md +409 -0
- package/docs/superpowers/specs/2026-05-08-hono-app-builder-convergence-design.md +411 -0
- package/package.json +4 -22
- package/src/client/call.test.ts +26 -0
- package/src/client/call.ts +4 -1
- package/src/client/fetch-adapter.test.ts +14 -1
- package/src/client/fetch-adapter.ts +3 -1
- package/src/client/index.test.ts +7 -7
- package/src/client/request-builder.ts +2 -2
- package/src/client/stream.test.ts +39 -7
- package/src/client/stream.ts +16 -2
- package/src/client/typed-error-dispatch.test.ts +7 -97
- package/src/client/types.ts +21 -3
- package/src/codegen/__fixtures__/users-envelope.json +119 -38
- package/src/codegen/e2e.test.ts +98 -24
- package/src/codegen/emit-errors.integration.test.ts +1 -1
- package/src/codegen/emit-scope.test.ts +395 -110
- package/src/codegen/emit-scope.ts +350 -55
- package/src/codegen/pipeline.test.ts +7 -7
- package/src/codegen/resolve-envelope.test.ts +5 -5
- package/src/codegen/resolve-envelope.ts +1 -1
- package/src/codegen/targets/_shared/route-slots.test.ts +109 -26
- package/src/codegen/targets/_shared/route-slots.ts +48 -11
- package/src/codegen/targets/kotlin/__fixtures__/users-golden.kt +73 -0
- package/src/codegen/targets/kotlin/emit-route-kotlin.test.ts +100 -17
- package/src/codegen/targets/kotlin/emit-scope-kotlin.test.ts +9 -6
- package/src/codegen/targets/kotlin/integration.test.ts +19 -0
- package/src/codegen/targets/swift/__fixtures__/users-golden.swift +79 -0
- package/src/codegen/targets/swift/access-level.test.ts +8 -11
- package/src/codegen/targets/swift/emit-route-swift.test.ts +103 -20
- package/src/codegen/targets/swift/emit-scope-swift.test.ts +12 -9
- package/src/codegen/targets/swift/integration.test.ts +17 -0
- package/src/create-http-stream.test.ts +97 -0
- package/src/create-http-stream.ts +191 -0
- package/src/create-http.test.ts +163 -0
- package/src/create-http.ts +211 -0
- package/src/create-stream.test.ts +565 -0
- package/src/create-stream.ts +228 -0
- package/src/create.test.ts +658 -0
- package/src/create.ts +172 -0
- package/src/exports.ts +2 -0
- package/src/implementations/http/README.md +135 -95
- package/src/implementations/http/astro/README.md +4 -5
- package/src/implementations/http/astro/index.test.ts +25 -18
- package/src/implementations/http/doc-registry.test.ts +42 -5
- package/src/implementations/http/doc-registry.ts +1 -1
- package/src/implementations/http/error-dispatch.test.ts +283 -0
- package/src/implementations/http/error-dispatch.ts +176 -0
- package/src/implementations/http/error-taxonomy.ts +5 -5
- package/src/implementations/http/hono/docs/http-doc.ts +43 -0
- package/src/implementations/http/hono/docs/http-stream-doc.ts +44 -0
- package/src/implementations/http/hono/docs/rpc-doc.ts +34 -0
- package/src/implementations/http/hono/docs/stream-doc.ts +53 -0
- package/src/implementations/http/hono/handlers/http-stream.test.ts +150 -0
- package/src/implementations/http/hono/handlers/http-stream.ts +152 -0
- package/src/implementations/http/hono/handlers/http.test.ts +130 -0
- package/src/implementations/http/hono/handlers/http.ts +147 -0
- package/src/implementations/http/hono/handlers/rpc.test.ts +81 -0
- package/src/implementations/http/hono/handlers/rpc.ts +54 -0
- package/src/implementations/http/hono/handlers/stream.test.ts +198 -0
- package/src/implementations/http/hono/handlers/stream.ts +208 -0
- package/src/implementations/http/hono/index.test.ts +329 -0
- package/src/implementations/http/hono/index.ts +204 -0
- package/src/implementations/http/hono/path.test.ts +96 -0
- package/src/implementations/http/hono/path.ts +59 -0
- package/src/implementations/http/hono/types.ts +93 -0
- package/src/implementations/http/on-request-error.test.ts +10 -116
- package/src/implementations/http/route-errors.test.ts +11 -77
- package/src/implementations/types.ts +44 -9
- package/src/index.test.ts +35 -1091
- package/src/index.ts +50 -474
- package/src/migration.test.ts +48 -0
- package/src/schema/compute-schema.ts +26 -12
- package/src/schema/parser.ts +62 -12
- package/src/stack-utils.ts +8 -0
- package/src/types.ts +133 -0
- package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/express-rpc.md +0 -137
- package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono-api.md +0 -173
- package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono-rpc.md +0 -142
- package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono-stream.md +0 -147
- package/build/implementations/http/express-rpc/error-taxonomy.test.js +0 -83
- package/build/implementations/http/express-rpc/error-taxonomy.test.js.map +0 -1
- package/build/implementations/http/express-rpc/index.d.ts +0 -125
- package/build/implementations/http/express-rpc/index.js +0 -216
- package/build/implementations/http/express-rpc/index.js.map +0 -1
- package/build/implementations/http/express-rpc/index.test.js +0 -684
- package/build/implementations/http/express-rpc/index.test.js.map +0 -1
- package/build/implementations/http/express-rpc/types.d.ts +0 -11
- package/build/implementations/http/express-rpc/types.js.map +0 -1
- package/build/implementations/http/hono-api/error-taxonomy.test.js +0 -137
- package/build/implementations/http/hono-api/error-taxonomy.test.js.map +0 -1
- package/build/implementations/http/hono-api/index.d.ts +0 -151
- package/build/implementations/http/hono-api/index.js +0 -344
- package/build/implementations/http/hono-api/index.js.map +0 -1
- package/build/implementations/http/hono-api/index.test.js +0 -992
- package/build/implementations/http/hono-api/index.test.js.map +0 -1
- package/build/implementations/http/hono-api/types.d.ts +0 -13
- package/build/implementations/http/hono-api/types.js.map +0 -1
- package/build/implementations/http/hono-rpc/error-taxonomy.test.js +0 -64
- package/build/implementations/http/hono-rpc/error-taxonomy.test.js.map +0 -1
- package/build/implementations/http/hono-rpc/index.d.ts +0 -130
- package/build/implementations/http/hono-rpc/index.js +0 -209
- package/build/implementations/http/hono-rpc/index.js.map +0 -1
- package/build/implementations/http/hono-rpc/index.test.js +0 -828
- package/build/implementations/http/hono-rpc/index.test.js.map +0 -1
- package/build/implementations/http/hono-rpc/types.d.ts +0 -11
- package/build/implementations/http/hono-rpc/types.js +0 -2
- package/build/implementations/http/hono-rpc/types.js.map +0 -1
- package/build/implementations/http/hono-stream/error-taxonomy.test.js +0 -159
- package/build/implementations/http/hono-stream/error-taxonomy.test.js.map +0 -1
- package/build/implementations/http/hono-stream/index.d.ts +0 -171
- package/build/implementations/http/hono-stream/index.js +0 -415
- package/build/implementations/http/hono-stream/index.js.map +0 -1
- package/build/implementations/http/hono-stream/index.test.js +0 -1383
- package/build/implementations/http/hono-stream/index.test.js.map +0 -1
- package/build/implementations/http/hono-stream/types.d.ts +0 -15
- package/build/implementations/http/hono-stream/types.js +0 -2
- package/build/implementations/http/hono-stream/types.js.map +0 -1
- package/src/implementations/http/express-rpc/README.md +0 -280
- package/src/implementations/http/express-rpc/error-taxonomy.test.ts +0 -103
- package/src/implementations/http/express-rpc/index.test.ts +0 -957
- package/src/implementations/http/express-rpc/index.ts +0 -327
- package/src/implementations/http/express-rpc/types.ts +0 -16
- package/src/implementations/http/hono-api/README.md +0 -284
- package/src/implementations/http/hono-api/error-taxonomy.test.ts +0 -179
- package/src/implementations/http/hono-api/index.test.ts +0 -1341
- package/src/implementations/http/hono-api/index.ts +0 -519
- package/src/implementations/http/hono-api/types.ts +0 -16
- package/src/implementations/http/hono-rpc/README.md +0 -357
- package/src/implementations/http/hono-rpc/error-taxonomy.test.ts +0 -82
- package/src/implementations/http/hono-rpc/index.test.ts +0 -1107
- package/src/implementations/http/hono-rpc/index.ts +0 -320
- package/src/implementations/http/hono-rpc/types.ts +0 -16
- package/src/implementations/http/hono-stream/README.md +0 -559
- package/src/implementations/http/hono-stream/error-taxonomy.test.ts +0 -178
- package/src/implementations/http/hono-stream/index.test.ts +0 -1804
- package/src/implementations/http/hono-stream/index.ts +0 -622
- package/src/implementations/http/hono-stream/types.ts +0 -20
- /package/build/{implementations/http/express-rpc/error-taxonomy.test.d.ts → create-http-stream.test.d.ts} +0 -0
- /package/build/{implementations/http/express-rpc/index.test.d.ts → create-http.test.d.ts} +0 -0
- /package/build/{implementations/http/hono-api/error-taxonomy.test.d.ts → create-stream.test.d.ts} +0 -0
- /package/build/{implementations/http/hono-api/index.test.d.ts → create.test.d.ts} +0 -0
- /package/build/implementations/http/{hono-rpc/error-taxonomy.test.d.ts → error-dispatch.test.d.ts} +0 -0
- /package/build/implementations/http/{hono-rpc/index.test.d.ts → hono/handlers/http-stream.test.d.ts} +0 -0
- /package/build/implementations/http/{hono-stream/error-taxonomy.test.d.ts → hono/handlers/http.test.d.ts} +0 -0
- /package/build/implementations/http/{hono-stream/index.test.d.ts → hono/handlers/rpc.test.d.ts} +0 -0
- /package/build/implementations/http/{express-rpc → hono}/types.js +0 -0
- /package/build/{implementations/http/hono-api/types.js → types.js} +0 -0
|
@@ -44,7 +44,7 @@ function makeStreamAdapter(
|
|
|
44
44
|
}),
|
|
45
45
|
stream: vi.fn(async (_req: AdapterRequest): Promise<AdapterStreamResponse> => ({
|
|
46
46
|
status: 200,
|
|
47
|
-
headers:
|
|
47
|
+
headers: new Headers(),
|
|
48
48
|
body: makeAsyncIterable(items ?? []),
|
|
49
49
|
...response,
|
|
50
50
|
})),
|
|
@@ -265,7 +265,7 @@ describe('executeStream', () => {
|
|
|
265
265
|
request: vi.fn(async (): Promise<never> => { throw new Error('not expected') }),
|
|
266
266
|
stream: vi.fn(async (req: AdapterRequest): Promise<AdapterStreamResponse> => {
|
|
267
267
|
capturedHeaders.push(req.headers ?? {})
|
|
268
|
-
return { status: 200, headers:
|
|
268
|
+
return { status: 200, headers: new Headers(), body: makeAsyncIterable([]) }
|
|
269
269
|
}),
|
|
270
270
|
}
|
|
271
271
|
|
|
@@ -287,7 +287,7 @@ describe('executeStream', () => {
|
|
|
287
287
|
request: vi.fn(async (): Promise<never> => { throw new Error('not expected') }),
|
|
288
288
|
stream: vi.fn(async (): Promise<AdapterStreamResponse> => {
|
|
289
289
|
order.push('adapter')
|
|
290
|
-
return { status: 200, headers:
|
|
290
|
+
return { status: 200, headers: new Headers(), body: makeAsyncIterable([]) }
|
|
291
291
|
}),
|
|
292
292
|
}
|
|
293
293
|
const hooks: ClientHooks = {
|
|
@@ -303,7 +303,7 @@ describe('executeStream', () => {
|
|
|
303
303
|
request: vi.fn(async (): Promise<never> => { throw new Error('not expected') }),
|
|
304
304
|
stream: vi.fn(async (): Promise<AdapterStreamResponse> => ({
|
|
305
305
|
status: 403,
|
|
306
|
-
headers:
|
|
306
|
+
headers: new Headers(),
|
|
307
307
|
body: makeAsyncIterable([]),
|
|
308
308
|
})),
|
|
309
309
|
}
|
|
@@ -368,7 +368,7 @@ describe('executeStream', () => {
|
|
|
368
368
|
request: vi.fn(async (): Promise<never> => { throw new Error('not expected') }),
|
|
369
369
|
stream: vi.fn(async (req: AdapterRequest): Promise<AdapterStreamResponse> => {
|
|
370
370
|
observedSignal = req.signal
|
|
371
|
-
return { status: 200, headers:
|
|
371
|
+
return { status: 200, headers: new Headers(), body: makeAsyncIterable([]) }
|
|
372
372
|
}),
|
|
373
373
|
}
|
|
374
374
|
|
|
@@ -406,7 +406,7 @@ describe('executeStream', () => {
|
|
|
406
406
|
request: vi.fn(async (): Promise<never> => { throw new Error('not expected') }),
|
|
407
407
|
stream: vi.fn(async (req: AdapterRequest): Promise<AdapterStreamResponse> => {
|
|
408
408
|
capturedUrls.push(req.url)
|
|
409
|
-
return { status: 200, headers:
|
|
409
|
+
return { status: 200, headers: new Headers(), body: makeAsyncIterable([]) }
|
|
410
410
|
}),
|
|
411
411
|
}
|
|
412
412
|
|
|
@@ -427,13 +427,45 @@ describe('executeStream', () => {
|
|
|
427
427
|
request: vi.fn(async (): Promise<never> => { throw new Error('not expected') }),
|
|
428
428
|
stream: vi.fn(async (req: AdapterRequest): Promise<AdapterStreamResponse> => {
|
|
429
429
|
observedMeta = req.meta
|
|
430
|
-
return { status: 200, headers:
|
|
430
|
+
return { status: 200, headers: new Headers(), body: makeAsyncIterable([]) }
|
|
431
431
|
}),
|
|
432
432
|
}
|
|
433
433
|
|
|
434
434
|
await run({ adapter, options: { meta: { traceId: 'stream-trace' } as never } })
|
|
435
435
|
expect(observedMeta).toEqual({ traceId: 'stream-trace' })
|
|
436
436
|
})
|
|
437
|
+
|
|
438
|
+
// ── TypedStream.headers (responseHeadersDeclared) ──
|
|
439
|
+
|
|
440
|
+
it('TypedStream.headers is populated when route declares res.headers', async () => {
|
|
441
|
+
const responseHeaders = new Headers({ 'x-stream-id': 'stream-abc' })
|
|
442
|
+
const adapter: ClientAdapter = {
|
|
443
|
+
request: vi.fn(async (): Promise<never> => { throw new Error('not expected') }),
|
|
444
|
+
stream: vi.fn(async (): Promise<AdapterStreamResponse> => ({
|
|
445
|
+
status: 200,
|
|
446
|
+
headers: responseHeaders,
|
|
447
|
+
body: makeAsyncIterable([]),
|
|
448
|
+
})),
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const stream = await run<string, void>({
|
|
452
|
+
adapter,
|
|
453
|
+
descriptor: makeDescriptor({ responseHeadersDeclared: true }),
|
|
454
|
+
})
|
|
455
|
+
for await (const _ of stream) { /* drain */ }
|
|
456
|
+
|
|
457
|
+
expect(stream.headers).toBe(responseHeaders)
|
|
458
|
+
expect(stream.headers?.get('x-stream-id')).toBe('stream-abc')
|
|
459
|
+
})
|
|
460
|
+
|
|
461
|
+
it('TypedStream.headers is undefined when route does not declare res.headers', async () => {
|
|
462
|
+
const adapter = makeStreamAdapter()
|
|
463
|
+
|
|
464
|
+
const stream = await run<string, void>({ adapter })
|
|
465
|
+
for await (const _ of stream) { /* drain */ }
|
|
466
|
+
|
|
467
|
+
expect(stream.headers).toBeUndefined()
|
|
468
|
+
})
|
|
437
469
|
})
|
|
438
470
|
|
|
439
471
|
// ── executeStream classifier integration ──────────────────
|
package/src/client/stream.ts
CHANGED
|
@@ -225,12 +225,18 @@ export async function executeStream<TYield, TReturn = void>(
|
|
|
225
225
|
throw finalError
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
+
// Convert the platform Headers object to a plain record for the hooks/error
|
|
229
|
+
// context (AdapterResponse.headers is Record<string, string>). The raw
|
|
230
|
+
// platform Headers object is kept separately for TypedStream.headers.
|
|
231
|
+
const headersRecord: Record<string, string> = {}
|
|
232
|
+
streamResponse.headers.forEach((value, key) => { headersRecord[key] = value })
|
|
233
|
+
|
|
228
234
|
// Build an AdapterResponse shape for the hooks. For success the body is null
|
|
229
235
|
// (the actual data flows through the async iterable); for non-2xx the adapter
|
|
230
236
|
// eagerly parses the JSON response body and surfaces it via `errorBody`.
|
|
231
237
|
const responseForHooks: AdapterResponse = {
|
|
232
238
|
status: streamResponse.status,
|
|
233
|
-
headers:
|
|
239
|
+
headers: headersRecord,
|
|
234
240
|
body: streamResponse.errorBody ?? null,
|
|
235
241
|
}
|
|
236
242
|
|
|
@@ -261,10 +267,18 @@ export async function executeStream<TYield, TReturn = void>(
|
|
|
261
267
|
// 8. Return the typed stream — pass the registry/descriptor through so
|
|
262
268
|
// mid-stream `event: 'error'` items dispatch through the same registry as
|
|
263
269
|
// RPC/API calls.
|
|
264
|
-
|
|
270
|
+
const typedStream = createTypedStream<TYield, TReturn>(streamResponse.body, descriptor.streamMode, {
|
|
265
271
|
errorRegistry,
|
|
266
272
|
errorStatus: responseForHooks.status,
|
|
267
273
|
procedureName: descriptor.name,
|
|
268
274
|
scope: descriptor.scope,
|
|
269
275
|
})
|
|
276
|
+
|
|
277
|
+
// Wire the initial response headers onto the stream when the route declares
|
|
278
|
+
// res.headers. The platform Headers object is passed directly from the adapter.
|
|
279
|
+
if (descriptor.responseHeadersDeclared) {
|
|
280
|
+
typedStream.headers = streamResponse.headers
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return typedStream
|
|
270
284
|
}
|
|
@@ -3,7 +3,7 @@ import { describe, expect, test } from 'vitest'
|
|
|
3
3
|
import { Type } from 'typebox'
|
|
4
4
|
import { Procedures } from '../index.js'
|
|
5
5
|
import { APIConfig } from '../implementations/types.js'
|
|
6
|
-
import {
|
|
6
|
+
import { HonoAppBuilder, defineErrorTaxonomy } from '../implementations/http/hono/index.js'
|
|
7
7
|
import { createClient } from './index.js'
|
|
8
8
|
import { ClientRequestError } from './errors.js'
|
|
9
9
|
import type { ClientAdapter, ErrorRegistry } from './types.js'
|
|
@@ -110,102 +110,12 @@ function honoAdapter(app: HonoAppLike): ClientAdapter {
|
|
|
110
110
|
// ---------------------------------------------------------------------------
|
|
111
111
|
|
|
112
112
|
describe('typed error dispatch — end-to-end', () => {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
API.Create(
|
|
116
|
-
'GetUser',
|
|
117
|
-
{
|
|
118
|
-
path: '/users/:id',
|
|
119
|
-
method: 'get',
|
|
120
|
-
schema: {
|
|
121
|
-
input: { pathParams: Type.Object({ id: Type.String() }) },
|
|
122
|
-
returnType: Type.Object({ id: Type.String() }),
|
|
123
|
-
},
|
|
124
|
-
},
|
|
125
|
-
async (_ctx, { pathParams }) => {
|
|
126
|
-
if (pathParams.id === 'missing') {
|
|
127
|
-
throw new UseCaseError('User not found', `no user with id=${pathParams.id}`)
|
|
128
|
-
}
|
|
129
|
-
return { id: pathParams.id }
|
|
130
|
-
}
|
|
131
|
-
)
|
|
132
|
-
return new HonoAPIAppBuilder({ errors: appErrors }).register(API, () => ({})).build()
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
test('server-thrown UseCaseError arrives on client as a typed class instance', async () => {
|
|
136
|
-
const app = buildApp()
|
|
137
|
-
const api = createClient({
|
|
138
|
-
adapter: honoAdapter(app),
|
|
139
|
-
basePath: '',
|
|
140
|
-
errorRegistry,
|
|
141
|
-
scopes: (client) => ({
|
|
142
|
-
getUser: (id: string) =>
|
|
143
|
-
client.call<{ id: string }>({
|
|
144
|
-
name: 'GetUser',
|
|
145
|
-
scope: 'users',
|
|
146
|
-
path: '/users/:id',
|
|
147
|
-
method: 'get',
|
|
148
|
-
kind: 'api',
|
|
149
|
-
params: { pathParams: { id } },
|
|
150
|
-
}),
|
|
151
|
-
}),
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
await expect(api.getUser('missing')).rejects.toBeInstanceOf(ApiUseCaseError)
|
|
155
|
-
try {
|
|
156
|
-
await api.getUser('missing')
|
|
157
|
-
} catch (err) {
|
|
158
|
-
expect(err).toBeInstanceOf(ApiUseCaseError)
|
|
159
|
-
expect(err).toBeInstanceOf(Error)
|
|
160
|
-
expect((err as ApiUseCaseError).status).toBe(422)
|
|
161
|
-
expect((err as ApiUseCaseError).procedureName).toBe('GetUser')
|
|
162
|
-
expect((err as ApiUseCaseError).message).toBe('User not found')
|
|
163
|
-
expect((err as ApiUseCaseError).body.name).toBe('UseCaseError')
|
|
164
|
-
}
|
|
165
|
-
})
|
|
113
|
+
// migrated to schema.req in Phase 3+ (CreateHttp)
|
|
114
|
+
test.todo('server-thrown UseCaseError arrives on client as a typed class instance')
|
|
166
115
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
// Omit the registry so dispatch can't match; client sees the raw
|
|
170
|
-
// transport error instead of a typed class.
|
|
171
|
-
const api = createClient({
|
|
172
|
-
adapter: honoAdapter(app),
|
|
173
|
-
basePath: '',
|
|
174
|
-
scopes: (client) => ({
|
|
175
|
-
getUser: (id: string) =>
|
|
176
|
-
client.call<{ id: string }>({
|
|
177
|
-
name: 'GetUser',
|
|
178
|
-
scope: 'users',
|
|
179
|
-
path: '/users/:id',
|
|
180
|
-
method: 'get',
|
|
181
|
-
kind: 'api',
|
|
182
|
-
params: { pathParams: { id } },
|
|
183
|
-
}),
|
|
184
|
-
}),
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
await expect(api.getUser('missing')).rejects.toBeInstanceOf(ClientRequestError)
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
test('success responses are not disturbed by dispatch logic', async () => {
|
|
191
|
-
const app = buildApp()
|
|
192
|
-
const api = createClient({
|
|
193
|
-
adapter: honoAdapter(app),
|
|
194
|
-
basePath: '',
|
|
195
|
-
errorRegistry,
|
|
196
|
-
scopes: (client) => ({
|
|
197
|
-
getUser: (id: string) =>
|
|
198
|
-
client.call<{ id: string }>({
|
|
199
|
-
name: 'GetUser',
|
|
200
|
-
scope: 'users',
|
|
201
|
-
path: '/users/:id',
|
|
202
|
-
method: 'get',
|
|
203
|
-
kind: 'api',
|
|
204
|
-
params: { pathParams: { id } },
|
|
205
|
-
}),
|
|
206
|
-
}),
|
|
207
|
-
})
|
|
116
|
+
// migrated to schema.req in Phase 3+ (CreateHttp)
|
|
117
|
+
test.todo('unregistered error body falls back to ClientRequestError')
|
|
208
118
|
|
|
209
|
-
|
|
210
|
-
|
|
119
|
+
// migrated to schema.req in Phase 3+ (CreateHttp)
|
|
120
|
+
test.todo('success responses are not disturbed by dispatch logic')
|
|
211
121
|
})
|
package/src/client/types.ts
CHANGED
|
@@ -99,7 +99,11 @@ export interface AdapterResponse {
|
|
|
99
99
|
|
|
100
100
|
export interface AdapterStreamResponse {
|
|
101
101
|
status: number
|
|
102
|
-
|
|
102
|
+
/**
|
|
103
|
+
* Platform `Headers` object populated by the adapter. Passed through to
|
|
104
|
+
* `TypedStream.headers` when the route declares `res.headers`.
|
|
105
|
+
*/
|
|
106
|
+
headers: Headers
|
|
103
107
|
body: AsyncIterable<unknown>
|
|
104
108
|
/**
|
|
105
109
|
* Populated when `status` is non-2xx — the parsed response body. Surfaced so
|
|
@@ -144,12 +148,19 @@ export interface CallDescriptor {
|
|
|
144
148
|
scope: string
|
|
145
149
|
path: string
|
|
146
150
|
method: string
|
|
147
|
-
kind: 'rpc' | 'api' | 'stream'
|
|
151
|
+
kind: 'rpc' | 'api' | 'stream' | 'http-stream'
|
|
148
152
|
params: unknown
|
|
153
|
+
/**
|
|
154
|
+
* Set by codegen when the route declares `schema.res.headers`. Drives
|
|
155
|
+
* whether `executeCall` returns bare body (`undefined`/false) or a
|
|
156
|
+
* `{ body, headers }` envelope (`true`). Mirrors the `HttpReturn<TRes>`
|
|
157
|
+
* conditional return shape on the server side.
|
|
158
|
+
*/
|
|
159
|
+
responseHeadersDeclared?: boolean
|
|
149
160
|
}
|
|
150
161
|
|
|
151
162
|
export interface StreamDescriptor extends CallDescriptor {
|
|
152
|
-
kind: 'stream'
|
|
163
|
+
kind: 'stream' | 'http-stream'
|
|
153
164
|
streamMode: 'sse' | 'text'
|
|
154
165
|
}
|
|
155
166
|
|
|
@@ -163,6 +174,13 @@ export interface TypedStream<TYield, TReturn = void> extends AsyncIterable<TYiel
|
|
|
163
174
|
* since resolution depends on the async generator running to completion.
|
|
164
175
|
*/
|
|
165
176
|
result: Promise<TReturn>
|
|
177
|
+
/**
|
|
178
|
+
* Platform `Headers` from the initial stream response. Populated only when
|
|
179
|
+
* the route declares `res.headers` (i.e. `responseHeadersDeclared: true` on
|
|
180
|
+
* the descriptor). Undefined otherwise — consumers without declared response
|
|
181
|
+
* headers should not rely on this field.
|
|
182
|
+
*/
|
|
183
|
+
headers?: Headers
|
|
166
184
|
}
|
|
167
185
|
|
|
168
186
|
// ── Request Options ──────────────────────────────────────
|
|
@@ -9,30 +9,32 @@
|
|
|
9
9
|
"scope": "users",
|
|
10
10
|
"method": "GET",
|
|
11
11
|
"fullPath": "/users/:id",
|
|
12
|
-
"
|
|
13
|
-
"
|
|
12
|
+
"jsonSchema": {
|
|
13
|
+
"req": {
|
|
14
14
|
"pathParams": {
|
|
15
15
|
"type": "object",
|
|
16
16
|
"properties": { "id": { "type": "string" } },
|
|
17
17
|
"required": ["id"]
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
20
|
+
"res": {
|
|
21
|
+
"body": {
|
|
22
|
+
"type": "object",
|
|
23
|
+
"properties": {
|
|
24
|
+
"id": { "type": "string" },
|
|
25
|
+
"name": { "type": "string" },
|
|
26
|
+
"created-at": { "type": "string", "format": "date-time" },
|
|
27
|
+
"address": {
|
|
28
|
+
"type": "object",
|
|
29
|
+
"properties": {
|
|
30
|
+
"street": { "type": "string" },
|
|
31
|
+
"city": { "type": "string" }
|
|
32
|
+
},
|
|
33
|
+
"required": ["street", "city"]
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"required": ["id", "name", "created-at", "address"]
|
|
37
|
+
}
|
|
36
38
|
}
|
|
37
39
|
},
|
|
38
40
|
"errors": ["NotFound"]
|
|
@@ -43,8 +45,8 @@
|
|
|
43
45
|
"scope": "users",
|
|
44
46
|
"method": "POST",
|
|
45
47
|
"fullPath": "/users",
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
+
"jsonSchema": {
|
|
49
|
+
"req": {
|
|
48
50
|
"body": {
|
|
49
51
|
"oneOf": [
|
|
50
52
|
{
|
|
@@ -67,10 +69,12 @@
|
|
|
67
69
|
]
|
|
68
70
|
}
|
|
69
71
|
},
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
"res": {
|
|
73
|
+
"body": {
|
|
74
|
+
"type": "object",
|
|
75
|
+
"properties": { "id": { "type": "string" } },
|
|
76
|
+
"required": ["id"]
|
|
77
|
+
}
|
|
74
78
|
}
|
|
75
79
|
},
|
|
76
80
|
"errors": ["ValidationError"]
|
|
@@ -81,8 +85,8 @@
|
|
|
81
85
|
"scope": "users",
|
|
82
86
|
"method": "GET",
|
|
83
87
|
"fullPath": "/users",
|
|
84
|
-
"
|
|
85
|
-
"
|
|
88
|
+
"jsonSchema": {
|
|
89
|
+
"req": {
|
|
86
90
|
"query": {
|
|
87
91
|
"type": "object",
|
|
88
92
|
"properties": {
|
|
@@ -91,22 +95,99 @@
|
|
|
91
95
|
}
|
|
92
96
|
}
|
|
93
97
|
},
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
|
|
97
|
-
"
|
|
98
|
-
"type": "array",
|
|
98
|
+
"res": {
|
|
99
|
+
"body": {
|
|
100
|
+
"type": "object",
|
|
101
|
+
"properties": {
|
|
99
102
|
"items": {
|
|
100
|
-
"type": "
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
"type": "array",
|
|
104
|
+
"items": {
|
|
105
|
+
"type": "object",
|
|
106
|
+
"properties": {
|
|
107
|
+
"id": { "type": "string" },
|
|
108
|
+
"name": { "type": "string" }
|
|
109
|
+
},
|
|
110
|
+
"required": ["id", "name"]
|
|
111
|
+
}
|
|
106
112
|
}
|
|
113
|
+
},
|
|
114
|
+
"required": ["items"]
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"errors": []
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"kind": "api",
|
|
122
|
+
"name": "DownloadUser",
|
|
123
|
+
"scope": "users",
|
|
124
|
+
"method": "GET",
|
|
125
|
+
"fullPath": "/users/:id/download",
|
|
126
|
+
"jsonSchema": {
|
|
127
|
+
"req": {
|
|
128
|
+
"pathParams": {
|
|
129
|
+
"type": "object",
|
|
130
|
+
"properties": { "id": { "type": "string" } },
|
|
131
|
+
"required": ["id"]
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
"res": {
|
|
135
|
+
"body": {
|
|
136
|
+
"type": "object",
|
|
137
|
+
"properties": { "url": { "type": "string" } },
|
|
138
|
+
"required": ["url"]
|
|
139
|
+
},
|
|
140
|
+
"headers": {
|
|
141
|
+
"type": "object",
|
|
142
|
+
"properties": {
|
|
143
|
+
"x-download-token": { "type": "string" },
|
|
144
|
+
"content-disposition": { "type": "string" }
|
|
145
|
+
},
|
|
146
|
+
"required": ["x-download-token"]
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
"errors": []
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"kind": "http-stream",
|
|
154
|
+
"name": "WatchUsers",
|
|
155
|
+
"scope": "users",
|
|
156
|
+
"method": "GET",
|
|
157
|
+
"fullPath": "/users/watch",
|
|
158
|
+
"streamMode": "sse",
|
|
159
|
+
"jsonSchema": {
|
|
160
|
+
"req": {
|
|
161
|
+
"query": {
|
|
162
|
+
"type": "object",
|
|
163
|
+
"properties": {
|
|
164
|
+
"status": { "type": "string", "enum": ["active", "inactive"] }
|
|
107
165
|
}
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
"res": {
|
|
169
|
+
"headers": {
|
|
170
|
+
"type": "object",
|
|
171
|
+
"properties": {
|
|
172
|
+
"x-stream-id": { "type": "string" }
|
|
173
|
+
},
|
|
174
|
+
"required": ["x-stream-id"]
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
"yield": {
|
|
178
|
+
"type": "object",
|
|
179
|
+
"properties": {
|
|
180
|
+
"id": { "type": "string" },
|
|
181
|
+
"event": { "type": "string", "enum": ["created", "updated", "deleted"] }
|
|
182
|
+
},
|
|
183
|
+
"required": ["id", "event"]
|
|
184
|
+
},
|
|
185
|
+
"returnType": {
|
|
186
|
+
"type": "object",
|
|
187
|
+
"properties": {
|
|
188
|
+
"count": { "type": "integer" }
|
|
108
189
|
},
|
|
109
|
-
"required": ["
|
|
190
|
+
"required": ["count"]
|
|
110
191
|
}
|
|
111
192
|
},
|
|
112
193
|
"errors": []
|