fastify 5.11.2 → 5.12.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.
Files changed (41) hide show
  1. package/README.md +7 -7
  2. package/SPONSORS.md +1 -0
  3. package/docs/Guides/Serverless.md +34 -2
  4. package/docs/Reference/Encapsulation.md +31 -2
  5. package/docs/Reference/Errors.md +76 -3
  6. package/docs/Reference/Reply.md +14 -0
  7. package/docs/Reference/Request.md +18 -6
  8. package/docs/Reference/Routes.md +13 -0
  9. package/fastify.js +1 -1
  10. package/lib/content-type-parser.js +4 -0
  11. package/lib/content-type.js +4 -1
  12. package/lib/decorate.js +1 -1
  13. package/lib/log-controller.js +1 -1
  14. package/lib/reply.js +18 -4
  15. package/package.json +2 -3
  16. package/test/content-parser.test.js +48 -18
  17. package/test/content-type.test.js +4 -0
  18. package/test/decorator-instance-properties.test.js +49 -0
  19. package/test/helper.js +73 -1
  20. package/test/hooks-async.test.js +1 -2
  21. package/test/hooks.test.js +1 -2
  22. package/test/internals/reply.test.js +76 -1
  23. package/test/listen.1.test.js +17 -52
  24. package/test/listen.5.test.js +6 -9
  25. package/test/logger/instantiation.test.js +1 -1
  26. package/test/logger/logging.test.js +1 -1
  27. package/test/logger/request.test.js +1 -1
  28. package/test/logger/response.test.js +1 -1
  29. package/test/plugin.4.test.js +5 -4
  30. package/test/reply-media-type.test.js +131 -0
  31. package/test/reply-trailers.test.js +92 -4
  32. package/test/route-prefix.test.js +1 -1
  33. package/test/route.6.test.js +4 -11
  34. package/test/schema-feature.test.js +36 -65
  35. package/test/schema-serialization.test.js +1 -1
  36. package/test/schema-special-usage.test.js +1 -1
  37. package/test/schema-validation.test.js +1 -1
  38. package/test/stream.2.test.js +1 -1
  39. package/types/logger.d.ts +1 -1
  40. package/.pi/bash-confirm-whitelist.json +0 -180
  41. package/test/toolkit.js +0 -63
@@ -142,6 +142,37 @@ test('send trailers when payload is stream', (t, testDone) => {
142
142
  })
143
143
  })
144
144
 
