Enable LLMs to handle webhooks with plaintext files
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

add fetch and express adapter tests

+579 -4
+5 -2
packages/express/package.json
··· 19 19 ], 20 20 "scripts": { 21 21 "build": "tsc --build", 22 - "test": "echo \"No tests\" && exit 0" 22 + "test": "vitest run" 23 23 }, 24 24 "packageManager": "pnpm@11.0.0-dev.1005", 25 25 "dependencies": { ··· 29 29 "devDependencies": { 30 30 "@standard-schema/spec": "^1.1.0", 31 31 "@types/express": "^5.0.6", 32 - "typescript": "^5.9.3" 32 + "@types/supertest": "^7.2.0", 33 + "supertest": "^7.2.2", 34 + "typescript": "^5.9.3", 35 + "vitest": "^4.1.0" 33 36 } 34 37 }
+191
packages/express/src/__tests__/index.test.ts
··· 1 + import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; 2 + import * as fs from "node:fs/promises"; 3 + import * as os from "node:os"; 4 + import * as path from "node:path"; 5 + import express from "express"; 6 + import request from "supertest"; 7 + import { createLureHandler } from "../index.js"; 8 + 9 + let tmpDir: string; 10 + 11 + beforeEach(async () => { 12 + tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "lure-express-test-")); 13 + }); 14 + 15 + afterEach(async () => { 16 + await fs.rm(tmpDir, { recursive: true, force: true }); 17 + }); 18 + 19 + async function writeLure(name: string, content: string): Promise<void> { 20 + const filePath = path.join(tmpDir, name); 21 + await fs.mkdir(path.dirname(filePath), { recursive: true }); 22 + await fs.writeFile(filePath, content, "utf-8"); 23 + } 24 + 25 + describe("createLureHandler (express)", () => { 26 + it("calls next() for non-matching path", async () => { 27 + const handle = await createLureHandler({ 28 + basePath: "/webhooks", 29 + luresDir: tmpDir, 30 + callback: vi.fn(), 31 + }); 32 + 33 + const app = express(); 34 + app.use(handle); 35 + app.use((_req, res) => res.status(200).json({ ok: true })); 36 + 37 + const res = await request(app).post("/other").send("data"); 38 + expect(res.status).toBe(200); 39 + }); 40 + 41 + it("calls next() when path matches base but no lure exists", async () => { 42 + const handle = await createLureHandler({ 43 + basePath: "/webhooks", 44 + luresDir: tmpDir, 45 + callback: vi.fn(), 46 + }); 47 + 48 + const app = express(); 49 + app.use(handle); 50 + app.use((_req, res) => res.status(200).json({ ok: true })); 51 + 52 + const res = await request(app).post("/webhooks/nonexistent"); 53 + expect(res.status).toBe(200); 54 + }); 55 + 56 + it("responds 204 for a matching lure", async () => { 57 + await writeLure("github.lure", "---\n---\nEvent received\n"); 58 + const handle = await createLureHandler({ 59 + basePath: "/webhooks", 60 + luresDir: tmpDir, 61 + callback: vi.fn().mockResolvedValue(undefined), 62 + }); 63 + 64 + const app = express(); 65 + app.use(handle); 66 + 67 + const res = await request(app).post("/webhooks/github").send("body"); 68 + expect(res.status).toBe(204); 69 + }); 70 + 71 + it("does not prefix-match partial basePath segments", async () => { 72 + await writeLure("test.lure", "---\n---\nHello\n"); 73 + const handle = await createLureHandler({ 74 + basePath: "/webhooks", 75 + luresDir: tmpDir, 76 + callback: vi.fn(), 77 + }); 78 + 79 + const app = express(); 80 + app.use(handle); 81 + app.use((_req, res) => res.status(200).json({ ok: true })); 82 + 83 + const res = await request(app).post("/webhooksextra/test"); 84 + expect(res.status).toBe(200); 85 + }); 86 + 87 + it("calls callback with rendered prompt", async () => { 88 + await writeLure("push.lure", "---\n---\nPush event\n"); 89 + let resolveCallback!: () => void; 90 + const callbackDone = new Promise<void>((r) => { 91 + resolveCallback = r; 92 + }); 93 + const callback = vi.fn().mockImplementation(async () => { 94 + resolveCallback(); 95 + }); 96 + 97 + const handle = await createLureHandler({ 98 + basePath: "/hooks", 99 + luresDir: tmpDir, 100 + callback, 101 + }); 102 + 103 + const app = express(); 104 + app.use(handle); 105 + 106 + await request(app).post("/hooks/push").send("body"); 107 + await callbackDone; 108 + expect(callback).toHaveBeenCalledWith("Push event", undefined); 109 + }); 110 + 111 + it("forwards request headers to the template", async () => { 112 + await writeLure( 113 + "event.lure", 114 + "---\n---\nToken: {{ headers['x-token'] }}\n", 115 + ); 116 + let resolveCallback!: () => void; 117 + const callbackDone = new Promise<void>((r) => { 118 + resolveCallback = r; 119 + }); 120 + const callback = vi.fn().mockImplementation(async () => { 121 + resolveCallback(); 122 + }); 123 + 124 + const handle = await createLureHandler({ 125 + basePath: "/hooks", 126 + luresDir: tmpDir, 127 + callback, 128 + }); 129 + 130 + const app = express(); 131 + app.use(handle); 132 + 133 + await request(app).post("/hooks/event").set("x-token", "abc123").send(""); 134 + await callbackDone; 135 + expect(callback).toHaveBeenCalledWith("Token: abc123", undefined); 136 + }); 137 + 138 + it("forwards query params to the template", async () => { 139 + await writeLure("search.lure", "---\n---\nQuery: {{ query.q }}\n"); 140 + let resolveCallback!: () => void; 141 + const callbackDone = new Promise<void>((r) => { 142 + resolveCallback = r; 143 + }); 144 + const callback = vi.fn().mockImplementation(async () => { 145 + resolveCallback(); 146 + }); 147 + 148 + const handle = await createLureHandler({ 149 + basePath: "/hooks", 150 + luresDir: tmpDir, 151 + callback, 152 + }); 153 + 154 + const app = express(); 155 + app.use(handle); 156 + 157 + await request(app).post("/hooks/search?q=hello").send(""); 158 + await callbackDone; 159 + expect(callback).toHaveBeenCalledWith("Query: hello", undefined); 160 + }); 161 + 162 + it("reads and forwards body as rawBody", async () => { 163 + await writeLure( 164 + "data.lure", 165 + "---\npayload:\n contentType: json\n---\nAction: {{ payload.action }}\n", 166 + ); 167 + let resolveCallback!: () => void; 168 + const callbackDone = new Promise<void>((r) => { 169 + resolveCallback = r; 170 + }); 171 + const callback = vi.fn().mockImplementation(async () => { 172 + resolveCallback(); 173 + }); 174 + 175 + const handle = await createLureHandler({ 176 + basePath: "/hooks", 177 + luresDir: tmpDir, 178 + callback, 179 + }); 180 + 181 + const app = express(); 182 + app.use(handle); 183 + 184 + await request(app) 185 + .post("/hooks/data") 186 + .set("content-type", "application/json") 187 + .send(JSON.stringify({ action: "opened" })); 188 + await callbackDone; 189 + expect(callback).toHaveBeenCalledWith("Action: opened", undefined); 190 + }); 191 + });
+3 -2
packages/fetch/package.json
··· 19 19 ], 20 20 "scripts": { 21 21 "build": "tsc --build", 22 - "test": "echo \"No tests\" && exit 0" 22 + "test": "vitest run" 23 23 }, 24 24 "packageManager": "pnpm@11.0.0-dev.1005", 25 25 "dependencies": { ··· 27 27 }, 28 28 "devDependencies": { 29 29 "@standard-schema/spec": "^1.1.0", 30 - "typescript": "^5.9.3" 30 + "typescript": "^5.9.3", 31 + "vitest": "^4.1.0" 31 32 } 32 33 }
+182
packages/fetch/src/__tests__/index.test.ts
··· 1 + import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; 2 + import * as fs from "node:fs/promises"; 3 + import * as os from "node:os"; 4 + import * as path from "node:path"; 5 + import { createLureHandler } from "../index.js"; 6 + 7 + let tmpDir: string; 8 + 9 + beforeEach(async () => { 10 + tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "lure-fetch-test-")); 11 + }); 12 + 13 + afterEach(async () => { 14 + await fs.rm(tmpDir, { recursive: true, force: true }); 15 + }); 16 + 17 + async function writeLure(name: string, content: string): Promise<void> { 18 + const filePath = path.join(tmpDir, name); 19 + await fs.mkdir(path.dirname(filePath), { recursive: true }); 20 + await fs.writeFile(filePath, content, "utf-8"); 21 + } 22 + 23 + describe("createLureHandler (fetch)", () => { 24 + it("returns null for non-matching path without reading body", async () => { 25 + await writeLure("github.lure", "---\n---\nHello\n"); 26 + const handle = await createLureHandler({ 27 + basePath: "/webhooks", 28 + luresDir: tmpDir, 29 + callback: vi.fn(), 30 + }); 31 + 32 + const req = new Request("http://localhost/other/path", { 33 + method: "POST", 34 + body: "data", 35 + }); 36 + const result = await handle(req); 37 + 38 + expect(result).toBeNull(); 39 + expect(req.bodyUsed).toBe(false); 40 + }); 41 + 42 + it("returns null for matching base but no lure", async () => { 43 + const handle = await createLureHandler({ 44 + basePath: "/webhooks", 45 + luresDir: tmpDir, 46 + callback: vi.fn(), 47 + }); 48 + 49 + const req = new Request("http://localhost/webhooks/nonexistent", { 50 + method: "POST", 51 + }); 52 + expect(await handle(req)).toBeNull(); 53 + }); 54 + 55 + it("returns 204 for a matching lure", async () => { 56 + await writeLure("github.lure", "---\n---\nEvent received\n"); 57 + const handle = await createLureHandler({ 58 + basePath: "/webhooks", 59 + luresDir: tmpDir, 60 + callback: vi.fn().mockResolvedValue(undefined), 61 + }); 62 + 63 + const req = new Request("http://localhost/webhooks/github", { 64 + method: "POST", 65 + }); 66 + const result = await handle(req); 67 + expect(result?.status).toBe(204); 68 + }); 69 + 70 + it("does not prefix-match partial basePath segments", async () => { 71 + await writeLure("test.lure", "---\n---\nHello\n"); 72 + const handle = await createLureHandler({ 73 + basePath: "/webhooks", 74 + luresDir: tmpDir, 75 + callback: vi.fn(), 76 + }); 77 + 78 + const req = new Request("http://localhost/webhooksextra/test", { 79 + method: "POST", 80 + }); 81 + expect(await handle(req)).toBeNull(); 82 + }); 83 + 84 + it("calls callback with rendered prompt", async () => { 85 + await writeLure("push.lure", "---\n---\nPush event\n"); 86 + let resolveCallback!: () => void; 87 + const callbackDone = new Promise<void>((r) => { 88 + resolveCallback = r; 89 + }); 90 + const callback = vi.fn().mockImplementation(async () => { 91 + resolveCallback(); 92 + }); 93 + 94 + const handle = await createLureHandler({ 95 + basePath: "/hooks", 96 + luresDir: tmpDir, 97 + callback, 98 + }); 99 + 100 + const req = new Request("http://localhost/hooks/push", { method: "POST" }); 101 + await handle(req); 102 + await callbackDone; 103 + expect(callback).toHaveBeenCalledWith("Push event", undefined); 104 + }); 105 + 106 + it("forwards query params to the template", async () => { 107 + await writeLure("search.lure", "---\n---\nQuery: {{ query.q }}\n"); 108 + let resolveCallback!: () => void; 109 + const callbackDone = new Promise<void>((r) => { 110 + resolveCallback = r; 111 + }); 112 + const callback = vi.fn().mockImplementation(async () => { 113 + resolveCallback(); 114 + }); 115 + 116 + const handle = await createLureHandler({ 117 + basePath: "/hooks", 118 + luresDir: tmpDir, 119 + callback, 120 + }); 121 + 122 + const req = new Request("http://localhost/hooks/search?q=hello", { 123 + method: "POST", 124 + }); 125 + await handle(req); 126 + await callbackDone; 127 + expect(callback).toHaveBeenCalledWith("Query: hello", undefined); 128 + }); 129 + 130 + it("forwards request headers to the template", async () => { 131 + await writeLure( 132 + "event.lure", 133 + "---\n---\nToken: {{ headers['x-token'] }}\n", 134 + ); 135 + let resolveCallback!: () => void; 136 + const callbackDone = new Promise<void>((r) => { 137 + resolveCallback = r; 138 + }); 139 + const callback = vi.fn().mockImplementation(async () => { 140 + resolveCallback(); 141 + }); 142 + 143 + const handle = await createLureHandler({ 144 + basePath: "/hooks", 145 + luresDir: tmpDir, 146 + callback, 147 + }); 148 + 149 + const req = new Request("http://localhost/hooks/event", { 150 + method: "POST", 151 + headers: { "x-token": "abc123" }, 152 + }); 153 + await handle(req); 154 + await callbackDone; 155 + expect(callback).toHaveBeenCalledWith("Token: abc123", undefined); 156 + }); 157 + 158 + it("reads and forwards body as rawBody", async () => { 159 + await writeLure("data.lure", "---\npayload:\n contentType: json\n---\nAction: {{ payload.action }}\n"); 160 + let resolveCallback!: () => void; 161 + const callbackDone = new Promise<void>((r) => { 162 + resolveCallback = r; 163 + }); 164 + const callback = vi.fn().mockImplementation(async () => { 165 + resolveCallback(); 166 + }); 167 + 168 + const handle = await createLureHandler({ 169 + basePath: "/hooks", 170 + luresDir: tmpDir, 171 + callback, 172 + }); 173 + 174 + const req = new Request("http://localhost/hooks/data", { 175 + method: "POST", 176 + body: JSON.stringify({ action: "opened" }), 177 + }); 178 + await handle(req); 179 + await callbackDone; 180 + expect(callback).toHaveBeenCalledWith("Action: opened", undefined); 181 + }); 182 + });
+198
pnpm-lock.yaml
··· 58 58 '@types/express': 59 59 specifier: ^5.0.6 60 60 version: 5.0.6 61 + '@types/supertest': 62 + specifier: ^7.2.0 63 + version: 7.2.0 64 + supertest: 65 + specifier: ^7.2.2 66 + version: 7.2.2 61 67 typescript: 62 68 specifier: ^5.9.3 63 69 version: 5.9.3 70 + vitest: 71 + specifier: ^4.1.0 72 + version: 4.1.0(@types/node@25.5.0)(vite@8.0.0(@types/node@25.5.0)) 64 73 65 74 packages/fetch: 66 75 dependencies: ··· 74 83 typescript: 75 84 specifier: ^5.9.3 76 85 version: 5.9.3 86 + vitest: 87 + specifier: ^4.1.0 88 + version: 4.1.0(@types/node@25.5.0)(vite@8.0.0(@types/node@25.5.0)) 77 89 78 90 packages: 79 91 ··· 97 109 98 110 '@napi-rs/wasm-runtime@1.1.1': 99 111 resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} 112 + 113 + '@noble/hashes@1.8.0': 114 + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} 115 + engines: {node: ^14.21.3 || >=16} 100 116 101 117 '@oxc-project/runtime@0.115.0': 102 118 resolution: {integrity: sha512-Rg8Wlt5dCbXhQnsXPrkOjL1DTSvXLgb2R/KYfnf1/K+R0k6UMLEmbQXPM+kwrWqSmWA2t0B1EtHy2/3zikQpvQ==} ··· 333 349 cpu: [x64] 334 350 os: [win32] 335 351 352 + '@paralleldrive/cuid2@2.3.1': 353 + resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==} 354 + 336 355 '@rolldown/binding-android-arm64@1.0.0-rc.9': 337 356 resolution: {integrity: sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==} 338 357 engines: {node: ^20.19.0 || >=22.12.0} ··· 440 459 '@types/connect@3.4.38': 441 460 resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} 442 461 462 + '@types/cookiejar@2.1.5': 463 + resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} 464 + 443 465 '@types/deep-eql@4.0.2': 444 466 resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} 445 467 ··· 454 476 455 477 '@types/http-errors@2.0.5': 456 478 resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} 479 + 480 + '@types/methods@1.1.4': 481 + resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} 457 482 458 483 '@types/node@25.5.0': 459 484 resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==} ··· 470 495 '@types/serve-static@2.2.0': 471 496 resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} 472 497 498 + '@types/superagent@8.1.9': 499 + resolution: {integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==} 500 + 501 + '@types/supertest@7.2.0': 502 + resolution: {integrity: sha512-uh2Lv57xvggst6lCqNdFAmDSvoMG7M/HDtX4iUCquxQ5EGPtaPM5PL5Hmi7LCvOG8db7YaCPNJEeoI8s/WzIQw==} 503 + 473 504 '@vitest/expect@4.1.0': 474 505 resolution: {integrity: sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==} 475 506 ··· 512 543 arktype@2.2.0: 513 544 resolution: {integrity: sha512-t54MZ7ti5BhOEvzEkgKnWvqj+UbDfWig+DHr5I34xatymPusKLS0lQpNJd8M6DzmIto2QGszHfNKoFIT8tMCZQ==} 514 545 546 + asap@2.0.6: 547 + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} 548 + 515 549 assertion-error@2.0.1: 516 550 resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} 517 551 engines: {node: '>=12'} 552 + 553 + asynckit@0.4.0: 554 + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} 518 555 519 556 body-parser@2.2.2: 520 557 resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} ··· 540 577 resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} 541 578 engines: {node: '>= 20.19.0'} 542 579 580 + combined-stream@1.0.8: 581 + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} 582 + engines: {node: '>= 0.8'} 583 + 543 584 commander@10.0.1: 544 585 resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} 545 586 engines: {node: '>=14'} 587 + 588 + component-emitter@1.3.1: 589 + resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} 546 590 547 591 content-disposition@1.0.1: 548 592 resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} ··· 563 607 resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} 564 608 engines: {node: '>= 0.6'} 565 609 610 + cookiejar@2.1.4: 611 + resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} 612 + 566 613 debug@4.4.3: 567 614 resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} 568 615 engines: {node: '>=6.0'} ··· 572 619 supports-color: 573 620 optional: true 574 621 622 + delayed-stream@1.0.0: 623 + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} 624 + engines: {node: '>=0.4.0'} 625 + 575 626 depd@2.0.0: 576 627 resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} 577 628 engines: {node: '>= 0.8'} ··· 579 630 detect-libc@2.1.2: 580 631 resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} 581 632 engines: {node: '>=8'} 633 + 634 + dezalgo@1.0.4: 635 + resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} 582 636 583 637 dunder-proto@1.0.1: 584 638 resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} ··· 606 660 resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} 607 661 engines: {node: '>= 0.4'} 608 662 663 + es-set-tostringtag@2.1.0: 664 + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} 665 + engines: {node: '>= 0.4'} 666 + 609 667 escape-html@1.0.3: 610 668 resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} 611 669 ··· 636 694 resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} 637 695 engines: {node: '>=0.10.0'} 638 696 697 + fast-safe-stringify@2.1.1: 698 + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} 699 + 639 700 fdir@6.5.0: 640 701 resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} 641 702 engines: {node: '>=12.0.0'} ··· 649 710 resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} 650 711 engines: {node: '>= 18.0.0'} 651 712 713 + form-data@4.0.5: 714 + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} 715 + engines: {node: '>= 6'} 716 + 717 + formidable@3.5.4: 718 + resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==} 719 + engines: {node: '>=14.0.0'} 720 + 652 721 forwarded@0.2.0: 653 722 resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} 654 723 engines: {node: '>= 0.6'} ··· 683 752 684 753 has-symbols@1.1.0: 685 754 resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} 755 + engines: {node: '>= 0.4'} 756 + 757 + has-tostringtag@1.0.2: 758 + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} 686 759 engines: {node: '>= 0.4'} 687 760 688 761 hasown@2.0.2: ··· 809 882 resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} 810 883 engines: {node: '>=18'} 811 884 885 + methods@1.1.2: 886 + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} 887 + engines: {node: '>= 0.6'} 888 + 889 + mime-db@1.52.0: 890 + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} 891 + engines: {node: '>= 0.6'} 892 + 812 893 mime-db@1.54.0: 813 894 resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} 814 895 engines: {node: '>= 0.6'} 815 896 897 + mime-types@2.1.35: 898 + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} 899 + engines: {node: '>= 0.6'} 900 + 816 901 mime-types@3.0.2: 817 902 resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} 818 903 engines: {node: '>=18'} 819 904 905 + mime@2.6.0: 906 + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} 907 + engines: {node: '>=4.0.0'} 908 + hasBin: true 909 + 820 910 ms@2.1.3: 821 911 resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 822 912 ··· 974 1064 resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} 975 1065 engines: {node: '>=0.10.0'} 976 1066 1067 + superagent@10.3.0: 1068 + resolution: {integrity: sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==} 1069 + engines: {node: '>=14.18.0'} 1070 + 1071 + supertest@7.2.2: 1072 + resolution: {integrity: sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==} 1073 + engines: {node: '>=14.18.0'} 1074 + 977 1075 tinybench@2.9.0: 978 1076 resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} 979 1077 ··· 1139 1237 '@tybys/wasm-util': 0.10.1 1140 1238 optional: true 1141 1239 1240 + '@noble/hashes@1.8.0': {} 1241 + 1142 1242 '@oxc-project/runtime@0.115.0': {} 1143 1243 1144 1244 '@oxc-project/types@0.115.0': {} ··· 1257 1357 '@oxlint/binding-win32-x64-msvc@1.56.0': 1258 1358 optional: true 1259 1359 1360 + '@paralleldrive/cuid2@2.3.1': 1361 + dependencies: 1362 + '@noble/hashes': 1.8.0 1363 + 1260 1364 '@rolldown/binding-android-arm64@1.0.0-rc.9': 1261 1365 optional: true 1262 1366 ··· 1326 1430 '@types/connect@3.4.38': 1327 1431 dependencies: 1328 1432 '@types/node': 25.5.0 1433 + 1434 + '@types/cookiejar@2.1.5': {} 1329 1435 1330 1436 '@types/deep-eql@4.0.2': {} 1331 1437 ··· 1346 1452 1347 1453 '@types/http-errors@2.0.5': {} 1348 1454 1455 + '@types/methods@1.1.4': {} 1456 + 1349 1457 '@types/node@25.5.0': 1350 1458 dependencies: 1351 1459 undici-types: 7.18.2 ··· 1363 1471 '@types/http-errors': 2.0.5 1364 1472 '@types/node': 25.5.0 1365 1473 1474 + '@types/superagent@8.1.9': 1475 + dependencies: 1476 + '@types/cookiejar': 2.1.5 1477 + '@types/methods': 1.1.4 1478 + '@types/node': 25.5.0 1479 + form-data: 4.0.5 1480 + 1481 + '@types/supertest@7.2.0': 1482 + dependencies: 1483 + '@types/methods': 1.1.4 1484 + '@types/superagent': 8.1.9 1485 + 1366 1486 '@vitest/expect@4.1.0': 1367 1487 dependencies: 1368 1488 '@standard-schema/spec': 1.1.0 ··· 1423 1543 '@ark/util': 0.56.0 1424 1544 arkregex: 0.0.5 1425 1545 1546 + asap@2.0.6: {} 1547 + 1426 1548 assertion-error@2.0.1: {} 1549 + 1550 + asynckit@0.4.0: {} 1427 1551 1428 1552 body-parser@2.2.2: 1429 1553 dependencies: ··· 1457 1581 dependencies: 1458 1582 readdirp: 5.0.0 1459 1583 1584 + combined-stream@1.0.8: 1585 + dependencies: 1586 + delayed-stream: 1.0.0 1587 + 1460 1588 commander@10.0.1: {} 1589 + 1590 + component-emitter@1.3.1: {} 1461 1591 1462 1592 content-disposition@1.0.1: {} 1463 1593 ··· 1469 1599 1470 1600 cookie@0.7.2: {} 1471 1601 1602 + cookiejar@2.1.4: {} 1603 + 1472 1604 debug@4.4.3: 1473 1605 dependencies: 1474 1606 ms: 2.1.3 1607 + 1608 + delayed-stream@1.0.0: {} 1475 1609 1476 1610 depd@2.0.0: {} 1477 1611 1478 1612 detect-libc@2.1.2: {} 1479 1613 1614 + dezalgo@1.0.4: 1615 + dependencies: 1616 + asap: 2.0.6 1617 + wrappy: 1.0.2 1618 + 1480 1619 dunder-proto@1.0.1: 1481 1620 dependencies: 1482 1621 call-bind-apply-helpers: 1.0.2 ··· 1496 1635 es-object-atoms@1.1.1: 1497 1636 dependencies: 1498 1637 es-errors: 1.3.0 1638 + 1639 + es-set-tostringtag@2.1.0: 1640 + dependencies: 1641 + es-errors: 1.3.0 1642 + get-intrinsic: 1.3.0 1643 + has-tostringtag: 1.0.2 1644 + hasown: 2.0.2 1499 1645 1500 1646 escape-html@1.0.3: {} 1501 1647 ··· 1548 1694 dependencies: 1549 1695 is-extendable: 0.1.1 1550 1696 1697 + fast-safe-stringify@2.1.1: {} 1698 + 1551 1699 fdir@6.5.0(picomatch@4.0.3): 1552 1700 optionalDependencies: 1553 1701 picomatch: 4.0.3 ··· 1563 1711 transitivePeerDependencies: 1564 1712 - supports-color 1565 1713 1714 + form-data@4.0.5: 1715 + dependencies: 1716 + asynckit: 0.4.0 1717 + combined-stream: 1.0.8 1718 + es-set-tostringtag: 2.1.0 1719 + hasown: 2.0.2 1720 + mime-types: 2.1.35 1721 + 1722 + formidable@3.5.4: 1723 + dependencies: 1724 + '@paralleldrive/cuid2': 2.3.1 1725 + dezalgo: 1.0.4 1726 + once: 1.4.0 1727 + 1566 1728 forwarded@0.2.0: {} 1567 1729 1568 1730 fresh@2.0.0: {} ··· 1600 1762 strip-bom-string: 1.0.0 1601 1763 1602 1764 has-symbols@1.1.0: {} 1765 + 1766 + has-tostringtag@1.0.2: 1767 + dependencies: 1768 + has-symbols: 1.1.0 1603 1769 1604 1770 hasown@2.0.2: 1605 1771 dependencies: ··· 1695 1861 1696 1862 merge-descriptors@2.0.0: {} 1697 1863 1864 + methods@1.1.2: {} 1865 + 1866 + mime-db@1.52.0: {} 1867 + 1698 1868 mime-db@1.54.0: {} 1699 1869 1870 + mime-types@2.1.35: 1871 + dependencies: 1872 + mime-db: 1.52.0 1873 + 1700 1874 mime-types@3.0.2: 1701 1875 dependencies: 1702 1876 mime-db: 1.54.0 1877 + 1878 + mime@2.6.0: {} 1703 1879 1704 1880 ms@2.1.3: {} 1705 1881 ··· 1914 2090 std-env@4.0.0: {} 1915 2091 1916 2092 strip-bom-string@1.0.0: {} 2093 + 2094 + superagent@10.3.0: 2095 + dependencies: 2096 + component-emitter: 1.3.1 2097 + cookiejar: 2.1.4 2098 + debug: 4.4.3 2099 + fast-safe-stringify: 2.1.1 2100 + form-data: 4.0.5 2101 + formidable: 3.5.4 2102 + methods: 1.1.2 2103 + mime: 2.6.0 2104 + qs: 6.15.0 2105 + transitivePeerDependencies: 2106 + - supports-color 2107 + 2108 + supertest@7.2.2: 2109 + dependencies: 2110 + cookie-signature: 1.2.2 2111 + methods: 1.1.2 2112 + superagent: 10.3.0 2113 + transitivePeerDependencies: 2114 + - supports-color 1917 2115 1918 2116 tinybench@2.9.0: {} 1919 2117