ts-procedures 5.3.0 → 5.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -0
- package/agent_config/claude-code/agents/ts-procedures-architect.md +15 -0
- package/agent_config/claude-code/skills/guide/anti-patterns.md +106 -0
- package/agent_config/claude-code/skills/guide/api-reference.md +150 -4
- package/agent_config/claude-code/skills/guide/patterns.md +155 -0
- package/agent_config/claude-code/skills/review/checklist.md +22 -0
- package/agent_config/claude-code/skills/scaffold/SKILL.md +3 -1
- package/agent_config/claude-code/skills/scaffold/templates/hono-api.md +169 -0
- package/agent_config/copilot/copilot-instructions.md +35 -0
- package/agent_config/cursor/cursorrules +35 -0
- package/build/implementations/http/hono-api/index.d.ts +102 -0
- package/build/implementations/http/hono-api/index.js +339 -0
- package/build/implementations/http/hono-api/index.js.map +1 -0
- package/build/implementations/http/hono-api/index.test.d.ts +1 -0
- package/build/implementations/http/hono-api/index.test.js +983 -0
- package/build/implementations/http/hono-api/index.test.js.map +1 -0
- package/build/implementations/http/hono-api/types.d.ts +13 -0
- package/build/implementations/http/hono-api/types.js +2 -0
- package/build/implementations/http/hono-api/types.js.map +1 -0
- package/build/implementations/types.d.ts +44 -0
- package/build/index.d.ts +28 -6
- package/build/index.js +28 -0
- package/build/index.js.map +1 -1
- package/build/schema/compute-schema.d.ts +5 -0
- package/build/schema/compute-schema.js +8 -1
- package/build/schema/compute-schema.js.map +1 -1
- package/build/schema/parser.d.ts +6 -5
- package/build/schema/parser.js +54 -0
- package/build/schema/parser.js.map +1 -1
- package/package.json +8 -4
- package/src/errors.test.ts +0 -163
- package/src/errors.ts +0 -107
- package/src/exports.ts +0 -7
- package/src/implementations/http/README.md +0 -217
- package/src/implementations/http/express-rpc/README.md +0 -281
- package/src/implementations/http/express-rpc/index.test.ts +0 -957
- package/src/implementations/http/express-rpc/index.ts +0 -265
- package/src/implementations/http/express-rpc/types.ts +0 -16
- package/src/implementations/http/hono-rpc/README.md +0 -358
- package/src/implementations/http/hono-rpc/index.test.ts +0 -1075
- package/src/implementations/http/hono-rpc/index.ts +0 -237
- package/src/implementations/http/hono-rpc/types.ts +0 -16
- package/src/implementations/http/hono-stream/README.md +0 -526
- package/src/implementations/http/hono-stream/index.test.ts +0 -1676
- package/src/implementations/http/hono-stream/index.ts +0 -435
- package/src/implementations/http/hono-stream/types.ts +0 -29
- package/src/implementations/types.ts +0 -75
- package/src/index.test.ts +0 -1194
- package/src/index.ts +0 -435
- package/src/schema/compute-schema.test.ts +0 -128
- package/src/schema/compute-schema.ts +0 -67
- package/src/schema/extract-json-schema.test.ts +0 -25
- package/src/schema/extract-json-schema.ts +0 -15
- package/src/schema/parser.test.ts +0 -182
- package/src/schema/parser.ts +0 -148
- package/src/schema/resolve-schema-lib.test.ts +0 -19
- package/src/schema/resolve-schema-lib.ts +0 -29
- package/src/schema/types.ts +0 -20
- package/src/stack-utils.test.ts +0 -94
- package/src/stack-utils.ts +0 -129
|
@@ -1,435 +0,0 @@
|
|
|
1
|
-
import { Hono, Context } from 'hono'
|
|
2
|
-
import { streamSSE, streamText } from 'hono/streaming'
|
|
3
|
-
import { kebabCase } from 'es-toolkit/string'
|
|
4
|
-
import { castArray } from 'es-toolkit/compat'
|
|
5
|
-
import { TStreamProcedureRegistration } from '../../../index.js'
|
|
6
|
-
import { ExtractConfig, ExtractContext, ProceduresFactory, RPCConfig } from '../../types.js'
|
|
7
|
-
import { HonoStreamFactoryItem, StreamHttpRouteDoc, StreamMode } from './types.js'
|
|
8
|
-
import { ProcedureValidationError } from '../../../errors.js'
|
|
9
|
-
|
|
10
|
-
export type { StreamHttpRouteDoc, StreamMode }
|
|
11
|
-
|
|
12
|
-
export type SSEOptions = {
|
|
13
|
-
event?: string
|
|
14
|
-
id?: string
|
|
15
|
-
retry?: number
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const sseMetadata = new WeakMap<object, SSEOptions>()
|
|
19
|
-
|
|
20
|
-
export function sse<T extends object>(data: T, options?: SSEOptions): T {
|
|
21
|
-
sseMetadata.set(data, options ?? {})
|
|
22
|
-
return data
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function getSSEMeta(value: unknown): SSEOptions | undefined {
|
|
26
|
-
if (typeof value === 'object' && value !== null) {
|
|
27
|
-
return sseMetadata.get(value)
|
|
28
|
-
}
|
|
29
|
-
return undefined
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Result from onMidStreamError callback.
|
|
34
|
-
* @property data - The data to write as the SSE `data:` field content (should match yieldType schema)
|
|
35
|
-
* @property closeStream - Whether to close the stream after writing (defaults to true)
|
|
36
|
-
*/
|
|
37
|
-
export type MidStreamErrorResult<TErrorData = unknown> = {
|
|
38
|
-
data: TErrorData
|
|
39
|
-
closeStream?: boolean
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export type HonoStreamAppBuilderConfig<TErrorData = unknown> = {
|
|
43
|
-
/**
|
|
44
|
-
* An existing Hono application instance to use.
|
|
45
|
-
* If not provided, a new instance will be created.
|
|
46
|
-
*/
|
|
47
|
-
app?: Hono
|
|
48
|
-
/** Optional path prefix for all stream routes. */
|
|
49
|
-
pathPrefix?: string
|
|
50
|
-
/** Default stream mode for all routes. Defaults to 'sse'. */
|
|
51
|
-
defaultStreamMode?: StreamMode
|
|
52
|
-
onRequestStart?: (c: Context) => void
|
|
53
|
-
onRequestEnd?: (c: Context) => void
|
|
54
|
-
onStreamStart?: (procedure: TStreamProcedureRegistration, c: Context, streamMode: StreamMode) => void
|
|
55
|
-
onStreamEnd?: (procedure: TStreamProcedureRegistration, c: Context, streamMode: StreamMode) => void
|
|
56
|
-
/**
|
|
57
|
-
* Called for errors BEFORE streaming starts (validation, auth, context resolution).
|
|
58
|
-
* Return value IS used as the HTTP response.
|
|
59
|
-
*/
|
|
60
|
-
onPreStreamError?: (
|
|
61
|
-
procedure: TStreamProcedureRegistration,
|
|
62
|
-
c: Context,
|
|
63
|
-
error: ProcedureValidationError | Error
|
|
64
|
-
) => Response | Promise<Response>
|
|
65
|
-
/**
|
|
66
|
-
* Called for errors DURING streaming (generator throws).
|
|
67
|
-
* Return value is written to the stream as a yield.
|
|
68
|
-
* Should return a value matching your yieldType schema (e.g., error variant of a union).
|
|
69
|
-
* Return undefined to use default behavior (writes { error: message }).
|
|
70
|
-
*
|
|
71
|
-
* Use sse() to attach SSE metadata (event, id, retry) to the error data object.
|
|
72
|
-
*
|
|
73
|
-
* @returns { data, closeStream? } - data to yield, whether to close after (default true)
|
|
74
|
-
*/
|
|
75
|
-
onMidStreamError?: (
|
|
76
|
-
procedure: TStreamProcedureRegistration,
|
|
77
|
-
c: Context,
|
|
78
|
-
error: Error
|
|
79
|
-
) => MidStreamErrorResult<TErrorData> | undefined
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Builder class for creating a Hono application with streaming RPC routes.
|
|
84
|
-
*
|
|
85
|
-
* Usage:
|
|
86
|
-
* const StreamRPC = Procedures<StreamContext, RPCConfig>()
|
|
87
|
-
*
|
|
88
|
-
* const streamApp = new HonoStreamAppBuilder()
|
|
89
|
-
* .register(StreamRPC, (c): Promise<StreamContext> => { /* context resolution logic * / })
|
|
90
|
-
* .build();
|
|
91
|
-
*
|
|
92
|
-
* const app = streamApp.app; // Hono application
|
|
93
|
-
* const docs = streamApp.docs; // Stream route documentation
|
|
94
|
-
*/
|
|
95
|
-
export class HonoStreamAppBuilder<TErrorData = unknown> {
|
|
96
|
-
/**
|
|
97
|
-
* Constructor for HonoStreamAppBuilder.
|
|
98
|
-
*/
|
|
99
|
-
constructor(readonly config?: HonoStreamAppBuilderConfig<TErrorData>) {
|
|
100
|
-
if (config?.app) {
|
|
101
|
-
this._app = config.app
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (config?.onRequestStart) {
|
|
105
|
-
this._app.use('*', async (c, next) => {
|
|
106
|
-
config.onRequestStart!(c)
|
|
107
|
-
await next()
|
|
108
|
-
})
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Generates the stream route path based on the RPC configuration.
|
|
114
|
-
*/
|
|
115
|
-
static makeStreamHttpRoutePath({
|
|
116
|
-
name,
|
|
117
|
-
config,
|
|
118
|
-
prefix,
|
|
119
|
-
}: {
|
|
120
|
-
name: string
|
|
121
|
-
prefix?: string
|
|
122
|
-
config: RPCConfig
|
|
123
|
-
}) {
|
|
124
|
-
const normalizedPrefix = prefix ? (prefix.startsWith('/') ? prefix : `/${prefix}`) : ''
|
|
125
|
-
|
|
126
|
-
return `${normalizedPrefix}/${castArray(config.scope).map(kebabCase).join('/')}/${kebabCase(name)}/${String(config.version).trim()}`
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Instance method wrapper for makeStreamHttpRoutePath that uses the builder's pathPrefix.
|
|
131
|
-
*/
|
|
132
|
-
makeStreamHttpRoutePath(name: string, config: RPCConfig): string {
|
|
133
|
-
return HonoStreamAppBuilder.makeStreamHttpRoutePath({
|
|
134
|
-
name,
|
|
135
|
-
config,
|
|
136
|
-
prefix: this.config?.pathPrefix,
|
|
137
|
-
})
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
private factories: HonoStreamFactoryItem<any>[] = []
|
|
141
|
-
|
|
142
|
-
private _app: Hono = new Hono()
|
|
143
|
-
private _docs: (StreamHttpRouteDoc & object)[] = []
|
|
144
|
-
|
|
145
|
-
get app(): Hono {
|
|
146
|
-
return this._app
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
get docs(): StreamHttpRouteDoc[] {
|
|
150
|
-
return this._docs
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Registers a procedure factory with its context.
|
|
155
|
-
* Only streaming procedures (created with CreateStream) will be registered.
|
|
156
|
-
*/
|
|
157
|
-
register<TFactory extends ProceduresFactory>(
|
|
158
|
-
factory: TFactory,
|
|
159
|
-
factoryContext:
|
|
160
|
-
| ExtractContext<TFactory>
|
|
161
|
-
| ((c: Context) => ExtractContext<TFactory> | Promise<ExtractContext<TFactory>>),
|
|
162
|
-
options?: {
|
|
163
|
-
streamMode?: StreamMode
|
|
164
|
-
extendProcedureDoc?: (params: {
|
|
165
|
-
base: StreamHttpRouteDoc
|
|
166
|
-
procedure: TStreamProcedureRegistration<any, ExtractConfig<TFactory>>
|
|
167
|
-
}) => Record<string, any>
|
|
168
|
-
}
|
|
169
|
-
): this {
|
|
170
|
-
this.factories.push({
|
|
171
|
-
factory,
|
|
172
|
-
factoryContext,
|
|
173
|
-
streamMode: options?.streamMode,
|
|
174
|
-
extendProcedureDoc: options?.extendProcedureDoc,
|
|
175
|
-
} as HonoStreamFactoryItem<any>)
|
|
176
|
-
return this
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Creates a route handler for streaming procedures.
|
|
181
|
-
*/
|
|
182
|
-
private createStreamHandler(
|
|
183
|
-
procedure: TStreamProcedureRegistration,
|
|
184
|
-
factoryContext: HonoStreamFactoryItem['factoryContext'],
|
|
185
|
-
streamMode: StreamMode
|
|
186
|
-
) {
|
|
187
|
-
return async (c: Context) => {
|
|
188
|
-
try {
|
|
189
|
-
const context =
|
|
190
|
-
typeof factoryContext === 'function' ? await factoryContext(c) : factoryContext
|
|
191
|
-
|
|
192
|
-
// GET: query params, POST: JSON body
|
|
193
|
-
const params =
|
|
194
|
-
c.req.method === 'GET'
|
|
195
|
-
? Object.fromEntries(new URL(c.req.url).searchParams)
|
|
196
|
-
: await c.req.json().catch(() => ({}))
|
|
197
|
-
|
|
198
|
-
// Validate params BEFORE starting the stream
|
|
199
|
-
if (procedure.config.validation?.params) {
|
|
200
|
-
const { errors } = procedure.config.validation.params(params)
|
|
201
|
-
if (errors) {
|
|
202
|
-
const error = new ProcedureValidationError(
|
|
203
|
-
procedure.name,
|
|
204
|
-
`Validation error for ${procedure.name}`,
|
|
205
|
-
errors
|
|
206
|
-
)
|
|
207
|
-
// Use onPreStreamError if provided
|
|
208
|
-
if (this.config?.onPreStreamError) {
|
|
209
|
-
return this.config.onPreStreamError(procedure, c, error)
|
|
210
|
-
}
|
|
211
|
-
return c.json({ error: error.message }, 400)
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (this.config?.onStreamStart) {
|
|
216
|
-
this.config.onStreamStart(procedure, c, streamMode)
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
if (streamMode === 'sse') {
|
|
220
|
-
return this.handleSSEStream(procedure, context, params, c)
|
|
221
|
-
} else {
|
|
222
|
-
return this.handleTextStream(procedure, context, params, c)
|
|
223
|
-
}
|
|
224
|
-
} catch (error) {
|
|
225
|
-
// Use onPreStreamError for context resolution errors
|
|
226
|
-
if (this.config?.onPreStreamError) {
|
|
227
|
-
return this.config.onPreStreamError(procedure, c, error as Error)
|
|
228
|
-
}
|
|
229
|
-
return c.json({ error: (error as Error).message }, 500)
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Handles SSE streaming mode.
|
|
236
|
-
*/
|
|
237
|
-
private handleSSEStream(
|
|
238
|
-
procedure: TStreamProcedureRegistration,
|
|
239
|
-
context: any,
|
|
240
|
-
params: any,
|
|
241
|
-
c: Context
|
|
242
|
-
) {
|
|
243
|
-
return streamSSE(c, async (stream) => {
|
|
244
|
-
// Pass isPrevalidated: true since we already validated params in createStreamHandler
|
|
245
|
-
const generator = procedure.handler({ ...context, signal: c.req.raw.signal, isPrevalidated: true }, params)
|
|
246
|
-
|
|
247
|
-
stream.onAbort(async () => {
|
|
248
|
-
await generator.return(undefined)
|
|
249
|
-
})
|
|
250
|
-
|
|
251
|
-
let eventId = 0
|
|
252
|
-
try {
|
|
253
|
-
for await (const value of generator) {
|
|
254
|
-
const currentId = eventId++
|
|
255
|
-
const meta = getSSEMeta(value)
|
|
256
|
-
|
|
257
|
-
const data =
|
|
258
|
-
typeof value === 'string'
|
|
259
|
-
? value
|
|
260
|
-
: value != null
|
|
261
|
-
? JSON.stringify(value)
|
|
262
|
-
: ''
|
|
263
|
-
|
|
264
|
-
await stream.writeSSE({
|
|
265
|
-
data,
|
|
266
|
-
event: meta?.event ?? procedure.name,
|
|
267
|
-
id: meta?.id ?? String(currentId),
|
|
268
|
-
...(meta?.retry !== undefined && { retry: meta.retry }),
|
|
269
|
-
})
|
|
270
|
-
}
|
|
271
|
-
} catch (error) {
|
|
272
|
-
// Get error yield value from callback (onMidStreamError)
|
|
273
|
-
let errorResult: MidStreamErrorResult<TErrorData> | undefined
|
|
274
|
-
|
|
275
|
-
if (this.config?.onMidStreamError) {
|
|
276
|
-
errorResult = this.config.onMidStreamError(procedure, c, error as Error)
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Write error value to stream
|
|
280
|
-
const errorData = errorResult?.data ?? { error: (error as Error).message }
|
|
281
|
-
const sseMeta = getSSEMeta(errorData)
|
|
282
|
-
|
|
283
|
-
await stream.writeSSE({
|
|
284
|
-
data: typeof errorData === 'string' ? errorData : JSON.stringify(errorData),
|
|
285
|
-
event: sseMeta?.event ?? (errorResult?.data !== undefined ? procedure.name : 'error'),
|
|
286
|
-
id: sseMeta?.id ?? String(eventId++),
|
|
287
|
-
...(sseMeta?.retry !== undefined && { retry: sseMeta.retry }),
|
|
288
|
-
})
|
|
289
|
-
|
|
290
|
-
// closeStream defaults to true if not specified
|
|
291
|
-
// (stream closes naturally after this handler completes)
|
|
292
|
-
} finally {
|
|
293
|
-
if (this.config?.onStreamEnd) {
|
|
294
|
-
this.config.onStreamEnd(procedure, c, 'sse')
|
|
295
|
-
}
|
|
296
|
-
if (this.config?.onRequestEnd) {
|
|
297
|
-
this.config.onRequestEnd(c)
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
})
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Handles text streaming mode.
|
|
305
|
-
*/
|
|
306
|
-
private handleTextStream(
|
|
307
|
-
procedure: TStreamProcedureRegistration,
|
|
308
|
-
context: any,
|
|
309
|
-
params: any,
|
|
310
|
-
c: Context
|
|
311
|
-
) {
|
|
312
|
-
return streamText(c, async (stream) => {
|
|
313
|
-
// Pass isPrevalidated: true since we already validated params in createStreamHandler
|
|
314
|
-
const generator = procedure.handler({ ...context, signal: c.req.raw.signal, isPrevalidated: true }, params)
|
|
315
|
-
|
|
316
|
-
stream.onAbort(async () => {
|
|
317
|
-
await generator.return(undefined)
|
|
318
|
-
})
|
|
319
|
-
|
|
320
|
-
try {
|
|
321
|
-
for await (const value of generator) {
|
|
322
|
-
await stream.writeln(JSON.stringify(value))
|
|
323
|
-
}
|
|
324
|
-
} catch (error) {
|
|
325
|
-
// Get error yield value from callback (onMidStreamError)
|
|
326
|
-
let errorResult: MidStreamErrorResult<TErrorData> | undefined
|
|
327
|
-
|
|
328
|
-
if (this.config?.onMidStreamError) {
|
|
329
|
-
errorResult = this.config.onMidStreamError(procedure, c, error as Error)
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// Write error value to stream
|
|
333
|
-
const errorData = errorResult?.data ?? { error: (error as Error).message }
|
|
334
|
-
await stream.writeln(JSON.stringify(errorData))
|
|
335
|
-
} finally {
|
|
336
|
-
if (this.config?.onStreamEnd) {
|
|
337
|
-
this.config.onStreamEnd(procedure, c, 'text')
|
|
338
|
-
}
|
|
339
|
-
if (this.config?.onRequestEnd) {
|
|
340
|
-
this.config.onRequestEnd(c)
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
})
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* Builds and returns the Hono application with registered streaming routes.
|
|
348
|
-
*/
|
|
349
|
-
build(): Hono {
|
|
350
|
-
this.factories.forEach(({ factory, factoryContext, streamMode, extendProcedureDoc }) => {
|
|
351
|
-
const mode = streamMode ?? this.config?.defaultStreamMode ?? 'sse'
|
|
352
|
-
|
|
353
|
-
factory
|
|
354
|
-
.getProcedures()
|
|
355
|
-
.filter(
|
|
356
|
-
(p: { isStream?: boolean }): p is TStreamProcedureRegistration => p.isStream === true
|
|
357
|
-
)
|
|
358
|
-
.forEach((procedure: TStreamProcedureRegistration<any, RPCConfig>) => {
|
|
359
|
-
const route = this.buildStreamHttpRouteDoc(procedure, mode, extendProcedureDoc)
|
|
360
|
-
|
|
361
|
-
this._docs.push(route)
|
|
362
|
-
|
|
363
|
-
const handler = this.createStreamHandler(procedure, factoryContext, mode)
|
|
364
|
-
|
|
365
|
-
// Register both GET and POST handlers
|
|
366
|
-
this._app.get(route.path, handler)
|
|
367
|
-
this._app.post(route.path, handler)
|
|
368
|
-
})
|
|
369
|
-
})
|
|
370
|
-
|
|
371
|
-
return this._app
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Generates the Stream HTTP route documentation for the given procedure.
|
|
376
|
-
*/
|
|
377
|
-
private buildStreamHttpRouteDoc(
|
|
378
|
-
procedure: TStreamProcedureRegistration<any, RPCConfig>,
|
|
379
|
-
streamMode: StreamMode,
|
|
380
|
-
extendProcedureDoc?: HonoStreamFactoryItem['extendProcedureDoc']
|
|
381
|
-
): StreamHttpRouteDoc {
|
|
382
|
-
const { config } = procedure
|
|
383
|
-
const path = HonoStreamAppBuilder.makeStreamHttpRoutePath({
|
|
384
|
-
name: procedure.name,
|
|
385
|
-
config,
|
|
386
|
-
prefix: this.config?.pathPrefix,
|
|
387
|
-
})
|
|
388
|
-
const methods = ['get', 'post'] as const
|
|
389
|
-
const jsonSchema: { params?: Record<string, unknown>; yieldType?: Record<string, unknown>; returnType?: Record<string, unknown> } = {}
|
|
390
|
-
|
|
391
|
-
if (config.schema?.params) {
|
|
392
|
-
jsonSchema.params = config.schema.params
|
|
393
|
-
}
|
|
394
|
-
if (streamMode === 'sse') {
|
|
395
|
-
jsonSchema.yieldType = {
|
|
396
|
-
type: 'object',
|
|
397
|
-
description: 'SSE message envelope. The data field contains the procedure yield value.',
|
|
398
|
-
required: ['data', 'event', 'id'],
|
|
399
|
-
properties: {
|
|
400
|
-
data: config.schema?.yieldType ?? {},
|
|
401
|
-
event: { type: 'string' },
|
|
402
|
-
id: { type: 'string' },
|
|
403
|
-
retry: { type: 'number' },
|
|
404
|
-
},
|
|
405
|
-
}
|
|
406
|
-
} else if (config.schema?.yieldType) {
|
|
407
|
-
// Text mode: pass through as-is
|
|
408
|
-
jsonSchema.yieldType = config.schema.yieldType
|
|
409
|
-
}
|
|
410
|
-
if (config.schema?.returnType) {
|
|
411
|
-
jsonSchema.returnType = config.schema.returnType
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
const base: StreamHttpRouteDoc = {
|
|
415
|
-
name: procedure.name,
|
|
416
|
-
version: config.version,
|
|
417
|
-
scope: config.scope,
|
|
418
|
-
path,
|
|
419
|
-
methods: [...methods],
|
|
420
|
-
streamMode,
|
|
421
|
-
jsonSchema,
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
let extendedDoc: object = {}
|
|
425
|
-
|
|
426
|
-
if (extendProcedureDoc) {
|
|
427
|
-
extendedDoc = extendProcedureDoc({ base, procedure })
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
return {
|
|
431
|
-
...extendedDoc,
|
|
432
|
-
...base,
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { Context } from 'hono'
|
|
2
|
-
import { TStreamProcedureRegistration } from '../../../index.js'
|
|
3
|
-
import { ExtractConfig, ExtractContext, RPCConfig } from '../../types.js'
|
|
4
|
-
|
|
5
|
-
export type StreamMode = 'sse' | 'text'
|
|
6
|
-
|
|
7
|
-
export interface StreamHttpRouteDoc extends RPCConfig {
|
|
8
|
-
name: string
|
|
9
|
-
path: string
|
|
10
|
-
methods: ('get' | 'post')[]
|
|
11
|
-
streamMode: StreamMode
|
|
12
|
-
jsonSchema: {
|
|
13
|
-
params?: Record<string, unknown>
|
|
14
|
-
yieldType?: Record<string, unknown>
|
|
15
|
-
returnType?: Record<string, unknown>
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export type HonoStreamFactoryItem<TFactory = any> = {
|
|
20
|
-
factory: TFactory
|
|
21
|
-
factoryContext:
|
|
22
|
-
| ExtractContext<TFactory>
|
|
23
|
-
| ((c: Context) => ExtractContext<TFactory> | Promise<ExtractContext<TFactory>>)
|
|
24
|
-
streamMode?: StreamMode
|
|
25
|
-
extendProcedureDoc?: (params: {
|
|
26
|
-
base: StreamHttpRouteDoc
|
|
27
|
-
procedure: TStreamProcedureRegistration<any, ExtractConfig<TFactory>>
|
|
28
|
-
}) => Record<string, any>
|
|
29
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { Procedures } from '../index.js'
|
|
2
|
-
|
|
3
|
-
export interface RPCConfig {
|
|
4
|
-
// Scope or scopes (scope segments) required to access the RPC
|
|
5
|
-
scope: string | string[]
|
|
6
|
-
version: number
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export type FactoryItem<C> = {
|
|
10
|
-
factory: ReturnType<typeof Procedures<C, RPCConfig>>
|
|
11
|
-
factoryContext: (req: Request) => C
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface RPCHttpRouteDoc extends RPCConfig {
|
|
15
|
-
name: string // procedure name
|
|
16
|
-
path: string
|
|
17
|
-
method: 'post'
|
|
18
|
-
jsonSchema: {
|
|
19
|
-
body?: Record<string, unknown>
|
|
20
|
-
response?: Record<string, unknown>
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export type StreamMode = 'sse' | 'text'
|
|
25
|
-
|
|
26
|
-
export interface StreamHttpRouteDoc extends RPCConfig {
|
|
27
|
-
name: string // procedure name
|
|
28
|
-
path: string
|
|
29
|
-
methods: ('get' | 'post')[]
|
|
30
|
-
streamMode: StreamMode
|
|
31
|
-
jsonSchema: {
|
|
32
|
-
params?: Record<string, unknown> // Query params (GET) or body (POST)
|
|
33
|
-
yieldType?: Record<string, unknown> // Schema for each streamed value
|
|
34
|
-
returnType?: Record<string, unknown> // Final return (optional)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// ================
|
|
39
|
-
// Utility types
|
|
40
|
-
// ================
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Extracts the TContext type from a Procedures factory return type.
|
|
44
|
-
* Uses the first parameter of the handler function to infer the context type.
|
|
45
|
-
*/
|
|
46
|
-
export type ExtractContext<TFactory> = TFactory extends {
|
|
47
|
-
getProcedures: () => Array<{ handler: (ctx: infer TContext, ...args: any[]) => any }>
|
|
48
|
-
}
|
|
49
|
-
? TContext
|
|
50
|
-
: never
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Extracts the TConfig type from a Procedures factory return type.
|
|
54
|
-
* Uses the config property of the procedure registration to infer the config type.
|
|
55
|
-
*/
|
|
56
|
-
export type ExtractConfig<TFactory> = TFactory extends {
|
|
57
|
-
getProcedures: () => Array<{ config: infer TConfig }>
|
|
58
|
-
}
|
|
59
|
-
? TConfig
|
|
60
|
-
: never
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Minimal structural type for a Procedures factory.
|
|
64
|
-
* Uses explicit `any` types to avoid variance issues with generic constraints.
|
|
65
|
-
*/
|
|
66
|
-
export type ProceduresFactory = {
|
|
67
|
-
getProcedures: () => Array<{
|
|
68
|
-
name: string
|
|
69
|
-
isStream?: boolean
|
|
70
|
-
config: any
|
|
71
|
-
handler: (ctx: any, params?: any) => Promise<any> | AsyncGenerator<any, any, unknown>
|
|
72
|
-
}>
|
|
73
|
-
Create: (...args: any[]) => any
|
|
74
|
-
CreateStream?: (...args: any[]) => any
|
|
75
|
-
}
|