+1279
package-lock.json
+1279
package-lock.json
···
12
12
},
13
13
"devDependencies": {
14
14
"@cloudflare/vitest-pool-workers": "^0.11.1",
15
+
"@vitest/coverage-istanbul": "^3.2.4",
16
+
"@vitest/coverage-v8": "^3.2.4",
15
17
"typescript": "^5.5.2",
16
18
"vitest": "^3.2.4",
17
19
"wrangler": "^4.21.2"
20
+
}
21
+
},
22
+
"node_modules/@ampproject/remapping": {
23
+
"version": "2.3.0",
24
+
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
25
+
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
26
+
"dev": true,
27
+
"license": "Apache-2.0",
28
+
"dependencies": {
29
+
"@jridgewell/gen-mapping": "^0.3.5",
30
+
"@jridgewell/trace-mapping": "^0.3.24"
31
+
},
32
+
"engines": {
33
+
"node": ">=6.0.0"
34
+
}
35
+
},
36
+
"node_modules/@ampproject/remapping/node_modules/@jridgewell/trace-mapping": {
37
+
"version": "0.3.31",
38
+
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
39
+
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
40
+
"dev": true,
41
+
"license": "MIT",
42
+
"dependencies": {
43
+
"@jridgewell/resolve-uri": "^3.1.0",
44
+
"@jridgewell/sourcemap-codec": "^1.4.14"
45
+
}
46
+
},
47
+
"node_modules/@babel/code-frame": {
48
+
"version": "7.27.1",
49
+
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
50
+
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
51
+
"dev": true,
52
+
"license": "MIT",
53
+
"dependencies": {
54
+
"@babel/helper-validator-identifier": "^7.27.1",
55
+
"js-tokens": "^4.0.0",
56
+
"picocolors": "^1.1.1"
57
+
},
58
+
"engines": {
59
+
"node": ">=6.9.0"
60
+
}
61
+
},
62
+
"node_modules/@babel/code-frame/node_modules/js-tokens": {
63
+
"version": "4.0.0",
64
+
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
65
+
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
66
+
"dev": true,
67
+
"license": "MIT"
68
+
},
69
+
"node_modules/@babel/compat-data": {
70
+
"version": "7.28.5",
71
+
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz",
72
+
"integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==",
73
+
"dev": true,
74
+
"license": "MIT",
75
+
"engines": {
76
+
"node": ">=6.9.0"
77
+
}
78
+
},
79
+
"node_modules/@babel/core": {
80
+
"version": "7.28.5",
81
+
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
82
+
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
83
+
"dev": true,
84
+
"license": "MIT",
85
+
"dependencies": {
86
+
"@babel/code-frame": "^7.27.1",
87
+
"@babel/generator": "^7.28.5",
88
+
"@babel/helper-compilation-targets": "^7.27.2",
89
+
"@babel/helper-module-transforms": "^7.28.3",
90
+
"@babel/helpers": "^7.28.4",
91
+
"@babel/parser": "^7.28.5",
92
+
"@babel/template": "^7.27.2",
93
+
"@babel/traverse": "^7.28.5",
94
+
"@babel/types": "^7.28.5",
95
+
"@jridgewell/remapping": "^2.3.5",
96
+
"convert-source-map": "^2.0.0",
97
+
"debug": "^4.1.0",
98
+
"gensync": "^1.0.0-beta.2",
99
+
"json5": "^2.2.3",
100
+
"semver": "^6.3.1"
101
+
},
102
+
"engines": {
103
+
"node": ">=6.9.0"
104
+
},
105
+
"funding": {
106
+
"type": "opencollective",
107
+
"url": "https://opencollective.com/babel"
108
+
}
109
+
},
110
+
"node_modules/@babel/core/node_modules/semver": {
111
+
"version": "6.3.1",
112
+
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
113
+
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
114
+
"dev": true,
115
+
"license": "ISC",
116
+
"bin": {
117
+
"semver": "bin/semver.js"
118
+
}
119
+
},
120
+
"node_modules/@babel/generator": {
121
+
"version": "7.28.5",
122
+
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
123
+
"integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
124
+
"dev": true,
125
+
"license": "MIT",
126
+
"dependencies": {
127
+
"@babel/parser": "^7.28.5",
128
+
"@babel/types": "^7.28.5",
129
+
"@jridgewell/gen-mapping": "^0.3.12",
130
+
"@jridgewell/trace-mapping": "^0.3.28",
131
+
"jsesc": "^3.0.2"
132
+
},
133
+
"engines": {
134
+
"node": ">=6.9.0"
135
+
}
136
+
},
137
+
"node_modules/@babel/generator/node_modules/@jridgewell/trace-mapping": {
138
+
"version": "0.3.31",
139
+
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
140
+
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
141
+
"dev": true,
142
+
"license": "MIT",
143
+
"dependencies": {
144
+
"@jridgewell/resolve-uri": "^3.1.0",
145
+
"@jridgewell/sourcemap-codec": "^1.4.14"
146
+
}
147
+
},
148
+
"node_modules/@babel/helper-compilation-targets": {
149
+
"version": "7.27.2",
150
+
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
151
+
"integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
152
+
"dev": true,
153
+
"license": "MIT",
154
+
"dependencies": {
155
+
"@babel/compat-data": "^7.27.2",
156
+
"@babel/helper-validator-option": "^7.27.1",
157
+
"browserslist": "^4.24.0",
158
+
"lru-cache": "^5.1.1",
159
+
"semver": "^6.3.1"
160
+
},
161
+
"engines": {
162
+
"node": ">=6.9.0"
163
+
}
164
+
},
165
+
"node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": {
166
+
"version": "5.1.1",
167
+
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
168
+
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
169
+
"dev": true,
170
+
"license": "ISC",
171
+
"dependencies": {
172
+
"yallist": "^3.0.2"
173
+
}
174
+
},
175
+
"node_modules/@babel/helper-compilation-targets/node_modules/semver": {
176
+
"version": "6.3.1",
177
+
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
178
+
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
179
+
"dev": true,
180
+
"license": "ISC",
181
+
"bin": {
182
+
"semver": "bin/semver.js"
183
+
}
184
+
},
185
+
"node_modules/@babel/helper-globals": {
186
+
"version": "7.28.0",
187
+
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
188
+
"integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
189
+
"dev": true,
190
+
"license": "MIT",
191
+
"engines": {
192
+
"node": ">=6.9.0"
193
+
}
194
+
},
195
+
"node_modules/@babel/helper-module-imports": {
196
+
"version": "7.27.1",
197
+
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
198
+
"integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
199
+
"dev": true,
200
+
"license": "MIT",
201
+
"dependencies": {
202
+
"@babel/traverse": "^7.27.1",
203
+
"@babel/types": "^7.27.1"
204
+
},
205
+
"engines": {
206
+
"node": ">=6.9.0"
207
+
}
208
+
},
209
+
"node_modules/@babel/helper-module-transforms": {
210
+
"version": "7.28.3",
211
+
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
212
+
"integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
213
+
"dev": true,
214
+
"license": "MIT",
215
+
"dependencies": {
216
+
"@babel/helper-module-imports": "^7.27.1",
217
+
"@babel/helper-validator-identifier": "^7.27.1",
218
+
"@babel/traverse": "^7.28.3"
219
+
},
220
+
"engines": {
221
+
"node": ">=6.9.0"
222
+
},
223
+
"peerDependencies": {
224
+
"@babel/core": "^7.0.0"
225
+
}
226
+
},
227
+
"node_modules/@babel/helper-string-parser": {
228
+
"version": "7.27.1",
229
+
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
230
+
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
231
+
"dev": true,
232
+
"license": "MIT",
233
+
"engines": {
234
+
"node": ">=6.9.0"
235
+
}
236
+
},
237
+
"node_modules/@babel/helper-validator-identifier": {
238
+
"version": "7.28.5",
239
+
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
240
+
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
241
+
"dev": true,
242
+
"license": "MIT",
243
+
"engines": {
244
+
"node": ">=6.9.0"
245
+
}
246
+
},
247
+
"node_modules/@babel/helper-validator-option": {
248
+
"version": "7.27.1",
249
+
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
250
+
"integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
251
+
"dev": true,
252
+
"license": "MIT",
253
+
"engines": {
254
+
"node": ">=6.9.0"
255
+
}
256
+
},
257
+
"node_modules/@babel/helpers": {
258
+
"version": "7.28.4",
259
+
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
260
+
"integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
261
+
"dev": true,
262
+
"license": "MIT",
263
+
"dependencies": {
264
+
"@babel/template": "^7.27.2",
265
+
"@babel/types": "^7.28.4"
266
+
},
267
+
"engines": {
268
+
"node": ">=6.9.0"
269
+
}
270
+
},
271
+
"node_modules/@babel/parser": {
272
+
"version": "7.28.5",
273
+
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
274
+
"integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
275
+
"dev": true,
276
+
"license": "MIT",
277
+
"dependencies": {
278
+
"@babel/types": "^7.28.5"
279
+
},
280
+
"bin": {
281
+
"parser": "bin/babel-parser.js"
282
+
},
283
+
"engines": {
284
+
"node": ">=6.0.0"
285
+
}
286
+
},
287
+
"node_modules/@babel/template": {
288
+
"version": "7.27.2",
289
+
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
290
+
"integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
291
+
"dev": true,
292
+
"license": "MIT",
293
+
"dependencies": {
294
+
"@babel/code-frame": "^7.27.1",
295
+
"@babel/parser": "^7.27.2",
296
+
"@babel/types": "^7.27.1"
297
+
},
298
+
"engines": {
299
+
"node": ">=6.9.0"
300
+
}
301
+
},
302
+
"node_modules/@babel/traverse": {
303
+
"version": "7.28.5",
304
+
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz",
305
+
"integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
306
+
"dev": true,
307
+
"license": "MIT",
308
+
"dependencies": {
309
+
"@babel/code-frame": "^7.27.1",
310
+
"@babel/generator": "^7.28.5",
311
+
"@babel/helper-globals": "^7.28.0",
312
+
"@babel/parser": "^7.28.5",
313
+
"@babel/template": "^7.27.2",
314
+
"@babel/types": "^7.28.5",
315
+
"debug": "^4.3.1"
316
+
},
317
+
"engines": {
318
+
"node": ">=6.9.0"
319
+
}
320
+
},
321
+
"node_modules/@babel/types": {
322
+
"version": "7.28.5",
323
+
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
324
+
"integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
325
+
"dev": true,
326
+
"license": "MIT",
327
+
"dependencies": {
328
+
"@babel/helper-string-parser": "^7.27.1",
329
+
"@babel/helper-validator-identifier": "^7.28.5"
330
+
},
331
+
"engines": {
332
+
"node": ">=6.9.0"
333
+
}
334
+
},
335
+
"node_modules/@bcoe/v8-coverage": {
336
+
"version": "1.0.2",
337
+
"resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz",
338
+
"integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==",
339
+
"dev": true,
340
+
"license": "MIT",
341
+
"engines": {
342
+
"node": ">=18"
18
343
}
19
344
},
20
345
"node_modules/@cloudflare/kv-asset-handler": {
···
1467
1792
"url": "https://opencollective.com/libvips"
1468
1793
}
1469
1794
},
1795
+
"node_modules/@isaacs/cliui": {
1796
+
"version": "8.0.2",
1797
+
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
1798
+
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
1799
+
"dev": true,
1800
+
"license": "ISC",
1801
+
"dependencies": {
1802
+
"string-width": "^5.1.2",
1803
+
"string-width-cjs": "npm:string-width@^4.2.0",
1804
+
"strip-ansi": "^7.0.1",
1805
+
"strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
1806
+
"wrap-ansi": "^8.1.0",
1807
+
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
1808
+
},
1809
+
"engines": {
1810
+
"node": ">=12"
1811
+
}
1812
+
},
1813
+
"node_modules/@istanbuljs/schema": {
1814
+
"version": "0.1.3",
1815
+
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
1816
+
"integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
1817
+
"dev": true,
1818
+
"license": "MIT",
1819
+
"engines": {
1820
+
"node": ">=8"
1821
+
}
1822
+
},
1823
+
"node_modules/@jridgewell/gen-mapping": {
1824
+
"version": "0.3.13",
1825
+
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
1826
+
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
1827
+
"dev": true,
1828
+
"license": "MIT",
1829
+
"dependencies": {
1830
+
"@jridgewell/sourcemap-codec": "^1.5.0",
1831
+
"@jridgewell/trace-mapping": "^0.3.24"
1832
+
}
1833
+
},
1834
+
"node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": {
1835
+
"version": "0.3.31",
1836
+
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
1837
+
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
1838
+
"dev": true,
1839
+
"license": "MIT",
1840
+
"dependencies": {
1841
+
"@jridgewell/resolve-uri": "^3.1.0",
1842
+
"@jridgewell/sourcemap-codec": "^1.4.14"
1843
+
}
1844
+
},
1845
+
"node_modules/@jridgewell/remapping": {
1846
+
"version": "2.3.5",
1847
+
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
1848
+
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
1849
+
"dev": true,
1850
+
"license": "MIT",
1851
+
"dependencies": {
1852
+
"@jridgewell/gen-mapping": "^0.3.5",
1853
+
"@jridgewell/trace-mapping": "^0.3.24"
1854
+
}
1855
+
},
1856
+
"node_modules/@jridgewell/remapping/node_modules/@jridgewell/trace-mapping": {
1857
+
"version": "0.3.31",
1858
+
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
1859
+
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
1860
+
"dev": true,
1861
+
"license": "MIT",
1862
+
"dependencies": {
1863
+
"@jridgewell/resolve-uri": "^3.1.0",
1864
+
"@jridgewell/sourcemap-codec": "^1.4.14"
1865
+
}
1866
+
},
1470
1867
"node_modules/@jridgewell/resolve-uri": {
1471
1868
"version": "3.1.2",
1472
1869
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
···
1493
1890
"dependencies": {
1494
1891
"@jridgewell/resolve-uri": "^3.0.3",
1495
1892
"@jridgewell/sourcemap-codec": "^1.4.10"
1893
+
}
1894
+
},
1895
+
"node_modules/@pkgjs/parseargs": {
1896
+
"version": "0.11.0",
1897
+
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
1898
+
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
1899
+
"dev": true,
1900
+
"license": "MIT",
1901
+
"optional": true,
1902
+
"engines": {
1903
+
"node": ">=14"
1496
1904
}
1497
1905
},
1498
1906
"node_modules/@poppinss/colors": {
···
1877
2285
"dev": true,
1878
2286
"license": "MIT"
1879
2287
},
2288
+
"node_modules/@vitest/coverage-istanbul": {
2289
+
"version": "3.2.4",
2290
+
"resolved": "https://registry.npmjs.org/@vitest/coverage-istanbul/-/coverage-istanbul-3.2.4.tgz",
2291
+
"integrity": "sha512-IDlpuFJiWU9rhcKLkpzj8mFu/lpe64gVgnV15ZOrYx1iFzxxrxCzbExiUEKtwwXRvEiEMUS6iZeYgnMxgbqbxQ==",
2292
+
"dev": true,
2293
+
"license": "MIT",
2294
+
"dependencies": {
2295
+
"@istanbuljs/schema": "^0.1.3",
2296
+
"debug": "^4.4.1",
2297
+
"istanbul-lib-coverage": "^3.2.2",
2298
+
"istanbul-lib-instrument": "^6.0.3",
2299
+
"istanbul-lib-report": "^3.0.1",
2300
+
"istanbul-lib-source-maps": "^5.0.6",
2301
+
"istanbul-reports": "^3.1.7",
2302
+
"magicast": "^0.3.5",
2303
+
"test-exclude": "^7.0.1",
2304
+
"tinyrainbow": "^2.0.0"
2305
+
},
2306
+
"funding": {
2307
+
"url": "https://opencollective.com/vitest"
2308
+
},
2309
+
"peerDependencies": {
2310
+
"vitest": "3.2.4"
2311
+
}
2312
+
},
2313
+
"node_modules/@vitest/coverage-v8": {
2314
+
"version": "3.2.4",
2315
+
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz",
2316
+
"integrity": "sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==",
2317
+
"dev": true,
2318
+
"license": "MIT",
2319
+
"dependencies": {
2320
+
"@ampproject/remapping": "^2.3.0",
2321
+
"@bcoe/v8-coverage": "^1.0.2",
2322
+
"ast-v8-to-istanbul": "^0.3.3",
2323
+
"debug": "^4.4.1",
2324
+
"istanbul-lib-coverage": "^3.2.2",
2325
+
"istanbul-lib-report": "^3.0.1",
2326
+
"istanbul-lib-source-maps": "^5.0.6",
2327
+
"istanbul-reports": "^3.1.7",
2328
+
"magic-string": "^0.30.17",
2329
+
"magicast": "^0.3.5",
2330
+
"std-env": "^3.9.0",
2331
+
"test-exclude": "^7.0.1",
2332
+
"tinyrainbow": "^2.0.0"
2333
+
},
2334
+
"funding": {
2335
+
"url": "https://opencollective.com/vitest"
2336
+
},
2337
+
"peerDependencies": {
2338
+
"@vitest/browser": "3.2.4",
2339
+
"vitest": "3.2.4"
2340
+
},
2341
+
"peerDependenciesMeta": {
2342
+
"@vitest/browser": {
2343
+
"optional": true
2344
+
}
2345
+
}
2346
+
},
1880
2347
"node_modules/@vitest/expect": {
1881
2348
"version": "3.2.4",
1882
2349
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz",
···
2015
2482
"node": ">=0.4.0"
2016
2483
}
2017
2484
},
2485
+
"node_modules/ansi-regex": {
2486
+
"version": "6.2.2",
2487
+
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
2488
+
"integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
2489
+
"dev": true,
2490
+
"license": "MIT",
2491
+
"engines": {
2492
+
"node": ">=12"
2493
+
},
2494
+
"funding": {
2495
+
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
2496
+
}
2497
+
},
2498
+
"node_modules/ansi-styles": {
2499
+
"version": "6.2.3",
2500
+
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
2501
+
"integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
2502
+
"dev": true,
2503
+
"license": "MIT",
2504
+
"engines": {
2505
+
"node": ">=12"
2506
+
},
2507
+
"funding": {
2508
+
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
2509
+
}
2510
+
},
2018
2511
"node_modules/assertion-error": {
2019
2512
"version": "2.0.1",
2020
2513
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
···
2025
2518
"node": ">=12"
2026
2519
}
2027
2520
},
2521
+
"node_modules/ast-v8-to-istanbul": {
2522
+
"version": "0.3.10",
2523
+
"resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.10.tgz",
2524
+
"integrity": "sha512-p4K7vMz2ZSk3wN8l5o3y2bJAoZXT3VuJI5OLTATY/01CYWumWvwkUw0SqDBnNq6IiTO3qDa1eSQDibAV8g7XOQ==",
2525
+
"dev": true,
2526
+
"license": "MIT",
2527
+
"dependencies": {
2528
+
"@jridgewell/trace-mapping": "^0.3.31",
2529
+
"estree-walker": "^3.0.3",
2530
+
"js-tokens": "^9.0.1"
2531
+
}
2532
+
},
2533
+
"node_modules/ast-v8-to-istanbul/node_modules/@jridgewell/trace-mapping": {
2534
+
"version": "0.3.31",
2535
+
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
2536
+
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
2537
+
"dev": true,
2538
+
"license": "MIT",
2539
+
"dependencies": {
2540
+
"@jridgewell/resolve-uri": "^3.1.0",
2541
+
"@jridgewell/sourcemap-codec": "^1.4.14"
2542
+
}
2543
+
},
2544
+
"node_modules/balanced-match": {
2545
+
"version": "1.0.2",
2546
+
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
2547
+
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
2548
+
"dev": true,
2549
+
"license": "MIT"
2550
+
},
2551
+
"node_modules/baseline-browser-mapping": {
2552
+
"version": "2.9.11",
2553
+
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz",
2554
+
"integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==",
2555
+
"dev": true,
2556
+
"license": "Apache-2.0",
2557
+
"bin": {
2558
+
"baseline-browser-mapping": "dist/cli.js"
2559
+
}
2560
+
},
2028
2561
"node_modules/birpc": {
2029
2562
"version": "0.2.14",
2030
2563
"resolved": "https://registry.npmjs.org/birpc/-/birpc-0.2.14.tgz",
···
2042
2575
"dev": true,
2043
2576
"license": "MIT"
2044
2577
},
2578
+
"node_modules/brace-expansion": {
2579
+
"version": "2.0.2",
2580
+
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
2581
+
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
2582
+
"dev": true,
2583
+
"license": "MIT",
2584
+
"dependencies": {
2585
+
"balanced-match": "^1.0.0"
2586
+
}
2587
+
},
2588
+
"node_modules/browserslist": {
2589
+
"version": "4.28.1",
2590
+
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
2591
+
"integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
2592
+
"dev": true,
2593
+
"funding": [
2594
+
{
2595
+
"type": "opencollective",
2596
+
"url": "https://opencollective.com/browserslist"
2597
+
},
2598
+
{
2599
+
"type": "tidelift",
2600
+
"url": "https://tidelift.com/funding/github/npm/browserslist"
2601
+
},
2602
+
{
2603
+
"type": "github",
2604
+
"url": "https://github.com/sponsors/ai"
2605
+
}
2606
+
],
2607
+
"license": "MIT",
2608
+
"dependencies": {
2609
+
"baseline-browser-mapping": "^2.9.0",
2610
+
"caniuse-lite": "^1.0.30001759",
2611
+
"electron-to-chromium": "^1.5.263",
2612
+
"node-releases": "^2.0.27",
2613
+
"update-browserslist-db": "^1.2.0"
2614
+
},
2615
+
"bin": {
2616
+
"browserslist": "cli.js"
2617
+
},
2618
+
"engines": {
2619
+
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
2620
+
}
2621
+
},
2045
2622
"node_modules/cac": {
2046
2623
"version": "6.7.14",
2047
2624
"resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
···
2051
2628
"engines": {
2052
2629
"node": ">=8"
2053
2630
}
2631
+
},
2632
+
"node_modules/caniuse-lite": {
2633
+
"version": "1.0.30001761",
2634
+
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001761.tgz",
2635
+
"integrity": "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==",
2636
+
"dev": true,
2637
+
"funding": [
2638
+
{
2639
+
"type": "opencollective",
2640
+
"url": "https://opencollective.com/browserslist"
2641
+
},
2642
+
{
2643
+
"type": "tidelift",
2644
+
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
2645
+
},
2646
+
{
2647
+
"type": "github",
2648
+
"url": "https://github.com/sponsors/ai"
2649
+
}
2650
+
],
2651
+
"license": "CC-BY-4.0"
2054
2652
},
2055
2653
"node_modules/chai": {
2056
2654
"version": "5.3.3",
···
2131
2729
"simple-swizzle": "^0.2.2"
2132
2730
}
2133
2731
},
2732
+
"node_modules/convert-source-map": {
2733
+
"version": "2.0.0",
2734
+
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
2735
+
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
2736
+
"dev": true,
2737
+
"license": "MIT"
2738
+
},
2134
2739
"node_modules/cookie": {
2135
2740
"version": "1.1.1",
2136
2741
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
···
2143
2748
"funding": {
2144
2749
"type": "opencollective",
2145
2750
"url": "https://opencollective.com/express"
2751
+
}
2752
+
},
2753
+
"node_modules/cross-spawn": {
2754
+
"version": "7.0.6",
2755
+
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
2756
+
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
2757
+
"dev": true,
2758
+
"license": "MIT",
2759
+
"dependencies": {
2760
+
"path-key": "^3.1.0",
2761
+
"shebang-command": "^2.0.0",
2762
+
"which": "^2.0.1"
2763
+
},
2764
+
"engines": {
2765
+
"node": ">= 8"
2146
2766
}
2147
2767
},
2148
2768
"node_modules/debug": {
···
2190
2810
"dev": true,
2191
2811
"license": "MIT"
2192
2812
},
2813
+
"node_modules/eastasianwidth": {
2814
+
"version": "0.2.0",
2815
+
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
2816
+
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
2817
+
"dev": true,
2818
+
"license": "MIT"
2819
+
},
2820
+
"node_modules/electron-to-chromium": {
2821
+
"version": "1.5.267",
2822
+
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
2823
+
"integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==",
2824
+
"dev": true,
2825
+
"license": "ISC"
2826
+
},
2827
+
"node_modules/emoji-regex": {
2828
+
"version": "9.2.2",
2829
+
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
2830
+
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
2831
+
"dev": true,
2832
+
"license": "MIT"
2833
+
},
2193
2834
"node_modules/error-stack-parser-es": {
2194
2835
"version": "1.0.5",
2195
2836
"resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz",
···
2249
2890
"@esbuild/win32-x64": "0.27.2"
2250
2891
}
2251
2892
},
2893
+
"node_modules/escalade": {
2894
+
"version": "3.2.0",
2895
+
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
2896
+
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
2897
+
"dev": true,
2898
+
"license": "MIT",
2899
+
"engines": {
2900
+
"node": ">=6"
2901
+
}
2902
+
},
2252
2903
"node_modules/estree-walker": {
2253
2904
"version": "3.0.3",
2254
2905
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
···
2300
2951
}
2301
2952
}
2302
2953
},
2954
+
"node_modules/foreground-child": {
2955
+
"version": "3.3.1",
2956
+
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
2957
+
"integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
2958
+
"dev": true,
2959
+
"license": "ISC",
2960
+
"dependencies": {
2961
+
"cross-spawn": "^7.0.6",
2962
+
"signal-exit": "^4.0.1"
2963
+
},
2964
+
"engines": {
2965
+
"node": ">=14"
2966
+
},
2967
+
"funding": {
2968
+
"url": "https://github.com/sponsors/isaacs"
2969
+
}
2970
+
},
2303
2971
"node_modules/fsevents": {
2304
2972
"version": "2.3.3",
2305
2973
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
···
2315
2983
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
2316
2984
}
2317
2985
},
2986
+
"node_modules/gensync": {
2987
+
"version": "1.0.0-beta.2",
2988
+
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
2989
+
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
2990
+
"dev": true,
2991
+
"license": "MIT",
2992
+
"engines": {
2993
+
"node": ">=6.9.0"
2994
+
}
2995
+
},
2996
+
"node_modules/glob": {
2997
+
"version": "10.5.0",
2998
+
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
2999
+
"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
3000
+
"dev": true,
3001
+
"license": "ISC",
3002
+
"dependencies": {
3003
+
"foreground-child": "^3.1.0",
3004
+
"jackspeak": "^3.1.2",
3005
+
"minimatch": "^9.0.4",
3006
+
"minipass": "^7.1.2",
3007
+
"package-json-from-dist": "^1.0.0",
3008
+
"path-scurry": "^1.11.1"
3009
+
},
3010
+
"bin": {
3011
+
"glob": "dist/esm/bin.mjs"
3012
+
},
3013
+
"funding": {
3014
+
"url": "https://github.com/sponsors/isaacs"
3015
+
}
3016
+
},
2318
3017
"node_modules/glob-to-regexp": {
2319
3018
"version": "0.4.1",
2320
3019
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
···
2322
3021
"dev": true,
2323
3022
"license": "BSD-2-Clause"
2324
3023
},
3024
+
"node_modules/has-flag": {
3025
+
"version": "4.0.0",
3026
+
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
3027
+
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
3028
+
"dev": true,
3029
+
"license": "MIT",
3030
+
"engines": {
3031
+
"node": ">=8"
3032
+
}
3033
+
},
3034
+
"node_modules/html-escaper": {
3035
+
"version": "2.0.2",
3036
+
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
3037
+
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
3038
+
"dev": true,
3039
+
"license": "MIT"
3040
+
},
2325
3041
"node_modules/is-arrayish": {
2326
3042
"version": "0.3.4",
2327
3043
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz",
···
2329
3045
"dev": true,
2330
3046
"license": "MIT"
2331
3047
},
3048
+
"node_modules/is-fullwidth-code-point": {
3049
+
"version": "3.0.0",
3050
+
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
3051
+
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
3052
+
"dev": true,
3053
+
"license": "MIT",
3054
+
"engines": {
3055
+
"node": ">=8"
3056
+
}
3057
+
},
3058
+
"node_modules/isexe": {
3059
+
"version": "2.0.0",
3060
+
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
3061
+
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
3062
+
"dev": true,
3063
+
"license": "ISC"
3064
+
},
3065
+
"node_modules/istanbul-lib-coverage": {
3066
+
"version": "3.2.2",
3067
+
"resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
3068
+
"integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==",
3069
+
"dev": true,
3070
+
"license": "BSD-3-Clause",
3071
+
"engines": {
3072
+
"node": ">=8"
3073
+
}
3074
+
},
3075
+
"node_modules/istanbul-lib-instrument": {
3076
+
"version": "6.0.3",
3077
+
"resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz",
3078
+
"integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==",
3079
+
"dev": true,
3080
+
"license": "BSD-3-Clause",
3081
+
"dependencies": {
3082
+
"@babel/core": "^7.23.9",
3083
+
"@babel/parser": "^7.23.9",
3084
+
"@istanbuljs/schema": "^0.1.3",
3085
+
"istanbul-lib-coverage": "^3.2.0",
3086
+
"semver": "^7.5.4"
3087
+
},
3088
+
"engines": {
3089
+
"node": ">=10"
3090
+
}
3091
+
},
3092
+
"node_modules/istanbul-lib-report": {
3093
+
"version": "3.0.1",
3094
+
"resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
3095
+
"integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
3096
+
"dev": true,
3097
+
"license": "BSD-3-Clause",
3098
+
"dependencies": {
3099
+
"istanbul-lib-coverage": "^3.0.0",
3100
+
"make-dir": "^4.0.0",
3101
+
"supports-color": "^7.1.0"
3102
+
},
3103
+
"engines": {
3104
+
"node": ">=10"
3105
+
}
3106
+
},
3107
+
"node_modules/istanbul-lib-report/node_modules/supports-color": {
3108
+
"version": "7.2.0",
3109
+
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
3110
+
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
3111
+
"dev": true,
3112
+
"license": "MIT",
3113
+
"dependencies": {
3114
+
"has-flag": "^4.0.0"
3115
+
},
3116
+
"engines": {
3117
+
"node": ">=8"
3118
+
}
3119
+
},
3120
+
"node_modules/istanbul-lib-source-maps": {
3121
+
"version": "5.0.6",
3122
+
"resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz",
3123
+
"integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==",
3124
+
"dev": true,
3125
+
"license": "BSD-3-Clause",
3126
+
"dependencies": {
3127
+
"@jridgewell/trace-mapping": "^0.3.23",
3128
+
"debug": "^4.1.1",
3129
+
"istanbul-lib-coverage": "^3.0.0"
3130
+
},
3131
+
"engines": {
3132
+
"node": ">=10"
3133
+
}
3134
+
},
3135
+
"node_modules/istanbul-lib-source-maps/node_modules/@jridgewell/trace-mapping": {
3136
+
"version": "0.3.31",
3137
+
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
3138
+
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
3139
+
"dev": true,
3140
+
"license": "MIT",
3141
+
"dependencies": {
3142
+
"@jridgewell/resolve-uri": "^3.1.0",
3143
+
"@jridgewell/sourcemap-codec": "^1.4.14"
3144
+
}
3145
+
},
3146
+
"node_modules/istanbul-reports": {
3147
+
"version": "3.2.0",
3148
+
"resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz",
3149
+
"integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==",
3150
+
"dev": true,
3151
+
"license": "BSD-3-Clause",
3152
+
"dependencies": {
3153
+
"html-escaper": "^2.0.0",
3154
+
"istanbul-lib-report": "^3.0.0"
3155
+
},
3156
+
"engines": {
3157
+
"node": ">=8"
3158
+
}
3159
+
},
3160
+
"node_modules/jackspeak": {
3161
+
"version": "3.4.3",
3162
+
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
3163
+
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
3164
+
"dev": true,
3165
+
"license": "BlueOak-1.0.0",
3166
+
"dependencies": {
3167
+
"@isaacs/cliui": "^8.0.2"
3168
+
},
3169
+
"funding": {
3170
+
"url": "https://github.com/sponsors/isaacs"
3171
+
},
3172
+
"optionalDependencies": {
3173
+
"@pkgjs/parseargs": "^0.11.0"
3174
+
}
3175
+
},
2332
3176
"node_modules/js-tokens": {
2333
3177
"version": "9.0.1",
2334
3178
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz",
···
2336
3180
"dev": true,
2337
3181
"license": "MIT"
2338
3182
},
3183
+
"node_modules/jsesc": {
3184
+
"version": "3.1.0",
3185
+
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
3186
+
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
3187
+
"dev": true,
3188
+
"license": "MIT",
3189
+
"bin": {
3190
+
"jsesc": "bin/jsesc"
3191
+
},
3192
+
"engines": {
3193
+
"node": ">=6"
3194
+
}
3195
+
},
3196
+
"node_modules/json5": {
3197
+
"version": "2.2.3",
3198
+
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
3199
+
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
3200
+
"dev": true,
3201
+
"license": "MIT",
3202
+
"bin": {
3203
+
"json5": "lib/cli.js"
3204
+
},
3205
+
"engines": {
3206
+
"node": ">=6"
3207
+
}
3208
+
},
2339
3209
"node_modules/kleur": {
2340
3210
"version": "4.1.5",
2341
3211
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
···
2353
3223
"dev": true,
2354
3224
"license": "MIT"
2355
3225
},
3226
+
"node_modules/lru-cache": {
3227
+
"version": "10.4.3",
3228
+
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
3229
+
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
3230
+
"dev": true,
3231
+
"license": "ISC"
3232
+
},
2356
3233
"node_modules/magic-string": {
2357
3234
"version": "0.30.21",
2358
3235
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
···
2363
3240
"@jridgewell/sourcemap-codec": "^1.5.5"
2364
3241
}
2365
3242
},
3243
+
"node_modules/magicast": {
3244
+
"version": "0.3.5",
3245
+
"resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz",
3246
+
"integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==",
3247
+
"dev": true,
3248
+
"license": "MIT",
3249
+
"dependencies": {
3250
+
"@babel/parser": "^7.25.4",
3251
+
"@babel/types": "^7.25.4",
3252
+
"source-map-js": "^1.2.0"
3253
+
}
3254
+
},
3255
+
"node_modules/make-dir": {
3256
+
"version": "4.0.0",
3257
+
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
3258
+
"integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
3259
+
"dev": true,
3260
+
"license": "MIT",
3261
+
"dependencies": {
3262
+
"semver": "^7.5.3"
3263
+
},
3264
+
"engines": {
3265
+
"node": ">=10"
3266
+
},
3267
+
"funding": {
3268
+
"url": "https://github.com/sponsors/sindresorhus"
3269
+
}
3270
+
},
2366
3271
"node_modules/mime": {
2367
3272
"version": "3.0.0",
2368
3273
"resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
···
2413
3318
"url": "https://github.com/sponsors/colinhacks"
2414
3319
}
2415
3320
},
3321
+
"node_modules/minimatch": {
3322
+
"version": "9.0.5",
3323
+
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
3324
+
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
3325
+
"dev": true,
3326
+
"license": "ISC",
3327
+
"dependencies": {
3328
+
"brace-expansion": "^2.0.1"
3329
+
},
3330
+
"engines": {
3331
+
"node": ">=16 || 14 >=14.17"
3332
+
},
3333
+
"funding": {
3334
+
"url": "https://github.com/sponsors/isaacs"
3335
+
}
3336
+
},
3337
+
"node_modules/minipass": {
3338
+
"version": "7.1.2",
3339
+
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
3340
+
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
3341
+
"dev": true,
3342
+
"license": "ISC",
3343
+
"engines": {
3344
+
"node": ">=16 || 14 >=14.17"
3345
+
}
3346
+
},
2416
3347
"node_modules/ms": {
2417
3348
"version": "2.1.3",
2418
3349
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
···
2445
3376
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
2446
3377
}
2447
3378
},
3379
+
"node_modules/node-releases": {
3380
+
"version": "2.0.27",
3381
+
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
3382
+
"integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
3383
+
"dev": true,
3384
+
"license": "MIT"
3385
+
},
3386
+
"node_modules/package-json-from-dist": {
3387
+
"version": "1.0.1",
3388
+
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
3389
+
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
3390
+
"dev": true,
3391
+
"license": "BlueOak-1.0.0"
3392
+
},
3393
+
"node_modules/path-key": {
3394
+
"version": "3.1.1",
3395
+
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
3396
+
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
3397
+
"dev": true,
3398
+
"license": "MIT",
3399
+
"engines": {
3400
+
"node": ">=8"
3401
+
}
3402
+
},
3403
+
"node_modules/path-scurry": {
3404
+
"version": "1.11.1",
3405
+
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
3406
+
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
3407
+
"dev": true,
3408
+
"license": "BlueOak-1.0.0",
3409
+
"dependencies": {
3410
+
"lru-cache": "^10.2.0",
3411
+
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
3412
+
},
3413
+
"engines": {
3414
+
"node": ">=16 || 14 >=14.18"
3415
+
},
3416
+
"funding": {
3417
+
"url": "https://github.com/sponsors/isaacs"
3418
+
}
3419
+
},
2448
3420
"node_modules/path-to-regexp": {
2449
3421
"version": "6.3.0",
2450
3422
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz",
···
2613
3585
"@img/sharp-win32-x64": "0.33.5"
2614
3586
}
2615
3587
},
3588
+
"node_modules/shebang-command": {
3589
+
"version": "2.0.0",
3590
+
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
3591
+
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
3592
+
"dev": true,
3593
+
"license": "MIT",
3594
+
"dependencies": {
3595
+
"shebang-regex": "^3.0.0"
3596
+
},
3597
+
"engines": {
3598
+
"node": ">=8"
3599
+
}
3600
+
},
3601
+
"node_modules/shebang-regex": {
3602
+
"version": "3.0.0",
3603
+
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
3604
+
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
3605
+
"dev": true,
3606
+
"license": "MIT",
3607
+
"engines": {
3608
+
"node": ">=8"
3609
+
}
3610
+
},
2616
3611
"node_modules/siginfo": {
2617
3612
"version": "2.0.0",
2618
3613
"resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
···
2620
3615
"dev": true,
2621
3616
"license": "ISC"
2622
3617
},
3618
+
"node_modules/signal-exit": {
3619
+
"version": "4.1.0",
3620
+
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
3621
+
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
3622
+
"dev": true,
3623
+
"license": "ISC",
3624
+
"engines": {
3625
+
"node": ">=14"
3626
+
},
3627
+
"funding": {
3628
+
"url": "https://github.com/sponsors/isaacs"
3629
+
}
3630
+
},
2623
3631
"node_modules/simple-swizzle": {
2624
3632
"version": "0.2.4",
2625
3633
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz",
···
2665
3673
"npm": ">=6"
2666
3674
}
2667
3675
},
3676
+
"node_modules/string-width": {
3677
+
"version": "5.1.2",
3678
+
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
3679
+
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
3680
+
"dev": true,
3681
+
"license": "MIT",
3682
+
"dependencies": {
3683
+
"eastasianwidth": "^0.2.0",
3684
+
"emoji-regex": "^9.2.2",
3685
+
"strip-ansi": "^7.0.1"
3686
+
},
3687
+
"engines": {
3688
+
"node": ">=12"
3689
+
},
3690
+
"funding": {
3691
+
"url": "https://github.com/sponsors/sindresorhus"
3692
+
}
3693
+
},
3694
+
"node_modules/string-width-cjs": {
3695
+
"name": "string-width",
3696
+
"version": "4.2.3",
3697
+
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
3698
+
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
3699
+
"dev": true,
3700
+
"license": "MIT",
3701
+
"dependencies": {
3702
+
"emoji-regex": "^8.0.0",
3703
+
"is-fullwidth-code-point": "^3.0.0",
3704
+
"strip-ansi": "^6.0.1"
3705
+
},
3706
+
"engines": {
3707
+
"node": ">=8"
3708
+
}
3709
+
},
3710
+
"node_modules/string-width-cjs/node_modules/ansi-regex": {
3711
+
"version": "5.0.1",
3712
+
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
3713
+
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
3714
+
"dev": true,
3715
+
"license": "MIT",
3716
+
"engines": {
3717
+
"node": ">=8"
3718
+
}
3719
+
},
3720
+
"node_modules/string-width-cjs/node_modules/emoji-regex": {
3721
+
"version": "8.0.0",
3722
+
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
3723
+
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
3724
+
"dev": true,
3725
+
"license": "MIT"
3726
+
},
3727
+
"node_modules/string-width-cjs/node_modules/strip-ansi": {
3728
+
"version": "6.0.1",
3729
+
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
3730
+
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
3731
+
"dev": true,
3732
+
"license": "MIT",
3733
+
"dependencies": {
3734
+
"ansi-regex": "^5.0.1"
3735
+
},
3736
+
"engines": {
3737
+
"node": ">=8"
3738
+
}
3739
+
},
3740
+
"node_modules/strip-ansi": {
3741
+
"version": "7.1.2",
3742
+
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
3743
+
"integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
3744
+
"dev": true,
3745
+
"license": "MIT",
3746
+
"dependencies": {
3747
+
"ansi-regex": "^6.0.1"
3748
+
},
3749
+
"engines": {
3750
+
"node": ">=12"
3751
+
},
3752
+
"funding": {
3753
+
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
3754
+
}
3755
+
},
3756
+
"node_modules/strip-ansi-cjs": {
3757
+
"name": "strip-ansi",
3758
+
"version": "6.0.1",
3759
+
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
3760
+
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
3761
+
"dev": true,
3762
+
"license": "MIT",
3763
+
"dependencies": {
3764
+
"ansi-regex": "^5.0.1"
3765
+
},
3766
+
"engines": {
3767
+
"node": ">=8"
3768
+
}
3769
+
},
3770
+
"node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
3771
+
"version": "5.0.1",
3772
+
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
3773
+
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
3774
+
"dev": true,
3775
+
"license": "MIT",
3776
+
"engines": {
3777
+
"node": ">=8"
3778
+
}
3779
+
},
2668
3780
"node_modules/strip-literal": {
2669
3781
"version": "3.1.0",
2670
3782
"resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz",
···
2691
3803
"url": "https://github.com/chalk/supports-color?sponsor=1"
2692
3804
}
2693
3805
},
3806
+
"node_modules/test-exclude": {
3807
+
"version": "7.0.1",
3808
+
"resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz",
3809
+
"integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==",
3810
+
"dev": true,
3811
+
"license": "ISC",
3812
+
"dependencies": {
3813
+
"@istanbuljs/schema": "^0.1.2",
3814
+
"glob": "^10.4.1",
3815
+
"minimatch": "^9.0.4"
3816
+
},
3817
+
"engines": {
3818
+
"node": ">=18"
3819
+
}
3820
+
},
2694
3821
"node_modules/tinybench": {
2695
3822
"version": "2.9.0",
2696
3823
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
···
2792
3919
"license": "MIT",
2793
3920
"dependencies": {
2794
3921
"pathe": "^2.0.3"
3922
+
}
3923
+
},
3924
+
"node_modules/update-browserslist-db": {
3925
+
"version": "1.2.3",
3926
+
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
3927
+
"integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
3928
+
"dev": true,
3929
+
"funding": [
3930
+
{
3931
+
"type": "opencollective",
3932
+
"url": "https://opencollective.com/browserslist"
3933
+
},
3934
+
{
3935
+
"type": "tidelift",
3936
+
"url": "https://tidelift.com/funding/github/npm/browserslist"
3937
+
},
3938
+
{
3939
+
"type": "github",
3940
+
"url": "https://github.com/sponsors/ai"
3941
+
}
3942
+
],
3943
+
"license": "MIT",
3944
+
"dependencies": {
3945
+
"escalade": "^3.2.0",
3946
+
"picocolors": "^1.1.1"
3947
+
},
3948
+
"bin": {
3949
+
"update-browserslist-db": "cli.js"
3950
+
},
3951
+
"peerDependencies": {
3952
+
"browserslist": ">= 4.21.0"
2795
3953
}
2796
3954
},
2797
3955
"node_modules/vite": {
···
2963
4121
"jsdom": {
2964
4122
"optional": true
2965
4123
}
4124
+
}
4125
+
},
4126
+
"node_modules/which": {
4127
+
"version": "2.0.2",
4128
+
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
4129
+
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
4130
+
"dev": true,
4131
+
"license": "ISC",
4132
+
"dependencies": {
4133
+
"isexe": "^2.0.0"
4134
+
},
4135
+
"bin": {
4136
+
"node-which": "bin/node-which"
4137
+
},
4138
+
"engines": {
4139
+
"node": ">= 8"
2966
4140
}
2967
4141
},
2968
4142
"node_modules/why-is-node-running": {
···
3539
4713
"@esbuild/win32-x64": "0.27.0"
3540
4714
}
3541
4715
},
4716
+
"node_modules/wrap-ansi": {
4717
+
"version": "8.1.0",
4718
+
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
4719
+
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
4720
+
"dev": true,
4721
+
"license": "MIT",
4722
+
"dependencies": {
4723
+
"ansi-styles": "^6.1.0",
4724
+
"string-width": "^5.0.1",
4725
+
"strip-ansi": "^7.0.1"
4726
+
},
4727
+
"engines": {
4728
+
"node": ">=12"
4729
+
},
4730
+
"funding": {
4731
+
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
4732
+
}
4733
+
},
4734
+
"node_modules/wrap-ansi-cjs": {
4735
+
"name": "wrap-ansi",
4736
+
"version": "7.0.0",
4737
+
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
4738
+
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
4739
+
"dev": true,
4740
+
"license": "MIT",
4741
+
"dependencies": {
4742
+
"ansi-styles": "^4.0.0",
4743
+
"string-width": "^4.1.0",
4744
+
"strip-ansi": "^6.0.0"
4745
+
},
4746
+
"engines": {
4747
+
"node": ">=10"
4748
+
},
4749
+
"funding": {
4750
+
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
4751
+
}
4752
+
},
4753
+
"node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
4754
+
"version": "5.0.1",
4755
+
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
4756
+
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
4757
+
"dev": true,
4758
+
"license": "MIT",
4759
+
"engines": {
4760
+
"node": ">=8"
4761
+
}
4762
+
},
4763
+
"node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
4764
+
"version": "4.3.0",
4765
+
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
4766
+
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
4767
+
"dev": true,
4768
+
"license": "MIT",
4769
+
"dependencies": {
4770
+
"color-convert": "^2.0.1"
4771
+
},
4772
+
"engines": {
4773
+
"node": ">=8"
4774
+
},
4775
+
"funding": {
4776
+
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
4777
+
}
4778
+
},
4779
+
"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
4780
+
"version": "8.0.0",
4781
+
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
4782
+
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
4783
+
"dev": true,
4784
+
"license": "MIT"
4785
+
},
4786
+
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
4787
+
"version": "4.2.3",
4788
+
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
4789
+
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
4790
+
"dev": true,
4791
+
"license": "MIT",
4792
+
"dependencies": {
4793
+
"emoji-regex": "^8.0.0",
4794
+
"is-fullwidth-code-point": "^3.0.0",
4795
+
"strip-ansi": "^6.0.1"
4796
+
},
4797
+
"engines": {
4798
+
"node": ">=8"
4799
+
}
4800
+
},
4801
+
"node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
4802
+
"version": "6.0.1",
4803
+
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
4804
+
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
4805
+
"dev": true,
4806
+
"license": "MIT",
4807
+
"dependencies": {
4808
+
"ansi-regex": "^5.0.1"
4809
+
},
4810
+
"engines": {
4811
+
"node": ">=8"
4812
+
}
4813
+
},
3542
4814
"node_modules/ws": {
3543
4815
"version": "8.18.0",
3544
4816
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
···
3560
4832
"optional": true
3561
4833
}
3562
4834
}
4835
+
},
4836
+
"node_modules/yallist": {
4837
+
"version": "3.1.1",
4838
+
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
4839
+
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
4840
+
"dev": true,
4841
+
"license": "ISC"
3563
4842
},
3564
4843
"node_modules/youch": {
3565
4844
"version": "4.1.0-beta.10",
+2
package.json
+2
package.json
+10
src/index.ts
+10
src/index.ts
···
256
256
}
257
257
}
258
258
259
+
// Export helper functions for testing
260
+
export {
261
+
base62ToBytes,
262
+
detectIdentifierFormat,
263
+
resolveHandleToDID,
264
+
resolvePDSHost,
265
+
fetchBlobCidFromRecord,
266
+
downloadBlobUnauthenticated,
267
+
};
268
+
259
269
export default {
260
270
async fetch(
261
271
request: Request,
+769
-18
test/index.spec.ts
+769
-18
test/index.spec.ts
···
1
1
import { env, createExecutionContext, waitOnExecutionContext, SELF } from 'cloudflare:test';
2
-
import { describe, it, expect } from 'vitest';
3
-
import worker from '../src/index';
2
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
3
+
import { CID } from 'multiformats/cid';
4
+
import worker, {
5
+
base62ToBytes,
6
+
detectIdentifierFormat,
7
+
resolveHandleToDID,
8
+
resolvePDSHost,
9
+
fetchBlobCidFromRecord,
10
+
downloadBlobUnauthenticated,
11
+
} from '../src/index';
4
12
5
-
// For now, you'll need to do something like this to get a correctly-typed
6
-
// `Request` to pass to `worker.fetch()`.
7
13
const IncomingRequest = Request<unknown, IncomingRequestCfProperties>;
8
14
9
-
describe('Hello World worker', () => {
10
-
it('responds with Hello World! (unit style)', async () => {
11
-
const request = new IncomingRequest('http://example.com');
12
-
// Create an empty context to pass to `worker.fetch()`.
13
-
const ctx = createExecutionContext();
14
-
const response = await worker.fetch(request, env, ctx);
15
-
// Wait for all `Promise`s passed to `ctx.waitUntil()` to settle before running test assertions
16
-
await waitOnExecutionContext(ctx);
17
-
expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`);
18
-
});
15
+
// Test constants
16
+
const TEST_DID = 'did:plc:ewvi7nxzyoun6zhxrhs64oiz';
17
+
const TEST_HANDLE = 'bsky.app';
18
+
const TEST_CID = 'bafkreihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku';
19
+
const TEST_TID = '3jui7kd5354sr';
20
+
const TEST_PDS = 'https://bsky.social';
21
+
22
+
describe('base62ToBytes', () => {
23
+
it('converts empty string to empty Uint8Array', () => {
24
+
const result = base62ToBytes('');
25
+
expect(result).toEqual(new Uint8Array([]));
26
+
});
27
+
28
+
it('converts single character correctly', () => {
29
+
const result = base62ToBytes('1');
30
+
expect(result).toEqual(new Uint8Array([1]));
31
+
});
32
+
33
+
it('converts known base62 to correct bytes', () => {
34
+
// Test with "10" which is 62 in decimal (1*62 + 0)
35
+
const result = base62ToBytes('10');
36
+
expect(result).toEqual(new Uint8Array([62]));
37
+
});
38
+
39
+
it('handles larger numbers correctly', () => {
40
+
// "100" = 1*62^2 + 0*62 + 0 = 3844
41
+
const result = base62ToBytes('100');
42
+
// 3844 = 0x0F04, so bytes are [15, 4]
43
+
expect(result).toEqual(new Uint8Array([15, 4]));
44
+
});
45
+
46
+
it('round-trips through CID decode', () => {
47
+
// Create a valid CID, encode to base62-ish bytes, then decode
48
+
const cid = CID.parse(TEST_CID);
49
+
const bytes = cid.bytes;
50
+
51
+
// Encode bytes to base62
52
+
const BASE62_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
53
+
let num = 0n;
54
+
for (const byte of bytes) {
55
+
num = num * 256n + BigInt(byte);
56
+
}
57
+
let base62 = '';
58
+
while (num > 0n) {
59
+
base62 = BASE62_CHARS[Number(num % 62n)] + base62;
60
+
num = num / 62n;
61
+
}
62
+
63
+
// Now decode back using our function
64
+
const decoded = base62ToBytes(base62);
65
+
const decodedCid = CID.decode(decoded);
66
+
expect(decodedCid.toString()).toBe(TEST_CID);
67
+
});
68
+
});
69
+
70
+
describe('detectIdentifierFormat', () => {
71
+
it('detects base32 CID format (bafkrei prefix)', () => {
72
+
expect(detectIdentifierFormat('bafkreihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku')).toBe('base32');
73
+
expect(detectIdentifierFormat('bafkreiabc')).toBe('base32');
74
+
});
75
+
76
+
it('detects TID format (13 chars matching pattern)', () => {
77
+
expect(detectIdentifierFormat('3jui7kd5354sr')).toBe('tid');
78
+
expect(detectIdentifierFormat('3kfg2b5fyjk2i')).toBe('tid');
79
+
});
80
+
81
+
it('returns base62 for other formats', () => {
82
+
expect(detectIdentifierFormat('abc123XYZ')).toBe('base62');
83
+
expect(detectIdentifierFormat('shortCID')).toBe('base62');
84
+
});
85
+
86
+
it('returns base62 for invalid TID length', () => {
87
+
// Too short
88
+
expect(detectIdentifierFormat('3jui7kd535')).toBe('base62');
89
+
// Too long
90
+
expect(detectIdentifierFormat('3jui7kd5354srx')).toBe('base62');
91
+
});
92
+
93
+
it('returns base62 for invalid TID characters', () => {
94
+
// Contains invalid first character (0, 1, or other invalid)
95
+
expect(detectIdentifierFormat('0jui7kd5354sr')).toBe('base62');
96
+
expect(detectIdentifierFormat('1jui7kd5354sr')).toBe('base62');
97
+
});
98
+
});
99
+
100
+
describe('resolveHandleToDID', () => {
101
+
beforeEach(() => {
102
+
vi.stubGlobal('fetch', vi.fn());
103
+
});
104
+
105
+
afterEach(() => {
106
+
vi.unstubAllGlobals();
107
+
});
108
+
109
+
it('resolves DID via DNS-over-HTTPS', async () => {
110
+
const mockFetch = vi.fn().mockResolvedValueOnce({
111
+
ok: true,
112
+
json: async () => ({
113
+
Answer: [
114
+
{ type: 16, data: `"did=${TEST_DID}"` }
115
+
]
116
+
})
117
+
});
118
+
vi.stubGlobal('fetch', mockFetch);
119
+
120
+
const did = await resolveHandleToDID(TEST_HANDLE);
121
+
expect(did).toBe(TEST_DID);
122
+
expect(mockFetch).toHaveBeenCalledWith(
123
+
expect.stringContaining(`_atproto.${TEST_HANDLE}`),
124
+
expect.any(Object)
125
+
);
126
+
});
127
+
128
+
it('falls back to HTTPS well-known when DNS fails', async () => {
129
+
const mockFetch = vi.fn()
130
+
// First call (DNS) fails
131
+
.mockResolvedValueOnce({
132
+
ok: false
133
+
})
134
+
// Second call (well-known) succeeds
135
+
.mockResolvedValueOnce({
136
+
status: 200,
137
+
text: async () => TEST_DID
138
+
});
139
+
vi.stubGlobal('fetch', mockFetch);
140
+
141
+
const did = await resolveHandleToDID(TEST_HANDLE);
142
+
expect(did).toBe(TEST_DID);
143
+
expect(mockFetch).toHaveBeenCalledTimes(2);
144
+
});
145
+
146
+
it('returns null when all resolution methods fail', async () => {
147
+
const mockFetch = vi.fn()
148
+
.mockResolvedValueOnce({ ok: false })
149
+
.mockResolvedValueOnce({ status: 404 });
150
+
vi.stubGlobal('fetch', mockFetch);
151
+
152
+
const did = await resolveHandleToDID('nonexistent.handle');
153
+
expect(did).toBeNull();
154
+
});
155
+
156
+
it('extracts DID from DNS response with quotes', async () => {
157
+
const mockFetch = vi.fn().mockResolvedValueOnce({
158
+
ok: true,
159
+
json: async () => ({
160
+
Answer: [
161
+
{ type: 16, data: '"did=did:plc:z72i7hdynmk6r22z27h6tvur"' }
162
+
]
163
+
})
164
+
});
165
+
vi.stubGlobal('fetch', mockFetch);
166
+
167
+
const did = await resolveHandleToDID('example.com');
168
+
expect(did).toBe('did:plc:z72i7hdynmk6r22z27h6tvur');
169
+
});
170
+
171
+
it('resolves DID via native DNS when available', async () => {
172
+
const mockResolveDns = vi.fn().mockResolvedValue(['did=did:plc:nativedns123']);
173
+
vi.stubGlobal('resolveDns', mockResolveDns);
174
+
175
+
const did = await resolveHandleToDID('native.test');
176
+
expect(did).toBe('did:plc:nativedns123');
177
+
expect(mockResolveDns).toHaveBeenCalledWith('_atproto.native.test', 'TXT');
178
+
179
+
vi.unstubAllGlobals();
180
+
});
181
+
182
+
it('handles native DNS throwing exception', async () => {
183
+
const mockResolveDns = vi.fn().mockRejectedValue(new Error('DNS error'));
184
+
vi.stubGlobal('resolveDns', mockResolveDns);
185
+
186
+
// DNS throws, should fall back to DNS-over-HTTPS
187
+
const mockFetch = vi.fn().mockResolvedValueOnce({
188
+
ok: true,
189
+
json: async () => ({
190
+
Answer: [{ type: 16, data: '"did=did:plc:fallback123"' }]
191
+
})
192
+
});
193
+
vi.stubGlobal('fetch', mockFetch);
194
+
195
+
const did = await resolveHandleToDID('fallback.test');
196
+
expect(did).toBe('did:plc:fallback123');
197
+
198
+
vi.unstubAllGlobals();
199
+
});
200
+
201
+
it('handles DNS-over-HTTPS fetch exception', async () => {
202
+
const mockFetch = vi.fn()
203
+
// DNS-over-HTTPS throws
204
+
.mockRejectedValueOnce(new Error('Network error'))
205
+
// HTTPS well-known succeeds
206
+
.mockResolvedValueOnce({
207
+
status: 200,
208
+
text: async () => 'did:plc:wellknown123'
209
+
});
210
+
vi.stubGlobal('fetch', mockFetch);
211
+
212
+
const did = await resolveHandleToDID('wellknown.test');
213
+
expect(did).toBe('did:plc:wellknown123');
214
+
});
215
+
216
+
it('handles HTTPS well-known fetch exception', async () => {
217
+
const mockFetch = vi.fn()
218
+
// DNS-over-HTTPS fails
219
+
.mockResolvedValueOnce({ ok: false })
220
+
// HTTPS well-known throws
221
+
.mockRejectedValueOnce(new Error('Connection refused'));
222
+
vi.stubGlobal('fetch', mockFetch);
223
+
224
+
const did = await resolveHandleToDID('error.test');
225
+
expect(did).toBeNull();
226
+
});
227
+
});
228
+
229
+
describe('resolvePDSHost', () => {
230
+
beforeEach(() => {
231
+
vi.stubGlobal('fetch', vi.fn());
232
+
});
233
+
234
+
afterEach(() => {
235
+
vi.unstubAllGlobals();
236
+
});
237
+
238
+
it('resolves PDS host for did:plc via PLC directory', async () => {
239
+
const mockFetch = vi.fn().mockResolvedValueOnce({
240
+
ok: true,
241
+
json: async () => ({
242
+
service: [
243
+
{ id: '#atproto_pds', type: 'AtprotoPersonalDataServer', serviceEndpoint: TEST_PDS }
244
+
]
245
+
})
246
+
});
247
+
vi.stubGlobal('fetch', mockFetch);
248
+
249
+
const pdsHost = await resolvePDSHost(TEST_DID);
250
+
expect(pdsHost).toBe(TEST_PDS);
251
+
expect(mockFetch).toHaveBeenCalled();
252
+
});
253
+
254
+
it('resolves PDS host for did:web via well-known', async () => {
255
+
const webDid = 'did:web:example.com';
256
+
const mockFetch = vi.fn().mockResolvedValueOnce({
257
+
ok: true,
258
+
json: async () => ({
259
+
service: [
260
+
{ id: '#atproto_pds', type: 'AtprotoPersonalDataServer', serviceEndpoint: 'https://pds.example.com' }
261
+
]
262
+
})
263
+
});
264
+
vi.stubGlobal('fetch', mockFetch);
265
+
266
+
const pdsHost = await resolvePDSHost(webDid);
267
+
expect(pdsHost).toBe('https://pds.example.com');
268
+
expect(mockFetch).toHaveBeenCalled();
269
+
});
270
+
271
+
it('returns null when PDS not found', async () => {
272
+
const mockFetch = vi.fn().mockResolvedValueOnce({
273
+
ok: false
274
+
});
275
+
vi.stubGlobal('fetch', mockFetch);
276
+
277
+
const pdsHost = await resolvePDSHost(TEST_DID);
278
+
expect(pdsHost).toBeNull();
279
+
});
280
+
281
+
it('returns null when service array is missing', async () => {
282
+
const mockFetch = vi.fn().mockResolvedValueOnce({
283
+
ok: true,
284
+
json: async () => ({})
285
+
});
286
+
vi.stubGlobal('fetch', mockFetch);
287
+
288
+
const pdsHost = await resolvePDSHost(TEST_DID);
289
+
expect(pdsHost).toBeNull();
290
+
});
291
+
292
+
it('returns null when fetch throws exception', async () => {
293
+
const mockFetch = vi.fn().mockRejectedValueOnce(new Error('Network error'));
294
+
vi.stubGlobal('fetch', mockFetch);
295
+
296
+
const pdsHost = await resolvePDSHost(TEST_DID);
297
+
expect(pdsHost).toBeNull();
298
+
});
299
+
});
300
+
301
+
describe('fetchBlobCidFromRecord', () => {
302
+
beforeEach(() => {
303
+
vi.stubGlobal('fetch', vi.fn());
304
+
});
305
+
306
+
afterEach(() => {
307
+
vi.unstubAllGlobals();
308
+
});
309
+
310
+
it('fetches blob CID from record', async () => {
311
+
const mockFetch = vi.fn()
312
+
// First call: resolvePDSHost
313
+
.mockResolvedValueOnce({
314
+
ok: true,
315
+
json: async () => ({
316
+
service: [{ id: '#atproto_pds', serviceEndpoint: TEST_PDS }]
317
+
})
318
+
})
319
+
// Second call: getRecord
320
+
.mockResolvedValueOnce({
321
+
ok: true,
322
+
json: async () => ({
323
+
uri: `at://${TEST_DID}/blue.imgs.blup.image/${TEST_TID}`,
324
+
cid: 'somecid',
325
+
value: {
326
+
blob: {
327
+
ref: { $link: TEST_CID }
328
+
}
329
+
}
330
+
})
331
+
});
332
+
vi.stubGlobal('fetch', mockFetch);
333
+
334
+
const blobCid = await fetchBlobCidFromRecord(TEST_DID, TEST_TID);
335
+
expect(blobCid).toBe(TEST_CID);
336
+
});
337
+
338
+
it('returns null when PDS resolution fails', async () => {
339
+
const mockFetch = vi.fn().mockResolvedValueOnce({
340
+
ok: false
341
+
});
342
+
vi.stubGlobal('fetch', mockFetch);
343
+
344
+
const blobCid = await fetchBlobCidFromRecord(TEST_DID, TEST_TID);
345
+
expect(blobCid).toBeNull();
346
+
});
347
+
348
+
it('returns null when record has no blob', async () => {
349
+
const mockFetch = vi.fn()
350
+
.mockResolvedValueOnce({
351
+
ok: true,
352
+
json: async () => ({
353
+
service: [{ id: '#atproto_pds', serviceEndpoint: TEST_PDS }]
354
+
})
355
+
})
356
+
.mockResolvedValueOnce({
357
+
ok: true,
358
+
json: async () => ({
359
+
uri: `at://${TEST_DID}/blue.imgs.blup.image/${TEST_TID}`,
360
+
cid: 'somecid',
361
+
value: {}
362
+
})
363
+
});
364
+
vi.stubGlobal('fetch', mockFetch);
365
+
366
+
const blobCid = await fetchBlobCidFromRecord(TEST_DID, TEST_TID);
367
+
expect(blobCid).toBeNull();
368
+
});
369
+
370
+
it('returns null when getRecord returns non-OK response', async () => {
371
+
const mockFetch = vi.fn()
372
+
.mockResolvedValueOnce({
373
+
ok: true,
374
+
json: async () => ({
375
+
service: [{ id: '#atproto_pds', serviceEndpoint: TEST_PDS }]
376
+
})
377
+
})
378
+
.mockResolvedValueOnce({
379
+
ok: false,
380
+
status: 404
381
+
});
382
+
vi.stubGlobal('fetch', mockFetch);
383
+
384
+
const blobCid = await fetchBlobCidFromRecord(TEST_DID, TEST_TID);
385
+
expect(blobCid).toBeNull();
386
+
});
387
+
388
+
it('returns null when getRecord fetch throws exception', async () => {
389
+
const mockFetch = vi.fn()
390
+
.mockResolvedValueOnce({
391
+
ok: true,
392
+
json: async () => ({
393
+
service: [{ id: '#atproto_pds', serviceEndpoint: TEST_PDS }]
394
+
})
395
+
})
396
+
.mockRejectedValueOnce(new Error('Network error'));
397
+
vi.stubGlobal('fetch', mockFetch);
398
+
399
+
const blobCid = await fetchBlobCidFromRecord(TEST_DID, TEST_TID);
400
+
expect(blobCid).toBeNull();
401
+
});
402
+
});
403
+
404
+
describe('downloadBlobUnauthenticated', () => {
405
+
beforeEach(() => {
406
+
vi.stubGlobal('fetch', vi.fn());
407
+
});
408
+
409
+
afterEach(() => {
410
+
vi.unstubAllGlobals();
411
+
});
412
+
413
+
it('downloads blob with correct headers', async () => {
414
+
const mockFetch = vi.fn()
415
+
// resolvePDSHost
416
+
.mockResolvedValueOnce({
417
+
ok: true,
418
+
json: async () => ({
419
+
service: [{ id: '#atproto_pds', serviceEndpoint: TEST_PDS }]
420
+
})
421
+
})
422
+
// getBlob
423
+
.mockResolvedValueOnce({
424
+
status: 200,
425
+
body: new ReadableStream(),
426
+
headers: new Headers({
427
+
'Content-Type': 'image/jpeg',
428
+
'Content-Length': '12345'
429
+
})
430
+
});
431
+
vi.stubGlobal('fetch', mockFetch);
432
+
433
+
const ctx = createExecutionContext();
434
+
const response = await downloadBlobUnauthenticated(TEST_DID, TEST_CID, ctx);
435
+
436
+
expect(response.status).toBe(200);
437
+
expect(response.headers.get('Content-Type')).toBe('image/jpeg');
438
+
expect(response.headers.get('Cache-Control')).toBe('public, max-age=31536000, immutable');
439
+
expect(response.headers.get('Access-Control-Allow-Origin')).toBe('*');
440
+
});
441
+
442
+
it('returns 400 when PDS resolution fails', async () => {
443
+
const mockFetch = vi.fn().mockResolvedValueOnce({
444
+
ok: false
445
+
});
446
+
vi.stubGlobal('fetch', mockFetch);
447
+
448
+
const ctx = createExecutionContext();
449
+
const response = await downloadBlobUnauthenticated(TEST_DID, TEST_CID, ctx);
450
+
451
+
expect(response.status).toBe(400);
452
+
expect(await response.text()).toBe('Failed to resolve PDS host');
453
+
});
454
+
455
+
it('returns 404 when blob not found', async () => {
456
+
const mockFetch = vi.fn()
457
+
.mockResolvedValueOnce({
458
+
ok: true,
459
+
json: async () => ({
460
+
service: [{ id: '#atproto_pds', serviceEndpoint: TEST_PDS }]
461
+
})
462
+
})
463
+
.mockResolvedValueOnce({
464
+
status: 404
465
+
});
466
+
vi.stubGlobal('fetch', mockFetch);
467
+
468
+
const ctx = createExecutionContext();
469
+
const response = await downloadBlobUnauthenticated(TEST_DID, TEST_CID, ctx);
470
+
471
+
expect(response.status).toBe(404);
472
+
});
473
+
474
+
it('returns 500 when fetch throws exception', async () => {
475
+
const mockFetch = vi.fn()
476
+
.mockResolvedValueOnce({
477
+
ok: true,
478
+
json: async () => ({
479
+
service: [{ id: '#atproto_pds', serviceEndpoint: TEST_PDS }]
480
+
})
481
+
})
482
+
.mockRejectedValueOnce(new Error('Network error'));
483
+
vi.stubGlobal('fetch', mockFetch);
484
+
485
+
const ctx = createExecutionContext();
486
+
const response = await downloadBlobUnauthenticated(TEST_DID, TEST_CID, ctx);
487
+
488
+
expect(response.status).toBe(500);
489
+
expect(await response.text()).toBe('Failed to download blob');
490
+
});
491
+
});
492
+
493
+
describe('Worker fetch handler', () => {
494
+
it('handles CORS preflight requests', async () => {
495
+
const request = new IncomingRequest('http://example.com/test/test', {
496
+
method: 'OPTIONS'
497
+
});
498
+
const ctx = createExecutionContext();
499
+
const response = await worker.fetch(request, env, ctx);
500
+
await waitOnExecutionContext(ctx);
501
+
502
+
expect(response.status).toBe(200);
503
+
expect(response.headers.get('Access-Control-Allow-Origin')).toBe('*');
504
+
expect(response.headers.get('Access-Control-Allow-Methods')).toBe('GET, OPTIONS');
505
+
});
506
+
507
+
it('returns 400 for invalid paths', async () => {
508
+
const request = new IncomingRequest('http://example.com/');
509
+
const ctx = createExecutionContext();
510
+
const response = await worker.fetch(request, env, ctx);
511
+
await waitOnExecutionContext(ctx);
512
+
513
+
expect(response.status).toBe(400);
514
+
expect(await response.text()).toContain('Invalid path');
515
+
});
516
+
517
+
it('returns 400 for paths with only handle', async () => {
518
+
const request = new IncomingRequest('http://example.com/bsky.app');
519
+
const ctx = createExecutionContext();
520
+
const response = await worker.fetch(request, env, ctx);
521
+
await waitOnExecutionContext(ctx);
522
+
523
+
expect(response.status).toBe(400);
524
+
});
525
+
526
+
it('strips file extensions from CID', async () => {
527
+
// This tests that extensions like .jpg, .png are stripped
528
+
// We'll test this by mocking and checking the CID used
529
+
const mockFetch = vi.fn()
530
+
// DNS resolution
531
+
.mockResolvedValueOnce({
532
+
ok: true,
533
+
json: async () => ({
534
+
Answer: [{ type: 16, data: `"did=${TEST_DID}"` }]
535
+
})
536
+
})
537
+
// PDS resolution
538
+
.mockResolvedValueOnce({
539
+
ok: true,
540
+
json: async () => ({
541
+
service: [{ id: '#atproto_pds', serviceEndpoint: TEST_PDS }]
542
+
})
543
+
})
544
+
// Blob download
545
+
.mockResolvedValueOnce({
546
+
status: 200,
547
+
body: new ReadableStream(),
548
+
headers: new Headers({ 'Content-Type': 'image/jpeg' })
549
+
});
550
+
vi.stubGlobal('fetch', mockFetch);
551
+
552
+
const request = new IncomingRequest(`http://example.com/${TEST_HANDLE}/${TEST_CID}.jpg`);
553
+
const ctx = createExecutionContext();
554
+
const response = await worker.fetch(request, env, ctx);
555
+
await waitOnExecutionContext(ctx);
556
+
557
+
// Should have made it to blob download (extension stripped correctly)
558
+
expect(mockFetch).toHaveBeenCalledTimes(3);
559
+
560
+
vi.unstubAllGlobals();
561
+
});
562
+
563
+
it('handles DID directly without resolution', async () => {
564
+
const mockFetch = vi.fn()
565
+
// PDS resolution
566
+
.mockResolvedValueOnce({
567
+
ok: true,
568
+
json: async () => ({
569
+
service: [{ id: '#atproto_pds', serviceEndpoint: TEST_PDS }]
570
+
})
571
+
})
572
+
// Blob download
573
+
.mockResolvedValueOnce({
574
+
status: 200,
575
+
body: new ReadableStream(),
576
+
headers: new Headers({ 'Content-Type': 'image/jpeg' })
577
+
});
578
+
vi.stubGlobal('fetch', mockFetch);
579
+
580
+
const request = new IncomingRequest(`http://example.com/${TEST_DID}/${TEST_CID}`);
581
+
const ctx = createExecutionContext();
582
+
const response = await worker.fetch(request, env, ctx);
583
+
await waitOnExecutionContext(ctx);
584
+
585
+
// Should skip DNS resolution since it's already a DID
586
+
expect(response.status).toBe(200);
587
+
expect(mockFetch).toHaveBeenCalledTimes(2);
588
+
589
+
vi.unstubAllGlobals();
590
+
});
591
+
592
+
it('returns 404 when handle cannot be resolved', async () => {
593
+
const mockFetch = vi.fn()
594
+
// DNS lookup fails
595
+
.mockResolvedValueOnce({ ok: false })
596
+
// HTTPS well-known fails
597
+
.mockResolvedValueOnce({ status: 404 });
598
+
vi.stubGlobal('fetch', mockFetch);
599
+
600
+
const request = new IncomingRequest('http://example.com/nonexistent.handle/somecid');
601
+
const ctx = createExecutionContext();
602
+
const response = await worker.fetch(request, env, ctx);
603
+
await waitOnExecutionContext(ctx);
604
+
605
+
expect(response.status).toBe(404);
606
+
expect(await response.text()).toBe('Handle not found');
607
+
608
+
vi.unstubAllGlobals();
609
+
});
610
+
611
+
it('uses cached DID from KV (plc format)', async () => {
612
+
const mockFetch = vi.fn()
613
+
// PDS resolution
614
+
.mockResolvedValueOnce({
615
+
ok: true,
616
+
json: async () => ({
617
+
service: [{ id: '#atproto_pds', serviceEndpoint: TEST_PDS }]
618
+
})
619
+
})
620
+
// Blob download
621
+
.mockResolvedValueOnce({
622
+
status: 200,
623
+
body: new ReadableStream(),
624
+
headers: new Headers({ 'Content-Type': 'image/jpeg' })
625
+
});
626
+
vi.stubGlobal('fetch', mockFetch);
19
627
20
-
it('responds with Hello World! (integration style)', async () => {
21
-
const response = await SELF.fetch('https://example.com');
22
-
expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`);
23
-
});
628
+
// Create a mock env with KV that returns a cached DID
629
+
const mockEnv = {
630
+
USER_CACHE: {
631
+
get: vi.fn().mockResolvedValue('ewvi7nxzyoun6zhxrhs64oiz'),
632
+
put: vi.fn()
633
+
}
634
+
};
635
+
636
+
const request = new IncomingRequest(`http://example.com/${TEST_HANDLE}/${TEST_CID}`);
637
+
const ctx = createExecutionContext();
638
+
const response = await worker.fetch(request, mockEnv as any, ctx);
639
+
await waitOnExecutionContext(ctx);
640
+
641
+
// Should use cached DID and skip DNS resolution
642
+
expect(response.status).toBe(200);
643
+
expect(mockEnv.USER_CACHE.get).toHaveBeenCalledWith(TEST_HANDLE);
644
+
// Only PDS + blob fetch, no DNS lookup
645
+
expect(mockFetch).toHaveBeenCalledTimes(2);
646
+
647
+
vi.unstubAllGlobals();
648
+
});
649
+
650
+
it('uses cached DID from KV (web format)', async () => {
651
+
const mockFetch = vi.fn()
652
+
// PDS resolution for did:web
653
+
.mockResolvedValueOnce({
654
+
ok: true,
655
+
json: async () => ({
656
+
service: [{ id: '#atproto_pds', serviceEndpoint: 'https://pds.example.com' }]
657
+
})
658
+
})
659
+
// Blob download
660
+
.mockResolvedValueOnce({
661
+
status: 200,
662
+
body: new ReadableStream(),
663
+
headers: new Headers({ 'Content-Type': 'image/png' })
664
+
});
665
+
vi.stubGlobal('fetch', mockFetch);
666
+
667
+
// Create a mock env with KV that returns a cached web DID
668
+
const mockEnv = {
669
+
USER_CACHE: {
670
+
get: vi.fn().mockResolvedValue('web:example.com'),
671
+
put: vi.fn()
672
+
}
673
+
};
674
+
675
+
const request = new IncomingRequest(`http://example.com/somehandle/${TEST_CID}`);
676
+
const ctx = createExecutionContext();
677
+
const response = await worker.fetch(request, mockEnv as any, ctx);
678
+
await waitOnExecutionContext(ctx);
679
+
680
+
expect(response.status).toBe(200);
681
+
expect(mockEnv.USER_CACHE.get).toHaveBeenCalled();
682
+
683
+
vi.unstubAllGlobals();
684
+
});
685
+
686
+
it('resolves TID to blob CID', async () => {
687
+
const mockFetch = vi.fn()
688
+
// PDS resolution (for fetchBlobCidFromRecord)
689
+
.mockResolvedValueOnce({
690
+
ok: true,
691
+
json: async () => ({
692
+
service: [{ id: '#atproto_pds', serviceEndpoint: TEST_PDS }]
693
+
})
694
+
})
695
+
// getRecord
696
+
.mockResolvedValueOnce({
697
+
ok: true,
698
+
json: async () => ({
699
+
uri: `at://${TEST_DID}/blue.imgs.blup.image/${TEST_TID}`,
700
+
cid: 'recordcid',
701
+
value: {
702
+
blob: { ref: { $link: TEST_CID } }
703
+
}
704
+
})
705
+
})
706
+
// PDS resolution (for downloadBlobUnauthenticated)
707
+
.mockResolvedValueOnce({
708
+
ok: true,
709
+
json: async () => ({
710
+
service: [{ id: '#atproto_pds', serviceEndpoint: TEST_PDS }]
711
+
})
712
+
})
713
+
// Blob download
714
+
.mockResolvedValueOnce({
715
+
status: 200,
716
+
body: new ReadableStream(),
717
+
headers: new Headers({ 'Content-Type': 'image/jpeg' })
718
+
});
719
+
vi.stubGlobal('fetch', mockFetch);
720
+
721
+
const request = new IncomingRequest(`http://example.com/${TEST_DID}/${TEST_TID}`);
722
+
const ctx = createExecutionContext();
723
+
const response = await worker.fetch(request, env, ctx);
724
+
await waitOnExecutionContext(ctx);
725
+
726
+
expect(response.status).toBe(200);
727
+
728
+
vi.unstubAllGlobals();
729
+
});
730
+
731
+
it('returns 404 when TID record not found', async () => {
732
+
const mockFetch = vi.fn()
733
+
// PDS resolution
734
+
.mockResolvedValueOnce({
735
+
ok: true,
736
+
json: async () => ({
737
+
service: [{ id: '#atproto_pds', serviceEndpoint: TEST_PDS }]
738
+
})
739
+
})
740
+
// getRecord returns no blob
741
+
.mockResolvedValueOnce({
742
+
ok: true,
743
+
json: async () => ({
744
+
uri: `at://${TEST_DID}/blue.imgs.blup.image/${TEST_TID}`,
745
+
cid: 'recordcid',
746
+
value: {}
747
+
})
748
+
});
749
+
vi.stubGlobal('fetch', mockFetch);
750
+
751
+
const request = new IncomingRequest(`http://example.com/${TEST_DID}/${TEST_TID}`);
752
+
const ctx = createExecutionContext();
753
+
const response = await worker.fetch(request, env, ctx);
754
+
await waitOnExecutionContext(ctx);
755
+
756
+
expect(response.status).toBe(404);
757
+
expect(await response.text()).toBe('Record not found');
758
+
759
+
vi.unstubAllGlobals();
760
+
});
761
+
762
+
it('returns 400 for invalid base62 CID encoding', async () => {
763
+
// Use a string that's detected as base62 but produces invalid CID bytes
764
+
// 'AAAA' is valid base62 but won't decode to a valid CID
765
+
const invalidBase62 = 'AAAA';
766
+
767
+
const request = new IncomingRequest(`http://example.com/${TEST_DID}/${invalidBase62}`);
768
+
const ctx = createExecutionContext();
769
+
const response = await worker.fetch(request, env, ctx);
770
+
await waitOnExecutionContext(ctx);
771
+
772
+
expect(response.status).toBe(400);
773
+
expect(await response.text()).toBe('Invalid CID encoding');
774
+
});
24
775
});