@openaddresses/batch-schema 10.25.0 → 10.26.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.
@@ -19,6 +19,71 @@ describe('Schema Server', () => {
19
19
  }, async (_, res) => {
20
20
  res.json({ ok: true });
21
21
  });
22
+ await schema.post('/json-only', {
23
+ description: 'Backwards compatible JSON body',
24
+ body: Type.Object({
25
+ name: Type.String()
26
+ }),
27
+ res: Type.Object({ ok: Type.Boolean() })
28
+ }, async (req, res) => {
29
+ res.json({ ok: typeof req.body.name === 'string' });
30
+ });
31
+ await schema.post('/multi-body', {
32
+ description: 'Multi content-type body',
33
+ body: {
34
+ 'application/json': Type.Object({ name: Type.String() }),
35
+ 'text/xml': true
36
+ },
37
+ res: Type.Object({
38
+ contentType: Type.String(),
39
+ body: Type.Any()
40
+ })
41
+ }, async (req, res) => {
42
+ res.json({
43
+ contentType: (req.headers['content-type'] || '').split(';')[0],
44
+ body: req.body
45
+ });
46
+ });
47
+ await schema.post('/wildcard-body', {
48
+ description: 'Wildcard content-type body',
49
+ body: {
50
+ 'application/json': Type.Object({ name: Type.String() }),
51
+ 'text/*': true
52
+ },
53
+ res: Type.Object({
54
+ contentType: Type.String(),
55
+ body: Type.Any()
56
+ })
57
+ }, async (req, res) => {
58
+ res.json({
59
+ contentType: (req.headers['content-type'] || '').split(';')[0],
60
+ body: req.body
61
+ });
62
+ });
63
+ await schema.post('/optional-body', {
64
+ description: 'Optional body',
65
+ body: Type.Object({ name: Type.String() }),
66
+ bodyRequired: false,
67
+ res: Type.Object({ ok: Type.Boolean() })
68
+ }, async (_req, res) => {
69
+ res.json({ ok: true });
70
+ });
71
+ await schema.post('/with-examples', {
72
+ description: 'Body with examples in OpenAPI',
73
+ body: {
74
+ 'application/json': {
75
+ schema: Type.Object({ name: Type.String() }),
76
+ example: { name: 'sample' },
77
+ examples: {
78
+ primary: { summary: 'Primary', value: { name: 'alice' } },
79
+ secondary: { summary: 'Secondary', value: { name: 'bob' } }
80
+ }
81
+ }
82
+ },
83
+ res: Type.Object({ ok: Type.Boolean() })
84
+ }, async (_req, res) => {
85
+ res.json({ ok: true });
86
+ });
22
87
  await schema.api();
23
88
  done();
24
89
  });