145
+ test('remove trailer while stream is being consumed', (t, testDone) => {
146
+ t.plan(5)
147
+
148
+ const fastify = Fastify()
149
+
150
+ fastify.get('/', function (request, reply) {
151
+ const stream = Readable.from((function * () {
152
+ reply.removeTrailer('ETag')
153
+ yield 'hello'
154
+ })())
155
+
156
+ reply.trailer('ETag', function () {
157
+ t.assert.fail('removed trailer should not be called')
158
+ })
159
+
160
+ reply.send(stream)
161
+ })
162
+
163
+ fastify.inject({
164
+ method: 'GET',
165
+ url: '/'
166
+ }, (error, res) => {
167
+ t.assert.ifError(error)
168
+ t.assert.strictEqual(res.statusCode, 200)
169
+ t.assert.strictEqual(res.payload, 'hello')
170
+ t.assert.strictEqual(res.headers.trailer, 'etag')
171
+ t.assert.strictEqual(res.trailers.etag, undefined)
172
+ testDone()
173
+ })
174
+ })
175
+
145
176
  test('send trailers when using async-await', (t, testDone) => {
146
177
  t.plan(5)
147
178
 
@@ -395,10 +426,67 @@ test('remove all trailers', (t, testDone) => {
395
426
  }, (error, res) => {
396
427
  t.assert.ifError(error)
397
428
  t.assert.strictEqual(res.statusCode, 200)
398
- t.assert.ok(!res.headers.trailer)
399
- t.assert.ok(!res.trailers.etag)
400
- t.assert.ok(!res.trailers['should-not-call'])
401
- t.assert.ok(!res.headers['content-length'])
429
+ t.assert.strictEqual(res.headers.trailer, undefined)
430
+ t.assert.strictEqual(res.trailers.etag, undefined)
431
+ t.assert.strictEqual(res.trailers['should-not-call'], undefined)
432
+ t.assert.strictEqual(res.headers['content-length'], '0')
433
+ testDone()
434
+ })
435
+ })
436
+
437
+ test('remove some trailers should keep trailer mode for the remaining ones', (t, testDone) => {
438
+ t.plan(6)
439
+
440
+ const fastify = Fastify()
441
+
442
+ fastify.get('/', function (request, reply) {
443
+ reply.trailer('ETag', function () {
444
+ t.assert.fail('removed trailer should not be called')
445
+ })
446
+ reply.removeTrailer('ETag')
447
+ reply.trailer('Content-MD5', function (reply, payload, done) {
448
+ done(null, 'custom-md5')
449
+ })
450
+ reply.send('hello')
451
+ })
452
+
453
+ fastify.inject({
454
+ method: 'GET',
455
+ url: '/'
456
+ }, (error, res) => {
457
+ t.assert.ifError(error)
458
+ t.assert.strictEqual(res.statusCode, 200)
459
+ t.assert.strictEqual(res.headers.trailer, 'content-md5')
460
+ t.assert.strictEqual(res.headers['transfer-encoding'], 'chunked')
461
+ t.assert.strictEqual(res.headers['content-length'], undefined)
462
+ t.assert.strictEqual(res.trailers['content-md5'], 'custom-md5')
463
+ testDone()
464
+ })
465
+ })
466
+
467
+ test('remove all trailers should behave like no trailers were registered', (t, testDone) => {
468
+ t.plan(6)
469
+
470
+ const fastify = Fastify()
471
+
472
+ fastify.get('/', function (request, reply) {
473
+ reply.trailer('ETag', function () {
474
+ t.assert.fail('removed trailer should not be called')
475
+ })
476
+ reply.removeTrailer('ETag')
477
+ reply.send('hello')
478
+ })
479
+
480
+ fastify.inject({
481
+ method: 'GET',
482
+ url: '/'
483
+ }, (error, res) => {
484
+ t.assert.ifError(error)
485
+ t.assert.strictEqual(res.statusCode, 200)
486
+ t.assert.strictEqual(res.headers.trailer, undefined)
487
+ t.assert.strictEqual(res.headers['transfer-encoding'], undefined)
488
+ t.assert.strictEqual(res.headers['content-length'], '5')
489
+ t.assert.strictEqual(res.trailers.etag, undefined)
402
490
  testDone()
403
491
  })
404
492
  })
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { test } = require('node:test')
4
4
  const Fastify = require('..')
5
- const { waitForCb } = require('./toolkit')
5
+ const { waitForCb } = require('./helper')
6
6
 
