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
|
@@ -34,11 +34,9 @@ describe('emitKotlinRoute', () => {
|
|
|
34
34
|
name: 'GetUser',
|
|
35
35
|
method: 'GET',
|
|
36
36
|
fullPath: '/users/:id',
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
},
|
|
41
|
-
returnType: { type: 'object' },
|
|
37
|
+
jsonSchema: {
|
|
38
|
+
req: { pathParams: { type: 'object' } },
|
|
39
|
+
res: { body: { type: 'object' } },
|
|
42
40
|
},
|
|
43
41
|
errors: [],
|
|
44
42
|
} as unknown as AnyHttpRouteDoc
|
|
@@ -64,7 +62,10 @@ describe('emitKotlinRoute', () => {
|
|
|
64
62
|
name: 'CreateUser',
|
|
65
63
|
method: 'POST',
|
|
66
64
|
fullPath: '/users',
|
|
67
|
-
|
|
65
|
+
jsonSchema: {
|
|
66
|
+
req: { body: { type: 'object' } },
|
|
67
|
+
res: { body: { type: 'object' } },
|
|
68
|
+
},
|
|
68
69
|
errors: [],
|
|
69
70
|
} as unknown as AnyHttpRouteDoc
|
|
70
71
|
|
|
@@ -84,7 +85,10 @@ describe('emitKotlinRoute', () => {
|
|
|
84
85
|
name: 'GetUser',
|
|
85
86
|
method: 'GET',
|
|
86
87
|
fullPath: '/users/:id',
|
|
87
|
-
|
|
88
|
+
jsonSchema: {
|
|
89
|
+
req: { pathParams: { type: 'object' } },
|
|
90
|
+
res: { body: { type: 'object' } },
|
|
91
|
+
},
|
|
88
92
|
errors: ['NotFound'],
|
|
89
93
|
} as unknown as AnyHttpRouteDoc
|
|
90
94
|
|
|
@@ -103,14 +107,14 @@ describe('emitKotlinRoute', () => {
|
|
|
103
107
|
it('silently skips error keys with no schema in the envelope map', () => {
|
|
104
108
|
const route = {
|
|
105
109
|
kind: 'api', name: 'GetUser', method: 'GET', fullPath: '/users',
|
|
106
|
-
|
|
110
|
+
jsonSchema: {}, errors: ['UnknownTaxonomyKey'],
|
|
107
111
|
} as unknown as AnyHttpRouteDoc
|
|
108
112
|
const result = emitKotlinRoute(route, createStubKotlinEmitter({}), new Map())
|
|
109
113
|
expect(result.code).not.toContain('object Errors {')
|
|
110
114
|
})
|
|
111
115
|
|
|
112
116
|
it('returns skipped:true for stream routes', () => {
|
|
113
|
-
const route = { kind: 'stream', name: 'WatchUsers', method: 'GET', path: '/users/stream',
|
|
117
|
+
const route = { kind: 'stream', name: 'WatchUsers', method: 'GET', path: '/users/stream', jsonSchema: {}, errors: [] } as unknown as AnyHttpRouteDoc
|
|
114
118
|
const result = emitKotlinRoute(route, createStubKotlinEmitter({}), noErrors)
|
|
115
119
|
expect(result.code).toBe('')
|
|
116
120
|
expect(result.skipped).toBe(true)
|
|
@@ -122,9 +126,9 @@ describe('emitKotlinRoute', () => {
|
|
|
122
126
|
name: 'GetUser',
|
|
123
127
|
method: 'GET',
|
|
124
128
|
fullPath: '/users/:id',
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
129
|
+
jsonSchema: {
|
|
130
|
+
req: { pathParams: { type: 'object' } },
|
|
131
|
+
res: { body: { type: 'object' } },
|
|
128
132
|
},
|
|
129
133
|
errors: ['NotFound'],
|
|
130
134
|
} as unknown as AnyHttpRouteDoc
|
|
@@ -155,13 +159,13 @@ describe('emitKotlinRoute', () => {
|
|
|
155
159
|
name: 'X',
|
|
156
160
|
method: 'POST',
|
|
157
161
|
fullPath: '/x/:id',
|
|
158
|
-
|
|
159
|
-
|
|
162
|
+
jsonSchema: {
|
|
163
|
+
req: {
|
|
160
164
|
pathParams: { type: 'object' },
|
|
161
165
|
query: { type: 'object' },
|
|
162
166
|
body: { type: 'object' },
|
|
163
167
|
},
|
|
164
|
-
|
|
168
|
+
res: { body: { type: 'object' } },
|
|
165
169
|
},
|
|
166
170
|
errors: ['Z'],
|
|
167
171
|
} as unknown as AnyHttpRouteDoc
|
|
@@ -187,7 +191,7 @@ describe('emitKotlinRoute', () => {
|
|
|
187
191
|
name: 'X',
|
|
188
192
|
method: 'GET',
|
|
189
193
|
fullPath: '/x',
|
|
190
|
-
|
|
194
|
+
jsonSchema: { res: { body: { type: 'object' } } },
|
|
191
195
|
errors: [],
|
|
192
196
|
} as unknown as AnyHttpRouteDoc
|
|
193
197
|
|
|
@@ -207,13 +211,92 @@ describe('emitKotlinRoute', () => {
|
|
|
207
211
|
expect(calls[0]!.opts.uncountableWords).toEqual(['data', 'metadata'])
|
|
208
212
|
})
|
|
209
213
|
|
|
214
|
+
it('emits ResponseHeaders slot when res.headers is declared on an api route', () => {
|
|
215
|
+
const route = {
|
|
216
|
+
kind: 'api',
|
|
217
|
+
name: 'DownloadUser',
|
|
218
|
+
method: 'GET',
|
|
219
|
+
fullPath: '/users/:id/download',
|
|
220
|
+
jsonSchema: {
|
|
221
|
+
req: { pathParams: { type: 'object' } },
|
|
222
|
+
res: {
|
|
223
|
+
body: { type: 'object' },
|
|
224
|
+
headers: { type: 'object' },
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
errors: [],
|
|
228
|
+
} as unknown as AnyHttpRouteDoc
|
|
229
|
+
|
|
230
|
+
const { emitter, calls } = makeSpyEmitter({
|
|
231
|
+
PathParams: ok('@Serializable data class PathParams(val id: String)', 'PathParams'),
|
|
232
|
+
Response: ok('@Serializable data class Response(val url: String)', 'Response'),
|
|
233
|
+
ResponseHeaders: ok(
|
|
234
|
+
'@Serializable\ndata class ResponseHeaders(\n @SerialName("x-download-token") val xDownloadToken: String,\n)',
|
|
235
|
+
'ResponseHeaders',
|
|
236
|
+
),
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
const result = emitKotlinRoute(route, emitter, noErrors)
|
|
240
|
+
expect(calls.map((c) => c.opts.rootTypeName)).toEqual(['PathParams', 'Response', 'ResponseHeaders'])
|
|
241
|
+
expect(result.code).toContain('@SerialName("x-download-token") val xDownloadToken: String')
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
it('does not emit ResponseHeaders when res.headers is absent', () => {
|
|
245
|
+
const route = {
|
|
246
|
+
kind: 'api',
|
|
247
|
+
name: 'GetUser',
|
|
248
|
+
method: 'GET',
|
|
249
|
+
fullPath: '/users/:id',
|
|
250
|
+
jsonSchema: {
|
|
251
|
+
req: { pathParams: { type: 'object' } },
|
|
252
|
+
res: { body: { type: 'object' } },
|
|
253
|
+
},
|
|
254
|
+
errors: [],
|
|
255
|
+
} as unknown as AnyHttpRouteDoc
|
|
256
|
+
|
|
257
|
+
const { emitter, calls } = makeSpyEmitter({
|
|
258
|
+
PathParams: ok('@Serializable data class PathParams(val id: String)', 'PathParams'),
|
|
259
|
+
Response: ok('@Serializable data class Response(val id: String)', 'Response'),
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
emitKotlinRoute(route, emitter, noErrors)
|
|
263
|
+
expect(calls.map((c) => c.opts.rootTypeName)).not.toContain('ResponseHeaders')
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
it('processes http-stream routes (not skipped) and emits Yield/ReturnType slots', () => {
|
|
267
|
+
const route = {
|
|
268
|
+
kind: 'http-stream',
|
|
269
|
+
name: 'TailLogs',
|
|
270
|
+
method: 'GET',
|
|
271
|
+
fullPath: '/logs/tail',
|
|
272
|
+
jsonSchema: {
|
|
273
|
+
req: { query: { type: 'object' } },
|
|
274
|
+
yield: { type: 'object' },
|
|
275
|
+
returnType: { type: 'object' },
|
|
276
|
+
},
|
|
277
|
+
errors: [],
|
|
278
|
+
} as unknown as AnyHttpRouteDoc
|
|
279
|
+
|
|
280
|
+
const { emitter, calls } = makeSpyEmitter({
|
|
281
|
+
Query: ok('@Serializable data class Query(val filter: String? = null)', 'Query'),
|
|
282
|
+
Yield: ok('@Serializable data class Yield(val line: String)', 'Yield'),
|
|
283
|
+
ReturnType: ok('@Serializable data class ReturnType(val count: Long)', 'ReturnType'),
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
const result = emitKotlinRoute(route, emitter, noErrors)
|
|
287
|
+
expect(result.skipped).toBeFalsy()
|
|
288
|
+
expect(calls.map((c) => c.opts.rootTypeName)).toEqual(['Query', 'Yield', 'ReturnType'])
|
|
289
|
+
expect(result.code).toContain('const val method = "GET"')
|
|
290
|
+
expect(result.code).toContain('const val path = "/logs/tail"')
|
|
291
|
+
})
|
|
292
|
+
|
|
210
293
|
it('does not include passthrough keys when caller omits them', () => {
|
|
211
294
|
const route = {
|
|
212
295
|
kind: 'api',
|
|
213
296
|
name: 'X',
|
|
214
297
|
method: 'GET',
|
|
215
298
|
fullPath: '/x',
|
|
216
|
-
|
|
299
|
+
jsonSchema: { res: { body: { type: 'object' } } },
|
|
217
300
|
errors: [],
|
|
218
301
|
} as unknown as AnyHttpRouteDoc
|
|
219
302
|
|
|
@@ -18,7 +18,10 @@ describe('emitKotlinScope', () => {
|
|
|
18
18
|
name: 'GetUser',
|
|
19
19
|
method: 'GET',
|
|
20
20
|
fullPath: '/users/:id',
|
|
21
|
-
|
|
21
|
+
jsonSchema: {
|
|
22
|
+
req: { pathParams: { type: 'object' } },
|
|
23
|
+
res: { body: { type: 'object' } },
|
|
24
|
+
},
|
|
22
25
|
errors: [],
|
|
23
26
|
} as unknown as AnyHttpRouteDoc
|
|
24
27
|
|
|
@@ -40,8 +43,8 @@ describe('emitKotlinScope', () => {
|
|
|
40
43
|
})
|
|
41
44
|
|
|
42
45
|
it('joins multiple routes inside one scope object', () => {
|
|
43
|
-
const route1 = { kind: 'api', name: 'GetUser', method: 'GET', fullPath: '/users/:id',
|
|
44
|
-
const route2 = { kind: 'api', name: 'CreateUser', method: 'POST', fullPath: '/users',
|
|
46
|
+
const route1 = { kind: 'api', name: 'GetUser', method: 'GET', fullPath: '/users/:id', jsonSchema: {}, errors: [] } as unknown as AnyHttpRouteDoc
|
|
47
|
+
const route2 = { kind: 'api', name: 'CreateUser', method: 'POST', fullPath: '/users', jsonSchema: {}, errors: [] } as unknown as AnyHttpRouteDoc
|
|
45
48
|
const group: ScopeGroup = { scopeKey: 'users', camelCase: 'users', routes: [route1, route2] }
|
|
46
49
|
const emitter = createStubKotlinEmitter({})
|
|
47
50
|
|
|
@@ -60,7 +63,7 @@ describe('emitKotlinScope', () => {
|
|
|
60
63
|
})
|
|
61
64
|
|
|
62
65
|
it('threads all 5 passthrough opts to every emitter call', () => {
|
|
63
|
-
const route = { kind: 'api', name: 'X', method: 'GET', fullPath: '/x',
|
|
66
|
+
const route = { kind: 'api', name: 'X', method: 'GET', fullPath: '/x', jsonSchema: { res: { body: { type: 'object' } } }, errors: [] } as unknown as AnyHttpRouteDoc
|
|
64
67
|
const group: ScopeGroup = { scopeKey: 'x', camelCase: 'x', routes: [route] }
|
|
65
68
|
|
|
66
69
|
const calls: KotlinEmitOptions[] = []
|
|
@@ -96,8 +99,8 @@ describe('emitKotlinScope', () => {
|
|
|
96
99
|
})
|
|
97
100
|
|
|
98
101
|
it('collects skipped stream-route names', () => {
|
|
99
|
-
const stream = { kind: 'stream', name: 'WatchUsers', method: 'GET', path: '/u/stream',
|
|
100
|
-
const api = { kind: 'api', name: 'GetUser', method: 'GET', fullPath: '/u',
|
|
102
|
+
const stream = { kind: 'stream', name: 'WatchUsers', method: 'GET', path: '/u/stream', jsonSchema: {}, errors: [] } as unknown as AnyHttpRouteDoc
|
|
103
|
+
const api = { kind: 'api', name: 'GetUser', method: 'GET', fullPath: '/u', jsonSchema: { res: { body: { type: 'object' } } }, errors: [] } as unknown as AnyHttpRouteDoc
|
|
101
104
|
const group: ScopeGroup = { scopeKey: 'u', camelCase: 'u', routes: [stream, api] }
|
|
102
105
|
|
|
103
106
|
const emitter = createStubKotlinEmitter({
|
|
@@ -61,6 +61,25 @@ describe('kotlin codegen — integration', () => {
|
|
|
61
61
|
'Query',
|
|
62
62
|
['kotlinx.serialization.Serializable', 'kotlinx.serialization.SerialName'],
|
|
63
63
|
),
|
|
64
|
+
|
|
65
|
+
// DownloadUser
|
|
66
|
+
ResponseHeaders: ok(
|
|
67
|
+
'@Serializable\ndata class ResponseHeaders(\n @SerialName("x-download-token") val xDownloadToken: String,\n @SerialName("content-disposition") val contentDisposition: String? = null,\n)',
|
|
68
|
+
'ResponseHeaders',
|
|
69
|
+
['kotlinx.serialization.Serializable', 'kotlinx.serialization.SerialName'],
|
|
70
|
+
),
|
|
71
|
+
|
|
72
|
+
// WatchUsers (http-stream) — reuses Query and ResponseHeaders stubs from above;
|
|
73
|
+
// adds Yield and ReturnType which are unique to the stream route.
|
|
74
|
+
Yield: ok(
|
|
75
|
+
'@Serializable\ndata class Yield(\n val id: String,\n val event: Event,\n) {\n @Serializable\n enum class Event {\n @SerialName("created") CREATED,\n @SerialName("updated") UPDATED,\n @SerialName("deleted") DELETED,\n }\n}',
|
|
76
|
+
'Yield',
|
|
77
|
+
['kotlinx.serialization.Serializable', 'kotlinx.serialization.SerialName'],
|
|
78
|
+
),
|
|
79
|
+
ReturnType: ok(
|
|
80
|
+
'@Serializable\ndata class ReturnType(\n val count: Long,\n)',
|
|
81
|
+
'ReturnType',
|
|
82
|
+
),
|
|
64
83
|
})
|
|
65
84
|
|
|
66
85
|
const files = await runPipeline({
|
|
@@ -120,4 +120,83 @@ public enum Users {
|
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
|
+
|
|
124
|
+
public enum DownloadUser {
|
|
125
|
+
public static let method = "GET"
|
|
126
|
+
public static let pathTemplate = "/users/{id}/download"
|
|
127
|
+
public static func path(_ p: PathParams) -> String { return "/users/\(p.id)/download" }
|
|
128
|
+
|
|
129
|
+
public struct PathParams: Codable {
|
|
130
|
+
public let id: String
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
public struct Response: Codable {
|
|
134
|
+
public let id: String
|
|
135
|
+
public let name: String
|
|
136
|
+
public let createdAt: Date
|
|
137
|
+
public let address: Address
|
|
138
|
+
|
|
139
|
+
enum CodingKeys: String, CodingKey {
|
|
140
|
+
case id, name
|
|
141
|
+
case createdAt = "created-at"
|
|
142
|
+
case address
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
public struct Address: Codable {
|
|
146
|
+
public let street: String
|
|
147
|
+
public let city: String
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
public struct ResponseHeaders: Codable {
|
|
152
|
+
public let xDownloadToken: String
|
|
153
|
+
public let contentDisposition: String?
|
|
154
|
+
|
|
155
|
+
enum CodingKeys: String, CodingKey {
|
|
156
|
+
case xDownloadToken = "x-download-token"
|
|
157
|
+
case contentDisposition = "content-disposition"
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
public enum WatchUsers {
|
|
163
|
+
public static let method = "GET"
|
|
164
|
+
public static let pathTemplate = "/users/watch"
|
|
165
|
+
public static let path = "/users/watch"
|
|
166
|
+
|
|
167
|
+
public struct Query: Codable {
|
|
168
|
+
public let status: Status?
|
|
169
|
+
public let limit: Int64?
|
|
170
|
+
|
|
171
|
+
public enum Status: String, Codable {
|
|
172
|
+
case active
|
|
173
|
+
case inactive
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
public struct ResponseHeaders: Codable {
|
|
178
|
+
public let xDownloadToken: String
|
|
179
|
+
public let contentDisposition: String?
|
|
180
|
+
|
|
181
|
+
enum CodingKeys: String, CodingKey {
|
|
182
|
+
case xDownloadToken = "x-download-token"
|
|
183
|
+
case contentDisposition = "content-disposition"
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
public struct Yield: Codable {
|
|
188
|
+
public let id: String
|
|
189
|
+
public let event: Event
|
|
190
|
+
|
|
191
|
+
public enum Event: String, Codable {
|
|
192
|
+
case created
|
|
193
|
+
case updated
|
|
194
|
+
case deleted
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
public struct ReturnType: Codable {
|
|
199
|
+
public let count: Int64
|
|
200
|
+
}
|
|
201
|
+
}
|
|
123
202
|
}
|
|
@@ -28,23 +28,20 @@ const envelope: DocEnvelope = {
|
|
|
28
28
|
method: 'get',
|
|
29
29
|
path: '/things/:id',
|
|
30
30
|
fullPath: '/things/:id',
|
|
31
|
-
jsonSchema: {
|
|
32
|
-
|
|
33
|
-
// `route.schema.returnType` directly; mirror what the codegen
|
|
34
|
-
// pipeline downstream expects rather than the doc-builder
|
|
35
|
-
// `jsonSchema` shape.
|
|
36
|
-
schema: {
|
|
37
|
-
input: {
|
|
31
|
+
jsonSchema: {
|
|
32
|
+
req: {
|
|
38
33
|
pathParams: {
|
|
39
34
|
type: 'object',
|
|
40
35
|
properties: { id: { type: 'string' } },
|
|
41
36
|
required: ['id'],
|
|
42
37
|
},
|
|
43
38
|
},
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
39
|
+
res: {
|
|
40
|
+
body: {
|
|
41
|
+
type: 'object',
|
|
42
|
+
properties: { id: { type: 'string' } },
|
|
43
|
+
required: ['id'],
|
|
44
|
+
},
|
|
48
45
|
},
|
|
49
46
|
},
|
|
50
47
|
errors: [],
|
|
@@ -34,11 +34,9 @@ describe('emitSwiftRoute', () => {
|
|
|
34
34
|
name: 'GetUser',
|
|
35
35
|
method: 'GET',
|
|
36
36
|
fullPath: '/users/:id',
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
},
|
|
41
|
-
returnType: { type: 'object' },
|
|
37
|
+
jsonSchema: {
|
|
38
|
+
req: { pathParams: { type: 'object' } },
|
|
39
|
+
res: { body: { type: 'object' } },
|
|
42
40
|
},
|
|
43
41
|
errors: [],
|
|
44
42
|
} as unknown as AnyHttpRouteDoc
|
|
@@ -64,7 +62,10 @@ describe('emitSwiftRoute', () => {
|
|
|
64
62
|
name: 'CreateUser',
|
|
65
63
|
method: 'POST',
|
|
66
64
|
fullPath: '/users',
|
|
67
|
-
|
|
65
|
+
jsonSchema: {
|
|
66
|
+
req: { body: { type: 'object' } },
|
|
67
|
+
res: { body: { type: 'object' } },
|
|
68
|
+
},
|
|
68
69
|
errors: [],
|
|
69
70
|
} as unknown as AnyHttpRouteDoc
|
|
70
71
|
|
|
@@ -84,7 +85,10 @@ describe('emitSwiftRoute', () => {
|
|
|
84
85
|
name: 'GetUser',
|
|
85
86
|
method: 'GET',
|
|
86
87
|
fullPath: '/users/:id',
|
|
87
|
-
|
|
88
|
+
jsonSchema: {
|
|
89
|
+
req: { pathParams: { type: 'object' } },
|
|
90
|
+
res: { body: { type: 'object' } },
|
|
91
|
+
},
|
|
88
92
|
errors: ['NotFound'],
|
|
89
93
|
} as unknown as AnyHttpRouteDoc
|
|
90
94
|
|
|
@@ -103,14 +107,14 @@ describe('emitSwiftRoute', () => {
|
|
|
103
107
|
it('silently skips error keys with no schema in the envelope map', () => {
|
|
104
108
|
const route = {
|
|
105
109
|
kind: 'api', name: 'GetUser', method: 'GET', fullPath: '/users',
|
|
106
|
-
|
|
110
|
+
jsonSchema: {}, errors: ['UnknownTaxonomyKey'],
|
|
107
111
|
} as unknown as AnyHttpRouteDoc
|
|
108
112
|
const result = emitSwiftRoute(route, createStubSwiftEmitter({}), new Map())
|
|
109
113
|
expect(result.code).not.toContain('enum Errors {')
|
|
110
114
|
})
|
|
111
115
|
|
|
112
116
|
it('returns skipped:true for stream routes', () => {
|
|
113
|
-
const route = { kind: 'stream', name: 'WatchUsers', method: 'GET', path: '/users/stream',
|
|
117
|
+
const route = { kind: 'stream', name: 'WatchUsers', method: 'GET', path: '/users/stream', jsonSchema: {}, errors: [] } as unknown as AnyHttpRouteDoc
|
|
114
118
|
const result = emitSwiftRoute(route, createStubSwiftEmitter({}), noErrors)
|
|
115
119
|
expect(result.code).toBe('')
|
|
116
120
|
expect(result.skipped).toBe(true)
|
|
@@ -122,9 +126,9 @@ describe('emitSwiftRoute', () => {
|
|
|
122
126
|
name: 'GetUser',
|
|
123
127
|
method: 'GET',
|
|
124
128
|
fullPath: '/users/:id',
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
129
|
+
jsonSchema: {
|
|
130
|
+
req: { pathParams: { type: 'object' } },
|
|
131
|
+
res: { body: { type: 'object' } },
|
|
128
132
|
},
|
|
129
133
|
errors: ['NotFound'],
|
|
130
134
|
} as unknown as AnyHttpRouteDoc
|
|
@@ -157,13 +161,13 @@ describe('emitSwiftRoute', () => {
|
|
|
157
161
|
name: 'X',
|
|
158
162
|
method: 'POST',
|
|
159
163
|
fullPath: '/x/:id',
|
|
160
|
-
|
|
161
|
-
|
|
164
|
+
jsonSchema: {
|
|
165
|
+
req: {
|
|
162
166
|
pathParams: { type: 'object' },
|
|
163
167
|
query: { type: 'object' },
|
|
164
168
|
body: { type: 'object' },
|
|
165
169
|
},
|
|
166
|
-
|
|
170
|
+
res: { body: { type: 'object' } },
|
|
167
171
|
},
|
|
168
172
|
errors: ['Z'],
|
|
169
173
|
} as unknown as AnyHttpRouteDoc
|
|
@@ -189,7 +193,7 @@ describe('emitSwiftRoute', () => {
|
|
|
189
193
|
name: 'X',
|
|
190
194
|
method: 'GET',
|
|
191
195
|
fullPath: '/x',
|
|
192
|
-
|
|
196
|
+
jsonSchema: { res: { body: { type: 'object' } } },
|
|
193
197
|
errors: [],
|
|
194
198
|
} as unknown as AnyHttpRouteDoc
|
|
195
199
|
|
|
@@ -209,13 +213,92 @@ describe('emitSwiftRoute', () => {
|
|
|
209
213
|
expect(calls[0]!.opts.uncountableWords).toEqual(['data', 'metadata'])
|
|
210
214
|
})
|
|
211
215
|
|
|
216
|
+
it('emits ResponseHeaders slot when res.headers is declared on an api route', () => {
|
|
217
|
+
const route = {
|
|
218
|
+
kind: 'api',
|
|
219
|
+
name: 'DownloadUser',
|
|
220
|
+
method: 'GET',
|
|
221
|
+
fullPath: '/users/:id/download',
|
|
222
|
+
jsonSchema: {
|
|
223
|
+
req: { pathParams: { type: 'object' } },
|
|
224
|
+
res: {
|
|
225
|
+
body: { type: 'object' },
|
|
226
|
+
headers: { type: 'object' },
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
errors: [],
|
|
230
|
+
} as unknown as AnyHttpRouteDoc
|
|
231
|
+
|
|
232
|
+
const { emitter, calls } = makeSpyEmitter({
|
|
233
|
+
PathParams: ok('public struct PathParams: Codable { public let id: String }', 'PathParams'),
|
|
234
|
+
Response: ok('public struct Response: Codable { public let url: String }', 'Response'),
|
|
235
|
+
ResponseHeaders: ok(
|
|
236
|
+
'public struct ResponseHeaders: Codable { public let xDownloadToken: String }',
|
|
237
|
+
'ResponseHeaders',
|
|
238
|
+
),
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
const result = emitSwiftRoute(route, emitter, noErrors)
|
|
242
|
+
expect(calls.map((c) => c.opts.rootTypeName)).toEqual(['PathParams', 'Response', 'ResponseHeaders'])
|
|
243
|
+
expect(result.code).toContain('public struct ResponseHeaders: Codable { public let xDownloadToken: String }')
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
it('does not emit ResponseHeaders when res.headers is absent', () => {
|
|
247
|
+
const route = {
|
|
248
|
+
kind: 'api',
|
|
249
|
+
name: 'GetUser',
|
|
250
|
+
method: 'GET',
|
|
251
|
+
fullPath: '/users/:id',
|
|
252
|
+
jsonSchema: {
|
|
253
|
+
req: { pathParams: { type: 'object' } },
|
|
254
|
+
res: { body: { type: 'object' } },
|
|
255
|
+
},
|
|
256
|
+
errors: [],
|
|
257
|
+
} as unknown as AnyHttpRouteDoc
|
|
258
|
+
|
|
259
|
+
const { emitter, calls } = makeSpyEmitter({
|
|
260
|
+
PathParams: ok('public struct PathParams: Codable { public let id: String }', 'PathParams'),
|
|
261
|
+
Response: ok('public struct Response: Codable { public let id: String }', 'Response'),
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
emitSwiftRoute(route, emitter, noErrors)
|
|
265
|
+
expect(calls.map((c) => c.opts.rootTypeName)).not.toContain('ResponseHeaders')
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
it('processes http-stream routes (not skipped) and emits Yield/ReturnType slots', () => {
|
|
269
|
+
const route = {
|
|
270
|
+
kind: 'http-stream',
|
|
271
|
+
name: 'TailLogs',
|
|
272
|
+
method: 'GET',
|
|
273
|
+
fullPath: '/logs/tail',
|
|
274
|
+
jsonSchema: {
|
|
275
|
+
req: { query: { type: 'object' } },
|
|
276
|
+
yield: { type: 'object' },
|
|
277
|
+
returnType: { type: 'object' },
|
|
278
|
+
},
|
|
279
|
+
errors: [],
|
|
280
|
+
} as unknown as AnyHttpRouteDoc
|
|
281
|
+
|
|
282
|
+
const { emitter, calls } = makeSpyEmitter({
|
|
283
|
+
Query: ok('public struct Query: Codable { public let filter: String? }', 'Query'),
|
|
284
|
+
Yield: ok('public struct Yield: Codable { public let line: String }', 'Yield'),
|
|
285
|
+
ReturnType: ok('public struct ReturnType: Codable { public let count: Int }', 'ReturnType'),
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
const result = emitSwiftRoute(route, emitter, noErrors)
|
|
289
|
+
expect(result.skipped).toBeFalsy()
|
|
290
|
+
expect(calls.map((c) => c.opts.rootTypeName)).toEqual(['Query', 'Yield', 'ReturnType'])
|
|
291
|
+
expect(result.code).toContain('public static let method = "GET"')
|
|
292
|
+
expect(result.code).toContain('public static let path = "/logs/tail"')
|
|
293
|
+
})
|
|
294
|
+
|
|
212
295
|
it('does not include passthrough keys when caller omits them', () => {
|
|
213
296
|
const route = {
|
|
214
297
|
kind: 'api',
|
|
215
298
|
name: 'X',
|
|
216
299
|
method: 'GET',
|
|
217
300
|
fullPath: '/x',
|
|
218
|
-
|
|
301
|
+
jsonSchema: { res: { body: { type: 'object' } } },
|
|
219
302
|
errors: [],
|
|
220
303
|
} as unknown as AnyHttpRouteDoc
|
|
221
304
|
|
|
@@ -246,7 +329,7 @@ describe('emitSwiftRoute', () => {
|
|
|
246
329
|
name: 'GetUser',
|
|
247
330
|
method: 'GET',
|
|
248
331
|
fullPath: '/users/:id',
|
|
249
|
-
|
|
332
|
+
jsonSchema: { req: { pathParams: { type: 'object' } } },
|
|
250
333
|
errors: [],
|
|
251
334
|
} as unknown as AnyHttpRouteDoc
|
|
252
335
|
|
|
@@ -265,7 +348,7 @@ describe('emitSwiftRoute', () => {
|
|
|
265
348
|
name: 'GetUser',
|
|
266
349
|
method: 'GET',
|
|
267
350
|
fullPath: '/users/:id',
|
|
268
|
-
|
|
351
|
+
jsonSchema: { req: { pathParams: { type: 'object' } } },
|
|
269
352
|
errors: ['NotFound'],
|
|
270
353
|
} as unknown as AnyHttpRouteDoc
|
|
271
354
|
|
|
@@ -291,7 +374,7 @@ describe('emitSwiftRoute', () => {
|
|
|
291
374
|
name: 'CreateUser',
|
|
292
375
|
method: 'POST',
|
|
293
376
|
fullPath: '/users',
|
|
294
|
-
|
|
377
|
+
jsonSchema: {},
|
|
295
378
|
errors: [],
|
|
296
379
|
} as unknown as AnyHttpRouteDoc
|
|
297
380
|
const result = emitSwiftRoute(route, createStubSwiftEmitter({}), noErrors, { accessLevel: 'internal' })
|
|
@@ -18,7 +18,10 @@ describe('emitSwiftScope', () => {
|
|
|
18
18
|
name: 'GetUser',
|
|
19
19
|
method: 'GET',
|
|
20
20
|
fullPath: '/users/:id',
|
|
21
|
-
|
|
21
|
+
jsonSchema: {
|
|
22
|
+
req: { pathParams: { type: 'object' } },
|
|
23
|
+
res: { body: { type: 'object' } },
|
|
24
|
+
},
|
|
22
25
|
errors: [],
|
|
23
26
|
} as unknown as AnyHttpRouteDoc
|
|
24
27
|
|
|
@@ -41,8 +44,8 @@ describe('emitSwiftScope', () => {
|
|
|
41
44
|
})
|
|
42
45
|
|
|
43
46
|
it('joins multiple routes inside one scope enum, separated by a blank line', () => {
|
|
44
|
-
const route1 = { kind: 'api', name: 'GetUser', method: 'GET', fullPath: '/users/:id',
|
|
45
|
-
const route2 = { kind: 'api', name: 'CreateUser', method: 'POST', fullPath: '/users',
|
|
47
|
+
const route1 = { kind: 'api', name: 'GetUser', method: 'GET', fullPath: '/users/:id', jsonSchema: {}, errors: [] } as unknown as AnyHttpRouteDoc
|
|
48
|
+
const route2 = { kind: 'api', name: 'CreateUser', method: 'POST', fullPath: '/users', jsonSchema: {}, errors: [] } as unknown as AnyHttpRouteDoc
|
|
46
49
|
const group: ScopeGroup = { scopeKey: 'users', camelCase: 'users', routes: [route1, route2] }
|
|
47
50
|
const emitter = createStubSwiftEmitter({})
|
|
48
51
|
|
|
@@ -74,8 +77,8 @@ describe('emitSwiftScope', () => {
|
|
|
74
77
|
})
|
|
75
78
|
|
|
76
79
|
it('dedupes and sorts imports across all routes in the scope', () => {
|
|
77
|
-
const route1 = { kind: 'api', name: 'A', method: 'GET', fullPath: '/a',
|
|
78
|
-
const route2 = { kind: 'api', name: 'B', method: 'GET', fullPath: '/b',
|
|
80
|
+
const route1 = { kind: 'api', name: 'A', method: 'GET', fullPath: '/a', jsonSchema: { res: { body: { type: 'object' } } }, errors: [] } as unknown as AnyHttpRouteDoc
|
|
81
|
+
const route2 = { kind: 'api', name: 'B', method: 'GET', fullPath: '/b', jsonSchema: { res: { body: { type: 'object' } } }, errors: [] } as unknown as AnyHttpRouteDoc
|
|
79
82
|
const group: ScopeGroup = { scopeKey: 'multi', camelCase: 'multi', routes: [route1, route2] }
|
|
80
83
|
const emitter = {
|
|
81
84
|
emit(_s: unknown, opts: SwiftEmitOptions): SwiftEmitResult {
|
|
@@ -93,7 +96,7 @@ describe('emitSwiftScope', () => {
|
|
|
93
96
|
})
|
|
94
97
|
|
|
95
98
|
it('threads all 6 passthrough opts to every emitter call', () => {
|
|
96
|
-
const route = { kind: 'api', name: 'X', method: 'GET', fullPath: '/x',
|
|
99
|
+
const route = { kind: 'api', name: 'X', method: 'GET', fullPath: '/x', jsonSchema: { res: { body: { type: 'object' } } }, errors: [] } as unknown as AnyHttpRouteDoc
|
|
97
100
|
const group: ScopeGroup = { scopeKey: 'x', camelCase: 'x', routes: [route] }
|
|
98
101
|
|
|
99
102
|
const calls: SwiftEmitOptions[] = []
|
|
@@ -130,7 +133,7 @@ describe('emitSwiftScope', () => {
|
|
|
130
133
|
})
|
|
131
134
|
|
|
132
135
|
it('applies accessLevel to the outer scope enum and the route enum wrappers', () => {
|
|
133
|
-
const route = { kind: 'api', name: 'GetUser', method: 'GET', fullPath: '/users',
|
|
136
|
+
const route = { kind: 'api', name: 'GetUser', method: 'GET', fullPath: '/users', jsonSchema: {}, errors: [] } as unknown as AnyHttpRouteDoc
|
|
134
137
|
const group: ScopeGroup = { scopeKey: 'users', camelCase: 'users', routes: [route] }
|
|
135
138
|
const emitter = createStubSwiftEmitter({})
|
|
136
139
|
|
|
@@ -142,8 +145,8 @@ describe('emitSwiftScope', () => {
|
|
|
142
145
|
})
|
|
143
146
|
|
|
144
147
|
it('collects skipped stream-route names', () => {
|
|
145
|
-
const stream = { kind: 'stream', name: 'WatchUsers', method: 'GET', path: '/u/stream',
|
|
146
|
-
const api = { kind: 'api', name: 'GetUser', method: 'GET', fullPath: '/u',
|
|
148
|
+
const stream = { kind: 'stream', name: 'WatchUsers', method: 'GET', path: '/u/stream', jsonSchema: {}, errors: [] } as unknown as AnyHttpRouteDoc
|
|
149
|
+
const api = { kind: 'api', name: 'GetUser', method: 'GET', fullPath: '/u', jsonSchema: { res: { body: { type: 'object' } } }, errors: [] } as unknown as AnyHttpRouteDoc
|
|
147
150
|
const group: ScopeGroup = { scopeKey: 'u', camelCase: 'u', routes: [stream, api] }
|
|
148
151
|
|
|
149
152
|
const emitter = createStubSwiftEmitter({
|