ts-procedures 5.14.0 → 5.16.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/agent_config/claude-code/skills/ts-procedures/SKILL.md +1 -1
- package/agent_config/claude-code/skills/ts-procedures/api-reference.md +59 -4
- package/agent_config/claude-code/skills/ts-procedures/patterns.md +103 -3
- package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/client.md +36 -5
- package/agent_config/copilot/copilot-instructions.md +56 -7
- package/agent_config/cursor/cursorrules +56 -7
- package/build/client/call.d.ts +18 -9
- package/build/client/call.js +25 -19
- package/build/client/call.js.map +1 -1
- package/build/client/call.test.js +167 -17
- package/build/client/call.test.js.map +1 -1
- package/build/client/index.d.ts +1 -1
- package/build/client/index.js +18 -3
- package/build/client/index.js.map +1 -1
- package/build/client/index.test.js +104 -0
- package/build/client/index.test.js.map +1 -1
- package/build/client/resolve-options.d.ts +45 -0
- package/build/client/resolve-options.js +82 -0
- package/build/client/resolve-options.js.map +1 -0
- package/build/client/resolve-options.test.js +158 -0
- package/build/client/resolve-options.test.js.map +1 -0
- package/build/client/stream.d.ts +18 -9
- package/build/client/stream.js +24 -19
- package/build/client/stream.js.map +1 -1
- package/build/client/stream.test.js +102 -46
- package/build/client/stream.test.js.map +1 -1
- package/build/client/types.d.ts +68 -1
- package/build/client/types.js +1 -1
- package/build/codegen/bin/cli.d.ts +2 -0
- package/build/codegen/bin/cli.js +11 -0
- package/build/codegen/bin/cli.js.map +1 -1
- package/build/codegen/bin/cli.test.js +30 -0
- package/build/codegen/bin/cli.test.js.map +1 -1
- package/build/codegen/e2e.test.js +141 -0
- package/build/codegen/e2e.test.js.map +1 -1
- package/build/codegen/emit-client-runtime.js +3 -0
- package/build/codegen/emit-client-runtime.js.map +1 -1
- package/build/codegen/index.d.ts +1 -0
- package/build/codegen/index.js +1 -0
- package/build/codegen/index.js.map +1 -1
- package/build/codegen/pipeline.d.ts +1 -0
- package/build/codegen/pipeline.js +8 -2
- package/build/codegen/pipeline.js.map +1 -1
- package/build/codegen/pipeline.test.js +34 -1
- package/build/codegen/pipeline.test.js.map +1 -1
- package/docs/client-and-codegen.md +125 -2
- package/package.json +1 -1
- package/src/client/call.test.ts +202 -29
- package/src/client/call.ts +41 -28
- package/src/client/index.test.ts +117 -0
- package/src/client/index.ts +25 -8
- package/src/client/resolve-options.test.ts +205 -0
- package/src/client/resolve-options.ts +113 -0
- package/src/client/stream.test.ts +132 -107
- package/src/client/stream.ts +40 -25
- package/src/client/types.ts +74 -2
- package/src/codegen/bin/cli.test.ts +35 -0
- package/src/codegen/bin/cli.ts +11 -0
- package/src/codegen/e2e.test.ts +151 -0
- package/src/codegen/emit-client-runtime.ts +3 -0
- package/src/codegen/index.ts +2 -0
- package/src/codegen/pipeline.test.ts +46 -1
- package/src/codegen/pipeline.ts +9 -2
- package/src/implementations/http/README.md +11 -1
- package/build/src/client/call.d.ts +0 -14
- package/build/src/client/call.js +0 -47
- package/build/src/client/call.js.map +0 -1
- package/build/src/client/call.test.js +0 -124
- package/build/src/client/call.test.js.map +0 -1
- package/build/src/client/errors.d.ts +0 -25
- package/build/src/client/errors.js +0 -33
- package/build/src/client/errors.js.map +0 -1
- package/build/src/client/errors.test.d.ts +0 -1
- package/build/src/client/errors.test.js +0 -41
- package/build/src/client/errors.test.js.map +0 -1
- package/build/src/client/fetch-adapter.d.ts +0 -12
- package/build/src/client/fetch-adapter.js +0 -156
- package/build/src/client/fetch-adapter.js.map +0 -1
- package/build/src/client/fetch-adapter.test.d.ts +0 -1
- package/build/src/client/fetch-adapter.test.js +0 -271
- package/build/src/client/fetch-adapter.test.js.map +0 -1
- package/build/src/client/hooks.d.ts +0 -17
- package/build/src/client/hooks.js +0 -40
- package/build/src/client/hooks.js.map +0 -1
- package/build/src/client/hooks.test.d.ts +0 -1
- package/build/src/client/hooks.test.js +0 -163
- package/build/src/client/hooks.test.js.map +0 -1
- package/build/src/client/index.d.ts +0 -22
- package/build/src/client/index.js +0 -67
- package/build/src/client/index.js.map +0 -1
- package/build/src/client/index.test.d.ts +0 -1
- package/build/src/client/index.test.js +0 -231
- package/build/src/client/index.test.js.map +0 -1
- package/build/src/client/request-builder.d.ts +0 -13
- package/build/src/client/request-builder.js +0 -53
- package/build/src/client/request-builder.js.map +0 -1
- package/build/src/client/request-builder.test.d.ts +0 -1
- package/build/src/client/request-builder.test.js +0 -160
- package/build/src/client/request-builder.test.js.map +0 -1
- package/build/src/client/stream.d.ts +0 -27
- package/build/src/client/stream.js +0 -118
- package/build/src/client/stream.js.map +0 -1
- package/build/src/client/stream.test.d.ts +0 -1
- package/build/src/client/stream.test.js +0 -228
- package/build/src/client/stream.test.js.map +0 -1
- package/build/src/client/types.d.ts +0 -78
- package/build/src/client/types.js +0 -3
- package/build/src/client/types.js.map +0 -1
- package/build/src/codegen/bin/cli.d.ts +0 -45
- package/build/src/codegen/bin/cli.js +0 -246
- package/build/src/codegen/bin/cli.js.map +0 -1
- package/build/src/codegen/bin/cli.test.d.ts +0 -1
- package/build/src/codegen/bin/cli.test.js +0 -220
- package/build/src/codegen/bin/cli.test.js.map +0 -1
- package/build/src/codegen/constants.d.ts +0 -1
- package/build/src/codegen/constants.js +0 -2
- package/build/src/codegen/constants.js.map +0 -1
- package/build/src/codegen/e2e.test.d.ts +0 -1
- package/build/src/codegen/e2e.test.js +0 -464
- package/build/src/codegen/e2e.test.js.map +0 -1
- package/build/src/codegen/emit-client-runtime.d.ts +0 -9
- package/build/src/codegen/emit-client-runtime.js +0 -99
- package/build/src/codegen/emit-client-runtime.js.map +0 -1
- package/build/src/codegen/emit-client-runtime.test.d.ts +0 -1
- package/build/src/codegen/emit-client-runtime.test.js +0 -78
- package/build/src/codegen/emit-client-runtime.test.js.map +0 -1
- package/build/src/codegen/emit-client-types.d.ts +0 -8
- package/build/src/codegen/emit-client-types.js +0 -25
- package/build/src/codegen/emit-client-types.js.map +0 -1
- package/build/src/codegen/emit-client-types.test.d.ts +0 -1
- package/build/src/codegen/emit-client-types.test.js +0 -33
- package/build/src/codegen/emit-client-types.test.js.map +0 -1
- package/build/src/codegen/emit-errors.d.ts +0 -19
- package/build/src/codegen/emit-errors.js +0 -59
- package/build/src/codegen/emit-errors.js.map +0 -1
- package/build/src/codegen/emit-errors.test.d.ts +0 -1
- package/build/src/codegen/emit-errors.test.js +0 -175
- package/build/src/codegen/emit-errors.test.js.map +0 -1
- package/build/src/codegen/emit-index.d.ts +0 -12
- package/build/src/codegen/emit-index.js +0 -41
- package/build/src/codegen/emit-index.js.map +0 -1
- package/build/src/codegen/emit-index.test.d.ts +0 -1
- package/build/src/codegen/emit-index.test.js +0 -106
- package/build/src/codegen/emit-index.test.js.map +0 -1
- package/build/src/codegen/emit-scope.d.ts +0 -15
- package/build/src/codegen/emit-scope.js +0 -299
- package/build/src/codegen/emit-scope.js.map +0 -1
- package/build/src/codegen/emit-scope.test.d.ts +0 -1
- package/build/src/codegen/emit-scope.test.js +0 -559
- package/build/src/codegen/emit-scope.test.js.map +0 -1
- package/build/src/codegen/emit-types.d.ts +0 -43
- package/build/src/codegen/emit-types.js +0 -111
- package/build/src/codegen/emit-types.js.map +0 -1
- package/build/src/codegen/emit-types.test.d.ts +0 -1
- package/build/src/codegen/emit-types.test.js +0 -184
- package/build/src/codegen/emit-types.test.js.map +0 -1
- package/build/src/codegen/group-routes.d.ts +0 -23
- package/build/src/codegen/group-routes.js +0 -46
- package/build/src/codegen/group-routes.js.map +0 -1
- package/build/src/codegen/group-routes.test.d.ts +0 -1
- package/build/src/codegen/group-routes.test.js +0 -131
- package/build/src/codegen/group-routes.test.js.map +0 -1
- package/build/src/codegen/index.d.ts +0 -15
- package/build/src/codegen/index.js +0 -16
- package/build/src/codegen/index.js.map +0 -1
- package/build/src/codegen/naming.d.ts +0 -7
- package/build/src/codegen/naming.js +0 -21
- package/build/src/codegen/naming.js.map +0 -1
- package/build/src/codegen/naming.test.d.ts +0 -1
- package/build/src/codegen/naming.test.js +0 -40
- package/build/src/codegen/naming.test.js.map +0 -1
- package/build/src/codegen/pipeline.d.ts +0 -17
- package/build/src/codegen/pipeline.js +0 -78
- package/build/src/codegen/pipeline.js.map +0 -1
- package/build/src/codegen/pipeline.test.d.ts +0 -1
- package/build/src/codegen/pipeline.test.js +0 -269
- package/build/src/codegen/pipeline.test.js.map +0 -1
- package/build/src/codegen/resolve-envelope.d.ts +0 -7
- package/build/src/codegen/resolve-envelope.js +0 -46
- package/build/src/codegen/resolve-envelope.js.map +0 -1
- package/build/src/codegen/resolve-envelope.test.d.ts +0 -1
- package/build/src/codegen/resolve-envelope.test.js +0 -69
- package/build/src/codegen/resolve-envelope.test.js.map +0 -1
- package/build/src/errors.d.ts +0 -33
- package/build/src/errors.js +0 -91
- package/build/src/errors.js.map +0 -1
- package/build/src/errors.test.d.ts +0 -1
- package/build/src/errors.test.js +0 -122
- package/build/src/errors.test.js.map +0 -1
- package/build/src/exports.d.ts +0 -7
- package/build/src/exports.js +0 -8
- package/build/src/exports.js.map +0 -1
- package/build/src/implementations/http/doc-registry.d.ts +0 -12
- package/build/src/implementations/http/doc-registry.js +0 -114
- package/build/src/implementations/http/doc-registry.js.map +0 -1
- package/build/src/implementations/http/doc-registry.test.d.ts +0 -1
- package/build/src/implementations/http/doc-registry.test.js +0 -347
- package/build/src/implementations/http/doc-registry.test.js.map +0 -1
- package/build/src/implementations/http/express-rpc/index.d.ts +0 -94
- package/build/src/implementations/http/express-rpc/index.js +0 -185
- package/build/src/implementations/http/express-rpc/index.js.map +0 -1
- package/build/src/implementations/http/express-rpc/index.test.d.ts +0 -1
- package/build/src/implementations/http/express-rpc/index.test.js +0 -684
- package/build/src/implementations/http/express-rpc/index.test.js.map +0 -1
- package/build/src/implementations/http/express-rpc/types.d.ts +0 -11
- package/build/src/implementations/http/express-rpc/types.js +0 -2
- package/build/src/implementations/http/express-rpc/types.js.map +0 -1
- package/build/src/implementations/http/hono-api/index.d.ts +0 -102
- package/build/src/implementations/http/hono-api/index.js +0 -341
- package/build/src/implementations/http/hono-api/index.js.map +0 -1
- package/build/src/implementations/http/hono-api/index.test.d.ts +0 -1
- package/build/src/implementations/http/hono-api/index.test.js +0 -992
- package/build/src/implementations/http/hono-api/index.test.js.map +0 -1
- package/build/src/implementations/http/hono-api/types.d.ts +0 -13
- package/build/src/implementations/http/hono-api/types.js +0 -2
- package/build/src/implementations/http/hono-api/types.js.map +0 -1
- package/build/src/implementations/http/hono-rpc/index.d.ts +0 -92
- package/build/src/implementations/http/hono-rpc/index.js +0 -161
- package/build/src/implementations/http/hono-rpc/index.js.map +0 -1
- package/build/src/implementations/http/hono-rpc/index.test.d.ts +0 -1
- package/build/src/implementations/http/hono-rpc/index.test.js +0 -803
- package/build/src/implementations/http/hono-rpc/index.test.js.map +0 -1
- package/build/src/implementations/http/hono-rpc/types.d.ts +0 -11
- package/build/src/implementations/http/hono-rpc/types.js +0 -2
- package/build/src/implementations/http/hono-rpc/types.js.map +0 -1
- package/build/src/implementations/http/hono-stream/index.d.ts +0 -120
- package/build/src/implementations/http/hono-stream/index.js +0 -309
- package/build/src/implementations/http/hono-stream/index.js.map +0 -1
- package/build/src/implementations/http/hono-stream/index.test.d.ts +0 -1
- package/build/src/implementations/http/hono-stream/index.test.js +0 -1356
- package/build/src/implementations/http/hono-stream/index.test.js.map +0 -1
- package/build/src/implementations/http/hono-stream/types.d.ts +0 -15
- package/build/src/implementations/http/hono-stream/types.js +0 -2
- package/build/src/implementations/http/hono-stream/types.js.map +0 -1
- package/build/src/implementations/types.d.ts +0 -142
- package/build/src/implementations/types.js +0 -2
- package/build/src/implementations/types.js.map +0 -1
- package/build/src/index.d.ts +0 -165
- package/build/src/index.js +0 -253
- package/build/src/index.js.map +0 -1
- package/build/src/index.test.d.ts +0 -1
- package/build/src/index.test.js +0 -890
- package/build/src/index.test.js.map +0 -1
- package/build/src/schema/compute-schema.d.ts +0 -35
- package/build/src/schema/compute-schema.js +0 -41
- package/build/src/schema/compute-schema.js.map +0 -1
- package/build/src/schema/compute-schema.test.d.ts +0 -1
- package/build/src/schema/compute-schema.test.js +0 -107
- package/build/src/schema/compute-schema.test.js.map +0 -1
- package/build/src/schema/extract-json-schema.d.ts +0 -2
- package/build/src/schema/extract-json-schema.js +0 -12
- package/build/src/schema/extract-json-schema.js.map +0 -1
- package/build/src/schema/extract-json-schema.test.d.ts +0 -1
- package/build/src/schema/extract-json-schema.test.js +0 -23
- package/build/src/schema/extract-json-schema.test.js.map +0 -1
- package/build/src/schema/parser.d.ts +0 -28
- package/build/src/schema/parser.js +0 -170
- package/build/src/schema/parser.js.map +0 -1
- package/build/src/schema/parser.test.d.ts +0 -1
- package/build/src/schema/parser.test.js +0 -120
- package/build/src/schema/parser.test.js.map +0 -1
- package/build/src/schema/resolve-schema-lib.d.ts +0 -12
- package/build/src/schema/resolve-schema-lib.js +0 -11
- package/build/src/schema/resolve-schema-lib.js.map +0 -1
- package/build/src/schema/resolve-schema-lib.test.d.ts +0 -1
- package/build/src/schema/resolve-schema-lib.test.js +0 -17
- package/build/src/schema/resolve-schema-lib.test.js.map +0 -1
- package/build/src/schema/types.d.ts +0 -8
- package/build/src/schema/types.js +0 -2
- package/build/src/schema/types.js.map +0 -1
- package/build/src/stack-utils.d.ts +0 -25
- package/build/src/stack-utils.js +0 -95
- package/build/src/stack-utils.js.map +0 -1
- package/build/src/stack-utils.test.d.ts +0 -1
- package/build/src/stack-utils.test.js +0 -80
- package/build/src/stack-utils.test.js.map +0 -1
- /package/build/{src/client/call.test.d.ts → client/resolve-options.test.d.ts} +0 -0
|
@@ -7,6 +7,8 @@ import type {
|
|
|
7
7
|
AdapterStreamResponse,
|
|
8
8
|
ClientHooks,
|
|
9
9
|
StreamDescriptor,
|
|
10
|
+
ProcedureCallDefaults,
|
|
11
|
+
ProcedureCallOptions,
|
|
10
12
|
} from './types.js'
|
|
11
13
|
|
|
12
14
|
// ── helpers ───────────────────────────────────────────────
|
|
@@ -47,6 +49,26 @@ function makeStreamAdapter(
|
|
|
47
49
|
}
|
|
48
50
|
}
|
|
49
51
|
|
|
52
|
+
interface RunConfig {
|
|
53
|
+
adapter: ClientAdapter
|
|
54
|
+
hooks?: ClientHooks
|
|
55
|
+
defaults?: ProcedureCallDefaults
|
|
56
|
+
options?: ProcedureCallOptions
|
|
57
|
+
descriptor?: StreamDescriptor
|
|
58
|
+
basePath?: string
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function run<TYield, TReturn = void>({
|
|
62
|
+
adapter,
|
|
63
|
+
hooks = {},
|
|
64
|
+
defaults,
|
|
65
|
+
options,
|
|
66
|
+
descriptor = makeDescriptor(),
|
|
67
|
+
basePath = 'https://api.example.com',
|
|
68
|
+
}: RunConfig) {
|
|
69
|
+
return executeStream<TYield, TReturn>({ descriptor, basePath, adapter, hooks, defaults, options })
|
|
70
|
+
}
|
|
71
|
+
|
|
50
72
|
// ── createTypedStream — SSE mode ──────────────────────────
|
|
51
73
|
|
|
52
74
|
describe('createTypedStream — SSE mode', () => {
|
|
@@ -55,15 +77,10 @@ describe('createTypedStream — SSE mode', () => {
|
|
|
55
77
|
{ data: { count: 1 }, event: 'update' },
|
|
56
78
|
{ data: { count: 2 }, event: 'update' },
|
|
57
79
|
]
|
|
58
|
-
const stream = createTypedStream<{ count: number }, void>(
|
|
59
|
-
makeAsyncIterable(sseItems),
|
|
60
|
-
'sse'
|
|
61
|
-
)
|
|
80
|
+
const stream = createTypedStream<{ count: number }, void>(makeAsyncIterable(sseItems), 'sse')
|
|
62
81
|
|
|
63
82
|
const received: { count: number }[] = []
|
|
64
|
-
for await (const item of stream)
|
|
65
|
-
received.push(item)
|
|
66
|
-
}
|
|
83
|
+
for await (const item of stream) received.push(item)
|
|
67
84
|
expect(received).toEqual([{ count: 1 }, { count: 2 }])
|
|
68
85
|
})
|
|
69
86
|
|
|
@@ -74,34 +91,23 @@ describe('createTypedStream — SSE mode', () => {
|
|
|
74
91
|
]
|
|
75
92
|
const stream = createTypedStream<{ count: number }, { total: number }>(
|
|
76
93
|
makeAsyncIterable(sseItems),
|
|
77
|
-
'sse'
|
|
94
|
+
'sse',
|
|
78
95
|
)
|
|
79
96
|
|
|
80
97
|
const yielded: { count: number }[] = []
|
|
81
|
-
for await (const item of stream)
|
|
82
|
-
yielded.push(item)
|
|
83
|
-
}
|
|
98
|
+
for await (const item of stream) yielded.push(item)
|
|
84
99
|
|
|
85
|
-
// Only the 'update' event is yielded
|
|
86
100
|
expect(yielded).toEqual([{ count: 1 }])
|
|
87
|
-
|
|
88
|
-
// The 'return' event resolves .result
|
|
89
|
-
const result = await stream.result
|
|
90
|
-
expect(result).toEqual({ total: 99 })
|
|
101
|
+
await expect(stream.result).resolves.toEqual({ total: 99 })
|
|
91
102
|
})
|
|
92
103
|
|
|
93
104
|
it('resolves .result with undefined when no return event', async () => {
|
|
94
|
-
const sseItems = [
|
|
95
|
-
{ data: 'hello', event: 'message' },
|
|
96
|
-
]
|
|
97
105
|
const stream = createTypedStream<string, undefined>(
|
|
98
|
-
makeAsyncIterable(
|
|
99
|
-
'sse'
|
|
106
|
+
makeAsyncIterable([{ data: 'hello', event: 'message' }]),
|
|
107
|
+
'sse',
|
|
100
108
|
)
|
|
101
|
-
|
|
102
109
|
for await (const _ of stream) { /* drain */ }
|
|
103
|
-
|
|
104
|
-
expect(result).toBeUndefined()
|
|
110
|
+
await expect(stream.result).resolves.toBeUndefined()
|
|
105
111
|
})
|
|
106
112
|
|
|
107
113
|
it('rejects .result and re-throws on error', async () => {
|
|
@@ -111,30 +117,19 @@ describe('createTypedStream — SSE mode', () => {
|
|
|
111
117
|
}
|
|
112
118
|
|
|
113
119
|
const stream = createTypedStream<string, void>(errorIterable(), 'sse')
|
|
114
|
-
|
|
115
|
-
// Consuming the stream should throw
|
|
116
120
|
await expect(async () => {
|
|
117
121
|
for await (const _ of stream) { /* drain */ }
|
|
118
122
|
}).rejects.toThrow('stream broke')
|
|
119
|
-
|
|
120
|
-
// .result should also reject
|
|
121
123
|
await expect(stream.result).rejects.toThrow('stream broke')
|
|
122
124
|
})
|
|
123
125
|
|
|
124
126
|
it('handles SSE items without event field (defaults to yielding data)', async () => {
|
|
125
|
-
const sseItems = [
|
|
126
|
-
{ data: 'a' },
|
|
127
|
-
{ data: 'b' },
|
|
128
|
-
]
|
|
129
127
|
const stream = createTypedStream<string, void>(
|
|
130
|
-
makeAsyncIterable(
|
|
131
|
-
'sse'
|
|
128
|
+
makeAsyncIterable([{ data: 'a' }, { data: 'b' }]),
|
|
129
|
+
'sse',
|
|
132
130
|
)
|
|
133
|
-
|
|
134
131
|
const yielded: string[] = []
|
|
135
|
-
for await (const item of stream)
|
|
136
|
-
yielded.push(item)
|
|
137
|
-
}
|
|
132
|
+
for await (const item of stream) yielded.push(item)
|
|
138
133
|
expect(yielded).toEqual(['a', 'b'])
|
|
139
134
|
})
|
|
140
135
|
})
|
|
@@ -144,27 +139,16 @@ describe('createTypedStream — SSE mode', () => {
|
|
|
144
139
|
describe('createTypedStream — text mode', () => {
|
|
145
140
|
it('yields each chunk as-is', async () => {
|
|
146
141
|
const chunks = ['chunk1', 'chunk2', 'chunk3']
|
|
147
|
-
const stream = createTypedStream<string, void>(
|
|
148
|
-
makeAsyncIterable(chunks),
|
|
149
|
-
'text'
|
|
150
|
-
)
|
|
151
|
-
|
|
142
|
+
const stream = createTypedStream<string, void>(makeAsyncIterable(chunks), 'text')
|
|
152
143
|
const received: string[] = []
|
|
153
|
-
for await (const chunk of stream)
|
|
154
|
-
received.push(chunk)
|
|
155
|
-
}
|
|
144
|
+
for await (const chunk of stream) received.push(chunk)
|
|
156
145
|
expect(received).toEqual(chunks)
|
|
157
146
|
})
|
|
158
147
|
|
|
159
148
|
it('.result resolves to void on normal completion', async () => {
|
|
160
|
-
const stream = createTypedStream<string, void>(
|
|
161
|
-
makeAsyncIterable(['a', 'b']),
|
|
162
|
-
'text'
|
|
163
|
-
)
|
|
164
|
-
|
|
149
|
+
const stream = createTypedStream<string, void>(makeAsyncIterable(['a', 'b']), 'text')
|
|
165
150
|
for await (const _ of stream) { /* drain */ }
|
|
166
|
-
|
|
167
|
-
expect(result).toBeUndefined()
|
|
151
|
+
await expect(stream.result).resolves.toBeUndefined()
|
|
168
152
|
})
|
|
169
153
|
|
|
170
154
|
it('rejects .result and re-throws on error', async () => {
|
|
@@ -172,13 +156,10 @@ describe('createTypedStream — text mode', () => {
|
|
|
172
156
|
yield 'before error'
|
|
173
157
|
throw new Error('text stream error')
|
|
174
158
|
}
|
|
175
|
-
|
|
176
159
|
const stream = createTypedStream<string, void>(errorIterable(), 'text')
|
|
177
|
-
|
|
178
160
|
await expect(async () => {
|
|
179
161
|
for await (const _ of stream) { /* drain */ }
|
|
180
162
|
}).rejects.toThrow('text stream error')
|
|
181
|
-
|
|
182
163
|
await expect(stream.result).rejects.toThrow('text stream error')
|
|
183
164
|
})
|
|
184
165
|
})
|
|
@@ -189,20 +170,11 @@ describe('executeStream', () => {
|
|
|
189
170
|
it('calls adapter.stream and returns a TypedStream', async () => {
|
|
190
171
|
const items = [{ data: 'hello', event: 'msg' }]
|
|
191
172
|
const adapter = makeStreamAdapter({}, items)
|
|
192
|
-
|
|
193
|
-
const stream = await executeStream<string, void>(
|
|
194
|
-
makeDescriptor(),
|
|
195
|
-
'https://api.example.com',
|
|
196
|
-
adapter,
|
|
197
|
-
{},
|
|
198
|
-
undefined
|
|
199
|
-
)
|
|
173
|
+
const stream = await run<string, void>({ adapter })
|
|
200
174
|
|
|
201
175
|
expect(adapter.stream).toHaveBeenCalledOnce()
|
|
202
176
|
const received: string[] = []
|
|
203
|
-
for await (const item of stream)
|
|
204
|
-
received.push(item)
|
|
205
|
-
}
|
|
177
|
+
for await (const item of stream) received.push(item)
|
|
206
178
|
expect(received).toEqual(['hello'])
|
|
207
179
|
})
|
|
208
180
|
|
|
@@ -216,23 +188,15 @@ describe('executeStream', () => {
|
|
|
216
188
|
}),
|
|
217
189
|
}
|
|
218
190
|
|
|
219
|
-
const
|
|
191
|
+
const hooks: ClientHooks = {
|
|
220
192
|
onBeforeRequest: (ctx) => ({
|
|
221
193
|
...ctx,
|
|
222
194
|
request: { ...ctx.request, headers: { 'x-stream-auth': 'stream-token' } },
|
|
223
195
|
}),
|
|
224
196
|
}
|
|
225
197
|
|
|
226
|
-
const stream = await
|
|
227
|
-
|
|
228
|
-
'https://api.example.com',
|
|
229
|
-
adapter,
|
|
230
|
-
globalHooks,
|
|
231
|
-
undefined
|
|
232
|
-
)
|
|
233
|
-
// Drain
|
|
234
|
-
for await (const _ of stream) { /* noop */ }
|
|
235
|
-
|
|
198
|
+
const stream = await run({ adapter, hooks })
|
|
199
|
+
for await (const _ of stream) { /* drain */ }
|
|
236
200
|
expect(capturedHeaders[0]?.['x-stream-auth']).toBe('stream-token')
|
|
237
201
|
})
|
|
238
202
|
|
|
@@ -245,12 +209,11 @@ describe('executeStream', () => {
|
|
|
245
209
|
return { status: 200, headers: {}, body: makeAsyncIterable([]) }
|
|
246
210
|
}),
|
|
247
211
|
}
|
|
248
|
-
const
|
|
212
|
+
const hooks: ClientHooks = {
|
|
249
213
|
onAfterResponse: () => { order.push('afterResponse') },
|
|
250
214
|
}
|
|
251
215
|
|
|
252
|
-
await
|
|
253
|
-
// After executeStream returns (before iteration), afterResponse should have fired
|
|
216
|
+
await run({ adapter, hooks })
|
|
254
217
|
expect(order).toEqual(['adapter', 'afterResponse'])
|
|
255
218
|
})
|
|
256
219
|
|
|
@@ -263,10 +226,7 @@ describe('executeStream', () => {
|
|
|
263
226
|
body: makeAsyncIterable([]),
|
|
264
227
|
})),
|
|
265
228
|
}
|
|
266
|
-
|
|
267
|
-
await expect(
|
|
268
|
-
executeStream(makeDescriptor(), 'https://api.example.com', adapter, {}, undefined)
|
|
269
|
-
).rejects.toThrow(ClientRequestError)
|
|
229
|
+
await expect(run({ adapter })).rejects.toThrow(ClientRequestError)
|
|
270
230
|
})
|
|
271
231
|
|
|
272
232
|
it('runs onError on adapter failure and re-throws', async () => {
|
|
@@ -276,13 +236,11 @@ describe('executeStream', () => {
|
|
|
276
236
|
stream: vi.fn(async () => { throw adapterError }),
|
|
277
237
|
}
|
|
278
238
|
const receivedErrors: unknown[] = []
|
|
279
|
-
const
|
|
239
|
+
const hooks: ClientHooks = {
|
|
280
240
|
onError: (ctx) => { receivedErrors.push(ctx.error) },
|
|
281
241
|
}
|
|
282
242
|
|
|
283
|
-
await expect(
|
|
284
|
-
executeStream(makeDescriptor(), 'https://api.example.com', adapter, globalHooks, undefined)
|
|
285
|
-
).rejects.toThrow('stream connection failed')
|
|
243
|
+
await expect(run({ adapter, hooks })).rejects.toThrow('stream connection failed')
|
|
286
244
|
expect(receivedErrors[0]).toBe(adapterError)
|
|
287
245
|
})
|
|
288
246
|
|
|
@@ -293,18 +251,13 @@ describe('executeStream', () => {
|
|
|
293
251
|
]
|
|
294
252
|
const adapter = makeStreamAdapter({}, sseItems)
|
|
295
253
|
|
|
296
|
-
const stream = await
|
|
297
|
-
makeDescriptor({ streamMode: 'sse' }),
|
|
298
|
-
'https://api.example.com',
|
|
254
|
+
const stream = await run<{ n: number }, { final: boolean }>({
|
|
299
255
|
adapter,
|
|
300
|
-
{},
|
|
301
|
-
|
|
302
|
-
)
|
|
256
|
+
descriptor: makeDescriptor({ streamMode: 'sse' }),
|
|
257
|
+
})
|
|
303
258
|
|
|
304
259
|
const yielded: { n: number }[] = []
|
|
305
|
-
for await (const item of stream)
|
|
306
|
-
yielded.push(item)
|
|
307
|
-
}
|
|
260
|
+
for await (const item of stream) yielded.push(item)
|
|
308
261
|
expect(yielded).toEqual([{ n: 1 }])
|
|
309
262
|
await expect(stream.result).resolves.toEqual({ final: true })
|
|
310
263
|
})
|
|
@@ -313,19 +266,91 @@ describe('executeStream', () => {
|
|
|
313
266
|
const chunks = ['line1', 'line2']
|
|
314
267
|
const adapter = makeStreamAdapter({}, chunks)
|
|
315
268
|
|
|
316
|
-
const stream = await
|
|
317
|
-
makeDescriptor({ streamMode: 'text' }),
|
|
318
|
-
'https://api.example.com',
|
|
269
|
+
const stream = await run<string, void>({
|
|
319
270
|
adapter,
|
|
320
|
-
{},
|
|
321
|
-
|
|
322
|
-
)
|
|
271
|
+
descriptor: makeDescriptor({ streamMode: 'text' }),
|
|
272
|
+
})
|
|
323
273
|
|
|
324
274
|
const received: string[] = []
|
|
325
|
-
for await (const chunk of stream)
|
|
326
|
-
received.push(chunk)
|
|
327
|
-
}
|
|
275
|
+
for await (const chunk of stream) received.push(chunk)
|
|
328
276
|
expect(received).toEqual(chunks)
|
|
329
277
|
await expect(stream.result).resolves.toBeUndefined()
|
|
330
278
|
})
|
|
279
|
+
|
|
280
|
+
// ── Per-call options ──
|
|
281
|
+
|
|
282
|
+
it('per-call timeout attaches a signal via AbortSignal.timeout', async () => {
|
|
283
|
+
const spy = vi.spyOn(AbortSignal, 'timeout')
|
|
284
|
+
try {
|
|
285
|
+
let observedSignal: AbortSignal | undefined
|
|
286
|
+
const adapter: ClientAdapter = {
|
|
287
|
+
request: vi.fn(async (): Promise<never> => { throw new Error('not expected') }),
|
|
288
|
+
stream: vi.fn(async (req: AdapterRequest): Promise<AdapterStreamResponse> => {
|
|
289
|
+
observedSignal = req.signal
|
|
290
|
+
return { status: 200, headers: {}, body: makeAsyncIterable([]) }
|
|
291
|
+
}),
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
await run({ adapter, options: { timeout: 5000 } })
|
|
295
|
+
expect(spy).toHaveBeenCalledWith(5000)
|
|
296
|
+
expect(observedSignal).toBeDefined()
|
|
297
|
+
} finally {
|
|
298
|
+
spy.mockRestore()
|
|
299
|
+
}
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
it('adapter receives a signal that reflects abort when the caller cancels', async () => {
|
|
303
|
+
const controller = new AbortController()
|
|
304
|
+
const adapter: ClientAdapter = {
|
|
305
|
+
request: vi.fn(async (): Promise<never> => { throw new Error('not expected') }),
|
|
306
|
+
stream: vi.fn(async (req: AdapterRequest): Promise<AdapterStreamResponse> => {
|
|
307
|
+
return new Promise((_resolve, reject) => {
|
|
308
|
+
const abort = () => reject(new Error('aborted'))
|
|
309
|
+
if (req.signal?.aborted) abort()
|
|
310
|
+
else req.signal?.addEventListener('abort', abort, { once: true })
|
|
311
|
+
})
|
|
312
|
+
}),
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const promise = run({ adapter, options: { signal: controller.signal } })
|
|
316
|
+
// Let executeStream reach the adapter before aborting
|
|
317
|
+
await Promise.resolve()
|
|
318
|
+
controller.abort()
|
|
319
|
+
await expect(promise).rejects.toThrow('aborted')
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
it('per-call basePath overrides base path for streams', async () => {
|
|
323
|
+
const capturedUrls: string[] = []
|
|
324
|
+
const adapter: ClientAdapter = {
|
|
325
|
+
request: vi.fn(async (): Promise<never> => { throw new Error('not expected') }),
|
|
326
|
+
stream: vi.fn(async (req: AdapterRequest): Promise<AdapterStreamResponse> => {
|
|
327
|
+
capturedUrls.push(req.url)
|
|
328
|
+
return { status: 200, headers: {}, body: makeAsyncIterable([]) }
|
|
329
|
+
}),
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const stream = await run({
|
|
333
|
+
adapter,
|
|
334
|
+
descriptor: makeDescriptor({ path: '/tail' }),
|
|
335
|
+
basePath: 'https://default.example.com',
|
|
336
|
+
options: { basePath: 'https://override.example.com' },
|
|
337
|
+
})
|
|
338
|
+
for await (const _ of stream) { /* drain */ }
|
|
339
|
+
|
|
340
|
+
expect(capturedUrls[0]).toBe('https://override.example.com/tail')
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
it('per-call meta is forwarded to the adapter', async () => {
|
|
344
|
+
let observedMeta: unknown
|
|
345
|
+
const adapter: ClientAdapter = {
|
|
346
|
+
request: vi.fn(async (): Promise<never> => { throw new Error('not expected') }),
|
|
347
|
+
stream: vi.fn(async (req: AdapterRequest): Promise<AdapterStreamResponse> => {
|
|
348
|
+
observedMeta = req.meta
|
|
349
|
+
return { status: 200, headers: {}, body: makeAsyncIterable([]) }
|
|
350
|
+
}),
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
await run({ adapter, options: { meta: { traceId: 'stream-trace' } as never } })
|
|
354
|
+
expect(observedMeta).toEqual({ traceId: 'stream-trace' })
|
|
355
|
+
})
|
|
331
356
|
})
|
package/src/client/stream.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { buildAdapterRequest } from './request-builder.js'
|
|
2
2
|
import { runBeforeRequest, runAfterResponse, runOnError } from './hooks.js'
|
|
3
|
+
import { applyRequestOptions, resolveBasePath } from './resolve-options.js'
|
|
3
4
|
import { ClientRequestError } from './errors.js'
|
|
4
5
|
import type {
|
|
5
6
|
ClientAdapter,
|
|
@@ -7,6 +8,8 @@ import type {
|
|
|
7
8
|
StreamDescriptor,
|
|
8
9
|
TypedStream,
|
|
9
10
|
AdapterResponse,
|
|
11
|
+
ProcedureCallDefaults,
|
|
12
|
+
ProcedureCallOptions,
|
|
10
13
|
} from './types.js'
|
|
11
14
|
|
|
12
15
|
// ── SSE item shape ────────────────────────────────────────
|
|
@@ -90,46 +93,58 @@ export function createTypedStream<TYield, TReturn = void>(
|
|
|
90
93
|
|
|
91
94
|
// ── executeStream ─────────────────────────────────────────
|
|
92
95
|
|
|
96
|
+
export interface ExecuteStreamConfig {
|
|
97
|
+
descriptor: StreamDescriptor
|
|
98
|
+
basePath: string
|
|
99
|
+
adapter: ClientAdapter
|
|
100
|
+
hooks: ClientHooks
|
|
101
|
+
defaults?: ProcedureCallDefaults
|
|
102
|
+
options?: ProcedureCallOptions
|
|
103
|
+
}
|
|
104
|
+
|
|
93
105
|
/**
|
|
94
106
|
* Executes a streaming procedure call through the adapter.
|
|
95
107
|
*
|
|
96
108
|
* Flow:
|
|
97
|
-
* 1.
|
|
98
|
-
* 2.
|
|
99
|
-
* 3.
|
|
100
|
-
* 4.
|
|
101
|
-
* 5.
|
|
102
|
-
* 6.
|
|
103
|
-
* 7.
|
|
109
|
+
* 1. Resolve base path and build AdapterRequest
|
|
110
|
+
* 2. Apply request options (headers, signal, timeout, meta) from defaults + per-call
|
|
111
|
+
* 3. Run onBeforeRequest hooks
|
|
112
|
+
* 4. Call adapter.stream()
|
|
113
|
+
* 5. On adapter error: run onError hooks, re-throw
|
|
114
|
+
* 6. Run onAfterResponse immediately (before iteration), body is null
|
|
115
|
+
* 7. If non-2xx: throw ClientRequestError
|
|
116
|
+
* 8. Return createTypedStream(streamResponse.body, descriptor.streamMode)
|
|
104
117
|
*/
|
|
105
118
|
export async function executeStream<TYield, TReturn = void>(
|
|
106
|
-
|
|
107
|
-
basePath: string,
|
|
108
|
-
adapter: ClientAdapter,
|
|
109
|
-
globalHooks: ClientHooks,
|
|
110
|
-
localHooks: ClientHooks | undefined
|
|
119
|
+
config: ExecuteStreamConfig,
|
|
111
120
|
): Promise<TypedStream<TYield, TReturn>> {
|
|
121
|
+
const { descriptor, basePath, adapter, hooks, defaults, options } = config
|
|
122
|
+
|
|
112
123
|
// 1. Build the initial request
|
|
113
|
-
|
|
124
|
+
const resolvedBasePath = resolveBasePath(defaults, options, basePath)
|
|
125
|
+
let request = buildAdapterRequest(descriptor, resolvedBasePath)
|
|
126
|
+
|
|
127
|
+
// 2. Apply request-level options (headers, signal, timeout, meta)
|
|
128
|
+
request = applyRequestOptions(request, defaults, options)
|
|
114
129
|
|
|
115
|
-
//
|
|
130
|
+
// 3. Run before-request hooks
|
|
116
131
|
const beforeCtx = await runBeforeRequest(
|
|
117
132
|
{ procedureName: descriptor.name, scope: descriptor.scope, request },
|
|
118
|
-
|
|
119
|
-
|
|
133
|
+
hooks,
|
|
134
|
+
options,
|
|
120
135
|
)
|
|
121
136
|
request = beforeCtx.request
|
|
122
137
|
|
|
123
|
-
//
|
|
138
|
+
// 4. Call the adapter
|
|
124
139
|
let streamResponse
|
|
125
140
|
try {
|
|
126
141
|
streamResponse = await adapter.stream(request)
|
|
127
142
|
} catch (err) {
|
|
128
|
-
//
|
|
143
|
+
// 5. On adapter error: run error hooks, re-throw
|
|
129
144
|
await runOnError(
|
|
130
145
|
{ procedureName: descriptor.name, scope: descriptor.scope, request, error: err },
|
|
131
|
-
|
|
132
|
-
|
|
146
|
+
hooks,
|
|
147
|
+
options,
|
|
133
148
|
)
|
|
134
149
|
throw err
|
|
135
150
|
}
|
|
@@ -141,14 +156,14 @@ export async function executeStream<TYield, TReturn = void>(
|
|
|
141
156
|
body: null,
|
|
142
157
|
}
|
|
143
158
|
|
|
144
|
-
//
|
|
159
|
+
// 6. Run after-response hooks immediately (before iteration)
|
|
145
160
|
await runAfterResponse(
|
|
146
161
|
{ procedureName: descriptor.name, scope: descriptor.scope, request, response: responseForHooks },
|
|
147
|
-
|
|
148
|
-
|
|
162
|
+
hooks,
|
|
163
|
+
options,
|
|
149
164
|
)
|
|
150
165
|
|
|
151
|
-
//
|
|
166
|
+
// 7. Check status after hooks (hooks may mutate responseForHooks.status)
|
|
152
167
|
if (responseForHooks.status < 200 || responseForHooks.status >= 300) {
|
|
153
168
|
throw new ClientRequestError({
|
|
154
169
|
status: responseForHooks.status,
|
|
@@ -159,6 +174,6 @@ export async function executeStream<TYield, TReturn = void>(
|
|
|
159
174
|
})
|
|
160
175
|
}
|
|
161
176
|
|
|
162
|
-
//
|
|
177
|
+
// 8. Return the typed stream
|
|
163
178
|
return createTypedStream<TYield, TReturn>(streamResponse.body, descriptor.streamMode)
|
|
164
179
|
}
|
package/src/client/types.ts
CHANGED
|
@@ -1,3 +1,35 @@
|
|
|
1
|
+
// ── Request Metadata ─────────────────────────────────────
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Per-request metadata visible to adapters and hooks. Defined as an empty
|
|
5
|
+
* interface so consumers can augment it via TypeScript declaration merging
|
|
6
|
+
* to get end-to-end type safety for their own metadata fields.
|
|
7
|
+
*
|
|
8
|
+
* @example With a non-self-contained client:
|
|
9
|
+
* ```ts
|
|
10
|
+
* declare module 'ts-procedures/client' {
|
|
11
|
+
* interface RequestMeta {
|
|
12
|
+
* traceId?: string
|
|
13
|
+
* priority?: 'high' | 'low'
|
|
14
|
+
* }
|
|
15
|
+
* }
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @example With a self-contained (code-generated) client:
|
|
19
|
+
* ```ts
|
|
20
|
+
* declare module './generated/_types' {
|
|
21
|
+
* interface RequestMeta {
|
|
22
|
+
* traceId?: string
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* After augmentation, `request.meta.traceId` is typed everywhere — per-call
|
|
28
|
+
* options, hooks, and adapters.
|
|
29
|
+
*/
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
31
|
+
export interface RequestMeta {}
|
|
32
|
+
|
|
1
33
|
// ── Adapter ──────────────────────────────────────────────
|
|
2
34
|
|
|
3
35
|
export interface ClientAdapter {
|
|
@@ -11,6 +43,11 @@ export interface AdapterRequest {
|
|
|
11
43
|
headers?: Record<string, string>
|
|
12
44
|
body?: unknown
|
|
13
45
|
signal?: AbortSignal
|
|
46
|
+
/**
|
|
47
|
+
* Per-request metadata. Augment `RequestMeta` via declaration merging to
|
|
48
|
+
* type your own fields. See {@link RequestMeta}.
|
|
49
|
+
*/
|
|
50
|
+
meta?: RequestMeta
|
|
14
51
|
}
|
|
15
52
|
|
|
16
53
|
export interface AdapterResponse {
|
|
@@ -81,14 +118,43 @@ export interface TypedStream<TYield, TReturn = void> extends AsyncIterable<TYiel
|
|
|
81
118
|
result: Promise<TReturn>
|
|
82
119
|
}
|
|
83
120
|
|
|
84
|
-
// ──
|
|
121
|
+
// ── Request Options ──────────────────────────────────────
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Request-level configuration that can be set as client-level defaults
|
|
125
|
+
* (via `CreateClientConfig.defaults`) or per-call (via `ProcedureCallOptions`).
|
|
126
|
+
*
|
|
127
|
+
* - `signal`: AbortSignal for cancellation. When both a default and per-call
|
|
128
|
+
* signal are provided, they're combined — whichever aborts first wins.
|
|
129
|
+
* - `timeout`: Timeout in milliseconds. Combined with `signal` the same way.
|
|
130
|
+
* A per-call `timeout: 0` disables an inherited default timeout.
|
|
131
|
+
* - `headers`: Extra headers merged into the request. Per-call keys win over
|
|
132
|
+
* default keys. Still subject to further mutation by `onBeforeRequest` hooks.
|
|
133
|
+
* - `basePath`: Override the base path for this call. Per-call > default > config.
|
|
134
|
+
* - `meta`: Per-request metadata typed via the {@link RequestMeta} interface.
|
|
135
|
+
* Merged shallowly (per-call keys win over default keys).
|
|
136
|
+
*/
|
|
137
|
+
export interface ProcedureCallDefaults {
|
|
138
|
+
signal?: AbortSignal
|
|
139
|
+
timeout?: number
|
|
140
|
+
headers?: Record<string, string>
|
|
141
|
+
basePath?: string
|
|
142
|
+
meta?: RequestMeta
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Per-call options. Extends both `ProcedureCallDefaults` (request config) and
|
|
147
|
+
* `ClientHooks` (hooks), so a single options bag covers both concerns.
|
|
148
|
+
*/
|
|
149
|
+
export interface ProcedureCallOptions extends ProcedureCallDefaults, ClientHooks {}
|
|
85
150
|
|
|
86
|
-
|
|
151
|
+
// ── Client Instance ──────────────────────────────────────
|
|
87
152
|
|
|
88
153
|
export interface ClientInstance {
|
|
89
154
|
basePath: string
|
|
90
155
|
adapter: ClientAdapter
|
|
91
156
|
hooks: ClientHooks
|
|
157
|
+
defaults: ProcedureCallDefaults
|
|
92
158
|
call<TResponse>(descriptor: CallDescriptor, options?: ProcedureCallOptions): Promise<TResponse>
|
|
93
159
|
stream<TYield, TReturn>(descriptor: StreamDescriptor, options?: ProcedureCallOptions): TypedStream<TYield, TReturn>
|
|
94
160
|
}
|
|
@@ -100,4 +166,10 @@ export interface CreateClientConfig<TScopes> {
|
|
|
100
166
|
basePath: string
|
|
101
167
|
scopes: (client: ClientInstance) => TScopes
|
|
102
168
|
hooks?: ClientHooks
|
|
169
|
+
/**
|
|
170
|
+
* Default request options applied to every call. Per-call options override
|
|
171
|
+
* these (except `signal`, which combines via AbortSignal.any — whichever
|
|
172
|
+
* fires first cancels the request).
|
|
173
|
+
*/
|
|
174
|
+
defaults?: ProcedureCallDefaults
|
|
103
175
|
}
|
|
@@ -170,6 +170,41 @@ describe('parseArgs', () => {
|
|
|
170
170
|
const result = parseArgs(['--url', 'http://x.com/docs', '--out', './out'])
|
|
171
171
|
expect(result.serviceName).toBeUndefined()
|
|
172
172
|
})
|
|
173
|
+
|
|
174
|
+
it('parses --clean-out-dir flag', () => {
|
|
175
|
+
const result = parseArgs(['--url', 'http://x.com/docs', '--out', './out', '--clean-out-dir'])
|
|
176
|
+
expect(result.cleanOutDir).toBe(true)
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('defaults cleanOutDir to false when not provided', () => {
|
|
180
|
+
const result = parseArgs(['--url', 'http://x.com/docs', '--out', './out'])
|
|
181
|
+
expect(result.cleanOutDir).toBe(false)
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('parses --no-clean-out-dir flag', () => {
|
|
185
|
+
const result = parseArgs(['--url', 'http://x.com/docs', '--out', './out', '--no-clean-out-dir'])
|
|
186
|
+
expect(result.cleanOutDir).toBe(false)
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it('CLI --no-clean-out-dir overrides config cleanOutDir', () => {
|
|
190
|
+
const config: CodegenConfig = {
|
|
191
|
+
url: 'http://config.com/docs',
|
|
192
|
+
outDir: './out',
|
|
193
|
+
cleanOutDir: true,
|
|
194
|
+
}
|
|
195
|
+
const result = parseArgs(['--no-clean-out-dir'], config)
|
|
196
|
+
expect(result.cleanOutDir).toBe(false)
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
it('config cleanOutDir is used as default', () => {
|
|
200
|
+
const config: CodegenConfig = {
|
|
201
|
+
url: 'http://config.com/docs',
|
|
202
|
+
outDir: './out',
|
|
203
|
+
cleanOutDir: true,
|
|
204
|
+
}
|
|
205
|
+
const result = parseArgs([], config)
|
|
206
|
+
expect(result.cleanOutDir).toBe(true)
|
|
207
|
+
})
|
|
173
208
|
})
|
|
174
209
|
|
|
175
210
|
describe('config file support', () => {
|