7
7
  test('Prefix options should add a prefix for all the routes inside a register / 1', (t, testDone) => {
8
8
  t.plan(6)
@@ -4,6 +4,7 @@ const stream = require('node:stream')
4
4
  const { ReadableStream } = require('node:stream/web')
5
5
  const { test } = require('node:test')
6
6
  const Fastify = require('..')
7
+ const { assertNoWarning } = require('./helper')
7
8
 
8
9
  test('Creates a HEAD route for a GET one with prefixTrailingSlash', async (t) => {
9
10
  t.plan(1)
@@ -281,6 +282,8 @@ test('route onSend can be function or array of functions', async t => {
281
282
  })
282
283
 
283
284
  test('no warning for exposeHeadRoute', async t => {
285
+ assertNoWarning(t)
286
+
284
287
  const fastify = Fastify()
285
288
 
286
289
  fastify.route({
@@ -292,15 +295,5 @@ test('no warning for exposeHeadRoute', async t => {
292
295
  }
293
296
  })
294
297
 
295
- const listener = (w) => {
296
- t.assert.fail('no warning')
297
- }
298
-
299
- process.on('warning', listener)
300
-
301
- await fastify.listen({ port: 0 })
302
-
303
- process.removeListener('warning', listener)
304
-
305
- await fastify.close()
298
+ await fastify.ready()
306
299
  })
@@ -1,13 +1,14 @@
1
1
  'use strict'
2
2
 
3
3
  const { test } = require('node:test')
4
- const Fastify = require('..')
5
4
  const fp = require('fastify-plugin')
5
+ const { spyWarning } = require('process-warning')
6
+ const Fastify = require('..')
6
7
  const deepClone = require('rfdc')({ circles: true, proto: false })
7
8
  const Ajv = require('ajv')
8
9
  const { kSchemaController } = require('../lib/symbols.js')
9
10
  const { FSTWRN001 } = require('../lib/warnings')
10
- const { waitForCb } = require('./toolkit')
11
+ const { waitForCb } = require('./helper')
11
12
 
12
13
  const echoParams = (req, reply) => { reply.send(req.params) }
13
14
  const echoBody = (req, reply) => { reply.send(req.body) }
@@ -320,18 +321,10 @@ test('Should not change the input schemas', (t, testDone) => {
320
321
 
321
322
  test('Should emit warning if the schema headers is undefined', (t, testDone) => {
322
323
  t.plan(4)
323
- const fastify = Fastify()
324
+ const spyData = spyWarning(FSTWRN001)
325
+ t.after(spyData.restore)
324
326
 
325
- process.on('warning', onWarning)
326
- function onWarning (warning) {
327
- t.assert.strictEqual(warning.name, 'FastifyWarning')
328
- t.assert.strictEqual(warning.code, FSTWRN001.code)
329
- }
330
-
331
- t.after(() => {
332
- process.removeListener('warning', onWarning)
333
- FSTWRN001.emitted = false
334
- })
327
+ const fastify = Fastify()
335
328
 
336
329
  fastify.post('/:id', {
337
330
  handler: echoParams,
@@ -346,24 +339,18 @@ test('Should emit warning if the schema headers is undefined', (t, testDone) =>
346
339
  }, (error, res) => {
347
340
  t.assert.ifError(error)
348
341
  t.assert.strictEqual(res.statusCode, 200)
342
+ t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['headers', 'POST', '/:id'], result: true }])
343
+ t.assert.strictEqual(spyData.callCount(), 1)
349
344
  testDone()
350
345
  })
351
346
  })
352
347
 
353
348
  test('Should emit warning if the schema body is undefined', (t, testDone) => {
354
349
  t.plan(4)
355
- const fastify = Fastify()
356
-
357
- process.on('warning', onWarning)
358
- function onWarning (warning) {
359
- t.assert.strictEqual(warning.name, 'FastifyWarning')
360
- t.assert.strictEqual(warning.code, FSTWRN001.code)
361
- }
350
+ const spyData = spyWarning(FSTWRN001)
351
+ t.after(spyData.restore)
362
352
 
363
- t.after(() => {
364
- process.removeListener('warning', onWarning)
365
- FSTWRN001.emitted = false
366
- })
353
+ const fastify = Fastify()
367
354
 
368
355
  fastify.post('/:id', {
369
356
  handler: echoParams,
@@ -378,24 +365,18 @@ test('Should emit warning if the schema body is undefined', (t, testDone) => {
378
365
  }, (error, res) => {
379
366
  t.assert.ifError(error)
380
367
  t.assert.strictEqual(res.statusCode, 200)
368
+ t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['body', 'POST', '/:id'], result: true }])
369
+ t.assert.strictEqual(spyData.callCount(), 1)
381
370
  testDone()
382
371
  })
383
372
  })
384
373
 
385
374
  test('Should emit warning if the schema query is undefined', (t, testDone) => {
386
375
  t.plan(4)
387
- const fastify = Fastify()
376
+ const spyData = spyWarning(FSTWRN001)
377
+ t.after(spyData.restore)
388
378
 
389
- process.on('warning', onWarning)
390
- function onWarning (warning) {
391
- t.assert.strictEqual(warning.name, 'FastifyWarning')
392
- t.assert.strictEqual(warning.code, FSTWRN001.code)
393
- }
394
-
395
- t.after(() => {
396
- process.removeListener('warning', onWarning)
397
- FSTWRN001.emitted = false
398
- })
379
+ const fastify = Fastify()
399
380
 
400
381
  fastify.post('/:id', {
401
382
  handler: echoParams,
@@ -410,24 +391,18 @@ test('Should emit warning if the schema query is undefined', (t, testDone) => {
410
391
  }, (error, res) => {
411
392
  t.assert.ifError(error)
412
393
  t.assert.strictEqual(res.statusCode, 200)
394
+ t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['querystring', 'POST', '/:id'], result: true }])
395
+ t.assert.strictEqual(spyData.callCount(), 1)
413
396
  testDone()
414
397
  })
415
398
  })
416
399
 
417
400
  test('Should emit warning if the schema params is undefined', (t, testDone) => {
418
401
  t.plan(4)
419
- const fastify = Fastify()
402
+ const spyData = spyWarning(FSTWRN001)
403
+ t.after(spyData.restore)
420
404
 
421
- process.on('warning', onWarning)
422
- function onWarning (warning) {
423
- t.assert.strictEqual(warning.name, 'FastifyWarning')
424
- t.assert.strictEqual(warning.code, FSTWRN001.code)
425
- }
426
-
427
- t.after(() => {
428
- process.removeListener('warning', onWarning)
429
- FSTWRN001.emitted = false
430
- })
405
+ const fastify = Fastify()
431
406
 
432
407
  fastify.post('/:id', {
433
408
  handler: echoParams,
@@ -442,31 +417,18 @@ test('Should emit warning if the schema params is undefined', (t, testDone) => {
442
417
  }, (error, res) => {
443
418
  t.assert.ifError(error)
444
419
  t.assert.strictEqual(res.statusCode, 200)
420
+ t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['params', 'POST', '/:id'], result: true }])
421
+ t.assert.strictEqual(spyData.callCount(), 1)
445
422
  testDone()
446
423
  })
447
424
  })
448
425
 
449
426
  test('Should emit a warning for every route with undefined schema', (t, testDone) => {
450
- t.plan(16)
451
- const fastify = Fastify()
452
-
453
- let runs = 0
454
- const expectedWarningEmitted = [0, 1, 2, 3]
455
- // It emits 4 warnings:
456
- // - 2 - GET and HEAD for /undefinedParams/:id
457
- // - 2 - GET and HEAD for /undefinedBody/:id
458
- // => 3 x 4 assertions = 12 assertions
459
- function onWarning (warning) {
460
- t.assert.strictEqual(warning.name, 'FastifyWarning')
461
- t.assert.strictEqual(warning.code, FSTWRN001.code)
462
- t.assert.strictEqual(runs++, expectedWarningEmitted.shift())
463
- }
427
+ t.plan(9)
428
+ const spyData = spyWarning(FSTWRN001)
429
+ t.after(spyData.restore)
464
430
 
465
- process.on('warning', onWarning)
466
- t.after(() => {
467
- process.removeListener('warning', onWarning)
468
- FSTWRN001.emitted = false
469
- })
431
+ const fastify = Fastify()
470
432
 
471
433
  fastify.get('/undefinedParams/:id', {
472
434
  handler: echoParams,
@@ -496,6 +458,15 @@ test('Should emit a warning for every route with undefined schema', (t, testDone
496
458
  }, (error, res) => {
497
459
  t.assert.ifError(error)
498
460
  t.assert.strictEqual(res.statusCode, 200)
461
+ // fastify.inject run in series
462
+ // last callback recieve all warnings at once
463
+ // GET /undefinedParams/123
464
+ t.assert.deepStrictEqual(spyData.calls[0], { arguments: ['params', 'GET', '/undefinedParams/:id'], result: true })
465
+ t.assert.deepStrictEqual(spyData.calls[1], { arguments: ['params', 'HEAD', '/undefinedParams/:id'], result: true })
466
+ // GET /undefinedBody/123
467
+ t.assert.deepStrictEqual(spyData.calls[2], { arguments: ['body', 'GET', '/undefinedBody/:id'], result: true })
468
+ t.assert.deepStrictEqual(spyData.calls[3], { arguments: ['body', 'HEAD', '/undefinedBody/:id'], result: true })
469
+ t.assert.strictEqual(spyData.callCount(), 4)
499
470
  testDone()
500
471
  })
501
472
  })
@@ -3,7 +3,7 @@
3
3
  const { test } = require('node:test')
4
4
  const Fastify = require('..')
5
5
  const ContentType = require('../lib/content-type')
6
- const { waitForCb } = require('./toolkit')
6
+ const { waitForCb } = require('./helper')
7
7
 
8
8
  const echoBody = (req, reply) => { reply.send(req.body) }
9
9
 
@@ -9,7 +9,7 @@ const Fastify = require('..')
9
9
  const ajvMergePatch = require('ajv-merge-patch')
10
10
  const ajvErrors = require('ajv-errors')
11
11
  const proxyquire = require('proxyquire')
12
- const { waitForCb } = require('./toolkit')
12
+ const { waitForCb } = require('./helper')
13
13
 
14
14
  test('Ajv plugins array parameter', (t, testDone) => {
15
15
  t.plan(3)
@@ -5,7 +5,7 @@ const Fastify = require('..')
5
5
 
6
6
  const AJV = require('ajv')
7
7
  const Schema = require('fluent-json-schema')
8
- const { waitForCb } = require('./toolkit')
8
+ const { waitForCb } = require('./helper')
9
9
  const { kRequestContentType } = require('../lib/symbols')
10
10
 
11
11
  const customSchemaCompilers = {
@@ -7,7 +7,7 @@ const resolve = require('node:path').resolve
7
7
  const zlib = require('node:zlib')
8
8
  const pipeline = require('node:stream').pipeline
9
9
  const Fastify = require('..')
10
- const { waitForCb } = require('./toolkit')
10
+ const { waitForCb } = require('./helper')
11
11
 
12
12
  test('onSend hook stream', t => {
13
13
  t.plan(4)
package/types/logger.d.ts CHANGED
@@ -124,7 +124,7 @@ export declare class LogController {
124
124
  isLogDisabled (request: FastifyRequest): boolean
125
125
  incomingRequest (request: FastifyRequest, reply: FastifyReply, metadata?: Record<string, unknown>): void
126
126
  requestCompleted (
127
- error: Error | null,
127
+ error: Error | null | undefined,
128
128
  request: FastifyRequest,
129
129
  reply: FastifyReply,
130
130
  metadata?: Record<string, unknown>
@@ -1,180 +0,0 @@
1
- {
2
- "entries": [
3
- {
4
- "type": "exact",
5
- "value": "git fetch --all --prune --quiet && echo FETCH_OK || echo FETCH_FAILED",
6
- "addedAt": "2026-02-07T10:03:03.909Z",
7
- "note": "Always accept",
8
- "source": "user"
9
- },
10
- {
11
- "type": "exact",
12
- "value": "npm install --ignore-scripts",
13
- "addedAt": "2026-02-07T10:21:13.104Z",
14
- "note": "Always accept",
15
- "source": "user"
16
- },
17
- {
18
- "type": "exact",
19
- "value": "npm run coverage:ci-check-coverage",
20
- "addedAt": "2026-02-07T15:10:08.412Z",
21
- "note": "Always accept",
22
- "source": "user"
23
- },
24
- {
25
- "type": "exact",
26
- "value": "node --test test/web-api.test.js",
27
- "addedAt": "2026-02-07T15:24:46.872Z",
28
- "note": "Always accept",
29
- "source": "user"
30
- },
31
- {
32
- "type": "exact",
33
- "value": "gpg --list-secret-keys --keyid-format=long || true && git config --get user.signingkey && git config --get commit.gpgsign",
34
- "addedAt": "2026-02-11T04:42:44.716Z",
35
- "note": "Always accept",
36
- "source": "user"
37
- },
38
- {
39
- "type": "exact",
40
- "value": "cd /home/matteo/repositories/fastify && npm run test:typescript",
41
- "addedAt": "2026-02-19T20:22:59.298Z",
42
- "note": "Always accept",
43
- "source": "user"
44
- },
45
- {
46
- "type": "exact",
47
- "value": "cd /home/matteo/repositories/fastify && git checkout -- types/route.d.ts",
48
- "addedAt": "2026-02-19T20:23:45.912Z",
49
- "note": "Always accept",
50
- "source": "user"
51
- },
52
- {
53
- "type": "exact",
54
- "value": "cd /home/matteo/repositories/fastify && git status --short && git diff -- types/route.d.ts test/types/route.test-d.ts | sed -n '1,220p'",
55
- "addedAt": "2026-02-19T20:25:10.175Z",
56
- "note": "Always accept",
57
- "source": "user"
58
- },
59
- {
60
- "type": "exact",
61
- "value": "cd /home/matteo/repositories/fastify && git checkout -- types/route.d.ts test/types/route.test-d.ts && npm run test:typescript",
62
- "addedAt": "2026-02-19T20:25:56.421Z",
63
- "note": "Always accept",
64
- "source": "user"
65
- },
66
- {
67
- "type": "exact",
68
- "value": "cd /home/matteo/repositories/fastify && npm run lint:eslint -- types/route.d.ts test/types/route.test-d.ts",
69
- "addedAt": "2026-02-19T20:27:12.547Z",
70
- "note": "Always accept",
71
- "source": "user"
72
- },
73
- {
74
- "type": "exact",
75
- "value": "cd /home/matteo/repositories/fastify && git checkout -b fix/6513-route-hook-meta-return",
76
- "addedAt": "2026-02-19T20:27:31.569Z",
77
- "note": "Always accept",
78
- "source": "user"
79
- },
80
- {
81
- "type": "exact",
82
- "value": "cd /home/matteo/repositories/fastify && git add types/route.d.ts test/types/route.test-d.ts && git commit -m \"fix(types): allow async route hooks in shorthand options\"",
83
- "addedAt": "2026-02-19T20:27:35.261Z",
84
- "note": "Always accept",
85
- "source": "user"
86
- },
87
- {
88
- "type": "exact",
89
- "value": "cd /home/matteo/repositories/fastify && npx tsc /tmp/ctx-test2.ts --noEmit --strict",
90
- "addedAt": "2026-02-19T20:44:45.451Z",
91
- "note": "Always accept",
92
- "source": "user"
93
- },
94
- {
95
- "type": "exact",
96
- "value": "cd /home/matteo/repositories/fastify && npx tsd --files test/types/hooks.test-d.ts",
97
- "addedAt": "2026-02-19T20:47:16.815Z",
98
- "note": "Always accept",
99
- "source": "user"
100
- },
101
- {
102
- "type": "exact",
103
- "value": "cd /home/matteo/repositories/fastify && npx tsd --files test/types/route.test-d.ts",
104
- "addedAt": "2026-02-19T20:47:37.607Z",
105
- "note": "Always accept",
106
- "source": "user"
107
- },
108
- {
109
- "type": "exact",
110
- "value": "cd /home/matteo/repositories/fastify && npx tsd",
111
- "addedAt": "2026-02-19T20:48:17.812Z",
112
- "note": "Always accept",
113
- "source": "user"
114
- },
115
- {
116
- "type": "exact",
117
- "value": "cd /home/matteo/repositories/fastify && npm run lint:eslint -- types/route.d.ts && npx tsd --files test/types/hooks.test-d.ts && npx tsd --files test/types/route.test-d.ts",
118
- "addedAt": "2026-02-19T20:51:05.173Z",
119
- "note": "Always accept",
120
- "source": "user"
121
- },
122
- {
123
- "type": "exact",
124
- "value": "which gh && gh --version && gh auth status",
125
- "addedAt": "2026-02-20T07:03:43.023Z",
126
- "note": "Always accept",
127
- "source": "user"
128
- },
129
- {
130
- "type": "exact",
131
- "value": "gh api repos/fastify/fastify/rulesets",
132
- "addedAt": "2026-02-22T22:29:22.020Z",
133
- "note": "Always accept",
134
- "source": "user"
135
- },
136
- {
137
- "type": "exact",
138
- "value": "node --test test/fastify-instance.test.js",
139
- "addedAt": "2026-02-23T07:23:34.147Z",
140
- "note": "Always accept",
141
- "source": "user"
142
- },
143
- {
144
- "type": "exact",
145
- "value": "nl -ba scripts/validate-ecosystem-links.js | sed -n '1,220p'",
146
- "addedAt": "2026-02-23T07:23:55.927Z",
147
- "note": "Always accept",
148
- "source": "user"
149
- },
150
- {
151
- "type": "pattern",
152
- "value": "^npx\\s+eslint\\s+[\\w./~-]+\\s+[\\w./~-]+\\s+[\\w./~-]+\\s+[\\w./~-]+$",
153
- "addedAt": "2026-03-09T12:41:11.065Z",
154
- "note": "Always accept generic",
155
- "source": "ai"
156
- },
157
- {
158
- "type": "pattern",
159
- "value": "^node\\s+--test\\s+[\\w./~-]+\\s+[\\w./~-]+$",
160
- "addedAt": "2026-03-09T12:41:58.425Z",
161
- "note": "Always accept generic",
162
- "source": "ai"
163
- },
164
- {
165
- "type": "pattern",
166
- "value": "^npm\\s+run\\s+test:typescript$",
167
- "addedAt": "2026-03-09T12:42:48.283Z",
168
- "note": "Always accept generic",
169
- "source": "ai"
170
- },
171
- {
172
- "type": "exact",
173
- "value": "gh search code \"request.host org:fastify\" --limit 20 --json repository,path,url",
174
- "addedAt": "2026-03-09T21:03:22.953Z",
175
- "note": "Always accept exact",
176
- "source": "user"
177
- }
178
- ],
179
- "version": 2
180
- }
package/test/toolkit.js DELETED
@@ -1,63 +0,0 @@
1
- 'use strict'
2
-
3
- exports.waitForCb = function (options) {
4
- let count = null
5
- let done = false
6
- let iResolve
7
- let iReject
8
-
9
- function stepIn () {
10
- if (done) {
11
- iReject(new Error('Unexpected done call'))
12
- return
13
- }
14
-
15
- if (--count) {
16
- return
17
- }
18
-
19
- done = true
20
- iResolve()
21
- }
22
-
23
- const patience = new Promise((resolve, reject) => {
24
- iResolve = resolve
25
- iReject = reject
26
- })
27
-
28
- count = options.steps || 1
29
- done = false
30
-
31
- return { stepIn, patience }
32
- }
33
-
34
- exports.partialDeepStrictEqual = function partialDeepStrictEqual (actual, expected) {
35
- if (typeof expected !== 'object' || expected === null) {
36
- return actual === expected
37
- }
38
-
39
- if (typeof actual !== 'object' || actual === null) {
40
- return false
41
- }
42
-
43
- if (Array.isArray(expected)) {
44
- if (!Array.isArray(actual)) return false
45
- if (expected.length > actual.length) return false
46
-
47
- for (let i = 0; i < expected.length; i++) {
48
- if (!partialDeepStrictEqual(actual[i], expected[i])) {
49
- return false
50
- }
51
- }
52
- return true
53
- }
54
-
55
- for (const key of Object.keys(expected)) {
56
- if (!(key in actual)) return false
57
- if (!partialDeepStrictEqual(actual[key], expected[key])) {
58
- return false
59
- }
60
- }
61
-
62
- return true
63
- }