@openclaw/slack 2026.9.9 → 2026.9.34

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.
Files changed (64) hide show
  1. package/dist/.setup/{action-runtime-DfWvuiZd.mjs → action-runtime-DrJGFq6u.mjs} +4 -4
  2. package/dist/.setup/action-runtime.runtime-BhXBcznI.mjs +1 -0
  3. package/dist/.setup/{actions-Df0CzcDl.mjs → actions-B-E1iXQh.mjs} +2 -2
  4. package/dist/.setup/{approval-handler.runtime-Ed6Sw5VK.mjs → approval-handler.runtime-B4zv4Cy-.mjs} +4 -4
  5. package/dist/.setup/{approval-message-updates-BadvtA7R.mjs → approval-message-updates-Psy357ys.mjs} +1 -1
  6. package/dist/.setup/{channel-C7bgPSTE.mjs → channel-C19w97Oe.mjs} +9 -9
  7. package/dist/.setup/{channel-type-ClOttrZ5.mjs → channel-type-DdNW4_3Y.mjs} +1 -1
  8. package/dist/.setup/{monitor-BPVfkumU.mjs → monitor-0RYQ8Lt4.mjs} +14 -14
  9. package/dist/.setup/{outbound-adapter-Bv48XUjq.mjs → outbound-adapter-BGeEF1Tb.mjs} +3 -3
  10. package/dist/.setup/{pipeline.runtime-B1H63Frf.mjs → pipeline.runtime-DhQXp3fP.mjs} +9 -9
  11. package/dist/.setup/{policy-D-jfM174.mjs → policy-D7nmaTmD.mjs} +1 -1
  12. package/dist/.setup/{relay-source-BpKdjsll.mjs → relay-source-DL5bqusA.mjs} +1 -1
  13. package/dist/.setup/{replies-BAnUtr2f.mjs → replies-BHFmop3k.mjs} +4 -4
  14. package/dist/.setup/{reply-blocks-SE1kTE3I.mjs → reply-blocks-BntCJ7mr.mjs} +1 -1
  15. package/dist/.setup/runtime-setter-api-Cx_PV-xu.mjs +2 -0
  16. package/dist/.setup/secret-contract-api-3duiGw0K.mjs +2 -0
  17. package/dist/.setup/{send-BOVLQi3n.mjs → send-BSQKD4nx.mjs} +1 -1
  18. package/dist/.setup/{session-status-DscajIsd.mjs → session-status-CPWDBkOb.mjs} +1 -1
  19. package/dist/.setup/setup-plugin-api-BVR4tSwT.mjs +2 -0
  20. package/dist/.setup/{slash-dispatch.runtime--AEzLVBF.mjs → slash-dispatch.runtime-D_QM_3Cg.mjs} +1 -1
  21. package/dist/api.js +6 -6
  22. package/dist/channel-config-api.js +1 -2
  23. package/dist/channel-plugin-api.js +1 -2
  24. package/dist/directory-contract-api.js +1 -2
  25. package/dist/doctor-contract-api.js +1 -2
  26. package/dist/http-routes-api.js +1 -2
  27. package/dist/message-tool-api.js +1 -2
  28. package/dist/runtime-api.js +5 -5
  29. package/dist/runtime-setter-api.js +1 -2
  30. package/dist/secret-contract-api.js +1 -2
  31. package/dist/security-contract-api.js +1 -2
  32. package/dist/setup-entry.js +3 -3
  33. package/dist/setup-plugin-api.js +1 -2
  34. package/node_modules/@slack/bolt/node_modules/body-parser/README.md +17 -13
  35. package/node_modules/@slack/bolt/node_modules/body-parser/lib/read.js +74 -57
  36. package/node_modules/@slack/bolt/node_modules/body-parser/lib/types/json.js +11 -11
  37. package/node_modules/@slack/bolt/node_modules/body-parser/lib/types/urlencoded.js +5 -5
  38. package/node_modules/@slack/bolt/node_modules/body-parser/lib/utils.js +0 -15
  39. package/node_modules/@slack/bolt/node_modules/body-parser/package.json +7 -12
  40. package/node_modules/zod/package.json +1 -1
  41. package/node_modules/zod/src/v4/classic/tests/array.test.ts +27 -0
  42. package/node_modules/zod/src/v4/classic/tests/map.test.ts +7 -0
  43. package/node_modules/zod/src/v4/classic/tests/set.test.ts +6 -0
  44. package/node_modules/zod/src/v4/core/compile.ts +9 -0
  45. package/node_modules/zod/src/v4/core/schemas.ts +35 -1
  46. package/node_modules/zod/src/v4/core/util.ts +13 -0
  47. package/node_modules/zod/src/v4/core/versions.ts +1 -1
  48. package/node_modules/zod/v4/core/compile.cjs +8 -0
  49. package/node_modules/zod/v4/core/compile.js +8 -0
  50. package/node_modules/zod/v4/core/schemas.cjs +30 -0
  51. package/node_modules/zod/v4/core/schemas.d.cts +3 -1
  52. package/node_modules/zod/v4/core/schemas.d.ts +3 -1
  53. package/node_modules/zod/v4/core/schemas.js +30 -0
  54. package/node_modules/zod/v4/core/util.cjs +15 -0
  55. package/node_modules/zod/v4/core/util.d.cts +2 -0
  56. package/node_modules/zod/v4/core/util.d.ts +2 -0
  57. package/node_modules/zod/v4/core/util.js +14 -0
  58. package/node_modules/zod/v4/core/versions.cjs +1 -1
  59. package/node_modules/zod/v4/core/versions.js +1 -1
  60. package/package.json +5 -5
  61. package/dist/.setup/action-runtime.runtime-Ck4bAWOX.mjs +0 -2
  62. package/dist/.setup/runtime-setter-api-CMFvhrAr.mjs +0 -3
  63. package/dist/.setup/secret-contract-api-Dp6290Vg.mjs +0 -3
  64. package/dist/.setup/setup-plugin-api-DTtbTKNk.mjs +0 -3
