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
package/src/index.ts
DELETED
|
@@ -1,435 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ProcedureError,
|
|
3
|
-
ProcedureValidationError,
|
|
4
|
-
ProcedureYieldValidationError,
|
|
5
|
-
} from './errors.js'
|
|
6
|
-
import { computeSchema } from './schema/compute-schema.js'
|
|
7
|
-
import { Prettify, TJSONSchema, TSchemaLib } from './schema/types.js'
|
|
8
|
-
import { captureDefinitionInfo } from './stack-utils.js'
|
|
9
|
-
|
|
10
|
-
export type TNoContextProvided = unknown
|
|
11
|
-
|
|
12
|
-
export type TLocalContext = {
|
|
13
|
-
error: (message: string, meta?: object) => ProcedureError
|
|
14
|
-
signal?: AbortSignal
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export type TStreamContext = TLocalContext & {
|
|
18
|
-
signal: AbortSignal
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export type TProcedureRegistration<TContext = unknown, TExtendedConfig = unknown> = {
|
|
22
|
-
name: string
|
|
23
|
-
isStream?: false
|
|
24
|
-
config: {
|
|
25
|
-
description?: string
|
|
26
|
-
schema?: {
|
|
27
|
-
params?: TJSONSchema
|
|
28
|
-
returnType?: TJSONSchema
|
|
29
|
-
}
|
|
30
|
-
validation?: {
|
|
31
|
-
params?: (params: any) => { errors?: any[] }
|
|
32
|
-
}
|
|
33
|
-
} & TExtendedConfig
|
|
34
|
-
|
|
35
|
-
handler: (ctx: TContext, params?: any) => Promise<any>
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export type TStreamProcedureRegistration<TContext = unknown, TExtendedConfig = unknown> = {
|
|
39
|
-
name: string
|
|
40
|
-
isStream: true
|
|
41
|
-
config: {
|
|
42
|
-
description?: string
|
|
43
|
-
schema?: {
|
|
44
|
-
params?: TJSONSchema
|
|
45
|
-
yieldType?: TJSONSchema
|
|
46
|
-
returnType?: TJSONSchema
|
|
47
|
-
}
|
|
48
|
-
validation?: {
|
|
49
|
-
params?: (params: any) => { errors?: any[] }
|
|
50
|
-
yield?: (value: any) => { errors?: any[] }
|
|
51
|
-
}
|
|
52
|
-
} & TExtendedConfig
|
|
53
|
-
|
|
54
|
-
handler: (ctx: TContext, params?: any) => AsyncGenerator<any, any, unknown>
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function Procedures<TContext = TNoContextProvided, TExtendedConfig = unknown>(
|
|
58
|
-
/**
|
|
59
|
-
* Optionally provided builder to register Procedures
|
|
60
|
-
*/
|
|
61
|
-
builder?: {
|
|
62
|
-
onCreate?: (
|
|
63
|
-
procedure: Prettify<{
|
|
64
|
-
name: string
|
|
65
|
-
isStream?: boolean
|
|
66
|
-
handler:
|
|
67
|
-
| ((ctx: Prettify<TContext>, params?: any) => Promise<any>)
|
|
68
|
-
| ((ctx: Prettify<TContext>, params?: any) => AsyncGenerator<any, any, unknown>)
|
|
69
|
-
config: Prettify<
|
|
70
|
-
{
|
|
71
|
-
description?: string
|
|
72
|
-
schema?: {
|
|
73
|
-
params?: TJSONSchema
|
|
74
|
-
yieldType?: TJSONSchema
|
|
75
|
-
returnType?: TJSONSchema
|
|
76
|
-
}
|
|
77
|
-
validation?: {
|
|
78
|
-
params?: (params: any) => { errors?: any[] }
|
|
79
|
-
yield?: (value: any) => { errors?: any[] }
|
|
80
|
-
}
|
|
81
|
-
} & TExtendedConfig
|
|
82
|
-
>
|
|
83
|
-
}>
|
|
84
|
-
) => void
|
|
85
|
-
}
|
|
86
|
-
) {
|
|
87
|
-
const procedures: Map<
|
|
88
|
-
string,
|
|
89
|
-
| TProcedureRegistration<TContext, TExtendedConfig>
|
|
90
|
-
| TStreamProcedureRegistration<TContext, TExtendedConfig>
|
|
91
|
-
> = new Map()
|
|
92
|
-
|
|
93
|
-
function Create<TName extends string, TParams, TReturnType>(
|
|
94
|
-
name: TName,
|
|
95
|
-
config: {
|
|
96
|
-
description?: string
|
|
97
|
-
schema?: {
|
|
98
|
-
params?: TParams
|
|
99
|
-
returnType?: TReturnType
|
|
100
|
-
}
|
|
101
|
-
} & TExtendedConfig,
|
|
102
|
-
handler: (
|
|
103
|
-
ctx: Prettify<TContext & TLocalContext & { isPrevalidated?: boolean }>,
|
|
104
|
-
params: TSchemaLib<TParams>
|
|
105
|
-
) => Promise<TSchemaLib<TReturnType>>
|
|
106
|
-
) {
|
|
107
|
-
// Capture definition location as first action
|
|
108
|
-
const definitionInfo = captureDefinitionInfo()
|
|
109
|
-
|
|
110
|
-
// BEFORE computeSchema - fail fast on duplicate
|
|
111
|
-
if (procedures.has(name)) {
|
|
112
|
-
throw new Error(`Procedure with name ${name} is already registered`)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const { jsonSchema, validations } = computeSchema(name, config.schema, definitionInfo)
|
|
116
|
-
|
|
117
|
-
// Create error factory once at registration time (outside handler)
|
|
118
|
-
const errorFactory = (message: string, meta?: object) => {
|
|
119
|
-
return new ProcedureError(name, message, meta, definitionInfo)
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const registeredProcedure = {
|
|
123
|
-
name,
|
|
124
|
-
config: {
|
|
125
|
-
// ctx: config.hook, ??? why was this here
|
|
126
|
-
...config,
|
|
127
|
-
description: config.description,
|
|
128
|
-
schema: jsonSchema,
|
|
129
|
-
validation: {
|
|
130
|
-
params: validations.params,
|
|
131
|
-
},
|
|
132
|
-
},
|
|
133
|
-
|
|
134
|
-
handler: async (ctx: Prettify<TContext>, params: TSchemaLib<TParams>) => {
|
|
135
|
-
try {
|
|
136
|
-
// Skip validation if caller has already validated (e.g., HonoStreamAppBuilder)
|
|
137
|
-
const isPrevalidated = (ctx as { isPrevalidated?: boolean }).isPrevalidated
|
|
138
|
-
if (validations?.params && !isPrevalidated) {
|
|
139
|
-
const { errors } = validations.params(params)
|
|
140
|
-
|
|
141
|
-
if (errors) {
|
|
142
|
-
throw new ProcedureValidationError(
|
|
143
|
-
name,
|
|
144
|
-
`Validation error for ${name}`,
|
|
145
|
-
errors,
|
|
146
|
-
definitionInfo
|
|
147
|
-
)
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const localCtx: TLocalContext = {
|
|
152
|
-
error: errorFactory, // Reuse pre-created factory
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return await handler(
|
|
156
|
-
{
|
|
157
|
-
...ctx,
|
|
158
|
-
...localCtx,
|
|
159
|
-
} as Prettify<TContext & TLocalContext & { isPrevalidated?: boolean }>,
|
|
160
|
-
params
|
|
161
|
-
)
|
|
162
|
-
} catch (error: any) {
|
|
163
|
-
if (error instanceof ProcedureError) {
|
|
164
|
-
throw error
|
|
165
|
-
} else {
|
|
166
|
-
const err = new ProcedureError(
|
|
167
|
-
name,
|
|
168
|
-
`Error in handler for ${name} - ${error?.message}`,
|
|
169
|
-
undefined,
|
|
170
|
-
definitionInfo
|
|
171
|
-
)
|
|
172
|
-
err.cause = error // Preserve original error
|
|
173
|
-
// Preserve original stack but append definition info
|
|
174
|
-
if (error.stack && definitionInfo.definedAt) {
|
|
175
|
-
const { file, line, column } = definitionInfo.definedAt
|
|
176
|
-
err.stack =
|
|
177
|
-
error.stack +
|
|
178
|
-
`\n--- Procedure "${name}" defined at ---\n at ${file}:${line}:${column}`
|
|
179
|
-
} else if (error.stack) {
|
|
180
|
-
err.stack = error.stack
|
|
181
|
-
}
|
|
182
|
-
throw err
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
},
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
procedures.set(name, registeredProcedure)
|
|
189
|
-
|
|
190
|
-
if (builder?.onCreate) {
|
|
191
|
-
builder.onCreate(registeredProcedure)
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const info = {
|
|
195
|
-
name,
|
|
196
|
-
...registeredProcedure.config,
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// return so can be called directly (ie: int/unit tests)
|
|
200
|
-
return {
|
|
201
|
-
[name]: registeredProcedure.handler,
|
|
202
|
-
procedure: registeredProcedure.handler,
|
|
203
|
-
info,
|
|
204
|
-
} as {
|
|
205
|
-
[K in TName]: (
|
|
206
|
-
ctx: Prettify<TContext>,
|
|
207
|
-
params: TSchemaLib<TParams>
|
|
208
|
-
) => Promise<TSchemaLib<TReturnType>>
|
|
209
|
-
} & {
|
|
210
|
-
procedure: (
|
|
211
|
-
ctx: Prettify<TContext>,
|
|
212
|
-
params: TSchemaLib<TParams>
|
|
213
|
-
) => Promise<TSchemaLib<TReturnType>>
|
|
214
|
-
info: {
|
|
215
|
-
name: TName
|
|
216
|
-
description?: string
|
|
217
|
-
schema: {
|
|
218
|
-
params?: TParams
|
|
219
|
-
returnType?: TReturnType
|
|
220
|
-
}
|
|
221
|
-
validation?: {
|
|
222
|
-
params?: (params: any) => { errors?: any[] }
|
|
223
|
-
}
|
|
224
|
-
} & TExtendedConfig
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function CreateStream<TName extends string, TParams, TYieldType, TReturnType = void>(
|
|
229
|
-
name: TName,
|
|
230
|
-
config: {
|
|
231
|
-
description?: string
|
|
232
|
-
schema?: {
|
|
233
|
-
params?: TParams
|
|
234
|
-
yieldType?: TYieldType
|
|
235
|
-
returnType?: TReturnType
|
|
236
|
-
}
|
|
237
|
-
validateYields?: boolean
|
|
238
|
-
} & TExtendedConfig,
|
|
239
|
-
handler: (
|
|
240
|
-
ctx: Prettify<TContext & TStreamContext & { isPrevalidated?: boolean }>,
|
|
241
|
-
params: TSchemaLib<TParams>
|
|
242
|
-
) => AsyncGenerator<TSchemaLib<TYieldType>, TSchemaLib<TReturnType> | void, unknown>
|
|
243
|
-
) {
|
|
244
|
-
// Capture definition location as first action
|
|
245
|
-
const definitionInfo = captureDefinitionInfo()
|
|
246
|
-
|
|
247
|
-
// BEFORE computeSchema - fail fast on duplicate
|
|
248
|
-
if (procedures.has(name)) {
|
|
249
|
-
throw new Error(`Procedure with name ${name} is already registered`)
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
const { jsonSchema, validations } = computeSchema(name, config.schema, definitionInfo)
|
|
253
|
-
|
|
254
|
-
// Create error factory once at registration time (outside handler)
|
|
255
|
-
const errorFactory = (message: string, meta?: object) => {
|
|
256
|
-
return new ProcedureError(name, message, meta, definitionInfo)
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
const validateYields = config.validateYields ?? false
|
|
260
|
-
|
|
261
|
-
const registeredProcedure: TStreamProcedureRegistration<TContext, TExtendedConfig> = {
|
|
262
|
-
name,
|
|
263
|
-
isStream: true,
|
|
264
|
-
config: {
|
|
265
|
-
...config,
|
|
266
|
-
description: config.description,
|
|
267
|
-
schema: jsonSchema,
|
|
268
|
-
validation: {
|
|
269
|
-
params: validations.params,
|
|
270
|
-
yield: validations.yield,
|
|
271
|
-
},
|
|
272
|
-
},
|
|
273
|
-
|
|
274
|
-
handler: async function* wrappedHandler(
|
|
275
|
-
ctx: Prettify<TContext>,
|
|
276
|
-
params: TSchemaLib<TParams>
|
|
277
|
-
) {
|
|
278
|
-
// Create abort controller for this stream
|
|
279
|
-
const abortController = new AbortController()
|
|
280
|
-
|
|
281
|
-
// Skip validation if caller has already validated (e.g., HonoStreamAppBuilder)
|
|
282
|
-
const isPrevalidated = (ctx as { isPrevalidated?: boolean }).isPrevalidated
|
|
283
|
-
if (validations?.params && !isPrevalidated) {
|
|
284
|
-
const { errors } = validations.params(params)
|
|
285
|
-
|
|
286
|
-
if (errors) {
|
|
287
|
-
throw new ProcedureValidationError(
|
|
288
|
-
name,
|
|
289
|
-
`Validation error for ${name}`,
|
|
290
|
-
errors,
|
|
291
|
-
definitionInfo
|
|
292
|
-
)
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// Combine with external signal (e.g., from HTTP request) if provided
|
|
297
|
-
const incomingSignal = (ctx as { signal?: AbortSignal }).signal
|
|
298
|
-
const signal = incomingSignal
|
|
299
|
-
? AbortSignal.any([incomingSignal, abortController.signal])
|
|
300
|
-
: abortController.signal
|
|
301
|
-
|
|
302
|
-
const streamCtx: TStreamContext = {
|
|
303
|
-
error: errorFactory,
|
|
304
|
-
signal,
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
const userGenerator = handler(
|
|
308
|
-
{
|
|
309
|
-
...ctx,
|
|
310
|
-
...streamCtx,
|
|
311
|
-
} as Prettify<TContext & TStreamContext & { isPrevalidated?: boolean }>,
|
|
312
|
-
params
|
|
313
|
-
)
|
|
314
|
-
|
|
315
|
-
try {
|
|
316
|
-
for await (const value of userGenerator) {
|
|
317
|
-
// Only validate if explicitly enabled via validateYields: true
|
|
318
|
-
if (validateYields && validations.yield) {
|
|
319
|
-
const { errors } = validations.yield(value)
|
|
320
|
-
if (errors) {
|
|
321
|
-
throw new ProcedureYieldValidationError(
|
|
322
|
-
name,
|
|
323
|
-
`Yield validation error for ${name}`,
|
|
324
|
-
errors,
|
|
325
|
-
definitionInfo
|
|
326
|
-
)
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
yield value
|
|
331
|
-
}
|
|
332
|
-
} catch (error: any) {
|
|
333
|
-
if (error instanceof ProcedureError) {
|
|
334
|
-
throw error
|
|
335
|
-
} else {
|
|
336
|
-
const err = new ProcedureError(
|
|
337
|
-
name,
|
|
338
|
-
`Error in streaming handler for ${name} - ${error?.message}`,
|
|
339
|
-
undefined,
|
|
340
|
-
definitionInfo
|
|
341
|
-
)
|
|
342
|
-
err.cause = error
|
|
343
|
-
if (error.stack && definitionInfo.definedAt) {
|
|
344
|
-
const { file, line, column } = definitionInfo.definedAt
|
|
345
|
-
err.stack =
|
|
346
|
-
error.stack +
|
|
347
|
-
`\n--- Procedure "${name}" defined at ---\n at ${file}:${line}:${column}`
|
|
348
|
-
} else if (error.stack) {
|
|
349
|
-
err.stack = error.stack
|
|
350
|
-
}
|
|
351
|
-
throw err
|
|
352
|
-
}
|
|
353
|
-
} finally {
|
|
354
|
-
abortController.abort('stream-completed')
|
|
355
|
-
}
|
|
356
|
-
} as (ctx: TContext, params?: any) => AsyncGenerator<any, any, unknown>,
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
procedures.set(name, registeredProcedure)
|
|
360
|
-
|
|
361
|
-
if (builder?.onCreate) {
|
|
362
|
-
builder.onCreate(registeredProcedure)
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
const info = {
|
|
366
|
-
name,
|
|
367
|
-
isStream: true as const,
|
|
368
|
-
...registeredProcedure.config,
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
// return so can be called directly (ie: int/unit tests)
|
|
372
|
-
return {
|
|
373
|
-
[name]: registeredProcedure.handler,
|
|
374
|
-
procedure: registeredProcedure.handler,
|
|
375
|
-
info,
|
|
376
|
-
} as {
|
|
377
|
-
[K in TName]: (
|
|
378
|
-
ctx: Prettify<TContext>,
|
|
379
|
-
params: TSchemaLib<TParams>
|
|
380
|
-
) => AsyncGenerator<TSchemaLib<TYieldType>, TSchemaLib<TReturnType> | void, unknown>
|
|
381
|
-
} & {
|
|
382
|
-
procedure: (
|
|
383
|
-
ctx: Prettify<TContext>,
|
|
384
|
-
params: TSchemaLib<TParams>
|
|
385
|
-
) => AsyncGenerator<TSchemaLib<TYieldType>, TSchemaLib<TReturnType> | void, unknown>
|
|
386
|
-
info: {
|
|
387
|
-
name: TName
|
|
388
|
-
isStream: true
|
|
389
|
-
description?: string
|
|
390
|
-
schema: {
|
|
391
|
-
params?: TParams
|
|
392
|
-
yieldType?: TYieldType
|
|
393
|
-
returnType?: TReturnType
|
|
394
|
-
}
|
|
395
|
-
validation?: {
|
|
396
|
-
params?: (params: any) => { errors?: any[] }
|
|
397
|
-
yield?: (value: any) => { errors?: any[] }
|
|
398
|
-
}
|
|
399
|
-
} & TExtendedConfig
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
return {
|
|
404
|
-
/**
|
|
405
|
-
* Get all registered procedures
|
|
406
|
-
*/
|
|
407
|
-
getProcedures: () => {
|
|
408
|
-
return Array.from(procedures.values())
|
|
409
|
-
},
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Get a specific procedure by name
|
|
413
|
-
*/
|
|
414
|
-
getProcedure: (name: string) => {
|
|
415
|
-
return procedures.get(name)
|
|
416
|
-
},
|
|
417
|
-
|
|
418
|
-
/**
|
|
419
|
-
* Remove a procedure by name
|
|
420
|
-
*/
|
|
421
|
-
removeProcedure: (name: string) => {
|
|
422
|
-
return procedures.delete(name)
|
|
423
|
-
},
|
|
424
|
-
|
|
425
|
-
/**
|
|
426
|
-
* Clear all registered procedures
|
|
427
|
-
*/
|
|
428
|
-
clear: () => {
|
|
429
|
-
procedures.clear()
|
|
430
|
-
},
|
|
431
|
-
|
|
432
|
-
Create,
|
|
433
|
-
CreateStream,
|
|
434
|
-
}
|
|
435
|
-
}
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import { Type } from 'typebox'
|
|
3
|
-
import { v } from 'suretype'
|
|
4
|
-
import { computeSchema } from './compute-schema.js'
|
|
5
|
-
import { ProcedureRegistrationError } from '../errors.js'
|
|
6
|
-
|
|
7
|
-
describe('computeSchema', () => {
|
|
8
|
-
it('should return empty schema and validations when no schema provided', () => {
|
|
9
|
-
const result = computeSchema('test-procedure')
|
|
10
|
-
|
|
11
|
-
expect(result).toEqual({
|
|
12
|
-
jsonSchema: { params: undefined, returnType: undefined },
|
|
13
|
-
validations: {}
|
|
14
|
-
})
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
describe('with Typebox schema', () => {
|
|
18
|
-
it('should correctly process params schema', () => {
|
|
19
|
-
const schema = {
|
|
20
|
-
params: Type.Object({
|
|
21
|
-
name: Type.String(),
|
|
22
|
-
age: Type.Number()
|
|
23
|
-
})
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const result = computeSchema('test-procedure', schema)
|
|
27
|
-
|
|
28
|
-
expect(result.jsonSchema.params).toBeDefined()
|
|
29
|
-
expect(result.validations.params).toBeDefined()
|
|
30
|
-
|
|
31
|
-
// Test validation function
|
|
32
|
-
const validInput = { name: 'John', age: 30 }
|
|
33
|
-
expect(result.validations.params?.(validInput).errors).toBeUndefined()
|
|
34
|
-
|
|
35
|
-
// Test invalid input
|
|
36
|
-
const invalidInput = { name: 123, age: 'invalid' }
|
|
37
|
-
expect(result.validations.params?.(invalidInput).errors).toBeDefined()
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
it('should correctly process returnType schema', () => {
|
|
41
|
-
const schema = {
|
|
42
|
-
returnType: Type.Object({
|
|
43
|
-
result: Type.Boolean()
|
|
44
|
-
})
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const result = computeSchema('test-procedure', schema)
|
|
48
|
-
|
|
49
|
-
expect(result.jsonSchema.returnType).toBeDefined()
|
|
50
|
-
expect(result.validations.params).toBeUndefined()
|
|
51
|
-
})
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
describe('with Suretype schema', () => {
|
|
55
|
-
it('should correctly process params schema', () => {
|
|
56
|
-
const schema = {
|
|
57
|
-
params: v.object({
|
|
58
|
-
name: v.string(),
|
|
59
|
-
age: v.number()
|
|
60
|
-
})
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const result = computeSchema('test-procedure', schema)
|
|
64
|
-
|
|
65
|
-
expect(result.jsonSchema.params).toBeDefined()
|
|
66
|
-
expect(result.validations.params).toBeDefined()
|
|
67
|
-
|
|
68
|
-
// Test validation function
|
|
69
|
-
const validInput = { name: 'John', age: 30 }
|
|
70
|
-
expect(result.validations.params?.(validInput).errors).toBeUndefined()
|
|
71
|
-
|
|
72
|
-
// Test invalid input
|
|
73
|
-
const invalidInput = { name: 123, age: 'invalid' }
|
|
74
|
-
expect(result.validations.params?.(invalidInput).errors).toBeDefined()
|
|
75
|
-
})
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
describe('error handling', () => {
|
|
79
|
-
it('should throw ProcedureRegistrationError for invalid schema', () => {
|
|
80
|
-
const invalidSchema = {
|
|
81
|
-
params: {
|
|
82
|
-
type: 'invalid-schema-type'
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
expect(() => computeSchema('test-procedure', invalidSchema))
|
|
87
|
-
.toThrow(ProcedureRegistrationError)
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it('should include procedure name in error message', () => {
|
|
91
|
-
const invalidSchema = {
|
|
92
|
-
params: {
|
|
93
|
-
type: 'invalid-schema-type'
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
try {
|
|
98
|
-
computeSchema('test-procedure', invalidSchema)
|
|
99
|
-
} catch (error: any) {
|
|
100
|
-
expect(error instanceof ProcedureRegistrationError).toBe(true)
|
|
101
|
-
expect(error.message).toContain('test-procedure')
|
|
102
|
-
}
|
|
103
|
-
})
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
describe('combined schemas', () => {
|
|
107
|
-
it('should handle both params and returnType schemas', () => {
|
|
108
|
-
const schema = {
|
|
109
|
-
params: Type.Object({
|
|
110
|
-
input: Type.String()
|
|
111
|
-
}),
|
|
112
|
-
returnType: Type.Object({
|
|
113
|
-
output: Type.Boolean()
|
|
114
|
-
})
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const result = computeSchema('test-procedure', schema)
|
|
118
|
-
|
|
119
|
-
expect(result.jsonSchema.params).toBeDefined()
|
|
120
|
-
expect(result.jsonSchema.returnType).toBeDefined()
|
|
121
|
-
expect(result.validations.params).toBeDefined()
|
|
122
|
-
|
|
123
|
-
// Test params validation
|
|
124
|
-
const validInput = { input: 'test' }
|
|
125
|
-
expect(result.validations.params?.(validInput).errors).toBeUndefined()
|
|
126
|
-
})
|
|
127
|
-
})
|
|
128
|
-
})
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { schemaParser, TSchemaValidationError } from './parser.js'
|
|
2
|
-
import { ProcedureRegistrationError } from '../errors.js'
|
|
3
|
-
import { TJSONSchema } from './types.js'
|
|
4
|
-
import { DefinitionInfo } from '../stack-utils.js'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* This function is used to compute the JSON schema and validation functions
|
|
8
|
-
* for a given schema.
|
|
9
|
-
*
|
|
10
|
-
* @param name The name of the procedure
|
|
11
|
-
* @param schema Procedure schema
|
|
12
|
-
* @param definitionInfo Optional definition info for error reporting
|
|
13
|
-
*/
|
|
14
|
-
export function computeSchema<TParamsSchemaType, TReturnTypeSchemaType, TYieldTypeSchemaType = unknown>(
|
|
15
|
-
name: string,
|
|
16
|
-
schema?: {
|
|
17
|
-
params?: TParamsSchemaType
|
|
18
|
-
returnType?: TReturnTypeSchemaType
|
|
19
|
-
yieldType?: TYieldTypeSchemaType
|
|
20
|
-
},
|
|
21
|
-
// Used for error stack trace details
|
|
22
|
-
definitionInfo?: DefinitionInfo
|
|
23
|
-
): {
|
|
24
|
-
jsonSchema: {
|
|
25
|
-
params?: TJSONSchema
|
|
26
|
-
returnType?: TJSONSchema
|
|
27
|
-
yieldType?: TJSONSchema
|
|
28
|
-
}
|
|
29
|
-
validations: {
|
|
30
|
-
params?: (params?: any) => { errors?: TSchemaValidationError[] }
|
|
31
|
-
yield?: (value?: any) => { errors?: TSchemaValidationError[] }
|
|
32
|
-
}
|
|
33
|
-
} {
|
|
34
|
-
const jsonSchema: { params?: TJSONSchema; returnType?: TJSONSchema; yieldType?: TJSONSchema } = {
|
|
35
|
-
params: undefined,
|
|
36
|
-
returnType: undefined,
|
|
37
|
-
yieldType: undefined,
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const validations: {
|
|
41
|
-
params?: (params?: any) => { errors?: TSchemaValidationError[] }
|
|
42
|
-
yield?: (value?: any) => { errors?: TSchemaValidationError[] }
|
|
43
|
-
} = {}
|
|
44
|
-
|
|
45
|
-
if (schema) {
|
|
46
|
-
const {
|
|
47
|
-
jsonSchema: { params, returnType, yieldType },
|
|
48
|
-
validation,
|
|
49
|
-
} = schemaParser(schema, (errors) => {
|
|
50
|
-
throw new ProcedureRegistrationError(
|
|
51
|
-
name,
|
|
52
|
-
`Error parsing schema for ${name} - ${Object.entries(errors)
|
|
53
|
-
.map(([key, error]) => `${key}: ${error}`)
|
|
54
|
-
.join(', ')}`,
|
|
55
|
-
definitionInfo
|
|
56
|
-
)
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
jsonSchema.params = params
|
|
60
|
-
jsonSchema.returnType = returnType
|
|
61
|
-
jsonSchema.yieldType = yieldType
|
|
62
|
-
validations.params = validation.params
|
|
63
|
-
validations.yield = validation.yield
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return { jsonSchema, validations }
|
|
67
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'vitest'
|
|
2
|
-
import { Type } from 'typebox'
|
|
3
|
-
import { v } from 'suretype'
|
|
4
|
-
import { extractJsonSchema } from './extract-json-schema.js'
|
|
5
|
-
|
|
6
|
-
describe('extractJsonSchema()', () => {
|
|
7
|
-
const typebox = Type.Object({ name: Type.String() })
|
|
8
|
-
const suretype = v.object({ name: v.string().required() })
|
|
9
|
-
|
|
10
|
-
test('it extracts TypeBox json-schema', async () => {
|
|
11
|
-
expect(extractJsonSchema(typebox)).toMatchObject({
|
|
12
|
-
type: 'object',
|
|
13
|
-
properties: { name: { type: 'string' } },
|
|
14
|
-
required: ['name'],
|
|
15
|
-
})
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
test('it extracts Suretype json-schema', async () => {
|
|
19
|
-
expect(extractJsonSchema(suretype)).toMatchObject({
|
|
20
|
-
type: 'object',
|
|
21
|
-
properties: { name: { type: 'string' } },
|
|
22
|
-
required: ['name'],
|
|
23
|
-
})
|
|
24
|
-
})
|
|
25
|
-
})
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { extractSingleJsonSchema } from 'suretype'
|
|
2
|
-
import { isSuretypeSchema, isTypeboxSchema } from './resolve-schema-lib.js'
|
|
3
|
-
import { TJSONSchema } from './types.js'
|
|
4
|
-
|
|
5
|
-
export function extractJsonSchema(libSchema: unknown): TJSONSchema | undefined {
|
|
6
|
-
if (isTypeboxSchema(libSchema)) {
|
|
7
|
-
return libSchema
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
if (isSuretypeSchema(libSchema)) {
|
|
11
|
-
return extractSingleJsonSchema(libSchema)?.schema
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
return undefined
|
|
15
|
-
}
|