ts-procedures 7.3.0 → 8.1.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 +104 -53
- package/agent_config/claude-code/skills/ts-procedures/api-reference.md +205 -232
- 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 +34 -48
- package/agent_config/cursor/cursorrules +34 -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 +418 -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 +351 -55
- package/build/codegen/emit-scope.js.map +1 -1
- package/build/codegen/emit-scope.test.js +540 -110
- package/build/codegen/emit-scope.test.js.map +1 -1
- package/build/codegen/emit-types.d.ts +6 -2
- package/build/codegen/emit-types.js +81 -20
- package/build/codegen/emit-types.js.map +1 -1
- package/build/codegen/emit-types.test.js +70 -1
- package/build/codegen/emit-types.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 +124 -124
- package/build/index.js +10 -221
- package/build/index.js.map +1 -1
- package/build/index.test.js +20 -919
- 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 +15 -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 +452 -24
- package/src/codegen/emit-errors.integration.test.ts +1 -1
- package/src/codegen/emit-scope.test.ts +581 -110
- package/src/codegen/emit-scope.ts +390 -61
- package/src/codegen/emit-types.test.ts +73 -1
- package/src/codegen/emit-types.ts +82 -21
- 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 +22 -1249
- package/src/index.ts +49 -485
- 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
package/src/codegen/e2e.test.ts
CHANGED
|
@@ -3,8 +3,9 @@ import { generateClient } from './index.js'
|
|
|
3
3
|
import { mkdirSync, rmSync, readFileSync, writeFileSync, existsSync } from 'node:fs'
|
|
4
4
|
import { join } from 'node:path'
|
|
5
5
|
import { tmpdir } from 'node:os'
|
|
6
|
-
import type { DocEnvelope, RPCHttpRouteDoc, APIHttpRouteDoc, StreamHttpRouteDoc, ErrorDoc } from '../implementations/types.js'
|
|
6
|
+
import type { DocEnvelope, RPCHttpRouteDoc, APIHttpRouteDoc, StreamHttpRouteDoc, HttpStreamRouteDoc, ErrorDoc } from '../implementations/types.js'
|
|
7
7
|
import { runTsc } from './test-helpers/run-tsc.js'
|
|
8
|
+
import { Type } from 'typebox'
|
|
8
9
|
|
|
9
10
|
// ---------------------------------------------------------------------------
|
|
10
11
|
// Fixtures
|
|
@@ -39,20 +40,24 @@ const apiRoute: APIHttpRouteDoc = {
|
|
|
39
40
|
method: 'put',
|
|
40
41
|
scope: 'users',
|
|
41
42
|
jsonSchema: {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
43
|
+
req: {
|
|
44
|
+
pathParams: {
|
|
45
|
+
type: 'object',
|
|
46
|
+
properties: { id: { type: 'string' } },
|
|
47
|
+
required: ['id'],
|
|
48
|
+
},
|
|
49
|
+
body: {
|
|
50
|
+
type: 'object',
|
|
51
|
+
properties: { name: { type: 'string' } },
|
|
52
|
+
required: ['name'],
|
|
53
|
+
},
|
|
51
54
|
},
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
res: {
|
|
56
|
+
body: {
|
|
57
|
+
type: 'object',
|
|
58
|
+
properties: { id: { type: 'string' }, name: { type: 'string' } },
|
|
59
|
+
required: ['id', 'name'],
|
|
60
|
+
},
|
|
56
61
|
},
|
|
57
62
|
},
|
|
58
63
|
}
|
|
@@ -94,6 +99,34 @@ const streamRoute: StreamHttpRouteDoc = {
|
|
|
94
99
|
},
|
|
95
100
|
}
|
|
96
101
|
|
|
102
|
+
const httpStreamRoute: HttpStreamRouteDoc = {
|
|
103
|
+
kind: 'http-stream',
|
|
104
|
+
name: 'WatchUsers',
|
|
105
|
+
scope: 'users',
|
|
106
|
+
method: 'get',
|
|
107
|
+
path: '/users/watch',
|
|
108
|
+
fullPath: '/api/users/watch',
|
|
109
|
+
streamMode: 'sse',
|
|
110
|
+
jsonSchema: {
|
|
111
|
+
req: {
|
|
112
|
+
query: {
|
|
113
|
+
type: 'object',
|
|
114
|
+
properties: { status: { type: 'string' } },
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
yield: {
|
|
118
|
+
type: 'object',
|
|
119
|
+
properties: { id: { type: 'string' }, event: { type: 'string' } },
|
|
120
|
+
required: ['id', 'event'],
|
|
121
|
+
},
|
|
122
|
+
returnType: {
|
|
123
|
+
type: 'object',
|
|
124
|
+
properties: { count: { type: 'number' } },
|
|
125
|
+
required: ['count'],
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
}
|
|
129
|
+
|
|
97
130
|
const procedureErrorDoc: ErrorDoc = {
|
|
98
131
|
name: 'ProcedureError',
|
|
99
132
|
statusCode: 500,
|
|
@@ -129,7 +162,7 @@ const envelope: DocEnvelope = {
|
|
|
129
162
|
basePath: '/api',
|
|
130
163
|
headers: [],
|
|
131
164
|
errors: [procedureErrorDoc, validationErrorDoc],
|
|
132
|
-
routes: [rpcRoute, apiRoute, streamRoute],
|
|
165
|
+
routes: [rpcRoute, apiRoute, streamRoute, httpStreamRoute],
|
|
133
166
|
}
|
|
134
167
|
|
|
135
168
|
// ---------------------------------------------------------------------------
|
|
@@ -189,20 +222,20 @@ describe('E2E: generateClient full pipeline', () => {
|
|
|
189
222
|
|
|
190
223
|
// ── users.ts — API route ───────────────────────────────────────────────────
|
|
191
224
|
|
|
192
|
-
it('users.ts contains
|
|
225
|
+
it('users.ts contains UpdateUserReq type (structured request type)', async () => {
|
|
193
226
|
tmpDir = makeTmpDir()
|
|
194
227
|
await generateClient({ envelope, outDir: tmpDir })
|
|
195
228
|
|
|
196
229
|
const content = readFileSync(join(tmpDir, 'users.ts'), 'utf-8')
|
|
197
|
-
expect(content).toContain('
|
|
230
|
+
expect(content).toContain('UpdateUserReq')
|
|
198
231
|
})
|
|
199
232
|
|
|
200
|
-
it('users.ts contains
|
|
233
|
+
it('users.ts contains UpdateUserResponseBody type', async () => {
|
|
201
234
|
tmpDir = makeTmpDir()
|
|
202
235
|
await generateClient({ envelope, outDir: tmpDir })
|
|
203
236
|
|
|
204
237
|
const content = readFileSync(join(tmpDir, 'users.ts'), 'utf-8')
|
|
205
|
-
expect(content).toContain('
|
|
238
|
+
expect(content).toContain('UpdateUserResponseBody')
|
|
206
239
|
})
|
|
207
240
|
|
|
208
241
|
it('users.ts API callable uses fullPath in the path field', async () => {
|
|
@@ -289,6 +322,44 @@ describe('E2E: generateClient full pipeline', () => {
|
|
|
289
322
|
expect(yieldTypeBody).not.toContain('retry')
|
|
290
323
|
})
|
|
291
324
|
|
|
325
|
+
// ── users.ts — http-stream route ──────────────────────────────────────────
|
|
326
|
+
|
|
327
|
+
it('users.ts contains WatchUsers callable using client.stream with kind http-stream', async () => {
|
|
328
|
+
tmpDir = makeTmpDir()
|
|
329
|
+
await generateClient({ envelope, outDir: tmpDir })
|
|
330
|
+
|
|
331
|
+
const content = readFileSync(join(tmpDir, 'users.ts'), 'utf-8')
|
|
332
|
+
expect(content).toContain('client.stream')
|
|
333
|
+
expect(content).toContain("kind: 'http-stream'")
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
it('users.ts http-stream callable passes params: req (not bare req shorthand)', async () => {
|
|
337
|
+
tmpDir = makeTmpDir()
|
|
338
|
+
await generateClient({ envelope, outDir: tmpDir })
|
|
339
|
+
|
|
340
|
+
const content = readFileSync(join(tmpDir, 'users.ts'), 'utf-8')
|
|
341
|
+
// Must be 'params: req,' not just 'req,' — the shorthand drops channel info
|
|
342
|
+
expect(content).toContain('params: req,')
|
|
343
|
+
expect(content).not.toMatch(/^\s+req,\s*$/m)
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
it('users.ts http-stream callable uses fullPath', async () => {
|
|
347
|
+
tmpDir = makeTmpDir()
|
|
348
|
+
await generateClient({ envelope, outDir: tmpDir })
|
|
349
|
+
|
|
350
|
+
const content = readFileSync(join(tmpDir, 'users.ts'), 'utf-8')
|
|
351
|
+
expect(content).toContain("path: '/api/users/watch'")
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
it('users.ts http-stream callable returns TypedStream', async () => {
|
|
355
|
+
tmpDir = makeTmpDir()
|
|
356
|
+
await generateClient({ envelope, outDir: tmpDir })
|
|
357
|
+
|
|
358
|
+
const content = readFileSync(join(tmpDir, 'users.ts'), 'utf-8')
|
|
359
|
+
expect(content).toContain('TypedStream<')
|
|
360
|
+
expect(content).toContain("streamMode: 'sse'")
|
|
361
|
+
})
|
|
362
|
+
|
|
292
363
|
// ── index.ts ───────────────────────────────────────────────────────────────
|
|
293
364
|
|
|
294
365
|
it('index.ts contains the default Api bindings factory', async () => {
|
|
@@ -520,7 +591,7 @@ const typedAdapter: ClientAdapter = {
|
|
|
520
591
|
async stream(req) {
|
|
521
592
|
const trace: string | undefined = req.meta?.traceId
|
|
522
593
|
void trace
|
|
523
|
-
return { status: 200, headers:
|
|
594
|
+
return { status: 200, headers: new Headers(), body: (async function*() {})() }
|
|
524
595
|
},
|
|
525
596
|
}
|
|
526
597
|
|
|
@@ -735,7 +806,9 @@ void run
|
|
|
735
806
|
const content = readFileSync(join(tmpDir, 'users.ts'), 'utf-8')
|
|
736
807
|
expect(content).toContain('export namespace Users {')
|
|
737
808
|
expect(content).toContain('export namespace GetUser {')
|
|
738
|
-
|
|
809
|
+
// API routes now emit Req/Response sub-namespaces instead of a bare UpdateUser namespace
|
|
810
|
+
expect(content).toContain('export namespace Req {')
|
|
811
|
+
expect(content).toContain('export namespace Response {')
|
|
739
812
|
})
|
|
740
813
|
|
|
741
814
|
it('users.ts uses qualified type references in callables', async () => {
|
|
@@ -743,9 +816,10 @@ void run
|
|
|
743
816
|
await generateClient({ envelope, outDir: tmpDir, namespaceTypes: true })
|
|
744
817
|
|
|
745
818
|
const content = readFileSync(join(tmpDir, 'users.ts'), 'utf-8')
|
|
746
|
-
//
|
|
819
|
+
// RPC route still uses Params/Response naming
|
|
747
820
|
expect(content).toContain('client.bindCallable<Users.GetUser.Params, Users.GetUser.Response>')
|
|
748
|
-
|
|
821
|
+
// API route uses Req sub-namespace for params and Response.Body for return
|
|
822
|
+
expect(content).toContain('client.bindCallable<Users.UpdateUser.Req, Users.UpdateUser.Response.Body>')
|
|
749
823
|
})
|
|
750
824
|
|
|
751
825
|
it('events.ts wraps stream types in namespace', async () => {
|
|
@@ -765,7 +839,8 @@ void run
|
|
|
765
839
|
const content = readFileSync(join(tmpDir, 'users.ts'), 'utf-8')
|
|
766
840
|
expect(content).not.toContain('export type GetUserParams')
|
|
767
841
|
expect(content).not.toContain('export type GetUserResponse')
|
|
768
|
-
expect(content).not.toContain('export type
|
|
842
|
+
expect(content).not.toContain('export type UpdateUserReqPathParams')
|
|
843
|
+
expect(content).not.toContain('export type UpdateUserResponseBody')
|
|
769
844
|
})
|
|
770
845
|
|
|
771
846
|
it('enumStyle: enum produces named enums inside namespace', async () => {
|
|
@@ -855,4 +930,357 @@ void run
|
|
|
855
930
|
})
|
|
856
931
|
})
|
|
857
932
|
})
|
|
933
|
+
|
|
934
|
+
// ── Duplicate-identifier bug: type shared between params and returnType ──────
|
|
935
|
+
//
|
|
936
|
+
// End-to-end gold-standard guards. ajsc glues sibling extracted sub-types
|
|
937
|
+
// with a single "\n"; emit-types only renames/dedups the first one, so a
|
|
938
|
+
// route whose body and response both yield >=2 sub-types emits the non-first
|
|
939
|
+
// sub-type twice -> `error TS2300: Duplicate identifier`. These compile the
|
|
940
|
+
// real generated output with tsc.
|
|
941
|
+
describe('shared nested types across body/response', () => {
|
|
942
|
+
const contactShape = {
|
|
943
|
+
type: 'object',
|
|
944
|
+
required: ['name', 'address'],
|
|
945
|
+
properties: {
|
|
946
|
+
name: { type: 'string' },
|
|
947
|
+
address: {
|
|
948
|
+
type: 'object',
|
|
949
|
+
required: ['street', 'city'],
|
|
950
|
+
properties: { street: { type: 'string' }, city: { type: 'string' } },
|
|
951
|
+
},
|
|
952
|
+
},
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
const sharedNestedEnvelope: DocEnvelope = {
|
|
956
|
+
basePath: '/api',
|
|
957
|
+
headers: [],
|
|
958
|
+
errors: [],
|
|
959
|
+
routes: [
|
|
960
|
+
{
|
|
961
|
+
kind: 'rpc',
|
|
962
|
+
name: 'SaveContact',
|
|
963
|
+
path: '/contacts/save/1',
|
|
964
|
+
method: 'post',
|
|
965
|
+
scope: 'contacts',
|
|
966
|
+
version: 1,
|
|
967
|
+
jsonSchema: {
|
|
968
|
+
body: { type: 'object', required: ['contact'], properties: { contact: contactShape } },
|
|
969
|
+
response: {
|
|
970
|
+
type: 'object',
|
|
971
|
+
required: ['contact', 'savedAt'],
|
|
972
|
+
properties: { savedAt: { type: 'string' }, contact: contactShape },
|
|
973
|
+
},
|
|
974
|
+
},
|
|
975
|
+
} satisfies RPCHttpRouteDoc,
|
|
976
|
+
],
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
it('generated client compiles when a nested object is shared between body and response', async () => {
|
|
980
|
+
tmpDir = makeTmpDir()
|
|
981
|
+
await generateClient({
|
|
982
|
+
envelope: sharedNestedEnvelope,
|
|
983
|
+
outDir: tmpDir,
|
|
984
|
+
selfContained: true,
|
|
985
|
+
namespaceTypes: true,
|
|
986
|
+
})
|
|
987
|
+
|
|
988
|
+
runTsc({
|
|
989
|
+
tmpDir,
|
|
990
|
+
tsconfigInline: {
|
|
991
|
+
compilerOptions: {
|
|
992
|
+
strict: true,
|
|
993
|
+
target: 'ES2022',
|
|
994
|
+
module: 'ES2022',
|
|
995
|
+
moduleResolution: 'bundler',
|
|
996
|
+
verbatimModuleSyntax: true,
|
|
997
|
+
noEmit: true,
|
|
998
|
+
skipLibCheck: true,
|
|
999
|
+
},
|
|
1000
|
+
include: ['_types.ts', '_client.ts', 'index.ts', 'contacts.ts', '_errors.ts'],
|
|
1001
|
+
},
|
|
1002
|
+
})
|
|
1003
|
+
})
|
|
1004
|
+
|
|
1005
|
+
// Latent correctness guard: same property name (`detail`), DIFFERENT shape
|
|
1006
|
+
// in body vs response. If the rename leaves the response's sub-type pointing
|
|
1007
|
+
// at the body's `Detail`, a consumer that reads the response-only field fails
|
|
1008
|
+
// to compile — catching the silent wrong-reference bug that TS2300 hides when
|
|
1009
|
+
// the shapes happen to be structurally identical.
|
|
1010
|
+
const divergentEnvelope: DocEnvelope = {
|
|
1011
|
+
basePath: '/api',
|
|
1012
|
+
headers: [],
|
|
1013
|
+
errors: [],
|
|
1014
|
+
routes: [
|
|
1015
|
+
{
|
|
1016
|
+
kind: 'rpc',
|
|
1017
|
+
name: 'SyncData',
|
|
1018
|
+
path: '/sync/1',
|
|
1019
|
+
method: 'post',
|
|
1020
|
+
scope: 'sync',
|
|
1021
|
+
version: 1,
|
|
1022
|
+
jsonSchema: {
|
|
1023
|
+
body: {
|
|
1024
|
+
type: 'object',
|
|
1025
|
+
required: ['outer'],
|
|
1026
|
+
properties: {
|
|
1027
|
+
outer: {
|
|
1028
|
+
type: 'object',
|
|
1029
|
+
required: ['detail'],
|
|
1030
|
+
properties: {
|
|
1031
|
+
detail: {
|
|
1032
|
+
type: 'object',
|
|
1033
|
+
required: ['fromClient'],
|
|
1034
|
+
properties: { fromClient: { type: 'string' } },
|
|
1035
|
+
},
|
|
1036
|
+
},
|
|
1037
|
+
},
|
|
1038
|
+
},
|
|
1039
|
+
},
|
|
1040
|
+
response: {
|
|
1041
|
+
type: 'object',
|
|
1042
|
+
required: ['outer'],
|
|
1043
|
+
properties: {
|
|
1044
|
+
outer: {
|
|
1045
|
+
type: 'object',
|
|
1046
|
+
required: ['detail'],
|
|
1047
|
+
properties: {
|
|
1048
|
+
detail: {
|
|
1049
|
+
type: 'object',
|
|
1050
|
+
required: ['fromServer'],
|
|
1051
|
+
properties: { fromServer: { type: 'number' } },
|
|
1052
|
+
},
|
|
1053
|
+
},
|
|
1054
|
+
},
|
|
1055
|
+
},
|
|
1056
|
+
},
|
|
1057
|
+
},
|
|
1058
|
+
} satisfies RPCHttpRouteDoc,
|
|
1059
|
+
],
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
it('response sub-type keeps its own shape when a name collides with a differently-shaped body sub-type', async () => {
|
|
1063
|
+
tmpDir = makeTmpDir()
|
|
1064
|
+
await generateClient({
|
|
1065
|
+
envelope: divergentEnvelope,
|
|
1066
|
+
outDir: tmpDir,
|
|
1067
|
+
selfContained: true,
|
|
1068
|
+
namespaceTypes: true,
|
|
1069
|
+
})
|
|
1070
|
+
|
|
1071
|
+
// Consumer asserts the response's `detail` exposes `fromServer: number`
|
|
1072
|
+
// (its own shape) — not the body's `{ fromClient: string }`. Imports the
|
|
1073
|
+
// scope file directly to validate the generated route type precisely.
|
|
1074
|
+
const consumer = `
|
|
1075
|
+
import type { Sync } from './sync'
|
|
1076
|
+
const r: Sync.SyncData.Response = null as any
|
|
1077
|
+
const n: number = r.outer.detail.fromServer
|
|
1078
|
+
void n
|
|
1079
|
+
`
|
|
1080
|
+
writeFileSync(join(tmpDir, '__consumer.ts'), consumer)
|
|
1081
|
+
|
|
1082
|
+
runTsc({
|
|
1083
|
+
tmpDir,
|
|
1084
|
+
tsconfigInline: {
|
|
1085
|
+
compilerOptions: {
|
|
1086
|
+
strict: true,
|
|
1087
|
+
target: 'ES2022',
|
|
1088
|
+
module: 'ES2022',
|
|
1089
|
+
moduleResolution: 'bundler',
|
|
1090
|
+
verbatimModuleSyntax: true,
|
|
1091
|
+
noEmit: true,
|
|
1092
|
+
skipLibCheck: true,
|
|
1093
|
+
},
|
|
1094
|
+
include: ['_types.ts', '_client.ts', 'index.ts', 'sync.ts', '_errors.ts', '__consumer.ts'],
|
|
1095
|
+
},
|
|
1096
|
+
})
|
|
1097
|
+
})
|
|
1098
|
+
})
|
|
1099
|
+
|
|
1100
|
+
// ── Complex TypeBox-derived schemas (broad duplication / correctness sweep) ──
|
|
1101
|
+
//
|
|
1102
|
+
// Builds realistic schemas with TypeBox, round-trips them to plain JSON Schema
|
|
1103
|
+
// (exactly what lands in a DocEnvelope), and stresses every known collision
|
|
1104
|
+
// vector at once:
|
|
1105
|
+
// - a deeply-nested object (Product → Manufacturer → Hq/Warehouse → Geo)
|
|
1106
|
+
// shared between a route's body AND response (forces the response copy to
|
|
1107
|
+
// rename all of its sub-types and re-point every cross-reference);
|
|
1108
|
+
// - the same Product reused by a SECOND route in the same scope (separate
|
|
1109
|
+
// route namespaces must not clash);
|
|
1110
|
+
// - a sub-type (Geo) referenced by two siblings (Hq + Warehouse) — the
|
|
1111
|
+
// rename must patch BOTH referrers;
|
|
1112
|
+
// - shared Filter/Range across an API route's query + body channels;
|
|
1113
|
+
// - a body property literally named `params`, colliding with the route's
|
|
1114
|
+
// own `Params` shortName;
|
|
1115
|
+
// - arrays-of-objects, optionals, literal unions, and records.
|
|
1116
|
+
//
|
|
1117
|
+
// Run under the default (inline-union) mode AND `enumStyle: 'enum'`, where a
|
|
1118
|
+
// 3-literal union explodes into three separate enums (plus ajsc's own
|
|
1119
|
+
// internal renaming) — the largest fused declaration block we emit. The tsc
|
|
1120
|
+
// compile is the authoritative duplicate-identifier (TS2300) and
|
|
1121
|
+
// dangling/wrong-reference guard; the consumer additionally pins deep field
|
|
1122
|
+
// access through the renamed types.
|
|
1123
|
+
describe('complex TypeBox-derived schemas', () => {
|
|
1124
|
+
const clean = (s: unknown): Record<string, unknown> =>
|
|
1125
|
+
JSON.parse(JSON.stringify(s)) as Record<string, unknown>
|
|
1126
|
+
|
|
1127
|
+
const Geo = Type.Object({ lat: Type.Number(), lng: Type.Number() })
|
|
1128
|
+
const Address = Type.Object({ street: Type.String(), city: Type.String(), geo: Geo })
|
|
1129
|
+
const Manufacturer = Type.Object({
|
|
1130
|
+
name: Type.String(),
|
|
1131
|
+
hq: Address,
|
|
1132
|
+
warehouses: Type.Array(Address),
|
|
1133
|
+
})
|
|
1134
|
+
const Variant = Type.Object({
|
|
1135
|
+
sku: Type.String(),
|
|
1136
|
+
price: Type.Number(),
|
|
1137
|
+
inStock: Type.Boolean(),
|
|
1138
|
+
})
|
|
1139
|
+
const Product = Type.Object({
|
|
1140
|
+
id: Type.String(),
|
|
1141
|
+
category: Type.Union([Type.Literal('book'), Type.Literal('food'), Type.Literal('toy')]),
|
|
1142
|
+
status: Type.Optional(Type.Union([Type.Literal('active'), Type.Literal('archived')])),
|
|
1143
|
+
dimensions: Type.Object({ w: Type.Number(), h: Type.Number(), d: Type.Number() }),
|
|
1144
|
+
tags: Type.Array(Type.String()),
|
|
1145
|
+
variants: Type.Array(Variant),
|
|
1146
|
+
manufacturer: Manufacturer,
|
|
1147
|
+
})
|
|
1148
|
+
|
|
1149
|
+
const Range = Type.Object({ min: Type.Number(), max: Type.Number() })
|
|
1150
|
+
const Filter = Type.Object({
|
|
1151
|
+
category: Type.Optional(Type.Union([Type.Literal('book'), Type.Literal('food')])),
|
|
1152
|
+
price: Range,
|
|
1153
|
+
})
|
|
1154
|
+
const SavedSearch = Type.Object({ name: Type.String(), filter: Filter })
|
|
1155
|
+
|
|
1156
|
+
// Route 1: rpc — body + response share Product; body also has a `params`
|
|
1157
|
+
// property colliding with the route's Params shortName.
|
|
1158
|
+
const upsertProduct: RPCHttpRouteDoc = {
|
|
1159
|
+
kind: 'rpc',
|
|
1160
|
+
name: 'UpsertProduct',
|
|
1161
|
+
path: '/catalog/upsert/1',
|
|
1162
|
+
method: 'post',
|
|
1163
|
+
scope: 'catalog',
|
|
1164
|
+
version: 1,
|
|
1165
|
+
jsonSchema: {
|
|
1166
|
+
body: clean(Type.Object({ product: Product, params: Type.Object({ dryRun: Type.Boolean() }) })),
|
|
1167
|
+
response: clean(
|
|
1168
|
+
Type.Object({
|
|
1169
|
+
product: Product,
|
|
1170
|
+
audit: Type.Object({
|
|
1171
|
+
revision: Type.Integer(),
|
|
1172
|
+
editor: Type.Object({ id: Type.String(), name: Type.String() }),
|
|
1173
|
+
}),
|
|
1174
|
+
}),
|
|
1175
|
+
),
|
|
1176
|
+
},
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
// Route 2: api — query + body share Filter/Range; res reuses Product.
|
|
1180
|
+
const searchCatalog: APIHttpRouteDoc = {
|
|
1181
|
+
kind: 'api',
|
|
1182
|
+
name: 'SearchCatalog',
|
|
1183
|
+
path: '/catalog/search',
|
|
1184
|
+
fullPath: '/api/catalog/search',
|
|
1185
|
+
method: 'post',
|
|
1186
|
+
scope: 'catalog',
|
|
1187
|
+
jsonSchema: {
|
|
1188
|
+
req: {
|
|
1189
|
+
query: clean(Type.Object({ page: Type.Integer(), filter: Type.Optional(Filter) })),
|
|
1190
|
+
body: clean(Type.Object({ saved: Type.Array(SavedSearch) })),
|
|
1191
|
+
},
|
|
1192
|
+
res: {
|
|
1193
|
+
body: clean(
|
|
1194
|
+
Type.Object({
|
|
1195
|
+
items: Type.Array(Product),
|
|
1196
|
+
total: Type.Integer(),
|
|
1197
|
+
byCategory: Type.Record(Type.String(), Type.Integer()),
|
|
1198
|
+
}),
|
|
1199
|
+
),
|
|
1200
|
+
},
|
|
1201
|
+
},
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
const complexEnvelope: DocEnvelope = {
|
|
1205
|
+
basePath: '/api',
|
|
1206
|
+
headers: [],
|
|
1207
|
+
errors: [],
|
|
1208
|
+
routes: [upsertProduct, searchCatalog],
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
const consumer = `
|
|
1212
|
+
import type { Catalog } from './catalog'
|
|
1213
|
+
|
|
1214
|
+
// Route 1 response: deep access through the shared-and-renamed Product, plus
|
|
1215
|
+
// the sub-type (Geo) shared by two referrers (hq + warehouses[]).
|
|
1216
|
+
const up: Catalog.UpsertProduct.Response = null as any
|
|
1217
|
+
const lat: number = up.product.manufacturer.hq.geo.lat
|
|
1218
|
+
const wlng: number = up.product.manufacturer.warehouses[0]!.geo.lng
|
|
1219
|
+
const sku: string = up.product.variants[0]!.sku
|
|
1220
|
+
const w: number = up.product.dimensions.w
|
|
1221
|
+
const rev: number = up.audit.revision
|
|
1222
|
+
const editorName: string = up.audit.editor.name
|
|
1223
|
+
void lat; void wlng; void sku; void w; void rev; void editorName
|
|
1224
|
+
|
|
1225
|
+
// Route 1 params (body): shared Product + the colliding 'params' property.
|
|
1226
|
+
const body: Catalog.UpsertProduct.Params = null as any
|
|
1227
|
+
const pid: string = body.product.id
|
|
1228
|
+
const dry: boolean = body.params.dryRun
|
|
1229
|
+
void pid; void dry
|
|
1230
|
+
|
|
1231
|
+
// Route 2 request: Filter/Range shared across query + body channels.
|
|
1232
|
+
const req: Catalog.SearchCatalog.Req = null as any
|
|
1233
|
+
const page: number = req.query.page
|
|
1234
|
+
const qMin: number | undefined = req.query.filter?.price.min
|
|
1235
|
+
const bMax: number = req.body.saved[0]!.filter.price.max
|
|
1236
|
+
void page; void qMin; void bMax
|
|
1237
|
+
|
|
1238
|
+
// Route 2 response: array of the shared Product + a record.
|
|
1239
|
+
const res: Catalog.SearchCatalog.Response.Body = null as any
|
|
1240
|
+
const total: number = res.total
|
|
1241
|
+
const firstSku: string = res.items[0]!.variants[0]!.sku
|
|
1242
|
+
const bookCount: number | undefined = res.byCategory['book']
|
|
1243
|
+
void total; void firstSku; void bookCount
|
|
1244
|
+
`
|
|
1245
|
+
|
|
1246
|
+
const tsconfig = {
|
|
1247
|
+
compilerOptions: {
|
|
1248
|
+
strict: true,
|
|
1249
|
+
target: 'ES2022',
|
|
1250
|
+
module: 'ES2022',
|
|
1251
|
+
moduleResolution: 'bundler',
|
|
1252
|
+
verbatimModuleSyntax: true,
|
|
1253
|
+
noEmit: true,
|
|
1254
|
+
skipLibCheck: true,
|
|
1255
|
+
},
|
|
1256
|
+
include: ['_types.ts', '_client.ts', 'index.ts', 'catalog.ts', '_errors.ts', '__consumer.ts'],
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
it('compiles end-to-end with deep shared/renamed types (default inline-union mode)', async () => {
|
|
1260
|
+
tmpDir = makeTmpDir()
|
|
1261
|
+
await generateClient({
|
|
1262
|
+
envelope: complexEnvelope,
|
|
1263
|
+
outDir: tmpDir,
|
|
1264
|
+
selfContained: true,
|
|
1265
|
+
namespaceTypes: true,
|
|
1266
|
+
})
|
|
1267
|
+
writeFileSync(join(tmpDir, '__consumer.ts'), consumer)
|
|
1268
|
+
runTsc({ tmpDir, tsconfigInline: tsconfig })
|
|
1269
|
+
})
|
|
1270
|
+
|
|
1271
|
+
it('compiles end-to-end under enumStyle: enum (union-of-literals explodes into many enums)', async () => {
|
|
1272
|
+
tmpDir = makeTmpDir()
|
|
1273
|
+
await generateClient({
|
|
1274
|
+
envelope: complexEnvelope,
|
|
1275
|
+
outDir: tmpDir,
|
|
1276
|
+
selfContained: true,
|
|
1277
|
+
namespaceTypes: true,
|
|
1278
|
+
ajsc: { enumStyle: 'enum' },
|
|
1279
|
+
})
|
|
1280
|
+
// In enum mode `category` becomes enum unions; the deep object access still
|
|
1281
|
+
// holds, so reuse the consumer (it never pins the enum member types).
|
|
1282
|
+
writeFileSync(join(tmpDir, '__consumer.ts'), consumer)
|
|
1283
|
+
runTsc({ tmpDir, tsconfigInline: tsconfig })
|
|
1284
|
+
})
|
|
1285
|
+
})
|
|
858
1286
|
})
|
|
@@ -52,7 +52,7 @@ describe('generated _errors.ts — runtime behavior', () => {
|
|
|
52
52
|
path: '/ping',
|
|
53
53
|
fullPath: '/api/ping',
|
|
54
54
|
method: 'get',
|
|
55
|
-
jsonSchema: { response: { type: 'object', properties: {} } },
|
|
55
|
+
jsonSchema: { response: { type: 'object', properties: {} } } as any, // TODO(Phase 8): migrate to new req/res envelope shape
|
|
56
56
|
},
|
|
57
57
|
],
|
|
58
58
|
}
|