@@ -37,6 +37,10 @@ This module provides the following parsers:
37
37
  * [Text body parser](#bodyparsertextoptions)
38
38
  * [URL-encoded form body parser](#bodyparserurlencodedoptions)
39
39
 
40
+ All parsers support automatic inflation of `gzip`, `br` (brotli), `deflate`
41
+ and `zstd` encodings. `zstd` is available in Node.js `^22.15.0` and
42
+ `>=23.8.0`.
43
+
40
44
  Other body parsers you might be interested in:
41
45
 
42
46
  - [body](https://www.npmjs.com/package/body#readme)
@@ -65,6 +69,12 @@ The `bodyParser` object exposes various factories to create middlewares. All
65
69
  middlewares will populate the `req.body` property with the parsed body when
66
70
  the `Content-Type` request header matches the `type` option.
67
71
 
72
+ **Note** As of v2, `req.body` is left `undefined` when no body is parsed (for
73
+ example when the request has neither `Content-Length` nor `Transfer-Encoding`,
74
+ or when `Content-Type` does not match). In v1, `req.body` was always initialized
75
+ to `{}`. Code that assumes `req.body` is always an object should use optional
76
+ chaining (for example `req.body?.username`) or set a default.
77
+
68
78
  The various errors returned by this module are described in the
69
79
  [errors section](#errors).
70
80
 
@@ -72,8 +82,7 @@ The various errors returned by this module are described in the
72
82
 
73
83
  Returns middleware that only parses `json` and only looks at requests where
74
84
  the `Content-Type` header matches the `type` option. This parser accepts any
75
- Unicode encoding of the body and supports automatic inflation of `gzip`,
76
- `br` (brotli) and `deflate` encodings.
85
+ Unicode encoding of the body.
77
86
 
78
87
  A new `body` object containing the parsed data is populated on the `request`
79
88
  object after the middleware (i.e. `req.body`).
@@ -110,7 +119,7 @@ argument. You can find more information on this argument
110
119
 
111
120
  ##### strict
112
121
 
113
- When set to `true`, will only accept arrays and objects; when `false` will
122
+ When set to `true`, it will only accept arrays and objects; when `false`, it will
114
123
  accept anything `JSON.parse` accepts. Defaults to `true`.
115
124
 
116
125
  ##### type
@@ -133,9 +142,7 @@ encoding of the request. The parsing can be aborted by throwing an error.
133
142
  ### bodyParser.raw([options])
134
143
 
135
144
  Returns middleware that parses all bodies as a `Buffer` and only looks at
136
- requests where the `Content-Type` header matches the `type` option. This
137
- parser supports automatic inflation of `gzip`, `br` (brotli) and `deflate`
138
- encodings.
145
+ requests where the `Content-Type` header matches the `type` option.
139
146
 
140
147
  A new `body` object containing the parsed data is populated on the `request`
141
148
  object after the middleware (i.e. `req.body`). This will be a `Buffer` object
@@ -181,9 +188,7 @@ encoding of the request. The parsing can be aborted by throwing an error.
181
188
  ### bodyParser.text([options])
182
189
 
183
190
  Returns middleware that parses all bodies as a string and only looks at
184
- requests where the `Content-Type` header matches the `type` option. This
185
- parser supports automatic inflation of `gzip`, `br` (brotli) and `deflate`
186
- encodings.
191
+ requests where the `Content-Type` header matches the `type` option.
187
192
 
188
193
  A new `body` string containing the parsed data is populated on the `request`
189
194
  object after the middleware (i.e. `req.body`). This will be a string of the
@@ -234,8 +239,7 @@ encoding of the request. The parsing can be aborted by throwing an error.
234
239
 
235
240
  Returns middleware that only parses `urlencoded` bodies and only looks at
236
241
  requests where the `Content-Type` header matches the `type` option. This
237
- parser accepts only UTF-8 and ISO-8859-1 encodings of the body and supports
238
- automatic inflation of `gzip`, `br` (brotli) and `deflate` encodings.
242
+ parser accepts only UTF-8 and ISO-8859-1 encodings of the body.
239
243
 
240
244
  A new `body` object containing the parsed data is populated on the `request`
241
245
  object after the middleware (i.e. `req.body`). This object will contain
@@ -346,7 +350,7 @@ the entity value that failed parsing.
346
350
  ### entity verify failed
347
351
 
348
352
  This error will occur when the request contained an entity that could not be
349
- failed verification by the defined `verify` option. The `status` property is
353
+ verified by the defined `verify` option. The `status` property is
350
354
  set to `403`, the `type` property is set to `'entity.verify.failed'`, and the
351
355
  `body` property is set to the entity value that failed verification.
352
356
 
@@ -413,7 +417,7 @@ property is set to the encoding that is unsupported.
413
417
 
414
418
  ### The input exceeded the depth
415
419
 
416
- This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. When the `depth` option is set to `32` (default value), the error will not be thrown.
420
+ This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. With the default `depth` of `32`, an input with 33 or more levels of nesting gets this error.
417
421
 
418
422
  ## Examples
419
423
 
@@ -17,7 +17,7 @@ const iconv = require('iconv-lite')
17
17
  const onFinished = require('on-finished')
18
18
  const zlib = require('node:zlib')
19
19
  const hasBody = require('type-is').hasBody
20
- const { getCharset } = require('./utils')
20
+ const contentType = require('content-type')
21
21
 
22
22
  /**
23
23
  * Module exports.
@@ -25,6 +25,12 @@ const { getCharset } = require('./utils')
25
25
 
26
26
  module.exports = read
27
27
 
28
+ /**
29
+ * @const
30
+ * whether current node version has zstandard support
31
+ */
32
+ const hasZstandardSupport = 'createZstdDecompress' in zlib
33
+
28
34
  /**
29
35
  * Read a request into a buffer and parse.
30
36
  *
@@ -79,38 +85,53 @@ function read (req, res, next, parse, debug, options) {
79
85
  }
80
86
 
81
87
  let length
82
- const opts = options
83
88
  let stream
84
89
 
85
- // read options
86
- const verify = opts.verify
90
+ const verify = options.verify
91
+
92
+ // assert charset is supported before creating the decompression stream, so an
93
+ // unsupported charset cannot leave a decoder piped with no error listener
94
+ if (verify && encoding !== null && !iconv.encodingExists(encoding)) {
95
+ return next(createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
96
+ charset: encoding.toLowerCase(),
97
+ type: 'charset.unsupported'
98
+ }))
99
+ }
87
100
 
88
101
  try {
102
+ const contentEncoding = (req.headers['content-encoding'] || 'identity').toLowerCase()
103
+
104
+ debug('content-encoding "%s"', contentEncoding)
105
+
106
+ if (options.inflate === false && contentEncoding !== 'identity') {
107
+ throw createError(415, 'content encoding unsupported', {
108
+ encoding: contentEncoding,
109
+ type: 'encoding.unsupported'
110
+ })
111
+ }
112
+
89
113
  // get the content stream
90
- stream = contentstream(req, debug, opts.inflate)
91
- length = stream.length
92
- stream.length = undefined
114
+ if (contentEncoding === 'identity') {
115
+ length = req.headers['content-length']
116
+ stream = req
117
+ } else {
118
+ stream = createDecompressionStream(contentEncoding, debug)
119
+ req.pipe(stream)
120
+ }
93
121
  } catch (err) {
94
122
  return next(err)
95
123
  }
96
124
 
97
125
  // set raw-body options
98
- opts.length = length
99
- opts.encoding = verify
100
- ? null
101
- : encoding
102
-
103
- // assert charset is supported
104
- if (opts.encoding === null && encoding !== null && !iconv.encodingExists(encoding)) {
105
- return next(createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
106
- charset: encoding.toLowerCase(),
107
- type: 'charset.unsupported'
108
- }))
126
+ const rawBodyOptions = {
127
+ length,
128
+ encoding: verify ? null : encoding,
129
+ limit: options.limit
109
130
  }
110
131
 
111
132
  // read body
112
133
  debug('read body')
113
- getBody(stream, opts, function (error, body) {
134
+ getBody(stream, rawBodyOptions, function (error, body) {
114
135
  if (error) {
115
136
  let _error
116
137
 
@@ -138,6 +159,17 @@ function read (req, res, next, parse, debug, options) {
138
159
  return
139
160
  }
140
161
 
162
+ // detach and destroy the decompression stream to
163
+ // avoid decompression errors on the finished stream
164
+ if (stream !== req) {
165
+ req.unpipe(stream)
166
+ stream.on('error', () => {})
167
+ stream.destroy()
168
+
169
+ // consume any remaining bytes so the HTTP request can finish
170
+ req.resume()
171
+ }
172
+
141
173
  // verify
142
174
  if (verify) {
143
175
  try {
@@ -145,7 +177,7 @@ function read (req, res, next, parse, debug, options) {
145
177
  verify(req, res, body, encoding)
146
178
  } catch (err) {
147
179
  next(createError(403, err, {
148
- body: body,
180
+ body,
149
181
  type: err.type || 'entity.verify.failed'
150
182
  }))
151
183
  return
@@ -172,38 +204,6 @@ function read (req, res, next, parse, debug, options) {
172
204
  })
173
205
  }
174
206
 
175
- /**
176
- * Get the content stream of the request.
177
- *
178
- * @param {Object} req
179
- * @param {Function} debug
180
- * @param {boolean} inflate
181
- * @returns {Object}
182
- * @private
183
- */
184
- function contentstream (req, debug, inflate) {
185
- const encoding = (req.headers['content-encoding'] || 'identity').toLowerCase()
186
- const length = req.headers['content-length']
187
-
188
- debug('content-encoding "%s"', encoding)
189
-
190
- if (inflate === false && encoding !== 'identity') {
191
- throw createError(415, 'content encoding unsupported', {
192
- encoding: encoding,
193
- type: 'encoding.unsupported'
194
- })
195
- }
196
-
197
- if (encoding === 'identity') {
198
- req.length = length
199
- return req
200
- }
201
-
202
- const stream = createDecompressionStream(encoding, debug)
203
- req.pipe(stream)
204
- return stream
205
- }
206
-
207
207
  /**
208
208
  * Create a decompression stream for the given encoding.
209
209
  * @param {string} encoding
@@ -222,12 +222,16 @@ function createDecompressionStream (encoding, debug) {
222
222
  case 'br':
223
223
  debug('brotli decompress body')
224
224
  return zlib.createBrotliDecompress()
225
- default:
226
- throw createError(415, 'unsupported content encoding "' + encoding + '"', {
227
- encoding: encoding,
228
- type: 'encoding.unsupported'
229
- })
225
+ case 'zstd':
226
+ if (hasZstandardSupport) {
227
+ debug('zstd decompress body')
228
+ return zlib.createZstdDecompress()
229
+ }
230
230
  }
231
+ throw createError(415, 'unsupported content encoding "' + encoding + '"', {
232
+ encoding,
233
+ type: 'encoding.unsupported'
234
+ })
231
235
  }
232
236
 
233
237
  /**
@@ -245,3 +249,16 @@ function dump (req, callback) {
245
249
  req.resume()
246
250
  }
247
251
  }
252
+
253
+ /**
254
+ * Get the charset of a request.
255
+ *
256
+ * @param {Object} req
257
+ * @returns {string | undefined}
258
+ * @private
259
+ */
260
+ function getCharset (req) {
261
+ const header = req.headers['content-type']
262
+ if (!header) return undefined
263
+ return contentType.parse(header).parameters.charset?.toLowerCase()
264
+ }
@@ -23,7 +23,7 @@ const { normalizeOptions } = require('../utils')
23
23
  module.exports = json
24
24
 
25
25
  /**
26
- * RegExp to match the first non-space in a string.
26
+ * RegExp to match the first non-whitespace character in a string.
27
27
  *
28
28
  * Allowed whitespace is defined in RFC 7159:
29
29
  *
@@ -33,7 +33,7 @@ module.exports = json
33
33
  * %x0A / ; Line feed or New line
34
34
  * %x0D ) ; Carriage return
35
35
  */
36
- const FIRST_CHAR_REGEXP = /^[\x20\x09\x0a\x0d]*([^\x20\x09\x0a\x0d])/ // eslint-disable-line no-control-regex
36
+ const FIRST_NON_WHITESPACE_REGEXP = /^[\x20\x09\x0a\x0d]*([^\x20\x09\x0a\x0d]|$)/ // eslint-disable-line no-control-regex
37
37
 
38
38
  const JSON_SYNTAX_CHAR = '#'
39
39
  const JSON_SYNTAX_REGEXP = /#+/g
@@ -80,10 +80,12 @@ function createJsonParser (options) {
80
80
  return {}
81
81
  }
82
82
 
83
- const first = firstchar(body)
84
- if (first !== '{' && first !== '[') {
85
- debug('strict violation')
86
- throw createStrictSyntaxError(body, first)
83
+ if (body[0] !== '{' && body[0] !== '[') {
84
+ const first = firstNonWhitespaceChar(body)
85
+ if (first !== '{' && first !== '[') {
86
+ debug('strict violation')
87
+ throw createStrictSyntaxError(body, first)
88
+ }
87
89
  }
88
90
 
89
91
  try {
@@ -152,12 +154,10 @@ function createStrictSyntaxError (str, char) {
152
154
  * @returns {string|undefined}
153
155
  * @private
154
156
  */
155
- function firstchar (str) {
156
- const match = FIRST_CHAR_REGEXP.exec(str)
157
+ function firstNonWhitespaceChar (str) {
158
+ const match = FIRST_NON_WHITESPACE_REGEXP.exec(str)
157
159
 
158
- return match
159
- ? match[1]
160
- : undefined
160
+ return match && match[1] ? match[1] : undefined
161
161
  }
162
162
 
163
163
  /**
@@ -98,12 +98,12 @@ function createQueryParser (options) {
98
98
  try {
99
99
  return qs.parse(body, {
100
100
  allowPrototypes: true,
101
- arrayLimit: arrayLimit,
102
- depth: depth,
103
- charsetSentinel: charsetSentinel,
104
- interpretNumericEntities: interpretNumericEntities,
101
+ arrayLimit,
102
+ depth,
103
+ charsetSentinel,
104
+ interpretNumericEntities,
105
105
  charset: encoding,
106
- parameterLimit: parameterLimit,
106
+ parameterLimit,
107
107
  strictDepth: true
108
108
  })
109
109
  } catch (err) {
@@ -5,31 +5,16 @@
5
5
  */
6
6
 
7
7
  const bytes = require('bytes')
8
- const contentType = require('content-type')
9
8
  const typeis = require('type-is')
10
9
 
11
10
  /**
12
11
  * Module exports.
13
12
  */
14
13
  module.exports = {
15
- getCharset,
16
14
  normalizeOptions,
17
15
  passthrough
18
16
  }
19
17
 
20
- /**
21
- * Get the charset of a request.
22
- *
23
- * @param {Object} req
24
- * @returns {string | undefined}
25
- * @private
26
- */
27
- function getCharset (req) {
28
- const header = req.headers['content-type']
29
- if (!header) return undefined
30
- return contentType.parse(header).parameters.charset?.toLowerCase()
31
- }
32
-
33
18
  /**
34
19
  * Get the simple type checker.
35
20
  *
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "body-parser",
3
3
  "description": "Node.js body parsing middleware",
4
- "version": "2.3.0",
4
+ "version": "2.4.0",
5
5
  "contributors": [
6
6
  "Douglas Christopher Wilson <doug@somethingdoug.com>",
7
7
  "Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)"
@@ -27,26 +27,21 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "bytes": "^3.1.2",
30
- "content-type": "^2.0.0",
30
+ "content-type": "^2.1.0",
31
31
  "debug": "^4.4.3",
32
32
  "http-errors": "^2.0.1",
33
- "iconv-lite": "^0.7.2",
33
+ "iconv-lite": "^0.7.3",
34
34
  "on-finished": "^2.4.1",
35
- "qs": "^6.15.2",
35
+ "qs": "^6.16.1",
36
36
  "raw-body": "^3.0.2",
37
37
  "type-is": "^2.1.0"
38
38
  },
39
39
  "devDependencies": {
40
- "eslint": "^8.57.1",
41
- "eslint-config-standard": "^14.1.1",
42
- "eslint-plugin-import": "^2.32.0",
43
- "eslint-plugin-markdown": "^3.0.1",
44
- "eslint-plugin-node": "^11.1.0",
45
- "eslint-plugin-promise": "^6.6.0",
46
- "eslint-plugin-standard": "^4.1.0",
40
+ "eslint": "^9.39.5",
47
41
  "mocha": "^11.7.6",
42
+ "neostandard": "^0.13.0",
48
43
  "nyc": "^17.1.0",
49
- "supertest": "^7.2.2"
44
+ "supertest": "7.3.0"
50
45
  },
51
46
  "files": [
52
47
  "lib/",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zod",
3
- "version": "4.6.5",
3
+ "version": "4.6.7",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": "Colin McDonnell <zod@colinhacks.com>",
@@ -140,6 +140,33 @@ test("continue parsing despite array size error", () => {
140
140
  `);
141
141
  });
142
142
 
143
+ test("an upper size bound fails before any element is parsed", () => {
144
+ let parsed = 0;
145
+ const el = z.string().refine(() => ++parsed);
146
+ let refined = 0;
147
+ const schema = z
148
+ .array(el)
149
+ .max(2, "too many")
150
+ .refine(() => ++refined);
151
+ expect(schema.safeParse([1, 2, 3]).error!.issues).toMatchObject([{ code: "too_big", path: [], message: "too many" }]);
152
+ expect(z.array(el).length(2).safeParse(["a", "b", "c"]).error!.issues).toMatchObject([
153
+ { code: "too_big", exact: true },
154
+ ]);
155
+ expect(z.object({ a: z.array(el).max(1) }).safeParse({ a: [1, 2] }).error!.issues).toMatchObject([
156
+ { code: "too_big", path: ["a"] },
157
+ ]);
158
+ expect([parsed, refined]).toEqual([0, 0]);
159
+
160
+ // a check that may rewrite the value ends the gate
161
+ expect(
162
+ z
163
+ .array(z.number())
164
+ .overwrite((a) => a.slice(0, 2))
165
+ .max(2)
166
+ .parse([1, 2, 3])
167
+ ).toEqual([1, 2]);
168
+ });
169
+
143
170
  test("parse should fail given sparse array", () => {
144
171
  const schema = z.array(z.string()).nonempty().min(1).max(3);
145
172
  const result = schema.safeParse(new Array(3));
@@ -97,6 +97,13 @@ test("failing when map is bigger than max() ", () => {
97
97
  expect(result.success).toEqual(false);
98
98
  expect(result.error!.issues.length).toEqual(1);
99
99
  expect(result.error!.issues[0].code).toEqual("too_big");
100
+ const bad = new Map([
101
+ [1, 1],
102
+ [2, 2],
103
+ ]);
104
+ const capped = z.map(z.string(), z.string()).max(1);
105
+ expect(capped.safeParse(bad).error!.issues).toMatchObject([{ code: "too_big" }]);
106
+ expect(capped.safeParse(new Set([1, 2])).error!.issues[0]).toMatchObject({ code: "invalid_type", expected: "map" });
100
107
  });
101
108
 
102
109
  test("failing when map does not match size() ", () => {
@@ -83,6 +83,12 @@ test("failing when set is bigger than max() ", () => {
83
83
  expect(result.success).toEqual(false);
84
84
  expect(result.error!.issues.length).toEqual(1);
85
85
  expect(result.error!.issues[0].code).toEqual("too_big");
86
+ expect(
87
+ z
88
+ .set(z.string())
89
+ .max(1)
90
+ .safeParse(new Set([1, 2])).error!.issues
91
+ ).toMatchObject([{ code: "too_big" }]);
86
92
  });
87
93
 
88
94
  test("failing when set does not match size() ", () => {
@@ -1551,6 +1551,12 @@ function generateNullableCheck(
1551
1551
  return outputVar;
1552
1552
  }
1553
1553
 
1554
+ function generateSizeGate(doc: Doc, schema: SomeType, size: string): void {
1555
+ for (const [max, check] of util.sizeCaps(schema._zod.def.checks)) {
1556
+ doc.write(`if (${size} > ${numericOperand(max, check._zod.def.check)}) return INVALID;`);
1557
+ }
1558
+ }
1559
+
1554
1560
  function generateArrayCheck(
1555
1561
  doc: Doc,
1556
1562
  ctx: CompileContext,
@@ -1560,6 +1566,7 @@ function generateArrayCheck(
1560
1566
  ): string | null {
1561
1567
  const def = schema._zod.def as unknown as { element: SomeType };
1562
1568
  doc.write(`if (!Array.isArray(${accessor})) return INVALID;`);
1569
+ generateSizeGate(doc, schema, `${accessor}.length`);
1563
1570
 
1564
1571
  // Build a new array with validated/transformed elements.
1565
1572
  const outputVar = buildsValue ? newVar(ctx) : null;
@@ -2094,6 +2101,7 @@ function generateMapCheck(doc: Doc, ctx: CompileContext, schema: SomeType, acces
2094
2101
  const def = schema._zod.def as unknown as { keyType: SomeType; valueType: SomeType };
2095
2102
 
2096
2103
  doc.write(`if (!(${accessor} instanceof Map)) return INVALID;`);
2104
+ generateSizeGate(doc, schema, `${accessor}.size`);
2097
2105
 
2098
2106
  const outputVar = newVar(ctx);
2099
2107
  const kVar = newVar(ctx);
@@ -2115,6 +2123,7 @@ function generateSetCheck(doc: Doc, ctx: CompileContext, schema: SomeType, acces
2115
2123
  const def = schema._zod.def as unknown as { valueType: SomeType };
2116
2124
 
2117
2125
  doc.write(`if (!(${accessor} instanceof Set)) return INVALID;`);
2126
+ generateSizeGate(doc, schema, `${accessor}.size`);
2118
2127
 
2119
2128
  const outputVar = newVar(ctx);
2120
2129
  const valVar = newVar(ctx);
@@ -1844,6 +1844,34 @@ export interface $ZodArrayInternals<T extends SomeType = $ZodType> extends _$Zod
1844
1844
 
1845
1845
  export interface $ZodArray<T extends SomeType = $ZodType> extends $ZodType<any, any, $ZodArrayInternals<T>> {}
1846
1846
 
1847
+ // an oversized container fails its leading size checks before any element is parsed
1848
+ function capSize(inst: $ZodType, size: (input: any) => number): void {
1849
+ const caps = util.sizeCaps(inst._zod.def.checks);
1850
+ if (!caps.length) return;
1851
+ const cap = Math.min(...caps.map((c) => c[0]));
1852
+ const parse = inst._zod.parse;
1853
+ inst._zod.parse = (payload, ctx) => {
1854
+ const n = size(payload.value);
1855
+ if (n > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
1856
+ for (const [max, ch] of util.sizeCaps(inst._zod.def.checks)) {
1857
+ if (n > max && ch._zod.def.when!(payload)) {
1858
+ const start = payload.issues.length;
1859
+ ch._zod.check(payload as ParsePayload<never>);
1860
+ // the value is still the raw input, so no later check may read it
1861
+ (payload.issues[start] as { continue?: boolean }).continue = false;
1862
+ util.attachSchema(payload.issues, start, inst);
1863
+ return payload;
1864
+ }
1865
+ }
1866
+ }
1867
+ return parse(payload, ctx);
1868
+ };
1869
+ }
1870
+
1871
+ const arraySize = (input: unknown): number => (Array.isArray(input) ? input.length : -1);
1872
+ const mapSize = (input: unknown): number => (input instanceof Map ? input.size : -1);
1873
+ const setSize = (input: unknown): number => (input instanceof Set ? input.size : -1);
1874
+
1847
1875
  function handleArrayResult(result: ParsePayload<any>, final: ParsePayload<any[]>, index: number) {
1848
1876
  if (result.issues.length) {
1849
1877
  final.issues.push(...util.prefixIssues(index, result.issues));
@@ -1899,6 +1927,7 @@ export const $ZodArray: core.$constructor<$ZodArray> = /*@__PURE__*/ core.$const
1899
1927
 
1900
1928
  return payload; //handleArrayResultsAsync(parseResults, final);
1901
1929
  };
1930
+ capSize(inst, arraySize);
1902
1931
  });
1903
1932
 
1904
1933
  //////////////////////////////////////////
@@ -3218,8 +3247,11 @@ export type $InferZodRecordInput<
3218
3247
  : Record<core.input<Key> & PropertyKey, core.input<Value>>;
3219
3248
 
3220
3249
  export interface $ZodRecordInternals<Key extends $ZodRecordKey = $ZodRecordKey, Value extends SomeType = $ZodType>
3221
- extends $ZodTypeInternals<$InferZodRecordOutput<Key, Value>, $InferZodRecordInput<Key, Value>> {
3250
+ extends $ZodTypeInternals {
3222
3251
  def: $ZodRecordDef<Key, Value>;
3252
+ // members rather than base type arguments, which TypeScript 7.1 reports as circular for `z.json()`
3253
+ output: $InferZodRecordOutput<Key, Value>;
3254
+ input: $InferZodRecordInput<Key, Value>;
3223
3255
  isst: errors.$ZodIssueInvalidType | errors.$ZodIssueInvalidKey<Record<PropertyKey, unknown>>;
3224
3256
  optin?: "optional" | undefined;
3225
3257
  optout?: "optional" | undefined;
@@ -3480,6 +3512,7 @@ export const $ZodMap: core.$constructor<$ZodMap> = /*@__PURE__*/ core.$construct
3480
3512
  if (proms.length) return Promise.all(proms).then(() => payload);
3481
3513
  return payload;
3482
3514
  };
3515
+ capSize(inst, mapSize);
3483
3516
  });
3484
3517
 
3485
3518
  function handleMapResult(
@@ -3582,6 +3615,7 @@ export const $ZodSet: core.$constructor<$ZodSet> = /*@__PURE__*/ core.$construct
3582
3615
  if (proms.length) return Promise.all(proms).then(() => payload);
3583
3616
  return payload;
3584
3617
  };
3618
+ capSize(inst, setSize);
3585
3619
  });
3586
3620
 
3587
3621
  function handleSetResult(result: ParsePayload, final: ParsePayload<Set<any>>) {
@@ -935,6 +935,19 @@ export function unwrapMessage(message: string | { message: string } | undefined
935
935
  }
936
936
 
937
937
  /* A check holds no link back to the schema it is attached to — the same check instance is shared by every clone of that schema — so the owner is stamped onto the issues a check just raised, at the only point where both are in scope. Runs on the failure path only; `start` is the issue count from before the check ran. */
938
+ /** The leading size checks of `defChecks` that set an upper bound, each paired with it. */
939
+ export function sizeCaps(defChecks: checks.$ZodCheck<never>[] | undefined): [number, checks.$ZodCheck<never>][] {
940
+ const caps: [number, checks.$ZodCheck<never>][] = [];
941
+ for (const ch of defChecks ?? []) {
942
+ const d = ch._zod.def as any;
943
+ // a later check may rewrite the value
944
+ if (!/_(length|size)$|_equals$/.test(d.check)) break;
945
+ const max = d.maximum ?? d.length ?? d.size;
946
+ if (max >= 0) caps.push([max, ch]);
947
+ }
948
+ return caps;
949
+ }
950
+
938
951
  export function attachSchema(issues: errors.$ZodRawIssue[], start: number, inst: schemas.$ZodType): void {
939
952
  for (let i = start; i < issues.length; i++) {
940
953
  (issues[i] as any).schema ??= inst;
@@ -1,5 +1,5 @@
1
1
  export const version = {
2
2
  major: 4,
3
3
  minor: 6,
4
- patch: 5 as number,
4
+ patch: 7 as number,
5
5
  } as const;
@@ -1241,9 +1241,15 @@ function generateNullableCheck(doc, ctx, schema, accessor, buildsValue = true) {
1241
1241
  doc.write(`}`);
1242
1242
  return outputVar;
1243
1243
  }
1244
+ function generateSizeGate(doc, schema, size) {
1245
+ for (const [max, check] of util.sizeCaps(schema._zod.def.checks)) {
1246
+ doc.write(`if (${size} > ${numericOperand(max, check._zod.def.check)}) return INVALID;`);
1247
+ }
1248
+ }
1244
1249
  function generateArrayCheck(doc, ctx, schema, accessor, buildsValue = true) {
1245
1250
  const def = schema._zod.def;
1246
1251
  doc.write(`if (!Array.isArray(${accessor})) return INVALID;`);
1252
+ generateSizeGate(doc, schema, `${accessor}.length`);
1247
1253
  // Build a new array with validated/transformed elements.
1248
1254
  const outputVar = buildsValue ? newVar(ctx) : null;
1249
1255
  const iVar = newVar(ctx);
@@ -1707,6 +1713,7 @@ function literalPropertyKey(ctx, key) {
1707
1713
  function generateMapCheck(doc, ctx, schema, accessor) {
1708
1714
  const def = schema._zod.def;
1709
1715
  doc.write(`if (!(${accessor} instanceof Map)) return INVALID;`);
1716
+ generateSizeGate(doc, schema, `${accessor}.size`);
1710
1717
  const outputVar = newVar(ctx);
1711
1718
  const kVar = newVar(ctx);
1712
1719
  const valVar = newVar(ctx);
@@ -1723,6 +1730,7 @@ function generateMapCheck(doc, ctx, schema, accessor) {
1723
1730
  function generateSetCheck(doc, ctx, schema, accessor) {
1724
1731
  const def = schema._zod.def;
1725
1732
  doc.write(`if (!(${accessor} instanceof Set)) return INVALID;`);
1733
+ generateSizeGate(doc, schema, `${accessor}.size`);
1726
1734
  const outputVar = newVar(ctx);
1727
1735
  const valVar = newVar(ctx);
1728
1736
  doc.write(`const ${outputVar} = new Set();`);