@@ -35,6 +100,31 @@ describe('Schema Server', () => {
35
100
  query: false,
36
101
  res: true
37
102
  },
103
+ 'POST /json-only': {
104
+ body: true,
105
+ query: false,
106
+ res: true
107
+ },
108
+ 'POST /multi-body': {
109
+ body: true,
110
+ query: false,
111
+ res: true
112
+ },
113
+ 'POST /wildcard-body': {
114
+ body: true,
115
+ query: false,
116
+ res: true
117
+ },
118
+ 'POST /optional-body': {
119
+ body: true,
120
+ query: false,
121
+ res: true
122
+ },
123
+ 'POST /with-examples': {
124
+ body: true,
125
+ query: false,
126
+ res: true
127
+ },
38
128
  'GET /schema': {
39
129
  body: false,
40
130
  query: true,
@@ -104,5 +194,131 @@ describe('Schema Server', () => {
104
194
  assert.strictEqual(body.paths['/legacy'].get.deprecated, true);
105
195
  assert.strictEqual(body.paths['/schema'].get.deprecated, false);
106
196
  });
197
+ it('POST: /json-only with valid JSON body (backwards compat)', async () => {
198
+ const res = await fetch('http://localhost:2000/api/json-only', {
199
+ method: 'POST',
200
+ headers: { 'Content-Type': 'application/json' },
201
+ body: JSON.stringify({ name: 'foo' })
202
+ });
203
+ assert.strictEqual(res.status, 200);
204
+ assert.deepStrictEqual(await res.json(), { ok: true });
205
+ });
206
+ it('POST: /multi-body with application/json', async () => {
207
+ const res = await fetch('http://localhost:2000/api/multi-body', {
208
+ method: 'POST',
209
+ headers: { 'Content-Type': 'application/json' },
210
+ body: JSON.stringify({ name: 'foo' })
211
+ });
212
+ assert.strictEqual(res.status, 200);
213
+ assert.deepStrictEqual(await res.json(), {
214
+ contentType: 'application/json',
215
+ body: { name: 'foo' }
216
+ });
217
+ });
218
+ it('POST: /multi-body with text/xml (true => no validation)', async () => {
219
+ const res = await fetch('http://localhost:2000/api/multi-body', {
220
+ method: 'POST',
221
+ headers: { 'Content-Type': 'text/xml' },
222
+ body: '<root><a>1</a></root>'
223
+ });
224
+ assert.strictEqual(res.status, 200);
225
+ assert.deepStrictEqual(await res.json(), {
226
+ contentType: 'text/xml',
227
+ body: '<root><a>1</a></root>'
228
+ });
229
+ });
230
+ it('POST: /multi-body with unsupported content-type returns 400', async () => {
231
+ const res = await fetch('http://localhost:2000/api/multi-body', {
232
+ method: 'POST',
233
+ headers: { 'Content-Type': 'text/plain' },
234
+ body: 'hello'
235
+ });
236
+ assert.strictEqual(res.status, 400);
237
+ const body = await res.json();
238
+ assert.strictEqual(body.status, 400);
239
+ assert.match(body.message, /Content-Type text\/plain not supported/);
240
+ });
241
+ it('POST: /multi-body with invalid JSON body fails validation', async () => {
242
+ const res = await fetch('http://localhost:2000/api/multi-body', {
243
+ method: 'POST',
244
+ headers: { 'Content-Type': 'application/json' },
245
+ body: JSON.stringify({})
246
+ });
247
+ assert.strictEqual(res.status, 400);
248
+ const body = await res.json();
249
+ assert.strictEqual(body.message, 'Validation Error POST /multi-body');
250
+ });
251
+ it('GET: api/openapi documents multiple content types', async () => {
252
+ const res = await fetch('http://localhost:2000/api/openapi');
253
+ const body = await res.json();
254
+ assert.deepStrictEqual(Object.keys(body.paths['/multi-body'].post.requestBody.content).sort(), ['application/json', 'text/xml']);
255
+ assert.deepStrictEqual(body.paths['/multi-body'].post.requestBody.content['text/xml'], { schema: {} });
256
+ assert.strictEqual(body.paths['/json-only'].post.requestBody.content['application/json'].schema.type, 'object');
257
+ });
258
+ it('POST: /wildcard-body matches text/* with text/csv', async () => {
259
+ const res = await fetch('http://localhost:2000/api/wildcard-body', {
260
+ method: 'POST',
261
+ headers: { 'Content-Type': 'text/csv' },
262
+ body: 'a,b,c\n1,2,3'
263
+ });
264
+ assert.strictEqual(res.status, 200);
265
+ assert.deepStrictEqual(await res.json(), {
266
+ contentType: 'text/csv',
267
+ body: 'a,b,c\n1,2,3'
268
+ });
269
+ });
270
+ it('POST: /wildcard-body still validates application/json strictly', async () => {
271
+ const res = await fetch('http://localhost:2000/api/wildcard-body', {
272
+ method: 'POST',
273
+ headers: { 'Content-Type': 'application/json' },
274
+ body: JSON.stringify({ name: 'foo' })
275
+ });
276
+ assert.strictEqual(res.status, 200);
277
+ });
278
+ it('POST: /wildcard-body rejects unsupported family', async () => {
279
+ const res = await fetch('http://localhost:2000/api/wildcard-body', {
280
+ method: 'POST',
281
+ headers: { 'Content-Type': 'image/png' },
282
+ body: 'binary'
283
+ });
284
+ assert.strictEqual(res.status, 400);
285
+ const body = await res.json();
286
+ assert.match(body.message, /Content-Type image\/png not supported/);
287
+ });
288
+ it('POST: /optional-body accepts no body when bodyRequired=false', async () => {
289
+ const res = await fetch('http://localhost:2000/api/optional-body', {
290
+ method: 'POST'
291
+ });
292
+ assert.strictEqual(res.status, 200);
293
+ assert.deepStrictEqual(await res.json(), { ok: true });
294
+ });
295
+ it('POST: /optional-body still validates when a body is sent', async () => {
296
+ const res = await fetch('http://localhost:2000/api/optional-body', {
297
+ method: 'POST',
298
+ headers: { 'Content-Type': 'application/json' },
299
+ body: JSON.stringify({})
300
+ });
301
+ assert.strictEqual(res.status, 400);
302
+ });
303
+ it('GET: api/openapi marks optional body required=false', async () => {
304
+ const res = await fetch('http://localhost:2000/api/openapi');
305
+ const body = await res.json();
306
+ assert.strictEqual(body.paths['/optional-body'].post.requestBody.required, false);
307
+ assert.strictEqual(body.paths['/json-only'].post.requestBody.required, true);
308
+ });
309
+ it('GET: api/openapi includes example and examples on media type', async () => {
310
+ const res = await fetch('http://localhost:2000/api/openapi');
311
+ const body = await res.json();
312
+ const media = body.paths['/with-examples'].post.requestBody.content['application/json'];
313
+ assert.deepStrictEqual(media.example, { name: 'sample' });
314
+ assert.deepStrictEqual(media.examples.primary, {
315
+ summary: 'Primary',
316
+ value: { name: 'alice' }
317
+ });
318
+ assert.deepStrictEqual(media.examples.secondary, {
319
+ summary: 'Secondary',
320
+ value: { name: 'bob' }
321
+ });
322
+ });
107
323
  });
