@furystack/rest-service 10.0.23 → 10.0.25
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/esm/api-manager.d.ts +47 -1
- package/esm/api-manager.d.ts.map +1 -1
- package/esm/api-manager.js +19 -5
- package/esm/api-manager.js.map +1 -1
- package/esm/authenticate.d.ts.map +1 -1
- package/esm/authenticate.js +3 -1
- package/esm/authenticate.js.map +1 -1
- package/esm/endpoint-generators/create-get-schema-action.d.ts +16 -0
- package/esm/endpoint-generators/create-get-schema-action.d.ts.map +1 -0
- package/esm/endpoint-generators/create-get-schema-action.js +21 -0
- package/esm/endpoint-generators/create-get-schema-action.js.map +1 -0
- package/esm/endpoint-generators/create-get-swagger-json-action.d.ts +14 -0
- package/esm/endpoint-generators/create-get-swagger-json-action.d.ts.map +1 -0
- package/esm/endpoint-generators/create-get-swagger-json-action.js +17 -0
- package/esm/endpoint-generators/create-get-swagger-json-action.js.map +1 -0
- package/esm/endpoint-generators/index.d.ts +1 -0
- package/esm/endpoint-generators/index.d.ts.map +1 -1
- package/esm/endpoint-generators/index.js +1 -0
- package/esm/endpoint-generators/index.js.map +1 -1
- package/esm/endpoint-generators/with-schema-and-swagger-action.d.ts +21 -0
- package/esm/endpoint-generators/with-schema-and-swagger-action.d.ts.map +1 -0
- package/esm/endpoint-generators/with-schema-and-swagger-action.js +2 -0
- package/esm/endpoint-generators/with-schema-and-swagger-action.js.map +1 -0
- package/esm/get-schema-from-api.d.ts +10 -0
- package/esm/get-schema-from-api.d.ts.map +1 -0
- package/esm/get-schema-from-api.js +55 -0
- package/esm/get-schema-from-api.js.map +1 -0
- package/esm/get-schema-from-api.spec.d.ts +2 -0
- package/esm/get-schema-from-api.spec.d.ts.map +1 -0
- package/esm/get-schema-from-api.spec.js +68 -0
- package/esm/get-schema-from-api.spec.js.map +1 -0
- package/esm/helpers.d.ts +4 -1
- package/esm/helpers.d.ts.map +1 -1
- package/esm/index.d.ts +10 -9
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +10 -9
- package/esm/index.js.map +1 -1
- package/esm/swagger/generate-swagger-json.d.ts +14 -0
- package/esm/swagger/generate-swagger-json.d.ts.map +1 -0
- package/esm/swagger/generate-swagger-json.js +106 -0
- package/esm/swagger/generate-swagger-json.js.map +1 -0
- package/esm/swagger/generate-swagger-json.spec.d.ts +2 -0
- package/esm/swagger/generate-swagger-json.spec.d.ts.map +1 -0
- package/esm/swagger/generate-swagger-json.spec.js +204 -0
- package/esm/swagger/generate-swagger-json.spec.js.map +1 -0
- package/esm/validate.d.ts.map +1 -1
- package/esm/validate.integration.spec.js +154 -2
- package/esm/validate.integration.spec.js.map +1 -1
- package/esm/validate.integration.spec.schema.json +0 -2
- package/esm/validate.js +4 -1
- package/esm/validate.js.map +1 -1
- package/package.json +10 -10
- package/src/api-manager.ts +68 -5
- package/src/authenticate.ts +5 -1
- package/src/endpoint-generators/create-get-schema-action.ts +29 -0
- package/src/endpoint-generators/create-get-swagger-json-action.ts +26 -0
- package/src/endpoint-generators/index.ts +1 -0
- package/src/endpoint-generators/with-schema-and-swagger-action.ts +11 -0
- package/src/get-schema-from-api.spec.ts +73 -0
- package/src/get-schema-from-api.ts +74 -0
- package/src/index.ts +10 -9
- package/src/swagger/generate-swagger-json.spec.ts +239 -0
- package/src/swagger/generate-swagger-json.ts +123 -0
- package/src/validate.integration.spec.schema.json +0 -2
- package/src/validate.integration.spec.ts +173 -2
- package/src/validate.ts +6 -1
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { ApiEndpointDefinition, Operation, ParameterObject, SwaggerDocument } from '@furystack/rest'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Converts a FuryStack API schema to an OpenAPI 3.1 compatible document
|
|
5
|
+
*
|
|
6
|
+
* @param schema - The FuryStack API schema to convert
|
|
7
|
+
* @returns A SwaggerDocument in OpenAPI 3.1 format
|
|
8
|
+
*/
|
|
9
|
+
export const generateSwaggerJsonFromApiSchema = ({
|
|
10
|
+
api,
|
|
11
|
+
title = 'FuryStack API',
|
|
12
|
+
description = 'API documentation generated from FuryStack API schema',
|
|
13
|
+
version = '1.0.0',
|
|
14
|
+
}: {
|
|
15
|
+
api: Record<string, ApiEndpointDefinition>
|
|
16
|
+
title?: string
|
|
17
|
+
description?: string
|
|
18
|
+
version?: string
|
|
19
|
+
}): SwaggerDocument => {
|
|
20
|
+
const swaggerJson: SwaggerDocument = {
|
|
21
|
+
openapi: '3.1.0',
|
|
22
|
+
info: {
|
|
23
|
+
title,
|
|
24
|
+
version,
|
|
25
|
+
description,
|
|
26
|
+
},
|
|
27
|
+
jsonSchemaDialect: 'https://spec.openapis.org/oas/3.1/dialect/base',
|
|
28
|
+
servers: [{ url: '/' }],
|
|
29
|
+
tags: [],
|
|
30
|
+
paths: {},
|
|
31
|
+
components: {
|
|
32
|
+
schemas: {},
|
|
33
|
+
securitySchemes: {
|
|
34
|
+
cookieAuth: {
|
|
35
|
+
type: 'apiKey',
|
|
36
|
+
in: 'cookie',
|
|
37
|
+
name: 'session',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
for (const [path, definition] of Object.entries(api)) {
|
|
44
|
+
// Normalize path to OpenAPI format (convert :param to {param})
|
|
45
|
+
const normalizedPath = path.replace(/:([^/]+)/g, '{$1}')
|
|
46
|
+
if (!swaggerJson.paths![normalizedPath]) {
|
|
47
|
+
swaggerJson.paths![normalizedPath] = {}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Extract path parameters
|
|
51
|
+
const pathParams = Array.from(path.matchAll(/:([^/]+)/g), (m) => m[1])
|
|
52
|
+
const parameters: ParameterObject[] = pathParams.map((param) => ({
|
|
53
|
+
name: param,
|
|
54
|
+
in: 'path',
|
|
55
|
+
required: true,
|
|
56
|
+
description: `Path parameter: ${param}`,
|
|
57
|
+
schema: { type: 'string' },
|
|
58
|
+
}))
|
|
59
|
+
|
|
60
|
+
// Build operation
|
|
61
|
+
const method = definition.method.toLowerCase()
|
|
62
|
+
const operation: Operation = {
|
|
63
|
+
summary: `${definition.method} ${path}`,
|
|
64
|
+
description: `Endpoint for ${path}`,
|
|
65
|
+
operationId: `${method}${path.replace(/\//g, '_').replace(/:/g, '').replace(/-/g, '_')}`,
|
|
66
|
+
security: definition.isAuthenticated ? [{ cookieAuth: [] }] : [],
|
|
67
|
+
parameters,
|
|
68
|
+
responses: {
|
|
69
|
+
'200': {
|
|
70
|
+
description: 'Successful operation',
|
|
71
|
+
content: {
|
|
72
|
+
'application/json': {
|
|
73
|
+
schema: definition.schemaName
|
|
74
|
+
? { $ref: `#/components/schemas/${definition.schemaName}` }
|
|
75
|
+
: { type: 'object' },
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
'401': { description: 'Unauthorized' },
|
|
80
|
+
'500': { description: 'Internal server error' },
|
|
81
|
+
},
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Add schema to components if not already there
|
|
85
|
+
if (definition.schema && definition.schemaName) {
|
|
86
|
+
swaggerJson.components!.schemas![definition.schemaName] = definition.schema
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Assign the operation to the correct HTTP method property of PathItem
|
|
90
|
+
const pathItem = swaggerJson.paths![normalizedPath]
|
|
91
|
+
switch (method) {
|
|
92
|
+
case 'get':
|
|
93
|
+
pathItem.get = operation
|
|
94
|
+
break
|
|
95
|
+
case 'put':
|
|
96
|
+
pathItem.put = operation
|
|
97
|
+
break
|
|
98
|
+
case 'post':
|
|
99
|
+
pathItem.post = operation
|
|
100
|
+
break
|
|
101
|
+
case 'delete':
|
|
102
|
+
pathItem.delete = operation
|
|
103
|
+
break
|
|
104
|
+
case 'options':
|
|
105
|
+
pathItem.options = operation
|
|
106
|
+
break
|
|
107
|
+
case 'head':
|
|
108
|
+
pathItem.head = operation
|
|
109
|
+
break
|
|
110
|
+
case 'patch':
|
|
111
|
+
pathItem.patch = operation
|
|
112
|
+
break
|
|
113
|
+
case 'trace':
|
|
114
|
+
pathItem.trace = operation
|
|
115
|
+
break
|
|
116
|
+
default:
|
|
117
|
+
// Ignore unknown methods
|
|
118
|
+
break
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return swaggerJson
|
|
123
|
+
}
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
"description": "Endpoint model for deleting entities",
|
|
7
7
|
"properties": {
|
|
8
8
|
"result": {
|
|
9
|
-
"additionalProperties": false,
|
|
10
9
|
"type": "object"
|
|
11
10
|
},
|
|
12
11
|
"url": {
|
|
@@ -304,7 +303,6 @@
|
|
|
304
303
|
"type": "object"
|
|
305
304
|
},
|
|
306
305
|
"result": {
|
|
307
|
-
"additionalProperties": false,
|
|
308
306
|
"type": "object"
|
|
309
307
|
},
|
|
310
308
|
"url": {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getStoreManager, InMemoryStore, User } from '@furystack/core'
|
|
2
2
|
import { getPort } from '@furystack/core/port-generator'
|
|
3
3
|
import { Injector } from '@furystack/inject'
|
|
4
|
+
import type { SwaggerDocument, WithSchemaAction } from '@furystack/rest'
|
|
4
5
|
import { createClient, ResponseError } from '@furystack/rest-client-fetch'
|
|
5
6
|
import { usingAsync } from '@furystack/utils'
|
|
6
7
|
import type Ajv from 'ajv'
|
|
@@ -14,15 +15,23 @@ import { Validate } from './validate.js'
|
|
|
14
15
|
|
|
15
16
|
// To recreate: yarn ts-json-schema-generator -f tsconfig.json --no-type-check -p packages/rest-service/src/validate.integration.schema.ts -o packages/rest-service/src/validate.integration.spec.schema.json
|
|
16
17
|
|
|
17
|
-
const
|
|
18
|
+
const name = crypto.randomUUID()
|
|
19
|
+
const description = crypto.randomUUID()
|
|
20
|
+
const version = crypto.randomUUID()
|
|
21
|
+
|
|
22
|
+
const createValidateApi = async (options = { enableGetSchema: false }) => {
|
|
18
23
|
const injector = new Injector()
|
|
19
24
|
const port = getPort()
|
|
20
25
|
|
|
21
26
|
getStoreManager(injector).addStore(new InMemoryStore({ model: User, primaryKey: 'username' }))
|
|
22
27
|
getStoreManager(injector).addStore(new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }))
|
|
23
28
|
|
|
24
|
-
await useRestService<ValidationApi>({
|
|
29
|
+
const api = await useRestService<ValidationApi>({
|
|
25
30
|
injector,
|
|
31
|
+
enableGetSchema: options.enableGetSchema,
|
|
32
|
+
name,
|
|
33
|
+
description,
|
|
34
|
+
version,
|
|
26
35
|
api: {
|
|
27
36
|
GET: {
|
|
28
37
|
'/validate-query': Validate({
|
|
@@ -66,11 +75,173 @@ const createValidateApi = async () => {
|
|
|
66
75
|
|
|
67
76
|
return {
|
|
68
77
|
[Symbol.asyncDispose]: injector[Symbol.asyncDispose].bind(injector),
|
|
78
|
+
injector,
|
|
79
|
+
api,
|
|
69
80
|
client,
|
|
70
81
|
}
|
|
71
82
|
}
|
|
72
83
|
|
|
73
84
|
describe('Validation integration tests', () => {
|
|
85
|
+
describe('swagger.json schema definition', () => {
|
|
86
|
+
it('Should include name, description and version in the generated swagger.json', async () => {
|
|
87
|
+
await usingAsync(await createValidateApi({ enableGetSchema: true }), async ({ client }) => {
|
|
88
|
+
const result = await (client as ReturnType<typeof createClient<any>>)({
|
|
89
|
+
method: 'GET',
|
|
90
|
+
action: '/swagger.json',
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
expect(result.response.status).toBe(200)
|
|
94
|
+
expect(result.result).toBeDefined()
|
|
95
|
+
|
|
96
|
+
// Verify swagger document structure
|
|
97
|
+
const swaggerJson = result.result as SwaggerDocument
|
|
98
|
+
expect(swaggerJson.openapi).toBe('3.1.0')
|
|
99
|
+
expect(swaggerJson.info).toBeDefined()
|
|
100
|
+
expect(swaggerJson.info?.title).toBe(name)
|
|
101
|
+
expect(swaggerJson.info?.description).toBe(description)
|
|
102
|
+
expect(swaggerJson.info?.version).toBe(version)
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('Should return a 404 when not enabled', async () => {
|
|
107
|
+
await usingAsync(await createValidateApi({ enableGetSchema: false }), async ({ client }) => {
|
|
108
|
+
try {
|
|
109
|
+
await (client as ReturnType<typeof createClient<any>>)({
|
|
110
|
+
method: 'GET',
|
|
111
|
+
action: '/swagger.json',
|
|
112
|
+
})
|
|
113
|
+
expect.fail('Expected response error but got success')
|
|
114
|
+
} catch (error) {
|
|
115
|
+
expect(error).toBeInstanceOf(ResponseError)
|
|
116
|
+
expect((error as ResponseError).response.status).toBe(404)
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('Should return a generated swagger.json when enabled', async () => {
|
|
122
|
+
await usingAsync(await createValidateApi({ enableGetSchema: true }), async ({ client }) => {
|
|
123
|
+
const result = await (client as ReturnType<typeof createClient<any>>)({
|
|
124
|
+
method: 'GET',
|
|
125
|
+
action: '/swagger.json',
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
expect(result.response.status).toBe(200)
|
|
129
|
+
expect(result.result).toBeDefined()
|
|
130
|
+
|
|
131
|
+
// Verify swagger document structure
|
|
132
|
+
const swaggerJson = result.result as SwaggerDocument
|
|
133
|
+
expect(swaggerJson.openapi).toBe('3.1.0')
|
|
134
|
+
expect(swaggerJson.info).toBeDefined()
|
|
135
|
+
expect(swaggerJson.info?.title).toBe(name)
|
|
136
|
+
expect(swaggerJson.info?.description).toBe(description)
|
|
137
|
+
expect(swaggerJson.info?.version).toBe(version)
|
|
138
|
+
expect(swaggerJson.paths).toBeDefined()
|
|
139
|
+
|
|
140
|
+
// Verify our API endpoints are included
|
|
141
|
+
expect(swaggerJson.paths?.['/validate-query']).toBeDefined()
|
|
142
|
+
expect(swaggerJson.paths?.['/validate-url/{id}']).toBeDefined()
|
|
143
|
+
expect(swaggerJson.paths?.['/validate-headers']).toBeDefined()
|
|
144
|
+
expect(swaggerJson.paths?.['/validate-body']).toBeDefined()
|
|
145
|
+
|
|
146
|
+
// Verify components section
|
|
147
|
+
expect(swaggerJson.components).toBeDefined()
|
|
148
|
+
expect(swaggerJson.components?.schemas).toBeDefined()
|
|
149
|
+
expect(swaggerJson.components?.schemas?.ValidateQuery).toBeDefined()
|
|
150
|
+
expect(swaggerJson.components?.schemas?.ValidateUrl).toBeDefined()
|
|
151
|
+
expect(swaggerJson.components?.schemas?.ValidateHeaders).toBeDefined()
|
|
152
|
+
expect(swaggerJson.components?.schemas?.ValidateBody).toBeDefined()
|
|
153
|
+
})
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
describe('Validation metadata', () => {
|
|
158
|
+
it('Should return 404 when not enabled', async () => {
|
|
159
|
+
await usingAsync(await createValidateApi({ enableGetSchema: false }), async ({ client }) => {
|
|
160
|
+
try {
|
|
161
|
+
await (client as ReturnType<typeof createClient<WithSchemaAction<ValidationApi>>>)({
|
|
162
|
+
method: 'GET',
|
|
163
|
+
action: '/schema',
|
|
164
|
+
headers: {
|
|
165
|
+
accept: 'application/schema+json',
|
|
166
|
+
},
|
|
167
|
+
})
|
|
168
|
+
} catch (error) {
|
|
169
|
+
expect(error).toBeInstanceOf(ResponseError)
|
|
170
|
+
expect((error as ResponseError).response.status).toBe(404)
|
|
171
|
+
}
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it('Should return a 406 when the accept header is not supported', async () => {
|
|
176
|
+
expect.assertions(2)
|
|
177
|
+
await usingAsync(await createValidateApi({ enableGetSchema: true }), async ({ client }) => {
|
|
178
|
+
try {
|
|
179
|
+
await (client as ReturnType<typeof createClient<WithSchemaAction<ValidationApi>>>)({
|
|
180
|
+
method: 'GET',
|
|
181
|
+
action: '/schema',
|
|
182
|
+
headers: {
|
|
183
|
+
accept: 'text/plain' as any,
|
|
184
|
+
},
|
|
185
|
+
})
|
|
186
|
+
} catch (error) {
|
|
187
|
+
expect(error).toBeInstanceOf(ResponseError)
|
|
188
|
+
expect((error as ResponseError).response.status).toBe(406)
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
it('Should return the validation metadata', async () => {
|
|
194
|
+
await usingAsync(await createValidateApi({ enableGetSchema: true }), async ({ client }) => {
|
|
195
|
+
const result = await (client as ReturnType<typeof createClient<WithSchemaAction<ValidationApi>>>)({
|
|
196
|
+
method: 'GET',
|
|
197
|
+
action: '/schema',
|
|
198
|
+
headers: {
|
|
199
|
+
accept: 'application/schema+json',
|
|
200
|
+
},
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
expect(result.response.status).toBe(200)
|
|
204
|
+
expect(result.result).toBeDefined()
|
|
205
|
+
|
|
206
|
+
expect(result.result.name).toBe(name)
|
|
207
|
+
expect(result.result.description).toBe(description)
|
|
208
|
+
expect(result.result.version).toBe(version)
|
|
209
|
+
|
|
210
|
+
expect(result.result.endpoints['/validate-query']).toBeDefined()
|
|
211
|
+
|
|
212
|
+
expect(result.result.endpoints['/validate-query'].schema).toStrictEqual(schema)
|
|
213
|
+
expect(result.result.endpoints['/validate-query'].schemaName).toBe('ValidateQuery')
|
|
214
|
+
expect(result.result.endpoints['/validate-query'].method).toBe('GET')
|
|
215
|
+
expect(result.result.endpoints['/validate-query'].path).toBe('/validate-query')
|
|
216
|
+
expect(result.result.endpoints['/validate-query'].isAuthenticated).toBe(false)
|
|
217
|
+
|
|
218
|
+
expect(result.result.endpoints['/validate-url/:id']).toBeDefined()
|
|
219
|
+
expect(result.result.endpoints['/validate-url/:id'].schema).toStrictEqual(schema)
|
|
220
|
+
expect(result.result.endpoints['/validate-url/:id'].schemaName).toBe('ValidateUrl')
|
|
221
|
+
expect(result.result.endpoints['/validate-url/:id'].method).toBe('GET')
|
|
222
|
+
expect(result.result.endpoints['/validate-url/:id'].path).toBe('/validate-url/:id')
|
|
223
|
+
expect(result.result.endpoints['/validate-url/:id'].isAuthenticated).toBe(false)
|
|
224
|
+
|
|
225
|
+
expect(result.result.endpoints['/validate-headers']).toBeDefined()
|
|
226
|
+
expect(result.result.endpoints['/validate-headers'].schema).toStrictEqual(schema)
|
|
227
|
+
expect(result.result.endpoints['/validate-headers'].schemaName).toBe('ValidateHeaders')
|
|
228
|
+
expect(result.result.endpoints['/validate-headers'].method).toBe('GET')
|
|
229
|
+
expect(result.result.endpoints['/validate-headers'].path).toBe('/validate-headers')
|
|
230
|
+
expect(result.result.endpoints['/validate-headers'].isAuthenticated).toBe(false)
|
|
231
|
+
|
|
232
|
+
expect(result.result.endpoints['/validate-body']).toBeDefined()
|
|
233
|
+
expect(result.result.endpoints['/validate-body'].schema).toStrictEqual(schema)
|
|
234
|
+
expect(result.result.endpoints['/validate-body'].schemaName).toBe('ValidateBody')
|
|
235
|
+
expect(result.result.endpoints['/validate-body'].method).toBe('POST')
|
|
236
|
+
expect(result.result.endpoints['/validate-body'].path).toBe('/validate-body')
|
|
237
|
+
expect(result.result.endpoints['/validate-body'].isAuthenticated).toBe(false)
|
|
238
|
+
|
|
239
|
+
expect(result.result.endpoints['/mock']).toBeUndefined()
|
|
240
|
+
expect(result.result.endpoints['/mock/:id']).toBeUndefined()
|
|
241
|
+
})
|
|
242
|
+
})
|
|
243
|
+
})
|
|
244
|
+
|
|
74
245
|
describe('Validation errors', () => {
|
|
75
246
|
it('Should validate query', async () => {
|
|
76
247
|
await usingAsync(await createValidateApi(), async ({ client }) => {
|
package/src/validate.ts
CHANGED
|
@@ -31,7 +31,7 @@ export const Validate =
|
|
|
31
31
|
|
|
32
32
|
const validator = new SchemaValidator(schema, { coerceTypes: true, strict: false })
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
const wrapped = async (args: RequestActionOptions<T>): Promise<ActionResult<T>> => {
|
|
35
35
|
const anyArgs = args as any
|
|
36
36
|
let body!: any
|
|
37
37
|
const { headers } = anyArgs
|
|
@@ -62,4 +62,9 @@ export const Validate =
|
|
|
62
62
|
getBody: () => Promise.resolve(body),
|
|
63
63
|
})
|
|
64
64
|
}
|
|
65
|
+
|
|
66
|
+
wrapped.schema = schema
|
|
67
|
+
wrapped.schemaName = validationOptions.schemaName
|
|
68
|
+
|
|
69
|
+
return wrapped
|
|
65
70
|
}
|