ts-procedures 5.4.0 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/agent_config/claude-code/skills/guide/SKILL.md +1 -0
- package/agent_config/claude-code/skills/guide/anti-patterns.md +37 -4
- package/agent_config/claude-code/skills/guide/api-reference.md +86 -0
- package/agent_config/claude-code/skills/guide/patterns.md +33 -0
- package/agent_config/claude-code/skills/review/checklist.md +2 -0
- package/agent_config/claude-code/skills/scaffold/templates/hono-api.md +1 -1
- package/agent_config/copilot/copilot-instructions.md +4 -0
- package/agent_config/cursor/cursorrules +4 -0
- package/build/implementations/http/doc-registry.d.ts +12 -0
- package/build/implementations/http/doc-registry.js +114 -0
- package/build/implementations/http/doc-registry.js.map +1 -0
- package/build/implementations/http/doc-registry.test.d.ts +1 -0
- package/build/implementations/http/doc-registry.test.js +321 -0
- package/build/implementations/http/doc-registry.test.js.map +1 -0
- package/build/implementations/types.d.ts +31 -0
- package/package.json +5 -2
- 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 -260
- 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-api/index.test.ts +0 -1328
- package/src/implementations/http/hono-api/index.ts +0 -461
- package/src/implementations/http/hono-api/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 -127
- package/src/index.test.ts +0 -1194
- package/src/index.ts +0 -512
- package/src/schema/compute-schema.test.ts +0 -128
- package/src/schema/compute-schema.ts +0 -88
- 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 -215
- 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,88 +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
|
-
input?: Record<string, unknown>
|
|
21
|
-
},
|
|
22
|
-
// Used for error stack trace details
|
|
23
|
-
definitionInfo?: DefinitionInfo
|
|
24
|
-
): {
|
|
25
|
-
jsonSchema: {
|
|
26
|
-
params?: TJSONSchema
|
|
27
|
-
returnType?: TJSONSchema
|
|
28
|
-
yieldType?: TJSONSchema
|
|
29
|
-
input?: Record<string, TJSONSchema>
|
|
30
|
-
}
|
|
31
|
-
validations: {
|
|
32
|
-
params?: (params?: any) => { errors?: TSchemaValidationError[] }
|
|
33
|
-
yield?: (value?: any) => { errors?: TSchemaValidationError[] }
|
|
34
|
-
input?: Record<string, (value?: any) => { errors?: TSchemaValidationError[] }>
|
|
35
|
-
}
|
|
36
|
-
} {
|
|
37
|
-
const jsonSchema: {
|
|
38
|
-
params?: TJSONSchema
|
|
39
|
-
returnType?: TJSONSchema
|
|
40
|
-
yieldType?: TJSONSchema
|
|
41
|
-
input?: Record<string, TJSONSchema>
|
|
42
|
-
} = {
|
|
43
|
-
params: undefined,
|
|
44
|
-
returnType: undefined,
|
|
45
|
-
yieldType: undefined,
|
|
46
|
-
input: undefined,
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const validations: {
|
|
50
|
-
params?: (params?: any) => { errors?: TSchemaValidationError[] }
|
|
51
|
-
yield?: (value?: any) => { errors?: TSchemaValidationError[] }
|
|
52
|
-
input?: Record<string, (value?: any) => { errors?: TSchemaValidationError[] }>
|
|
53
|
-
} = {}
|
|
54
|
-
|
|
55
|
-
// Mutual exclusivity: params and input cannot both be defined
|
|
56
|
-
if (schema?.params && schema?.input) {
|
|
57
|
-
throw new ProcedureRegistrationError(
|
|
58
|
-
name,
|
|
59
|
-
`schema.params and schema.input are mutually exclusive for procedure "${name}". Use schema.params for flat input or schema.input for structured multi-channel input.`,
|
|
60
|
-
definitionInfo
|
|
61
|
-
)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (schema) {
|
|
65
|
-
const {
|
|
66
|
-
jsonSchema: { params, returnType, yieldType, input },
|
|
67
|
-
validation,
|
|
68
|
-
} = schemaParser(schema, (errors) => {
|
|
69
|
-
throw new ProcedureRegistrationError(
|
|
70
|
-
name,
|
|
71
|
-
`Error parsing schema for ${name} - ${Object.entries(errors)
|
|
72
|
-
.map(([key, error]) => `${key}: ${error}`)
|
|
73
|
-
.join(', ')}`,
|
|
74
|
-
definitionInfo
|
|
75
|
-
)
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
jsonSchema.params = params
|
|
79
|
-
jsonSchema.returnType = returnType
|
|
80
|
-
jsonSchema.yieldType = yieldType
|
|
81
|
-
jsonSchema.input = input
|
|
82
|
-
validations.params = validation.params
|
|
83
|
-
validations.yield = validation.yield
|
|
84
|
-
validations.input = validation.input
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return { jsonSchema, validations }
|
|
88
|
-
}
|
|
@@ -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
|
-
}
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'vitest'
|
|
2
|
-
import { extractSingleJsonSchema, v } from 'suretype'
|
|
3
|
-
import { schemaParser } from './parser.js'
|
|
4
|
-
import { Type } from 'typebox'
|
|
5
|
-
|
|
6
|
-
describe('schemaParser', () => {
|
|
7
|
-
test('it parses params to json-schema', async () => {
|
|
8
|
-
let done: () => void = () => void 0
|
|
9
|
-
const promise = new Promise<void>((r) => {
|
|
10
|
-
done = r
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
const params = v.object({
|
|
14
|
-
name: v.string(),
|
|
15
|
-
age: v.number(),
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
const result = schemaParser(
|
|
19
|
-
{
|
|
20
|
-
params: params,
|
|
21
|
-
},
|
|
22
|
-
(errors) => {
|
|
23
|
-
throw new Error(JSON.stringify(errors))
|
|
24
|
-
},
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
expect(result.jsonSchema.params).toEqual(
|
|
28
|
-
extractSingleJsonSchema(params)?.schema,
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
done()
|
|
32
|
-
|
|
33
|
-
await promise
|
|
34
|
-
await promise
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
test('it parses params and generates a validator function', async () => {
|
|
38
|
-
let done: () => void = () => void 0
|
|
39
|
-
const promise = new Promise<void>((r) => {
|
|
40
|
-
done = r
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
const params = v.object({
|
|
44
|
-
name: v.string(),
|
|
45
|
-
age: v.number().required(),
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
const result = schemaParser(
|
|
49
|
-
{
|
|
50
|
-
params: params,
|
|
51
|
-
},
|
|
52
|
-
(errors) => {
|
|
53
|
-
throw new Error(JSON.stringify(errors))
|
|
54
|
-
},
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
expect(
|
|
58
|
-
result.validation.params?.({
|
|
59
|
-
name: 'John',
|
|
60
|
-
age: 30,
|
|
61
|
-
})?.errors,
|
|
62
|
-
).toBeUndefined()
|
|
63
|
-
|
|
64
|
-
expect(
|
|
65
|
-
result.validation.params?.({
|
|
66
|
-
name: { name: '' },
|
|
67
|
-
age: 'poop',
|
|
68
|
-
})?.errors,
|
|
69
|
-
).toBeDefined()
|
|
70
|
-
|
|
71
|
-
expect(
|
|
72
|
-
result.validation.params?.({
|
|
73
|
-
name: { name: '' },
|
|
74
|
-
age: 'poop',
|
|
75
|
-
})?.errors?.length,
|
|
76
|
-
).toEqual(2)
|
|
77
|
-
|
|
78
|
-
done()
|
|
79
|
-
|
|
80
|
-
await promise
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
test('it parses returnType to json-schema', async () => {
|
|
84
|
-
let done: () => void = () => void 0
|
|
85
|
-
const promise = new Promise<void>((r) => {
|
|
86
|
-
done = r
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
const returnType = v.object({
|
|
90
|
-
name: v.string(),
|
|
91
|
-
age: v.number(),
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
const result = schemaParser(
|
|
95
|
-
{
|
|
96
|
-
returnType: returnType,
|
|
97
|
-
},
|
|
98
|
-
(errors) => {
|
|
99
|
-
throw new Error(JSON.stringify(errors))
|
|
100
|
-
},
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
expect(result.jsonSchema.returnType).toEqual(
|
|
104
|
-
extractSingleJsonSchema(returnType)?.schema,
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
done()
|
|
108
|
-
|
|
109
|
-
await promise
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
test('it throws a meaningful error to the dev', async () => {
|
|
113
|
-
schemaParser(
|
|
114
|
-
// invalid params schema
|
|
115
|
-
{ params: { test: Type.String() } },
|
|
116
|
-
(errors) => {
|
|
117
|
-
expect(errors.params).toMatch(/Error extracting json schema schema.params/)
|
|
118
|
-
},
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
schemaParser(
|
|
122
|
-
// invalid returnType schema
|
|
123
|
-
{ returnType: 'string value' },
|
|
124
|
-
(errors) => {
|
|
125
|
-
expect(errors.returnType).toMatch(/Error extracting json schema schema.returnType/)
|
|
126
|
-
},
|
|
127
|
-
)
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
test('it parses multiple schemas correct', async () => {
|
|
131
|
-
const schema = schemaParser(
|
|
132
|
-
{
|
|
133
|
-
params: Type.Object({ a: Type.String() }),
|
|
134
|
-
returnType: Type.Object({ b: Type.Null() }),
|
|
135
|
-
},
|
|
136
|
-
(error) => {
|
|
137
|
-
throw new Error(JSON.stringify(error))
|
|
138
|
-
},
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
const schema2= schemaParser(
|
|
142
|
-
{
|
|
143
|
-
params: Type.Object({ c: Type.String() }),
|
|
144
|
-
returnType: Type.Object({ d: Type.Number() }),
|
|
145
|
-
},
|
|
146
|
-
(error) => {
|
|
147
|
-
throw new Error(JSON.stringify(error))
|
|
148
|
-
},
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
expect(schema.validation.params?.({}).errors?.[0]?.message).toMatch(/must have required property 'a'/)
|
|
152
|
-
expect(schema2.validation.params?.({ c: 'test' })
|
|
153
|
-
).toMatchObject({})
|
|
154
|
-
expect(schema.validation.params?.({}).errors?.[0]?.message).toMatch(/must have required property 'a'/)
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
test('validation returns error if validator fails to initialize', async () => {
|
|
158
|
-
// Create a schema that will pass extraction but creates a validation function
|
|
159
|
-
// that handles the uninitialized case gracefully
|
|
160
|
-
let parseErrorCalled = false
|
|
161
|
-
|
|
162
|
-
const result = schemaParser(
|
|
163
|
-
{
|
|
164
|
-
// Using a valid schema to get through extraction, but we'll test the guard
|
|
165
|
-
params: Type.Object({ name: Type.String() }),
|
|
166
|
-
},
|
|
167
|
-
() => {
|
|
168
|
-
parseErrorCalled = true
|
|
169
|
-
},
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
// The validator should be initialized for valid schemas
|
|
173
|
-
expect(result.validation.params).toBeDefined()
|
|
174
|
-
|
|
175
|
-
// Test that the validation function works correctly
|
|
176
|
-
const validResult = result.validation.params?.({ name: 'test' })
|
|
177
|
-
expect(validResult?.errors).toBeUndefined()
|
|
178
|
-
|
|
179
|
-
const invalidResult = result.validation.params?.({})
|
|
180
|
-
expect(invalidResult?.errors).toBeDefined()
|
|
181
|
-
})
|
|
182
|
-
})
|
package/src/schema/parser.ts
DELETED
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
import { default as addFormats } from 'ajv-formats'
|
|
2
|
-
import * as AJV from 'ajv'
|
|
3
|
-
import { extractJsonSchema } from './extract-json-schema.js'
|
|
4
|
-
import { TJSONSchema } from './types.js'
|
|
5
|
-
|
|
6
|
-
export type TSchemaParsed = {
|
|
7
|
-
jsonSchema: {
|
|
8
|
-
params?: TJSONSchema
|
|
9
|
-
returnType?: TJSONSchema
|
|
10
|
-
yieldType?: TJSONSchema
|
|
11
|
-
input?: Record<string, TJSONSchema>
|
|
12
|
-
}
|
|
13
|
-
validation: {
|
|
14
|
-
params?: (params: any) => { errors?: TSchemaValidationError[] }
|
|
15
|
-
yield?: (value: any) => { errors?: TSchemaValidationError[] }
|
|
16
|
-
input?: Record<string, (value: any) => { errors?: TSchemaValidationError[] }>
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export type TSchemaValidationError = AJV.ErrorObject
|
|
21
|
-
|
|
22
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
23
|
-
// @ts-expect-error
|
|
24
|
-
const ajv = addFormats(
|
|
25
|
-
new AJV.Ajv({
|
|
26
|
-
allErrors: true,
|
|
27
|
-
coerceTypes: true,
|
|
28
|
-
removeAdditional: true,
|
|
29
|
-
})
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
export function schemaParser(
|
|
33
|
-
schema: { params?: unknown; returnType?: unknown; yieldType?: unknown; input?: Record<string, unknown> },
|
|
34
|
-
onParseError: (errors: Record<string, string>) => void
|
|
35
|
-
): TSchemaParsed {
|
|
36
|
-
const jsonSchema: TSchemaParsed['jsonSchema'] = {}
|
|
37
|
-
const validation: TSchemaParsed['validation'] = {}
|
|
38
|
-
|
|
39
|
-
if (schema.params) {
|
|
40
|
-
try {
|
|
41
|
-
const extracted = extractJsonSchema(schema.params as TJSONSchema)
|
|
42
|
-
|
|
43
|
-
if (extracted) {
|
|
44
|
-
jsonSchema.params = extracted
|
|
45
|
-
}
|
|
46
|
-
} catch (e: any) {
|
|
47
|
-
onParseError({
|
|
48
|
-
params: `Error extracting json schema schema.params - ${e.message}`,
|
|
49
|
-
})
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (!jsonSchema.params) {
|
|
53
|
-
onParseError({
|
|
54
|
-
params: `Error extracting json schema schema.params - schema.params might be empty or it is not a valid suretype or typebox type`,
|
|
55
|
-
})
|
|
56
|
-
} else {
|
|
57
|
-
let paramsValidator: AJV.ValidateFunction | undefined
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
paramsValidator = ajv.compile(jsonSchema.params as TJSONSchema)
|
|
61
|
-
} catch (e: any) {
|
|
62
|
-
onParseError({
|
|
63
|
-
params: `Error compiling schema.params for validator - ${e.message}`,
|
|
64
|
-
})
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
validation.params = (params: any) => {
|
|
68
|
-
if (!paramsValidator) {
|
|
69
|
-
return { errors: [{ message: 'Validator not initialized', keyword: 'internal' } as TSchemaValidationError] }
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const valid = paramsValidator(params)
|
|
73
|
-
|
|
74
|
-
if (!valid) {
|
|
75
|
-
const errors = paramsValidator.errors
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
errors: errors?.length ? errors : undefined,
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return {}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (schema.returnType) {
|
|
88
|
-
try {
|
|
89
|
-
const extracted = extractJsonSchema(schema.returnType as TJSONSchema)
|
|
90
|
-
|
|
91
|
-
jsonSchema.returnType = extracted
|
|
92
|
-
} catch (e: any) {
|
|
93
|
-
onParseError({
|
|
94
|
-
returnType: `Error extracting json schema schema.returnType - ${e.message}`,
|
|
95
|
-
})
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (!jsonSchema.returnType) {
|
|
99
|
-
onParseError({
|
|
100
|
-
returnType: `Error extracting json schema schema.returnType - schema.returnType might be empty or it is not a valid suretype or typebox type`,
|
|
101
|
-
})
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (schema.yieldType) {
|
|
106
|
-
try {
|
|
107
|
-
const extracted = extractJsonSchema(schema.yieldType as TJSONSchema)
|
|
108
|
-
|
|
109
|
-
if (extracted) {
|
|
110
|
-
jsonSchema.yieldType = extracted
|
|
111
|
-
}
|
|
112
|
-
} catch (e: any) {
|
|
113
|
-
onParseError({
|
|
114
|
-
yieldType: `Error extracting json schema schema.yieldType - ${e.message}`,
|
|
115
|
-
})
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (!jsonSchema.yieldType) {
|
|
119
|
-
onParseError({
|
|
120
|
-
yieldType: `Error extracting json schema schema.yieldType - schema.yieldType might be empty or it is not a valid suretype or typebox type`,
|
|
121
|
-
})
|
|
122
|
-
} else {
|
|
123
|
-
let yieldValidator: AJV.ValidateFunction | undefined
|
|
124
|
-
|
|
125
|
-
try {
|
|
126
|
-
yieldValidator = ajv.compile(jsonSchema.yieldType as TJSONSchema)
|
|
127
|
-
} catch (e: any) {
|
|
128
|
-
onParseError({
|
|
129
|
-
yieldType: `Error compiling schema.yieldType for validator - ${e.message}`,
|
|
130
|
-
})
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
validation.yield = (value: any) => {
|
|
134
|
-
if (!yieldValidator) {
|
|
135
|
-
return { errors: [{ message: 'Validator not initialized', keyword: 'internal' } as TSchemaValidationError] }
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const valid = yieldValidator(value)
|
|
139
|
-
|
|
140
|
-
if (!valid) {
|
|
141
|
-
const errors = yieldValidator.errors
|
|
142
|
-
|
|
143
|
-
return {
|
|
144
|
-
errors: errors?.length ? errors : undefined,
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return {}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (schema.input) {
|
|
154
|
-
jsonSchema.input = {}
|
|
155
|
-
validation.input = {}
|
|
156
|
-
|
|
157
|
-
for (const [channelName, channelSchema] of Object.entries(schema.input)) {
|
|
158
|
-
if (!channelSchema) continue
|
|
159
|
-
|
|
160
|
-
let channelJsonSchema: TJSONSchema | undefined
|
|
161
|
-
|
|
162
|
-
try {
|
|
163
|
-
const extracted = extractJsonSchema(channelSchema as TJSONSchema)
|
|
164
|
-
if (extracted) {
|
|
165
|
-
channelJsonSchema = extracted
|
|
166
|
-
jsonSchema.input[channelName] = extracted
|
|
167
|
-
}
|
|
168
|
-
} catch (e: any) {
|
|
169
|
-
onParseError({
|
|
170
|
-
[`input.${channelName}`]: `Error extracting json schema schema.input.${channelName} - ${e.message}`,
|
|
171
|
-
})
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (!channelJsonSchema) {
|
|
175
|
-
onParseError({
|
|
176
|
-
[`input.${channelName}`]: `Error extracting json schema schema.input.${channelName} - might be empty or not a valid suretype or typebox type`,
|
|
177
|
-
})
|
|
178
|
-
} else {
|
|
179
|
-
let channelValidator: AJV.ValidateFunction | undefined
|
|
180
|
-
|
|
181
|
-
try {
|
|
182
|
-
channelValidator = ajv.compile(channelJsonSchema as TJSONSchema)
|
|
183
|
-
} catch (e: any) {
|
|
184
|
-
onParseError({
|
|
185
|
-
[`input.${channelName}`]: `Error compiling schema.input.${channelName} for validator - ${e.message}`,
|
|
186
|
-
})
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
validation.input[channelName] = (value: any) => {
|
|
190
|
-
if (!channelValidator) {
|
|
191
|
-
return {
|
|
192
|
-
errors: [
|
|
193
|
-
{ message: 'Validator not initialized', keyword: 'internal' } as TSchemaValidationError,
|
|
194
|
-
],
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const valid = channelValidator(value)
|
|
199
|
-
|
|
200
|
-
if (!valid) {
|
|
201
|
-
const errors = channelValidator.errors
|
|
202
|
-
|
|
203
|
-
return {
|
|
204
|
-
errors: errors?.length ? errors : undefined,
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
return {}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
return { jsonSchema, validation }
|
|
215
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'vitest'
|
|
2
|
-
import { isSuretypeSchema, isTypeboxSchema } from './resolve-schema-lib.js'
|
|
3
|
-
import { Type } from 'typebox'
|
|
4
|
-
import { v } from 'suretype'
|
|
5
|
-
|
|
6
|
-
describe('lib schema resolvers', () => {
|
|
7
|
-
const typebox = Type.Object({ name: Type.String() })
|
|
8
|
-
const suretype = v.object({ name: v.string() })
|
|
9
|
-
|
|
10
|
-
test('it recognizes TypeBox schema', async () => {
|
|
11
|
-
expect(isTypeboxSchema(typebox)).toBe(true)
|
|
12
|
-
expect(isTypeboxSchema(suretype)).toBe(false)
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
test('it recognizes Suretype schema', async () => {
|
|
16
|
-
expect(isSuretypeSchema(suretype)).toBe(true)
|
|
17
|
-
expect(isSuretypeSchema(typebox)).toBe(false)
|
|
18
|
-
})
|
|
19
|
-
})
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { CoreValidator } from 'suretype'
|
|
2
|
-
import { Type } from 'typebox'
|
|
3
|
-
|
|
4
|
-
export type IsTypeboxSchema<TSchema> = TSchema extends {
|
|
5
|
-
static: unknown
|
|
6
|
-
params: unknown
|
|
7
|
-
}
|
|
8
|
-
? true
|
|
9
|
-
: false
|
|
10
|
-
|
|
11
|
-
export function isTypeboxSchema(schema: any): schema is Type.TSchema {
|
|
12
|
-
return (
|
|
13
|
-
// typebox v1
|
|
14
|
-
(typeof schema === 'object' && '~kind' in schema) ||
|
|
15
|
-
// @sinclair/typebox v0.3x
|
|
16
|
-
(typeof schema === 'object' && Symbol.for('TypeBox.Kind') in schema)
|
|
17
|
-
)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export type IsSuretypeSchema<TSchema> = TSchema extends {
|
|
21
|
-
required: () => object
|
|
22
|
-
nullable?: never
|
|
23
|
-
}
|
|
24
|
-
? true
|
|
25
|
-
: false
|
|
26
|
-
|
|
27
|
-
export function isSuretypeSchema(schema: any): schema is CoreValidator<any> {
|
|
28
|
-
return typeof schema === 'object' && 'getJsonSchemaObject' in schema
|
|
29
|
-
}
|
package/src/schema/types.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { CoreValidator, TypeOf } from 'suretype'
|
|
2
|
-
import { Static, TSchema } from 'typebox'
|
|
3
|
-
|
|
4
|
-
// Determine if the generic "SchemaLibType" is Suretype's CoreValidator or Typebox's TSchema
|
|
5
|
-
export type TSchemaLib<SchemaLibType> =
|
|
6
|
-
SchemaLibType extends CoreValidator<any>
|
|
7
|
-
? TypeOf<SchemaLibType>
|
|
8
|
-
: SchemaLibType extends TSchema
|
|
9
|
-
? Static<SchemaLibType>
|
|
10
|
-
: unknown
|
|
11
|
-
|
|
12
|
-
// AsyncGenerator type extraction for streaming procedures
|
|
13
|
-
export type TSchemaLibGenerator<TYield, TReturn = void> =
|
|
14
|
-
AsyncGenerator<TSchemaLib<TYield>, TSchemaLib<TReturn>, unknown>
|
|
15
|
-
|
|
16
|
-
export type TJSONSchema = Record<string, any>
|
|
17
|
-
|
|
18
|
-
export type Prettify<TObject> = {
|
|
19
|
-
[Key in keyof TObject]: TObject[Key]
|
|
20
|
-
} & {}
|
package/src/stack-utils.test.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'vitest'
|
|
2
|
-
import { captureDefinitionInfo, formatDefinitionInfo, DefinitionInfo } from './stack-utils.js'
|
|
3
|
-
|
|
4
|
-
describe('Stack Utils', () => {
|
|
5
|
-
describe('captureDefinitionInfo', () => {
|
|
6
|
-
test('returns definition info with definedAt', () => {
|
|
7
|
-
const info = captureDefinitionInfo()
|
|
8
|
-
|
|
9
|
-
// Should capture the call site in this test file
|
|
10
|
-
expect(info).toBeDefined()
|
|
11
|
-
expect(info.definitionStack).toBeDefined()
|
|
12
|
-
expect(typeof info.definitionStack).toBe('string')
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
test('definedAt contains file, line, column when available', () => {
|
|
16
|
-
const info = captureDefinitionInfo()
|
|
17
|
-
|
|
18
|
-
// The definedAt should be present since we're calling from user code (test file)
|
|
19
|
-
if (info.definedAt) {
|
|
20
|
-
expect(info.definedAt.file).toBeDefined()
|
|
21
|
-
expect(typeof info.definedAt.file).toBe('string')
|
|
22
|
-
expect(info.definedAt.line).toBeDefined()
|
|
23
|
-
expect(typeof info.definedAt.line).toBe('number')
|
|
24
|
-
expect(info.definedAt.line).toBeGreaterThan(0)
|
|
25
|
-
expect(info.definedAt.column).toBeDefined()
|
|
26
|
-
expect(typeof info.definedAt.column).toBe('number')
|
|
27
|
-
expect(info.definedAt.column).toBeGreaterThan(0)
|
|
28
|
-
expect(info.definedAt.raw).toBeDefined()
|
|
29
|
-
expect(typeof info.definedAt.raw).toBe('string')
|
|
30
|
-
}
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
test('definitionStack contains Error stack trace', () => {
|
|
34
|
-
const info = captureDefinitionInfo()
|
|
35
|
-
|
|
36
|
-
expect(info.definitionStack).toContain('Error')
|
|
37
|
-
expect(info.definitionStack).toContain('at ')
|
|
38
|
-
})
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
describe('formatDefinitionInfo', () => {
|
|
42
|
-
test('returns undefined when definedAt is not present', () => {
|
|
43
|
-
const info: DefinitionInfo = {}
|
|
44
|
-
const result = formatDefinitionInfo(info, 'TestProcedure')
|
|
45
|
-
|
|
46
|
-
expect(result).toBeUndefined()
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
test('returns formatted string when definedAt is present', () => {
|
|
50
|
-
const info: DefinitionInfo = {
|
|
51
|
-
definedAt: {
|
|
52
|
-
file: '/app/procedures/test.ts',
|
|
53
|
-
line: 42,
|
|
54
|
-
column: 5,
|
|
55
|
-
raw: 'at Object.<anonymous> (/app/procedures/test.ts:42:5)',
|
|
56
|
-
},
|
|
57
|
-
}
|
|
58
|
-
const result = formatDefinitionInfo(info, 'TestProcedure')
|
|
59
|
-
|
|
60
|
-
expect(result).toBeDefined()
|
|
61
|
-
expect(result).toContain('--- Procedure "TestProcedure" defined at ---')
|
|
62
|
-
expect(result).toContain('/app/procedures/test.ts:42:5')
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
test('includes procedure name in formatted output', () => {
|
|
66
|
-
const info: DefinitionInfo = {
|
|
67
|
-
definedAt: {
|
|
68
|
-
file: '/path/to/file.ts',
|
|
69
|
-
line: 10,
|
|
70
|
-
column: 3,
|
|
71
|
-
raw: 'at /path/to/file.ts:10:3',
|
|
72
|
-
},
|
|
73
|
-
}
|
|
74
|
-
const result = formatDefinitionInfo(info, 'MyCustomProcedure')
|
|
75
|
-
|
|
76
|
-
expect(result).toContain('"MyCustomProcedure"')
|
|
77
|
-
})
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
describe('integration with procedure creation', () => {
|
|
81
|
-
test('captures location from calling code', () => {
|
|
82
|
-
// Helper to simulate what happens in Create()
|
|
83
|
-
function simulateCreate() {
|
|
84
|
-
return captureDefinitionInfo()
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const info = simulateCreate()
|
|
88
|
-
|
|
89
|
-
// Should have captured the location of the simulateCreate() call
|
|
90
|
-
expect(info).toBeDefined()
|
|
91
|
-
expect(info.definitionStack).toBeDefined()
|
|
92
|
-
})
|
|
93
|
-
})
|
|
94
|
-
})
|