108
324
  //# sourceMappingURL=schema.srv.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.srv.test.js","sourceRoot":"","sources":["../../test/schema.srv.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema.srv.test.js","sourceRoot":"","sources":["../../test/schema.srv.test.ts"],"names":[],"mappings":"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"}
package/lib/openapi.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { OpenAPIV3 as Doc } from 'openapi-types'
2
2
  import type { RequestValidation } from './types.js';
3
+ import { normalizeBody } from './types.js';
3
4
  import type { TSchema } from '@sinclair/typebox';
4
5
 
5
6
  export type OpenAPIDocumentInput = {
@@ -165,13 +166,20 @@ export default class Docs {
165
166
  }
166
167
 
167
168
  if (schemas.body) {
169
+ const normalized = normalizeBody(schemas.body) || {};
170
+ const content: Record<string, Doc.MediaTypeObject> = {};
171
+ for (const ct of Object.keys(normalized)) {
172
+ const entry = normalized[ct];
173
+ const media: Doc.MediaTypeObject = {
174
+ schema: entry.schema === true ? {} : entry.schema as Doc.SchemaObject
175
+ };
176
+ if (entry.example !== undefined) media.example = entry.example;
177
+ if (entry.examples !== undefined) media.examples = entry.examples as Record<string, Doc.ExampleObject>;
178
+ content[ct] = media;
179
+ }
168
180
  document.requestBody = {
169
- required: true,
170
- content: {
171
- 'application/json': {
172
- schema: schemas.body
173
- }
174
- }
181
+ required: schemas.bodyRequired !== false,
182
+ content
175
183
  };
176
184
  }
177
185
 
package/lib/schema.ts CHANGED
@@ -13,6 +13,7 @@ import { Ajv } from 'ajv'
13
13
  import type { ErrorObject } from 'ajv'
14
14
  import addFormatsModule from 'ajv-formats'
15
15
  import type { RequestValidation } from './types.js';
16
+ import { normalizeBody, matchContentType } from './types.js';
16
17
  import type { OpenAPIDocumentInput } from './openapi.js'
17
18
  import SchemaAPI from './api.js';
18
19
  import Docs from './openapi.js';
@@ -53,6 +54,67 @@ export default class Schemas {
53
54
  return new Err(400, null, `Validation Error ${String(method).toUpperCase()} ${path}`);
54
55
  }
55
56
 
57
+ /**
58
+ * Compile body validators keyed by content-type. Returns null when no
59
+ * body validation is configured. Each entry is either `true` (any body
60
+ * accepted for this content-type) or a compiled ajv validator function.
61
+ */
62
+ compileBodyValidators(
63
+ body: RequestValidation<TSchema, TSchema, TSchema, TSchema>['body']
64
+ ): Map<string, true | ReturnType<typeof ajv.compile>> | null {
65
+ const normalized = normalizeBody(body);
66
+ if (!normalized) return null;
67
+
68
+ const map = new Map<string, true | ReturnType<typeof ajv.compile>>();
69
+ for (const ct of Object.keys(normalized)) {
70
+ const schema = normalized[ct].schema;
71
+ if (schema === true || schema === undefined) {
72
+ map.set(ct, true);
73
+ } else {
74
+ map.set(ct, ajv.compile(schema));
75
+ }
76
+ }
77
+ return map;
78
+ }
79
+
80
+ /**
81
+ * Validate the incoming request body against the compiled validator
82
+ * map for the route. Pushes a Body error onto `errors` if validation
83
+ * fails; returns an Err to respond with for an unsupported content-type.
84
+ *
85
+ * If `bodyRequired` is false and the request has no body / no
86
+ * content-type, validation is skipped.
87
+ */
88
+ validateBody(
89
+ req: Request,
90
+ validators: Map<string, true | ReturnType<typeof ajv.compile>>,
91
+ errors: Array<ErrorListItem>,
92
+ opts: { required?: boolean } = {}
93
+ ): Err | null {
94
+ const raw = (req.headers['content-type'] || '').split(';')[0].trim().toLowerCase();
95
+ const required = opts.required !== false;
96
+
97
+ if (!raw && !required) return null;
98
+
99
+ const normalizedMap: Record<string, { schema: true }> = {};
100
+ for (const key of validators.keys()) normalizedMap[key] = { schema: true };
101
+ const matched = matchContentType(raw, normalizedMap);
102
+ const validator = matched ? validators.get(matched) : undefined;
103
+
104
+ if (!validator) {
105
+ const supported = Array.from(validators.keys()).join(', ');
106
+ return new Err(400, null, `Content-Type ${raw || '(none)'} not supported. Supported: ${supported}`);
107
+ }
108
+
109
+ if (validator !== true) {
110
+ if (!validator(req.body)) {
111
+ errors.push({ type: 'Body', errors: validator.errors as ErrorObject[] });
112
+ }
113
+ }
114
+
115
+ return null;
116
+ }
117
+
56
118
  constructor(
57
119
  router: Router,
58
120
  opts: {
@@ -74,8 +136,16 @@ export default class Schemas {
74
136
  this.router.use(morgan('combined', morganOptions));
75
137
  }
76
138
 
77
- this.router.use(bodyparser.urlencoded({ extended: true }));
139
+ this.router.use(bodyparser.urlencoded({ extended: true, limit: `${opts.limit || 50}mb` }));
78
140
  this.router.use(bodyparser.json({ limit: `${opts.limit || 50}mb` }));
141
+ this.router.use(bodyparser.text({
142
+ limit: `${opts.limit || 50}mb`,
143
+ type: ['text/*', 'application/xml', 'application/*+xml']
144
+ }));
145
+ this.router.use(bodyparser.raw({
146
+ limit: `${opts.limit || 50}mb`,
147
+ type: ['application/octet-stream']
148
+ }));
79
149
 
80
150
  if (opts.error) {
81
151
  this.error = opts.error;
@@ -264,7 +334,7 @@ export default class Schemas {
264
334
  const resValidation = validation.res && !(validation.res instanceof Type.Any) && !(validation.res instanceof Type.Unknown) && ajv.compile(validation.res);
265
335
  const paramsValidation = validation.params && ajv.compile(validation.params);
266
336
  const queryValidation = validation.query && ajv.compile(validation.query);
267
- const bodyValidation = validation.body && ajv.compile(validation.body);
337
+ const bodyValidators = this.compileBodyValidators(validation.body);
268
338
 
269
339
  const _handler: RequestHandler = (req, res, next) => {
270
340
  if (req.query) { // Ref: https://github.com/cdimascio/express-openapi-validator/issues/969
@@ -277,7 +347,10 @@ export default class Schemas {
277
347
  const errors: Array<ErrorListItem> = [];
278
348
  if (paramsValidation && !paramsValidation(req.params)) errors.push({ type: 'Params', errors: paramsValidation.errors as ErrorObject[] });
279
349
  if (queryValidation && !queryValidation(req.query)) errors.push({ type: 'Query', errors: queryValidation.errors as ErrorObject[] });
280
- if (bodyValidation && !bodyValidation(req.body)) errors.push({ type: 'Body', errors: bodyValidation.errors as ErrorObject[] });
350
+ if (bodyValidators) {
351
+ const ctErr = this.validateBody(req, bodyValidators, errors, { required: validation.bodyRequired });
352
+ if (ctErr) return Err.respond(ctErr, res);
353
+ }
281
354
  if (errors.length) return Err.respond(this.validationError(Doc.HttpMethods.POST, path), res, errors);
282
355
 
283
356
  const json = res.json;
@@ -315,7 +388,7 @@ export default class Schemas {
315
388
  const resValidation = validation.res && !(validation.res instanceof Type.Any) && !(validation.res instanceof Type.Unknown) && ajv.compile(validation.res);
316
389
  const paramsValidation = validation.params && ajv.compile(validation.params);
317
390
  const queryValidation = validation.query && ajv.compile(validation.query);
318
- const bodyValidation = validation.body && ajv.compile(validation.body);
391
+ const bodyValidators = this.compileBodyValidators(validation.body);
319
392
 
320
393
  const _handler: RequestHandler = (req, res, next) => {
321
394
  if (req.query) { // Ref: https://github.com/cdimascio/express-openapi-validator/issues/969
@@ -328,7 +401,10 @@ export default class Schemas {
328
401
  const errors: Array<ErrorListItem> = [];
329
402
  if (paramsValidation && !paramsValidation(req.params)) errors.push({ type: 'Params', errors: paramsValidation.errors as ErrorObject[] });
330
403
  if (queryValidation && !queryValidation(req.query)) errors.push({ type: 'Query', errors: queryValidation.errors as ErrorObject[] });
331
- if (bodyValidation && !bodyValidation(req.body)) errors.push({ type: 'Body', errors: bodyValidation.errors as ErrorObject[] });
404
+ if (bodyValidators) {
405
+ const ctErr = this.validateBody(req, bodyValidators, errors, { required: validation.bodyRequired });
406
+ if (ctErr) return Err.respond(ctErr, res);
407
+ }
332
408
  if (errors.length) return Err.respond(this.validationError(Doc.HttpMethods.PATCH, path), res, errors);
333
409
 
334
410
  const json = res.json;
@@ -366,7 +442,7 @@ export default class Schemas {
366
442
  const resValidation = validation.res && !(validation.res instanceof Type.Any) && !(validation.res instanceof Type.Unknown) && ajv.compile(validation.res);
367
443
  const paramsValidation = validation.params && ajv.compile(validation.params);
368
444
  const queryValidation = validation.query && ajv.compile(validation.query);
369
- const bodyValidation = validation.body && ajv.compile(validation.body);
445
+ const bodyValidators = this.compileBodyValidators(validation.body);
370
446
 
371
447
  const _handler: RequestHandler = (req, res, next) => {
372
448
  if (req.query) { // Ref: https://github.com/cdimascio/express-openapi-validator/issues/969
@@ -379,7 +455,10 @@ export default class Schemas {
379
455
  const errors: Array<ErrorListItem> = [];
380
456
  if (paramsValidation && !paramsValidation(req.params)) errors.push({ type: 'Params', errors: paramsValidation.errors as ErrorObject[] });
381
457
  if (queryValidation && !queryValidation(req.query)) errors.push({ type: 'Query', errors: queryValidation.errors as ErrorObject[] });
382
- if (bodyValidation && !bodyValidation(req.body)) errors.push({ type: 'Body', errors: bodyValidation.errors as ErrorObject[] });
458
+ if (bodyValidators) {
459
+ const ctErr = this.validateBody(req, bodyValidators, errors, { required: validation.bodyRequired });
460
+ if (ctErr) return Err.respond(ctErr, res);
461
+ }
383
462
  if (errors.length) return Err.respond(this.validationError(Doc.HttpMethods.PUT, path), res, errors);
384
463
 
385
464
  const json = res.json;
package/lib/types.ts CHANGED
@@ -1,5 +1,88 @@
1
+ import { Kind } from '@sinclair/typebox';
1
2
  import type { TSchema } from '@sinclair/typebox';
2
3
 
4
+ /**
5
+ * Per-content-type body entry. May be:
6
+ * - a TypeBox schema (validated, no example)
7
+ * - `true` (any body accepted, no validation)
8
+ * - an object with optional `schema` (TSchema | true), `example`, `examples`
9
+ * (forwarded to the OpenAPI media type object)
10
+ */
11
+ export type BodyEntryConfig = {
12
+ schema?: TSchema | true;
13
+ example?: unknown;
14
+ examples?: Record<string, { summary?: string; description?: string; value: unknown }>;
15
+ };
16
+
17
+ export type BodyEntry = TSchema | true | BodyEntryConfig;
18
+
19
+ /**
20
+ * Body validation can either be:
21
+ * - a TypeBox schema (treated as `application/json` for backwards compatibility)
22
+ * - a record mapping content-type strings to a `BodyEntry`. Content-type
23
+ * keys may include wildcards: `text/*`, `application/*`, or `* / *`.
24
+ */
25
+ export type BodyValidation<TBody extends TSchema> =
26
+ | TBody
27
+ | Record<string, BodyEntry>;
28
+
29
+ /**
30
+ * Normalized internal representation: every entry has the expanded
31
+ * `BodyEntryConfig` shape with `schema` always set (defaulting to `true`).
32
+ */
33
+ export type NormalizedBody = Record<string, Required<Pick<BodyEntryConfig, 'schema'>> & BodyEntryConfig>;
34
+
35
+ /**
36
+ * Detect whether a given value is a TypeBox schema. TypeBox attaches a
37
+ * `[Kind]` symbol to every schema instance which is what we use to
38
+ * distinguish a schema from a content-type-keyed body record.
39
+ */
40
+ export function isTSchema(value: unknown): value is TSchema {
41
+ return !!value && typeof value === 'object' && Kind in (value as object);
42
+ }
43
+
44
+ function normalizeEntry(entry: BodyEntry): NormalizedBody[string] {
45
+ if (entry === true) return { schema: true };
46
+ if (isTSchema(entry)) return { schema: entry };
47
+ return { schema: true, ...entry };
48
+ }
49
+
50
+ /**
51
+ * Normalize the body option into a `NormalizedBody` map. A bare TypeBox
52
+ * schema is mapped to `application/json` for backwards compatibility.
53
+ */
54
+ export function normalizeBody(
55
+ body: TSchema | Record<string, BodyEntry> | undefined
56
+ ): NormalizedBody | undefined {
57
+ if (!body) return undefined;
58
+ if (isTSchema(body)) return { 'application/json': { schema: body } };
59
+
60
+ const out: NormalizedBody = {};
61
+ for (const ct of Object.keys(body)) {
62
+ out[ct.toLowerCase()] = normalizeEntry(body[ct]);
63
+ }
64
+ return out;
65
+ }
66
+
67
+ /**
68
+ * Match an incoming content-type against a normalized body map, honoring
69
+ * wildcard entries like `text/*` or `*\/*`. Exact matches always win.
70
+ */
71
+ export function matchContentType(
72
+ incoming: string,
73
+ normalized: NormalizedBody
74
+ ): string | undefined {
75
+ if (!incoming) {
76
+ return normalized['*/*'] ? '*/*' : undefined;
77
+ }
78
+ if (normalized[incoming]) return incoming;
79
+
80
+ const [type] = incoming.split('/');
81
+ if (type && normalized[`${type}/*`]) return `${type}/*`;
82
+ if (normalized['*/*']) return '*/*';
83
+ return undefined;
84
+ }
85
+
3
86
  export type RequestValidation<
4
87
  TParams extends TSchema,
5
88
  TQuery extends TSchema,
@@ -13,6 +96,8 @@ export type RequestValidation<
13
96
  description?: string;
14
97
  params?: TParams;
15
98
  query?: TQuery;
16
- body?: TBody;
99
+ body?: BodyValidation<TBody>;
100
+ /** Whether the request body is required. Defaults to true when `body` is set. */
101
+ bodyRequired?: boolean;
17
102
  res?: TResponse
18
103
  };
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "root": true,
5
5
  "main": "dist/index.js",
6
6
  "types": "index.ts",
7
- "version": "10.25.0",
7
+ "version": "10.26.0",
8
8
  "description": "Strongly Validated JSON Schema support for address",
9
9
  "repository": {
10
10
  "type": "git",