From 504207707263f7f41869d9da7b909cf187037306 Mon Sep 17 00:00:00 2001 From: Denis Carriere Date: Mon, 12 Feb 2024 18:47:41 -0500 Subject: [PATCH 1/6] refactor auth --- .env.example | 11 +- README.md | 74 +- bin/cli.ts | 12 +- biome.json | 2 +- examples/bun/Dockerfile | 13 +- examples/bun/http.ts | 18 +- examples/deno/Dockerfile | 10 - examples/deno/http.ts | 39 - examples/deno/package.json | 6 - examples/express/{http.js => http.ts} | 24 +- examples/express/package-lock.json | 715 +++++++++++++++++- examples/express/package.json | 13 +- examples/http/ping-isVerified.http | 7 +- examples/http/ping-not-isVerified.http | 8 +- examples/http/post-invalid-headers.http | 4 + .../http/post-invalid-signature-length.http | 6 + examples/http/post-invalid-signature.http | 6 + examples/http/post-invalid-timestamp.http | 6 + examples/http/post.http | 7 +- examples/node/http.ts | 22 +- examples/node/package-lock.json | 2 +- index.ts | 8 +- package-lock.json | 356 +++++++-- package.json | 10 +- src/auth/cached.ts | 56 -- src/auth/ed25519.bench.ts | 21 +- src/auth/ed25519.spec.ts | 56 ++ src/auth/ed25519.ts | 57 +- src/auth/index.ts | 2 - src/auth/sign.test.ts | 43 -- src/auth/verify.test.ts | 58 -- src/ping.ts | 6 +- src/postWebhook.ts | 18 +- tsconfig.json | 3 +- 34 files changed, 1216 insertions(+), 483 deletions(-) delete mode 100644 examples/deno/Dockerfile delete mode 100644 examples/deno/http.ts delete mode 100644 examples/deno/package.json rename examples/express/{http.js => http.ts} (53%) create mode 100644 examples/http/post-invalid-headers.http create mode 100644 examples/http/post-invalid-signature-length.http create mode 100644 examples/http/post-invalid-signature.http create mode 100644 examples/http/post-invalid-timestamp.http delete mode 100644 src/auth/cached.ts create mode 100644 src/auth/ed25519.spec.ts delete mode 100644 src/auth/index.ts delete mode 100644 src/auth/sign.test.ts delete mode 100644 src/auth/verify.test.ts diff --git a/.env.example b/.env.example index 2a5fbe6..def1213 100644 --- a/.env.example +++ b/.env.example @@ -1,15 +1,20 @@ # Webhook SECRET_KEY="" -PUBLIC_KEY="" WEBHOOK_URL=http://127.0.0.1:3000 # Get Substreams API Key # https://app.pinax.network # https://app.streamingfast.io/ -SUBSTREAMS_API_KEY="" +SUBSTREAMS_API_KEY="" SUBSTREAMS_ENDPOINT=https://eth.substreams.pinax.network:443 -# Substreams package +# Substreams Package (*.spkg) MANIFEST=https://github.com/pinax-network/substreams/releases/download/blocks-v0.1.0/blocks-v0.1.0.spkg MODULE_NAME=map_blocks START_BLOCK=-10 + +# Webhook (Optional) +DISABLE_PING=false +DISABLE_SIGNATURE=false +VERBOSE=true +MAXIMUM_ATTEMPTS=100 diff --git a/README.md b/README.md index 6651b95..29bcdac 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,9 @@ ## HTTP Server examples - [`Bun`](/examples/bun) - https://bun.sh/ -- [`Deno`](/examples/deno) - https://deno.com/runtime - [`Express`](/examples/express) - https://expressjs.com/ -- [`node:http`](/examples/node:http) - https://nodejs.org/api/http.html -- [POST request](/examples/post.http) +- [`node:http`](/examples/node) - https://nodejs.org/api/http.html +- [POST request](/examples/http) ## 📖 References @@ -30,8 +29,8 @@ The POST message will be a JSON object with the following structure: ```http POST http://localhost:3000 HTTP/1.1 content-type: application/json -x-signature-ed25519: 6ec208fc250059fdb0fa543e01339ee3c6967da6fc7b6bf86dcd8217fa2e130ce2e17a5258fcf9bbe415de223d00eaee2f6949ef3a44594b42e7fb1a53481802 -x-signature-timestamp: 1696733583 +x-signature-ed25519: 8f01c66ccda5b987c43d913290419572ea586dbef2077fa166c4a84797e1d2c76b305bc67ed43efb1fc841562620a61cb59c4d8a13de689a2e98ead19190f80c +x-signature-timestamp: 1707776632 ``` **body** @@ -41,23 +40,23 @@ x-signature-timestamp: 1696733583 ```json { "status": 200, - "cursor": "T0S2BNqDj6a8pKMA6bEXAaWwLpc_DFltXAvkKhhBj4L29XqRiMmiVjVzbU_UxPzyiRLsSV-q2tzLEih6oMZR7oLpwbA2vHI_F39_l9vm_ODoe6CjP1tJdekzCuzcN9DRWD7eYgv7c7EK6dXiMqeMM0ZkNsEjfmLn2j0EpYJWdaUVunUzlT2vdc6Ag_iU-dAQrOV0QLelxyOkUzJ-fx5cbJ6GNaPKuW51bQ==", + "cursor": "OJGbpO9ZnZcwvxW38_FO8KWwLpcyA1lrUQPgKRFL04Py8yCW35v1VTB1O0-Elami3RztQlOp2tmcHC9y9ZQFuoDrxLpj6yU-FXorwoHr_OfqLPumMQwTJ-hgWeuKYNLeWDjTagn4ersEtNGzbvLaY0UxZZUhK2G62z1VptdXJfEWuiJmyjmrIZrRhK-WoNAS_rEkQ7L1xCmhDzJ4K0dTPcSDNPKZuDR2", "session": { - "traceId": "4ebea20349c16844d92bf6c961f627fa", - "resolvedStartBlock": 32900744 + "traceId": "3cbb0a1c772a47a72995d95f4c6d2cff", + "resolvedStartBlock": 53448515 }, "clock": { - "timestamp": "2022-09-09T20:23:38.000Z", - "number": 32901090, - "id": "9058ded4fd65b4de2d772564366f1b61bc328bac7a4c4b87d73ca6ab4bae6be8" + "timestamp": "2024-02-12T22:23:51.000Z", + "number": 53448530, + "id": "f843bc26cea0cbd50b09699546a8a97de6a1727646c17a857c5d8d868fc26142" }, "manifest": { "substreamsEndpoint": "https://polygon.substreams.pinax.network:443", "chain": "polygon", "finalBlockOnly": "false", - "moduleName": "map_block_stats", - "type": "subtivity.v1.BlockStats", - "moduleHash": "6fb7bbc60685bbfc1cd209d26697639e05efdb24" + "moduleName": "map_blocks", + "type": "sf.substreams.v1.Clock", + "moduleHash": "44c506941d5f30db6cca01692624395d1ac40cd1" }, "data": { ... @@ -71,21 +70,18 @@ x-signature-timestamp: 1696733583 import nacl from "tweetnacl"; // ...HTTP server +const PUBLIC_KEY = "APPLICATION_PUBLIC_KEY"; // get headers and body from POST request -const rawBody = await request.text(); const signature = request.headers.get("x-signature-ed25519"); -const expiry = Number(request.headers.get("x-signature-ed25519-expiry")); -const publicKey = request.headers.get("x-signature-ed25519-public-key"); - -if (new Date().getTime() >= expiry) return new Response("signature expired", { status: 401 }); +const timestamp = request.headers.get("x-signature-timestamp"); +const body = await request.text(); // validate signature using public key -const payload = JSON.stringify({ exp: expiry, id: publicKey }); const isVerified = nacl.sign.detached.verify( - Buffer.from(payload), + Buffer.from(timestamp + body), Buffer.from(signature, "hex"), - Buffer.from(publicKey, "hex") + Buffer.from(PUBLIC_KEY, "hex") ); if (!isVerified) { @@ -98,17 +94,23 @@ if (!isVerified) { ```env # Webhook SECRET_KEY="" -PUBLIC_KEY="" WEBHOOK_URL=http://127.0.0.1:3000 -# Substreams endpoint -SUBSTREAMS_API_TOKEN="" -SUBSTREAMS_ENDPOINT=https://polygon.substreams.pinax.network:443 - -# Substreams package -MANIFEST=https://github.com/pinax-network/subtivity-substreams/releases/download/v0.3.0/subtivity-ethereum-v0.3.0.spkg -MODULE_NAME=map_block_stats -START_BLOCK=-1 +# Get Substreams API Key +# https://app.pinax.network +# https://app.streamingfast.io/ +SUBSTREAMS_API_KEY="" + +# Substreams Package (*.spkg) +MANIFEST=https://github.com/pinax-network/substreams/releases/download/blocks-v0.1.0/blocks-v0.1.0.spkg +MODULE_NAME=map_blocks +START_BLOCK=-10 + +# Webhook (Optional) +DISABLE_PING=false +DISABLE_SIGNATURE=false +VERBOSE=true +MAXIMUM_ATTEMPTS=100 ``` ## Help @@ -127,12 +129,11 @@ Options: -s --start-block Start block to stream from (defaults to -1, which means the initialBlock of the first module you are streaming) (default: "-1", env: START_BLOCK) -t --stop-block Stop block to end stream at, inclusively (env: STOP_BLOCK) -p, --params Set a params for parameterizable modules. Can be specified multiple times. (ex: -p module1=valA -p module2=valX&valY) (default: [], env: PARAMS) - --substreams-api-token API token for the substream endpoint or API key if '--auth-issue-url' is specified (default: "", env: SUBSTREAMS_API_TOKEN) - --auth-issue-url URL used to issue a token (default: "https://auth.pinax.network/v1/auth/issue", env: AUTH_ISSUE_URL) + --substreams-api-key API key for the Substream endpoint (env: SUBSTREAMS_API_KEY) --delay-before-start Delay (ms) before starting Substreams (default: 0, env: DELAY_BEFORE_START) --cursor-path File path or URL to cursor lock file (default: "cursor.lock", env: CURSOR_PATH) --http-cursor-auth Basic auth credentials for http cursor (ex: username:password) (env: HTTP_CURSOR_AUTH) - --production-mode Enable production mode, allows cached substreams data if available (default: "false", env: PRODUCTION_MODE) + --production-mode Enable production mode, allows cached Substreams data if available (default: "false", env: PRODUCTION_MODE) --inactivity-seconds If set, the sink will stop when inactive for over a certain amount of seconds (default: 300, env: INACTIVITY_SECONDS) --hostname The process will listen on this hostname for any HTTP and Prometheus metrics requests (default: "localhost", env: HOSTNAME) --port The process will listen on this port for any HTTP and Prometheus metrics requests (default: 9102, env: PORT) @@ -143,7 +144,10 @@ Options: --verbose Enable verbose logging (default: "false", env: VERBOSE) --webhook-url Webhook URL to send POST (env: WEBHOOK_URL) --secret-key TweetNaCl Secret-key to sign POST data payload (env: SECRET_KEY) - --disable-ping Disable ping on init (default: false, env: DISABLE_PING) + --disable-ping Disable ping on init (choices: "true", "false", default: false, env: DISABLE_PING) + --maximum-attempts Maximum attempts to retry POST (default: 100, env: MAXIMUM_ATTEMPTS) + --disable-signature Disable Ed25519 signature (choices: "true", "false", default: false, env: DISABLE_SIGNATURE) + --expiry-time Time before a transmission becomes invalid (in seconds) (default: 40, env: EXPIRY_TIME) -h, --help display help for command ``` diff --git a/bin/cli.ts b/bin/cli.ts index 6fb5af2..72c4437 100755 --- a/bin/cli.ts +++ b/bin/cli.ts @@ -10,18 +10,22 @@ import { ping } from "../src/ping.js"; export interface WebhookRunOptions extends commander.RunOptions { webhookUrl: string; secretKey: string; - disablePing: boolean; expiryTime: number; + maximumAttempts: number; + disablePing: string; + disableSignature: string; } -const expirationOption = new Option("--expiry-time ", "Time before a transmission becomes invalid (in seconds)").env("EXPIRY_TIME").default(40) +const expirationOption = new Option("--expiry-time ", "Time before a transmission becomes invalid (in seconds)").env("EXPIRY_TIME").default(40); // Run Webhook Sink const program = commander.program(pkg); const command = commander.run(program, pkg); command.addOption(new Option("--webhook-url ", "Webhook URL to send POST").makeOptionMandatory().env("WEBHOOK_URL")); command.addOption(new Option("--secret-key ", "TweetNaCl Secret-key to sign POST data payload").makeOptionMandatory().env("SECRET_KEY")); -command.addOption(new Option("--disable-ping", "Disable ping on init").env("DISABLE_PING").default(false)); +command.addOption(new Option("--disable-ping ", "Disable ping on init").choices(["true", "false"]).env("DISABLE_PING").default(false)); +command.addOption(new Option("--maximum-attempts ", "Maximum attempts to retry POST").env("MAXIMUM_ATTEMPTS").default(100)); +command.addOption(new Option("--disable-signature ", "Disable Ed25519 signature").choices(["true", "false"]).env("DISABLE_SIGNATURE").default(false)); command.addOption(expirationOption); command.action(action); @@ -42,7 +46,7 @@ program .addOption(expirationOption) .action(async (options: WebhookRunOptions) => { logger.settings.type = "hidden"; - const response = await ping(options.webhookUrl, options.secretKey, options.expiryTime); + const response = await ping(options.webhookUrl, options.secretKey); if (response) console.log("✅ OK"); else console.log("⁉️ ERROR"); }); diff --git a/biome.json b/biome.json index 8ea7552..5e600f8 100644 --- a/biome.json +++ b/biome.json @@ -18,7 +18,7 @@ }, "formatter": { "indentStyle": "space", - "lineWidth": 120 + "lineWidth": 160 }, "linter": { "rules": { diff --git a/examples/bun/Dockerfile b/examples/bun/Dockerfile index 1e46fd9..bac1252 100644 --- a/examples/bun/Dockerfile +++ b/examples/bun/Dockerfile @@ -1,15 +1,4 @@ FROM oven/bun - -WORKDIR /app - -ENV PUBLIC_KEY $PUBLIC_KEY -ENV PORT $PORT - -COPY bun.lockb ./ -COPY package*.json ./ - -RUN bun install - COPY . . - +RUN bun install CMD [ "bun", "http.ts" ] diff --git a/examples/bun/http.ts b/examples/bun/http.ts index c7383bb..cee0a60 100644 --- a/examples/bun/http.ts +++ b/examples/bun/http.ts @@ -12,28 +12,20 @@ export default { async fetch(request) { // get headers and body from POST request const signature = request.headers.get("x-signature-ed25519"); - const expiry = Number(request.headers.get("x-signature-ed25519-expiry")); - const publicKey = request.headers.get("x-signature-ed25519-public-key"); - + const timestamp = Number(request.headers.get("x-signature-timestamp")); const body = await request.text(); if (!signature) return new Response("missing required signature in headers", { status: 400 }); - if (!expiry) return new Response("missing required expiry in headers", { status: 400 }); - if (!publicKey) return new Response("missing required public key in headers", { status: 400 }); + if (!timestamp) return new Response("missing required timestamp in headers", { status: 400 }); if (!body) return new Response("missing body", { status: 400 }); - if (new Date().getTime() >= expiry) return new Response("signature expired", { status: 401 }); - if (publicKey !== PUBLIC_KEY) return new Response("unknown public key", { status: 401 }); - // validate signature using public key - console.log({signature, expiry, publicKey}); - const payload = JSON.stringify({ exp: expiry, id: publicKey }); const isVerified = nacl.sign.detached.verify( - Buffer.from(payload), + Buffer.from(timestamp + body), Buffer.from(signature, "hex"), - Buffer.from(publicKey, "hex"), + Buffer.from(PUBLIC_KEY, "hex"), ); - console.log({ isVerified, signature }); + console.log({ isVerified, timestamp, signature }); console.log(body); if (!isVerified) { diff --git a/examples/deno/Dockerfile b/examples/deno/Dockerfile deleted file mode 100644 index 2e426c1..0000000 --- a/examples/deno/Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM denoland/deno:alpine - -WORKDIR /app - -ENV PUBLIC_KEY $PUBLIC_KEY -ENV PORT $PORT - -COPY . . - -CMD [ "deno", "run", "--allow-net", "--allow-read", "--allow-env", "http.ts" ] diff --git a/examples/deno/http.ts b/examples/deno/http.ts deleted file mode 100644 index 5e220eb..0000000 --- a/examples/deno/http.ts +++ /dev/null @@ -1,39 +0,0 @@ -import "https://deno.land/std@0.190.0/dotenv/load.ts"; -import { encode } from "https://deno.land/std@0.190.0/encoding/hex.ts"; -import { serve } from "https://deno.land/std@0.190.0/http/server.ts"; -import nacl from "npm:tweetnacl"; - -const PORT = Deno.env.get("PORT"); -const PUBLIC_KEY = Deno.env.get("PUBLIC_KEY"); - -const handler = async (request: Request) => { - // get headers and body from POST request - const signature = request.headers.get("x-signature-ed25519"); - const expiry = Number(request.headers.get("x-signature-ed25519-expiry")); - const publicKey = request.headers.get("x-signature-ed25519-public-key"); - - const body = await request.text(); - - if (!signature) return new Response("missing required signature in headers", { status: 400 }); - if (!expiry) return new Response("missing required expiry in headers", { status: 400 }); - if (!publicKey) return new Response("missing required public key in headers", { status: 400 }); - if (!body) return new Response("missing body", { status: 400 }); - - if (new Date().getTime() >= expiry) return new Response("signature expired", { status: 401 }); - if (publicKey !== PUBLIC_KEY) return new Response("unknown public key", { status: 401 }); - - // TO-DO: 🚨 FIX CODE BELOW 🚨 - // validate signature using public key - const payload = JSON.stringify({ exp: expiry, id: publicKey }); - const isVerified = nacl.sign.detached.verify(encode(payload), encode(signature), encode(PUBLIC_KEY)); - - console.dir({ signature, isVerified }); - console.dir(body); - if (!isVerified) { - return new Response("invalid request signature", { status: 401 }); - } - return new Response("OK"); -}; - -serve(handler, { port: PORT }); -console.log(`Signature validation using ${PUBLIC_KEY}`); diff --git a/examples/deno/package.json b/examples/deno/package.json deleted file mode 100644 index 8da0f5f..0000000 --- a/examples/deno/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "type": "module", - "scripts": { - "start": "deno run --allow-net --allow-read --allow-env http.ts" - } -} diff --git a/examples/express/http.js b/examples/express/http.ts similarity index 53% rename from examples/express/http.js rename to examples/express/http.ts index bef72ae..e3adb90 100644 --- a/examples/express/http.js +++ b/examples/express/http.ts @@ -8,32 +8,26 @@ const app = express(); app.use(express.text({ type: "application/json" })); -app.use(async (req, res) => { +app.use(async (request, res) => { // get headers and body from POST request - const signature = request.headers.get("x-signature-ed25519"); - const expiry = request.headers.get("x-signature-ed25519-expiry"); - const publicKey = request.headers.get("x-signature-ed25519-public-key"); - - const body = await request.text(); + const signature = String(request.headers["x-signature-ed25519"]); + const timestamp = Number(request.headers["x-signature-timestamp"]); + const body = await request.body; if (!signature) return new Response("missing required signature in headers", { status: 400 }); - if (!expiry) return new Response("missing required expiry in headers", { status: 400 }); - if (!publicKey) return new Response("missing required public key in headers", { status: 400 }); + if (!timestamp) return new Response("missing required timestamp in headers", { status: 400 }); if (!body) return new Response("missing body", { status: 400 }); - if (new Date().getTime() >= expiry) return new Response("signature expired", { status: 401 }); - if (publicKey !== PUBLIC_KEY) return new Response("unknown public key", { status: 401 }); - // validate signature using public key - const payload = JSON.stringify({ exp: expiry, id: publicKey }); const isVerified = nacl.sign.detached.verify( - Buffer.from(payload), + Buffer.from(timestamp + body), Buffer.from(signature, "hex"), - Buffer.from(PUBLIC_KEY, "hex") + Buffer.from(PUBLIC_KEY, "hex"), ); - console.dir({ signature, isVerified }); + console.dir({ signature, timestamp, isVerified }); console.dir(body); + res.setHeader("Content-Type", "text/plain;charset=utf-8"); if (!isVerified) { return res.send("invalid request signature").status(401); } diff --git a/examples/express/package-lock.json b/examples/express/package-lock.json index 71e1918..54fad18 100644 --- a/examples/express/package-lock.json +++ b/examples/express/package-lock.json @@ -8,6 +8,476 @@ "dotenv": "latest", "express": "latest", "tweetnacl": "latest" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@types/node": "latest", + "tsx": "latest" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.43", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz", + "integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.11.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz", + "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/qs": { + "version": "6.9.11", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", + "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", + "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/mime": "*", + "@types/node": "*" } }, "node_modules/accepts": { @@ -59,12 +529,17 @@ } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.6.tgz", + "integrity": "sha512-Mj50FLHtlsoVfRfnHaZvyrooHcrlceNZdL/QBvJJVd9Ta55qCQK0gs4ss2oZDeV9zFCs6ewzYgVE5yfVmfFpVg==", "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.3", + "set-function-length": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -110,6 +585,23 @@ "ms": "2.0.0" } }, + "node_modules/define-data-property": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.3.tgz", + "integrity": "sha512-h3GBouC+RPtNX2N0hHVLo2ZwPYurq8mLmXpOLTsw71gr7lHt5VaI4vVkDUNOfiWmm48JEXe3VM7PmLX45AMmmg==", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -128,14 +620,14 @@ } }, "node_modules/dotenv": { - "version": "16.1.4", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.1.4.tgz", - "integrity": "sha512-m55RtE8AsPeJBpOIFKihEmqUcoVncQIwo7x9U8ZwLEZw9ZpXboz2c+rvog+jUaJvVrZ5kBOeYQBX5+8Aa/OZQw==", + "version": "16.4.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.3.tgz", + "integrity": "sha512-II98GFrje5psQTSve0E7bnwMFybNLqT8Vu8JIFWRjsE3khyNUm/loZupuy5DVzG2IXf/ysxvrixYOQnM6mjD3A==", "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/motdotla/dotenv?sponsor=1" + "url": "https://dotenvx.com" } }, "node_modules/ee-first": { @@ -151,6 +643,52 @@ "node": ">= 0.8" } }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -238,34 +776,78 @@ "node": ">= 0.6" } }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "node_modules/get-tsconfig": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", + "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", + "dev": true, "dependencies": { - "function-bind": "^1.1.1" + "resolve-pkg-maps": "^1.0.0" }, - "engines": { - "node": ">= 0.4.0" + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "dependencies": { + "get-intrinsic": "^1.2.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-proto": { @@ -290,6 +872,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -394,9 +987,9 @@ } }, "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -473,6 +1066,15 @@ "node": ">= 0.8" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -539,19 +1141,39 @@ "node": ">= 0.8.0" } }, + "node_modules/set-function-length": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", + "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "dependencies": { + "define-data-property": "^1.1.2", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", + "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -573,6 +1195,25 @@ "node": ">=0.6" } }, + "node_modules/tsx": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.7.1.tgz", + "integrity": "sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==", + "dev": true, + "dependencies": { + "esbuild": "~0.19.10", + "get-tsconfig": "^4.7.2" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/tweetnacl": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", @@ -590,6 +1231,12 @@ "node": ">= 0.6" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/examples/express/package.json b/examples/express/package.json index 3d4dcb6..c97f7df 100644 --- a/examples/express/package.json +++ b/examples/express/package.json @@ -1,11 +1,16 @@ { "type": "module", "scripts": { - "start": "node http.js" + "start": "tsx http.ts" }, "dependencies": { - "tweetnacl": "latest", "dotenv": "latest", - "express": "latest" + "express": "latest", + "tweetnacl": "latest" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@types/node": "latest", + "tsx": "latest" } -} \ No newline at end of file +} diff --git a/examples/http/ping-isVerified.http b/examples/http/ping-isVerified.http index bb9744e..5968939 100644 --- a/examples/http/ping-isVerified.http +++ b/examples/http/ping-isVerified.http @@ -1,7 +1,6 @@ POST http://localhost:3000 HTTP/1.1 content-type: application/json -x-signature-ed25519: d26299022b13c25e4889191cdb6f4ab8fa30a524bca44b1742bedeeabb145ca99790ba09467f1365f870aee1236ec8682cdc3690eda4c8266cff512447d7270b -x-signature-ed25519-expiry: 2524626000000 -x-signature-ed25519-public-key: a3cb7366ee8ca77225b4d41772e270e4e831d171d1de71d91707c42e7ba82cc9 +x-signature-ed25519: 1853ea82cfce16ef0cfbe78e9c3637a3db563317fa2a2f94ea5b81ad648af31dfeecadacc3efa70c685a2b48200cf17d49356178ff7c606c4be21dfd29b2e20e +x-signature-timestamp: 1707777331 -{"message":"PING"} \ No newline at end of file +{"message":"PING"} diff --git a/examples/http/ping-not-isVerified.http b/examples/http/ping-not-isVerified.http index 45fbc18..f51d1ff 100644 --- a/examples/http/ping-not-isVerified.http +++ b/examples/http/ping-not-isVerified.http @@ -1,8 +1,6 @@ POST http://localhost:3000 HTTP/1.1 content-type: application/json -x-signature-ed25519: 32c4f322a21ac05e7c9b7374bb702ccd834e56aeebe8320048440833f2e18358014a5790302fbe3ead8c956cdf2b05c9181b787c55c3e40dc6dbc3ab2cfe730f -x-signature-ed25519-expiry: 2524626000000 -x-signature-ed25519-public-key: a3cb7366ee8ca77225b4d41772e270e4e831d171d1de71d91707c42e7ba82cc9 +x-signature-ed25519: 27ebd0c33f440e8caedd97e85ae32827098061e477871c7cded447014dd734399bfde1542884421b947059a827c3e4e4d1b60485f518e7eeb111e7fdc0145d0d +x-signature-timestamp: 1707777331 - -{"message":"PING"} \ No newline at end of file +{"message":"PING"} diff --git a/examples/http/post-invalid-headers.http b/examples/http/post-invalid-headers.http new file mode 100644 index 0000000..aaae246 --- /dev/null +++ b/examples/http/post-invalid-headers.http @@ -0,0 +1,4 @@ +POST http://localhost:3000 HTTP/1.1 +content-type: application/json + +{"status":200,"cursor":"OJGbpO9ZnZcwvxW38_FO8KWwLpcyA1lrUQPgKRFL04Py8yCW35v1VTB1O0-Elami3RztQlOp2tmcHC9y9ZQFuoDrxLpj6yU-FXorwoHr_OfqLPumMQwTJ-hgWeuKYNLeWDjTagn4ersEtNGzbvLaY0UxZZUhK2G62z1VptdXJfEWuiJmyjmrIZrRhK-WoNAS_rEkQ7L1xCmhDzJ4K0dTPcSDNPKZuDR2","session":{"traceId":"3cbb0a1c772a47a72995d95f4c6d2cff","resolvedStartBlock":53448515},"clock":{"timestamp":"2024-02-12T22:23:51.000Z","number":53448530,"id":"f843bc26cea0cbd50b09699546a8a97de6a1727646c17a857c5d8d868fc26142"},"manifest":{"substreamsEndpoint":"https://polygon.substreams.pinax.network:443","chain":"polygon","finalBlockOnly":"false","moduleName":"map_blocks","type":"sf.substreams.v1.Clock","moduleHash":"44c506941d5f30db6cca01692624395d1ac40cd1"},"data":{"id":"f843bc26cea0cbd50b09699546a8a97de6a1727646c17a857c5d8d868fc26142","number":"53448530","timestamp":"2024-02-12T22:23:51Z"}} \ No newline at end of file diff --git a/examples/http/post-invalid-signature-length.http b/examples/http/post-invalid-signature-length.http new file mode 100644 index 0000000..c5953a5 --- /dev/null +++ b/examples/http/post-invalid-signature-length.http @@ -0,0 +1,6 @@ +POST http://localhost:3000 HTTP/1.1 +content-type: application/json +x-signature-ed25519: foobar +x-signature-timestamp: 1707776632 + +{"status":200,"cursor":"OJGbpO9ZnZcwvxW38_FO8KWwLpcyA1lrUQPgKRFL04Py8yCW35v1VTB1O0-Elami3RztQlOp2tmcHC9y9ZQFuoDrxLpj6yU-FXorwoHr_OfqLPumMQwTJ-hgWeuKYNLeWDjTagn4ersEtNGzbvLaY0UxZZUhK2G62z1VptdXJfEWuiJmyjmrIZrRhK-WoNAS_rEkQ7L1xCmhDzJ4K0dTPcSDNPKZuDR2","session":{"traceId":"3cbb0a1c772a47a72995d95f4c6d2cff","resolvedStartBlock":53448515},"clock":{"timestamp":"2024-02-12T22:23:51.000Z","number":53448530,"id":"f843bc26cea0cbd50b09699546a8a97de6a1727646c17a857c5d8d868fc26142"},"manifest":{"substreamsEndpoint":"https://polygon.substreams.pinax.network:443","chain":"polygon","finalBlockOnly":"false","moduleName":"map_blocks","type":"sf.substreams.v1.Clock","moduleHash":"44c506941d5f30db6cca01692624395d1ac40cd1"},"data":{"id":"f843bc26cea0cbd50b09699546a8a97de6a1727646c17a857c5d8d868fc26142","number":"53448530","timestamp":"2024-02-12T22:23:51Z"}} \ No newline at end of file diff --git a/examples/http/post-invalid-signature.http b/examples/http/post-invalid-signature.http new file mode 100644 index 0000000..0764424 --- /dev/null +++ b/examples/http/post-invalid-signature.http @@ -0,0 +1,6 @@ +POST http://localhost:3000 HTTP/1.1 +content-type: application/json +x-signature-ed25519: 5d5ca4921dfb61a55361dff2b79ad58a3e3c44e06de812a955a006c5d7e1501b163a66b451dd108415a60a0e842dc1aa210f80b2abb32aae8b77c0cc4317660b +x-signature-timestamp: 1707776632 + +{"status":200,"cursor":"OJGbpO9ZnZcwvxW38_FO8KWwLpcyA1lrUQPgKRFL04Py8yCW35v1VTB1O0-Elami3RztQlOp2tmcHC9y9ZQFuoDrxLpj6yU-FXorwoHr_OfqLPumMQwTJ-hgWeuKYNLeWDjTagn4ersEtNGzbvLaY0UxZZUhK2G62z1VptdXJfEWuiJmyjmrIZrRhK-WoNAS_rEkQ7L1xCmhDzJ4K0dTPcSDNPKZuDR2","session":{"traceId":"3cbb0a1c772a47a72995d95f4c6d2cff","resolvedStartBlock":53448515},"clock":{"timestamp":"2024-02-12T22:23:51.000Z","number":53448530,"id":"f843bc26cea0cbd50b09699546a8a97de6a1727646c17a857c5d8d868fc26142"},"manifest":{"substreamsEndpoint":"https://polygon.substreams.pinax.network:443","chain":"polygon","finalBlockOnly":"false","moduleName":"map_blocks","type":"sf.substreams.v1.Clock","moduleHash":"44c506941d5f30db6cca01692624395d1ac40cd1"},"data":{"id":"f843bc26cea0cbd50b09699546a8a97de6a1727646c17a857c5d8d868fc26142","number":"53448530","timestamp":"2024-02-12T22:23:51Z"}} \ No newline at end of file diff --git a/examples/http/post-invalid-timestamp.http b/examples/http/post-invalid-timestamp.http new file mode 100644 index 0000000..be775ee --- /dev/null +++ b/examples/http/post-invalid-timestamp.http @@ -0,0 +1,6 @@ +POST http://localhost:3000 HTTP/1.1 +content-type: application/json +x-signature-ed25519: 8f01c66ccda5b987c43d913290419572ea586dbef2077fa166c4a84797e1d2c76b305bc67ed43efb1fc841562620a61cb59c4d8a13de689a2e98ead19190f80c +x-signature-timestamp: 9999999999 + +{"status":200,"cursor":"OJGbpO9ZnZcwvxW38_FO8KWwLpcyA1lrUQPgKRFL04Py8yCW35v1VTB1O0-Elami3RztQlOp2tmcHC9y9ZQFuoDrxLpj6yU-FXorwoHr_OfqLPumMQwTJ-hgWeuKYNLeWDjTagn4ersEtNGzbvLaY0UxZZUhK2G62z1VptdXJfEWuiJmyjmrIZrRhK-WoNAS_rEkQ7L1xCmhDzJ4K0dTPcSDNPKZuDR2","session":{"traceId":"3cbb0a1c772a47a72995d95f4c6d2cff","resolvedStartBlock":53448515},"clock":{"timestamp":"2024-02-12T22:23:51.000Z","number":53448530,"id":"f843bc26cea0cbd50b09699546a8a97de6a1727646c17a857c5d8d868fc26142"},"manifest":{"substreamsEndpoint":"https://polygon.substreams.pinax.network:443","chain":"polygon","finalBlockOnly":"false","moduleName":"map_blocks","type":"sf.substreams.v1.Clock","moduleHash":"44c506941d5f30db6cca01692624395d1ac40cd1"},"data":{"id":"f843bc26cea0cbd50b09699546a8a97de6a1727646c17a857c5d8d868fc26142","number":"53448530","timestamp":"2024-02-12T22:23:51Z"}} \ No newline at end of file diff --git a/examples/http/post.http b/examples/http/post.http index f1ff9cb..e229e4f 100644 --- a/examples/http/post.http +++ b/examples/http/post.http @@ -1,7 +1,6 @@ POST http://localhost:3000 HTTP/1.1 content-type: application/json -x-signature-ed25519: d26299022b13c25e4889191cdb6f4ab8fa30a524bca44b1742bedeeabb145ca99790ba09467f1365f870aee1236ec8682cdc3690eda4c8266cff512447d7270b -x-signature-ed25519-expiry: 2524626000000 -x-signature-ed25519-public-key: a3cb7366ee8ca77225b4d41772e270e4e831d171d1de71d91707c42e7ba82cc9 +x-signature-ed25519: 8f01c66ccda5b987c43d913290419572ea586dbef2077fa166c4a84797e1d2c76b305bc67ed43efb1fc841562620a61cb59c4d8a13de689a2e98ead19190f80c +x-signature-timestamp: 1707776632 -{"status":200,"cursor":"3ErAq5aeVa2E561uHfBu6qWwLpcyAlJrUAPhKxFLhtnz9HLH3JikBTQmaRqEkKz52RO4HQuk2I3EFi8p88JXtNa8kb4y6XdtRH5-loC_qLHscPOmawkSIu9kDrmJYdLfUzjSagj7c7tRsdLlPKaLY0BkY850fTOwizxW8IYFJqNAv3Mykm2ucMfVgf6fooJArbYgFuyinCzyBz16Kk4LO8TQZ_bN7jx1","session":{"traceId":"06eb726db08090e476eb2dbeff72f1bb","resolvedStartBlock":48458405},"clock":{"timestamp":"2023-10-08T02:53:03.000Z","number":48458410,"id":"3b54021525ec17d05946cfa86b92ab12787fb6f4fe25b59ac5380db39cd6ac73"},"manifest":{"substreamsEndpoint":"https://polygon.substreams.pinax.network:9000","moduleName":"map_block_stats","type":"subtivity.v1.BlockStats","moduleHash":"0a363b2a63aadb76a525208f1973531d3616fbae","chain":"polygon"},"data":{"transactionTraces":"36","traceCalls":"212","uaw":["d6b1cca00889daa9adc1d6e76b9a120086a13aab","675fe893a74815a35f867a12cbdd0637b7d7d6d4","42b07d313de7a38dc5cea48e326e545450cc4322","8ed47843e5030b6f06e6f204fcf2725378bb837a","9ced478d8d6fcaad332d9abf30415c8e48ac8079","21c3de23d98caddc406e3d31b25e807addf33633","2f59cde588b6d3661e8792632844f511d5e2da02","84a611b71254f5fccb1e5a619ad723cad8a03638","7ba865f70e32c9f46f67e33fe06139c8c31a2fad","18264397296fd982e432b4cd4942295c5bca50f8","258cfdaeee1b411bbb63a48cb030faed6720bb15","207cf8cdaec06610d7f9c92fec513e70520ce655","f746fb75a9c1d0f1c9799e434aea2aef90f7aa22","d3961bdbf7ad806b8e870a1cfbf7e54b5247020e","314c9a7a79ec28835ae68bcf5c0fd696141f85b4","2802fa14557b4f1afdf94af082b18c37d5786a2e","74eb675ed60a6f332e156c5a9ac376ee8d4d905d","5543ff441d3b0fcce59aa08eb52f15d27294af21","a1ab1c841898fe94900d00d9312ba954e4f81501","3dd12eb5ae0f1a106fb358c8b99830ab5690a7a2","51fafb35f31c434066267fc86ea24d8424115d2a","8709264ba5b56be8750193dad1a99f8b9d6ad3d6","c2b5f79a5768893b8087667b391c1381c502ab5c","85d8d0fc4e5a1f6dc823ee4baf486758a2fcb19c","7537cb7b7e8083ff8e68cb5c0ca18553ab54946f","d0a8cb58efcee1caee48f3c357074862ca8210dc"]}} \ No newline at end of file +{"status":200,"cursor":"OJGbpO9ZnZcwvxW38_FO8KWwLpcyA1lrUQPgKRFL04Py8yCW35v1VTB1O0-Elami3RztQlOp2tmcHC9y9ZQFuoDrxLpj6yU-FXorwoHr_OfqLPumMQwTJ-hgWeuKYNLeWDjTagn4ersEtNGzbvLaY0UxZZUhK2G62z1VptdXJfEWuiJmyjmrIZrRhK-WoNAS_rEkQ7L1xCmhDzJ4K0dTPcSDNPKZuDR2","session":{"traceId":"3cbb0a1c772a47a72995d95f4c6d2cff","resolvedStartBlock":53448515},"clock":{"timestamp":"2024-02-12T22:23:51.000Z","number":53448530,"id":"f843bc26cea0cbd50b09699546a8a97de6a1727646c17a857c5d8d868fc26142"},"manifest":{"substreamsEndpoint":"https://polygon.substreams.pinax.network:443","chain":"polygon","finalBlockOnly":"false","moduleName":"map_blocks","type":"sf.substreams.v1.Clock","moduleHash":"44c506941d5f30db6cca01692624395d1ac40cd1"},"data":{"id":"f843bc26cea0cbd50b09699546a8a97de6a1727646c17a857c5d8d868fc26142","number":"53448530","timestamp":"2024-02-12T22:23:51Z"}} \ No newline at end of file diff --git a/examples/node/http.ts b/examples/node/http.ts index 7e2d6d0..e08588f 100644 --- a/examples/node/http.ts +++ b/examples/node/http.ts @@ -21,37 +21,29 @@ function rawBody(request: http.IncomingMessage) { // Create a local server to serve Prometheus gauges server.on("request", async (req, res) => { - res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }); - // get headers and body from POST request const signature = String(req.headers["x-signature-ed25519"]); - const expiry = Number(req.headers["x-signature-ed25519-expiry"]); - const publicKey = String(req.headers["x-signature-ed25519-public-key"]); - + const timestamp = Number(req.headers["x-signature-timestamp"]); const body = await rawBody(req); if (!signature) return new Response("missing required signature in headers", { status: 400 }); - if (!expiry) return new Response("missing required expiry in headers", { status: 400 }); - if (!publicKey) return new Response("missing required public key in headers", { status: 400 }); + if (!timestamp) return new Response("missing required timestamp in headers", { status: 400 }); if (!body) return new Response("missing body", { status: 400 }); - if (new Date().getTime() >= expiry) return new Response("signature expired", { status: 401 }); - if (publicKey !== PUBLIC_KEY) return new Response("unknown public key", { status: 401 }); - // validate signature using public key - const payload = JSON.stringify({ exp: expiry, id: publicKey }); const isVerified = nacl.sign.detached.verify( - Buffer.from(payload), + Buffer.from(timestamp + body), Buffer.from(signature, "hex"), - Buffer.from(PUBLIC_KEY, "hex") + Buffer.from(PUBLIC_KEY, "hex"), ); - console.dir({ signature, isVerified }); + console.dir({ signature, timestamp, isVerified }); console.dir(body); + res.setHeader("Content-Type", "text/plain;charset=utf-8"); if (!isVerified) { return res.writeHead(401).end("invalid request signature"); } - return res.end("OK"); + return res.writeHead(200).end("OK"); }); server.listen(PORT, () => { diff --git a/examples/node/package-lock.json b/examples/node/package-lock.json index d547f59..30d50af 100644 --- a/examples/node/package-lock.json +++ b/examples/node/package-lock.json @@ -1,5 +1,5 @@ { - "name": "node:http", + "name": "node", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/index.ts b/index.ts index 4e5a5ce..3474436 100644 --- a/index.ts +++ b/index.ts @@ -8,7 +8,7 @@ import { banner } from "./src/banner.js"; import { toText } from "./src/http.js"; import { ping } from "./src/ping.js"; -export * from "./src/auth/index.js"; +export * from "./src/auth/ed25519.js"; export * from "./src/schemas.js"; export async function action(options: WebhookRunOptions) { @@ -19,8 +19,8 @@ export async function action(options: WebhookRunOptions) { const queue = new PQueue({ concurrency: 1 }); // all messages are sent in block order, no need to parallelize // Ping URL to check if it's valid - if (!options.disablePing) { - if (!(await ping(options.webhookUrl, options.secretKey, options.expiryTime))) { + if (options.disablePing === "false") { + if (!(await ping(options.webhookUrl, options.secretKey))) { logger.error("exiting from invalid PING response"); process.exit(1); } @@ -60,7 +60,7 @@ export async function action(options: WebhookRunOptions) { // Queue POST queue.add(async () => { - const response = await postWebhook(options.webhookUrl, body, options.secretKey, options.expiryTime); + const response = await postWebhook(options.webhookUrl, body, options.secretKey, options); logger.info("POST", response, metadata); }); }); diff --git a/package-lock.json b/package-lock.json index e1e57a4..985bdd3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,34 +1,41 @@ { "name": "substreams-sink-webhook", - "version": "0.7.5", + "version": "0.8.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "substreams-sink-webhook", - "version": "0.7.5", + "version": "0.8.0", "dependencies": { "@substreams/sink-database-changes": "^0.3.2", "@substreams/sink-entity-changes": "^0.3.4", + "hono": "^4.0.1", "p-queue": "latest", "substreams-sink": "^0.14.0", "tweetnacl": "latest", - "zod": "^3.22.4" + "viem": "^2.7.8", + "zod": "latest" }, "bin": { "substreams-sink-webhook": "dist/bin/cli.js" }, "devDependencies": { - "@biomejs/biome": "1.3.3", + "@biomejs/biome": "latest", "bun-types": "latest", "mitata": "latest", "typescript": "latest" } }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz", + "integrity": "sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==" + }, "node_modules/@biomejs/biome": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.3.3.tgz", - "integrity": "sha512-vTJn7RBzLWIabUuUIoEopO860YyBrbPEu4Pztfd28jRU5QD074hKZ9IQs24pFO6A2R296gaeYmN62f4u7pUruQ==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.5.3.tgz", + "integrity": "sha512-yvZCa/g3akwTaAQ7PCwPWDCkZs3Qa5ONg/fgOUT9e6wAWsPftCjLQFPXBeGxPK30yZSSpgEmRCfpGTmVbUjGgg==", "dev": true, "hasInstallScript": true, "bin": { @@ -42,18 +49,20 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "1.3.3", - "@biomejs/cli-darwin-x64": "1.3.3", - "@biomejs/cli-linux-arm64": "1.3.3", - "@biomejs/cli-linux-x64": "1.3.3", - "@biomejs/cli-win32-arm64": "1.3.3", - "@biomejs/cli-win32-x64": "1.3.3" + "@biomejs/cli-darwin-arm64": "1.5.3", + "@biomejs/cli-darwin-x64": "1.5.3", + "@biomejs/cli-linux-arm64": "1.5.3", + "@biomejs/cli-linux-arm64-musl": "1.5.3", + "@biomejs/cli-linux-x64": "1.5.3", + "@biomejs/cli-linux-x64-musl": "1.5.3", + "@biomejs/cli-win32-arm64": "1.5.3", + "@biomejs/cli-win32-x64": "1.5.3" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.3.3.tgz", - "integrity": "sha512-2X87ZfbmWwe4NGukrUvnoYdI//muSgjNUCAHJ2DO+kS1sB7kDy1s6PN/IYyTJuqRcJtDuOnSpaUDE7KxR1YhtA==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.5.3.tgz", + "integrity": "sha512-ImU7mh1HghEDyqNmxEZBoMPr8SxekkZuYcs+gynKlNW+TALQs7swkERiBLkG9NR0K1B3/2uVzlvYowXrmlW8hw==", "cpu": [ "arm64" ], @@ -67,9 +76,9 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.3.3.tgz", - "integrity": "sha512-t+7DWTCbSgHOBcPsGKuwS1qh1z9zbXFK8i8ktE18yW7iF/W0zI62k44fYqYeFJKlb0Q08aqUvez3L+AQJFsn+w==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.5.3.tgz", + "integrity": "sha512-vCdASqYnlpq/swErH7FD6nrFz0czFtK4k/iLgj0/+VmZVjineFPgevOb+Sr9vz0tk0GfdQO60bSpI74zU8M9Dw==", "cpu": [ "x64" ], @@ -83,9 +92,25 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.3.3.tgz", - "integrity": "sha512-D8CvXaB8lkXXBQ6B3n0MXSSZFiE60+aNHorBLimVTtKiMod8QvAP425oQFZFul5wMXZqPLGTKFjXbAi/rvnc1A==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.5.3.tgz", + "integrity": "sha512-cupBQv0sNF1OKqBfx7EDWMSsKwRrBUZfjXawT4s6hKV6ALq7p0QzWlxr/sDmbKMLOaLQtw2Qgu/77N9rm+f9Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.5.3.tgz", + "integrity": "sha512-DYuMizUYUBYfS0IHGjDrOP1RGipqWfMGEvNEJ398zdtmCKLXaUvTimiox5dvx4X15mBK5M2m8wgWUgOP1giUpQ==", "cpu": [ "arm64" ], @@ -99,9 +124,25 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.3.3.tgz", - "integrity": "sha512-bqB05fwJnRZwRlcm/BS/s4qPickqiXZkiU/nOYvHApfsPeqgSHgv5HWoBYuSUjgqBbX3XZJArsC5dCcVW7vAJw==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.5.3.tgz", + "integrity": "sha512-YQrSArQvcv4FYsk7Q91Yv4uuu5F8hJyORVcv3zsjCLGkjIjx2RhjYLpTL733SNL7v33GmOlZY0eFR1ko38tuUw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.5.3.tgz", + "integrity": "sha512-UUHiAnlDqr2Y/LpvshBFhUYMWkl2/Jn+bi3U6jKuav0qWbbBKU/ByHgR4+NBxpKBYoCtWxhnmatfH1bpPIuZMw==", "cpu": [ "x64" ], @@ -115,9 +156,9 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.3.3.tgz", - "integrity": "sha512-muFOjAv1ONMfaJDlo4Ds+Qb9lkdSLM2XaxOe3AJPejSq3Vi0aRr51ZnE02BofMnL2sVsOA9cO54wibsuTcopbw==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.5.3.tgz", + "integrity": "sha512-HxatYH7vf/kX9nrD+pDYuV2GI9GV8EFo6cfKkahAecTuZLPxryHx1WEfJthp5eNsE0+09STGkKIKjirP0ufaZA==", "cpu": [ "arm64" ], @@ -131,9 +172,9 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.3.3.tgz", - "integrity": "sha512-PMkMhS4smmmTMflxuZUx3REFSazEL9xsGscvZO1dKWI4ET23la+KxEM4TlSpjOyO66UerqSkuUlZecn0QhD63A==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.5.3.tgz", + "integrity": "sha512-fMvbSouZEASU7mZH8SIJSANDm5OqsjgtVXlbUqxwed6BP7uuHRSs396Aqwh2+VoW8fwTpp6ybIUoC9FrzB0kyA==", "cpu": [ "x64" ], @@ -320,10 +361,73 @@ "node": ">=14" } }, + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.7.0.tgz", + "integrity": "sha512-AdY5wvN0P2vXBi3b29hxZgSFvdhdxPB9+f0B6s//P9Q8nibRWeA3cHm8UmLpio9ABigkVHJ5NMPk+Mz8VCCyrw==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@scure/base": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.5.tgz", + "integrity": "sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.2.tgz", + "integrity": "sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA==", + "dependencies": { + "@noble/curves": "~1.2.0", + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "dependencies": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@sinclair/typebox": { - "version": "0.31.28", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.31.28.tgz", - "integrity": "sha512-/s55Jujywdw/Jpan+vsy6JZs1z2ZTGxTmbZTPiuSL2wz9mfzA2gN1zzaqmvfi4pq+uOt7Du85fkiwv5ymW84aQ==" + "version": "0.32.14", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.32.14.tgz", + "integrity": "sha512-EC77Mw8huT2z9YlYbWfpIQgN6shZE1tH4NP4/Trig8UBel9FZNMZRJ42ubJI8PLor2uIU+waLml1dce5ReCOPg==" }, "node_modules/@substreams/core": { "version": "0.15.1", @@ -385,34 +489,76 @@ "zod": "latest" } }, + "node_modules/@types/node": { + "version": "20.11.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz", + "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abitype": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.0.tgz", + "integrity": "sha512-NMeMah//6bJ56H5XRj8QCV4AwuW6hB6zqz2LnhhLdcWVQOsXki6/Pn3APeqxCma62nXIcmZWdu1DlHWS74umVQ==", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3 >=3.22.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/bintrees": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==" }, "node_modules/bun-types": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-0.8.1.tgz", - "integrity": "sha512-VuCBox66P/3a8gVOffLCWIS6vdpXq4y3eJuF3VnsyC5HpykmIjkcr5wYDn22qQdeTUmOfCcBy1SZmtrZCeUr3A==", - "dev": true + "version": "1.0.26", + "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.0.26.tgz", + "integrity": "sha512-VcSj+SCaWIcMb0uSGIAtr8P92zq9q+unavcQmx27fk6HulCthXHBVrdGuXxAZbFtv7bHVjizRzR2mk9r/U8Nkg==", + "dev": true, + "dependencies": { + "@types/node": "~20.11.3", + "@types/ws": "~8.5.10" + } }, "node_modules/commander": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", - "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz", + "integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==", "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/dotenv": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", - "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "version": "16.4.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.3.tgz", + "integrity": "sha512-II98GFrje5psQTSve0E7bnwMFybNLqT8Vu8JIFWRjsE3khyNUm/loZupuy5DVzG2IXf/ysxvrixYOQnM6mjD3A==", "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/motdotla/dotenv?sponsor=1" + "url": "https://dotenvx.com" } }, "node_modules/effect": { @@ -448,47 +594,70 @@ "node": ">=8.0.0" } }, + "node_modules/hono": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.0.1.tgz", + "integrity": "sha512-S9cREGPJIAK437RhroOf1PGlJPIlt5itl69OmQ6onPLo5pdCbSHGL8v4uAKxrdHjcTyuoyvKPqWm5jv0dGkdFA==", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/isows": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.3.tgz", + "integrity": "sha512-2cKei4vlmg2cxEjm3wVSqn8pcoRF/LX/wpifuuNquFO4SQmPwarClT+SUCA2lt+l581tTeZIPIZuIDo2jWN1fg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wagmi-dev" + } + ], + "peerDependencies": { + "ws": "*" + } + }, "node_modules/mitata": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/mitata/-/mitata-0.1.6.tgz", - "integrity": "sha512-VKQ0r3jriTOU9E2Z+mwbZrUmbg4Li4QyFfi7kfHKl6reZhGzL0AYlu3wE0VPXzIwA5xnFzmEQoBwCcNT8stUkA==", + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/mitata/-/mitata-0.1.9.tgz", + "integrity": "sha512-/Nr2NeYkCh9r5GHV8YZ5pZHlIhKx04O4TH2M9Ydk2yQeJnpgtBl20HJQDY34TUq4ozL1MNHajZGjLJm7K5U2Gg==", "dev": true }, "node_modules/p-queue": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-7.4.0.tgz", - "integrity": "sha512-1FYFpop2WjLUfhSpKb0pi01JL+Z+H0Z8F2XPn2g04WxGynU/X8mx8s/66fb7jZ39pS2ZliAWCX97EoTSIdMmtw==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.0.1.tgz", + "integrity": "sha512-NXzu9aQJTAzbBqOt2hwsR63ea7yvxJc0PwN/zobNAudYfb1B7R08SzB4TsLeSbUCuG467NhnoT0oO6w1qRO+BA==", "dependencies": { "eventemitter3": "^5.0.1", - "p-timeout": "^5.0.2" + "p-timeout": "^6.1.2" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-timeout": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-5.1.0.tgz", - "integrity": "sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.2.tgz", + "integrity": "sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ==", "engines": { - "node": ">=12" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/prom-client": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-14.2.0.tgz", - "integrity": "sha512-sF308EhTenb/pDRPakm+WgiN+VdM/T1RaHj1x+MvAuT8UiQP8JmOEbxVqtkbfR4LrvOg5n7ic01kRBDGXjYikA==", + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-15.1.0.tgz", + "integrity": "sha512-cCD7jLTqyPdjEPBo/Xk4Iu8jxjuZgZJ3e/oET3L+ZwOuap/7Cw3dH/TJSsZKs1TQLZ2IHpIlRAKw82ef06kmMw==", "dependencies": { + "@opentelemetry/api": "^1.4.0", "tdigest": "^0.1.1" }, "engines": { - "node": ">=10" + "node": "^16 || ^18 || >=20" } }, "node_modules/pure-rand": { @@ -549,10 +718,10 @@ "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" }, "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "dev": true, + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -572,6 +741,61 @@ "node": ">=14.0" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/viem": { + "version": "2.7.8", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.7.8.tgz", + "integrity": "sha512-5r5pkBDBmihCvMx4b3MqtP0FoZCRWE2ML1DssU80+vhJQur0PKd4yHdLbbvoiGGVD6bYiA394juhfdSvXIGgFA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "dependencies": { + "@adraffy/ens-normalize": "1.10.0", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@scure/bip32": "1.3.2", + "@scure/bip39": "1.2.1", + "abitype": "1.0.0", + "isows": "1.0.3", + "ws": "8.13.0" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/yaml": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", diff --git a/package.json b/package.json index 37029c5..169a95b 100644 --- a/package.json +++ b/package.json @@ -28,23 +28,27 @@ } }, "scripts": { - "start": "tsc && node ./dist/bin/cli.js run", + "start": "tsc && node ./dist/bin/cli.js run --help", "pretest": "tsc --noEmit", "test": "bun test", + "posttest": "bunx @biomejs/biome check .", "prepublishOnly": "tsc", + "format": "bunx @biomejs/biome format --write ./", "build": "bun build --compile ./index.ts --outfile substreams-sink-webhook", "bench": "bun ./src/**/*.bench.ts" }, "dependencies": { "@substreams/sink-database-changes": "^0.3.2", "@substreams/sink-entity-changes": "^0.3.4", + "hono": "^4.0.1", "p-queue": "latest", "substreams-sink": "^0.14.0", "tweetnacl": "latest", - "zod": "^3.22.4" + "viem": "^2.7.8", + "zod": "latest" }, "devDependencies": { - "@biomejs/biome": "1.3.3", + "@biomejs/biome": "latest", "bun-types": "latest", "mitata": "latest", "typescript": "latest" diff --git a/src/auth/cached.ts b/src/auth/cached.ts deleted file mode 100644 index 9590b29..0000000 --- a/src/auth/cached.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { sign, verify } from "./ed25519.js"; - -// Keep in memory the latest generated signature for every secret key. -// We do not regenerate them if they are still valid. -const latestSignatures = new Map>(); - -export function cachedSign(...args: Parameters): ReturnType { - const [secretKey, durationInSecs] = args; - - // Do not recalculate a signature it the latest one expires in less than 40% of the expiryTime - let latestSignature = latestSignatures.get(secretKey); - if (!latestSignature || generatedSignatureIsExpired(latestSignature.expirationTime, durationInSecs)) { - latestSignature = sign(...args); - latestSignatures.set(secretKey, latestSignature); - } - - return latestSignature; -} - -function generatedSignatureIsExpired(expirationTime: number, signatureDurationInSecs: number) { - return expirationTime - new Date().getTime() <= 0.4 * signatureDurationInSecs * 1000; -} - -// Keep in memory which signatures are currently valid, and at what time they become invalid. -// This allows to skip the ed25519 validation process each time and only compare the expiration time. -const validSignatures = new Map(); - -export function cachedVerify(...args: Parameters): ReturnType { - const [signature, expiry] = args; - - // Quick return if the signature is already known - const cachedSignatureExpiry = validSignatures.get(signature); - if (cachedSignatureExpiry !== undefined) { - if (receivedSignatureIsExpired(cachedSignatureExpiry)) { - return new Error("signature is expired"); - } - - return true; - } - - // Cleanup expired values from cache - for (const [signature, expiry] of validSignatures) { - if (receivedSignatureIsExpired(expiry)) { - validSignatures.delete(signature); - } - } - - // If it is a new signature, process it normally - const result = verify(...args); - validSignatures.set(signature, expiry); - return result; -} - -function receivedSignatureIsExpired(expirationTime: number): boolean { - return new Date().getTime() >= expirationTime; -} diff --git a/src/auth/ed25519.bench.ts b/src/auth/ed25519.bench.ts index ce4a036..dfe4748 100644 --- a/src/auth/ed25519.bench.ts +++ b/src/auth/ed25519.bench.ts @@ -1,20 +1,15 @@ import { bench, group, run } from "mitata"; -import { cachedSign, cachedVerify } from "./cached.js"; -import { sign, verify } from "./ed25519.js"; +import { createTimestamp, sign, verify } from "./ed25519.js"; -const secretKey = - "3faae992336ea6599fbee55bb2605f1a1297c7288b860725cdfc8794413559dba3cb7366ee8ca77225b4d41772e270e4e831d171d1de71d91707c42e7ba82cc9"; +const secretKey = "3faae992336ea6599fbee55bb2605f1a1297c7288b860725cdfc8794413559dba3cb7366ee8ca77225b4d41772e270e4e831d171d1de71d91707c42e7ba82cc9"; const publicKey = "a3cb7366ee8ca77225b4d41772e270e4e831d171d1de71d91707c42e7ba82cc9"; -const { signature, expirationTime } = sign(secretKey, 60); +const body = "hello world"; +const timestamp = createTimestamp(); +const signature = sign(timestamp, body, secretKey); -group("sign", () => { - bench("sign - cache disabled", () => sign(secretKey, 60)); - bench("sign - cache enabled", () => cachedSign(secretKey, 60)); -}); - -group("verify", () => { - bench("verify - cache disabled", () => verify(signature, expirationTime, publicKey)); - bench("verify - cache enabled", () => cachedVerify(signature, expirationTime, publicKey)); +group("ed25519", () => { + bench("sign", () => sign(timestamp, body, secretKey)); + bench("verify", () => verify(timestamp, body, signature, publicKey)); }); await run({ avg: true, json: false, colors: true, min_max: true, collect: false, percentiles: false }); diff --git a/src/auth/ed25519.spec.ts b/src/auth/ed25519.spec.ts new file mode 100644 index 0000000..a2044d2 --- /dev/null +++ b/src/auth/ed25519.spec.ts @@ -0,0 +1,56 @@ +import { describe, expect, test } from "bun:test"; +import * as auth from "./ed25519.js"; + +const secretKey = "3faae992336ea6599fbee55bb2605f1a1297c7288b860725cdfc8794413559dba3cb7366ee8ca77225b4d41772e270e4e831d171d1de71d91707c42e7ba82cc9"; +const publicKey = "a3cb7366ee8ca77225b4d41772e270e4e831d171d1de71d91707c42e7ba82cc9"; +const body = `{"status":200,"cursor":"OJGbpO9ZnZcwvxW38_FO8KWwLpcyA1lrUQPgKRFL04Py8yCW35v1VTB1O0-Elami3RztQlOp2tmcHC9y9ZQFuoDrxLpj6yU-FXorwoHr_OfqLPumMQwTJ-hgWeuKYNLeWDjTagn4ersEtNGzbvLaY0UxZZUhK2G62z1VptdXJfEWuiJmyjmrIZrRhK-WoNAS_rEkQ7L1xCmhDzJ4K0dTPcSDNPKZuDR2","session":{"traceId":"3cbb0a1c772a47a72995d95f4c6d2cff","resolvedStartBlock":53448515},"clock":{"timestamp":"2024-02-12T22:23:51.000Z","number":53448530,"id":"f843bc26cea0cbd50b09699546a8a97de6a1727646c17a857c5d8d868fc26142"},"manifest":{"substreamsEndpoint":"https://polygon.substreams.pinax.network:443","chain":"polygon","finalBlockOnly":"false","moduleName":"map_blocks","type":"sf.substreams.v1.Clock","moduleHash":"44c506941d5f30db6cca01692624395d1ac40cd1"},"data":{"id":"f843bc26cea0cbd50b09699546a8a97de6a1727646c17a857c5d8d868fc26142","number":"53448530","timestamp":"2024-02-12T22:23:51Z"}}`; +const signature = "8f01c66ccda5b987c43d913290419572ea586dbef2077fa166c4a84797e1d2c76b305bc67ed43efb1fc841562620a61cb59c4d8a13de689a2e98ead19190f80c"; +const timestamp = 1707776632; + +describe("sign", () => { + test("sign", () => { + const signature = auth.sign(timestamp, body, secretKey); + expect(signature).toBe(signature); + }); + + test("error - missing body", () => { + expect(() => auth.sign(timestamp, "", secretKey)).toThrow("missing body"); + }); + + test("error - invalid secret key length", () => { + expect(() => auth.sign(timestamp, body, "")).toThrow("invalid secret key length"); + }); + + test("error - invalid timestamp", () => { + expect(() => auth.sign(0, body, secretKey)).toThrow("invalid timestamp"); + expect(() => auth.sign(Number("abc"), body, secretKey)).toThrow("invalid timestamp"); + }); +}); + +describe("verify", () => { + test("verify", () => { + expect(auth.verify(timestamp, body, signature, publicKey)).toBeTrue(); + }); + + test("error - invalid signature", () => { + const invalidPublicKey = "36657c7498f2ff2e9a520dcfbdad4e7c1e5354a75623165e28f6577a45a9eec3"; + expect(() => auth.verify(timestamp, body, signature, invalidPublicKey)).toThrow("invalid signature"); + }); + + test("error - missing body", () => { + expect(() => auth.verify(timestamp, "", signature, publicKey)).toThrow("missing body"); + }); + + test("error - invalid signature length", () => { + expect(() => auth.verify(timestamp, body, "foobar", publicKey)).toThrow("invalid signature length"); + }); + + test("error - invalid timestamp", () => { + expect(() => auth.verify(0, body, signature, publicKey)).toThrow("invalid timestamp"); + expect(() => auth.verify(Number("abc"), body, signature, publicKey)).toThrow("invalid timestamp"); + }); + + test("error - invalid public key length", () => { + expect(() => auth.verify(timestamp, body, signature, "foobar")).toThrow("invalid public key length"); + }); +}); diff --git a/src/auth/ed25519.ts b/src/auth/ed25519.ts index 61a2929..67cabf1 100644 --- a/src/auth/ed25519.ts +++ b/src/auth/ed25519.ts @@ -1,31 +1,54 @@ import nacl from "tweetnacl"; -export function sign(secretKey: string, durationInSecs: number) { - const publicKey = secretKey.substring(nacl.sign.secretKeyLength); - const expirationTime = new Date().getTime() + durationInSecs * 1000; +export function createTimestamp() { + return Math.floor(Date.now() / 1000); +} + +export function sign(timestamp: number, body: string, secretKey: string) { + const secretKeyHex = Buffer.from(secretKey, "hex"); - const payload = JSON.stringify({ exp: expirationTime, id: publicKey }); - const signedBuffer = nacl.sign.detached(Buffer.from(payload), Buffer.from(secretKey, "hex")); + // input validation + if (secretKeyHex.length !== nacl.sign.secretKeyLength) { + throw Error("invalid secret key length"); + } + if (!body) { + throw Error("missing body"); + } + if (!(timestamp > 0)) { + throw Error("invalid timestamp"); + } - return { signature: Buffer.from(signedBuffer).toString("hex"), expirationTime, publicKey }; + // sign message + const msg = Buffer.from(timestamp + body); + const signedBuffer = nacl.sign.detached(msg, secretKeyHex); + return Buffer.from(signedBuffer).toString("hex"); } -export function verify(signature: string, expiry: number, publicKey: string): Error | true { - if (new Date().getTime() >= expiry) { - return new Error("signature has expired"); +export function verify(timestamp: number, body: string, signature: string, publicKey: string): true { + const signatureHex = Buffer.from(signature, "hex"); + const publicKeyHex = Buffer.from(publicKey, "hex"); + + // input validation + if (signatureHex.length !== nacl.sign.signatureLength) { + throw Error("invalid signature length"); + } + if (!body) { + throw Error("missing body"); + } + if (!(timestamp > 0)) { + throw Error("invalid timestamp"); + } + if (publicKeyHex.length !== nacl.sign.publicKeyLength) { + throw Error("invalid public key length"); } - const payload = JSON.stringify({ exp: expiry, id: publicKey }); - const isVerified = nacl.sign.detached.verify( - Buffer.from(payload), - Buffer.from(signature, "hex"), - Buffer.from(publicKey, "hex"), - ); + // verify signature + const msg = Buffer.from(timestamp + body); + const isVerified = nacl.sign.detached.verify(msg, signatureHex, publicKeyHex); if (!isVerified) { - return new Error("invalid signature"); + throw Error("invalid signature"); } - return true; } diff --git a/src/auth/index.ts b/src/auth/index.ts deleted file mode 100644 index 413f94f..0000000 --- a/src/auth/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./cached.js"; -export * from "./ed25519.js"; diff --git a/src/auth/sign.test.ts b/src/auth/sign.test.ts deleted file mode 100644 index 9930467..0000000 --- a/src/auth/sign.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { beforeEach, expect, setSystemTime, spyOn, test } from "bun:test"; -import { cachedSign } from "./cached.js"; -import * as auth from "./ed25519.js"; - -const secretKey = - "3faae992336ea6599fbee55bb2605f1a1297c7288b860725cdfc8794413559dba3cb7366ee8ca77225b4d41772e270e4e831d171d1de71d91707c42e7ba82cc9"; - -beforeEach(() => { - setSystemTime(); -}); - -// This test will be invalid from January 1, 2050 -test("sign", () => { - const expectedSignature = - "d26299022b13c25e4889191cdb6f4ab8fa30a524bca44b1742bedeeabb145ca99790ba09467f1365f870aee1236ec8682cdc3690eda4c8266cff512447d7270b"; - - // Make the token expire in 2050 by modifying the current time - setSystemTime(new Date(2050, 0, 1)); - const { signature, expirationTime, publicKey } = auth.sign(secretKey, 0); - setSystemTime(); - - expect(signature).toBe(expectedSignature); - expect(auth.verify(signature, expirationTime, publicKey)).toBeTrue(); -}); - -test("sign cache", () => { - setSystemTime(new Date("2000-01-01T00:00:00.000Z")); - - const refreshSignatureSpy = spyOn(auth, "sign"); - - cachedSign(secretKey, 60); - expect(refreshSignatureSpy).toHaveBeenCalledTimes(1); - - // Requesting the signature in the first 60% of the time window will not regenerate it (0.6*60s = 36s) - setSystemTime(new Date("2000-01-01T00:00:35.000Z")); - cachedSign(secretKey, 60); - expect(refreshSignatureSpy).toHaveBeenCalledTimes(1); - - // Requesting the signature after the first 60% of the time window will regenerate it - setSystemTime(new Date("2000-01-01T00:00:36.000Z")); - cachedSign(secretKey, 60); - expect(refreshSignatureSpy).toHaveBeenCalledTimes(2); -}); diff --git a/src/auth/verify.test.ts b/src/auth/verify.test.ts deleted file mode 100644 index 767bf42..0000000 --- a/src/auth/verify.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { expect, setSystemTime, spyOn, test } from "bun:test"; -import { cachedVerify } from "./cached.js"; -import * as auth from "./ed25519.js"; - -const secretKey = - "3faae992336ea6599fbee55bb2605f1a1297c7288b860725cdfc8794413559dba3cb7366ee8ca77225b4d41772e270e4e831d171d1de71d91707c42e7ba82cc9"; -const publicKey = "a3cb7366ee8ca77225b4d41772e270e4e831d171d1de71d91707c42e7ba82cc9"; - -// This test will be invalid from January 1, 2050 -test("verify", () => { - const invalidPublicKey = "36657c7498f2ff2e9a520dcfbdad4e7c1e5354a75623165e28f6577a45a9eec3"; - - const expiry = new Date(2050, 0, 1); - const expired = new Date(2000, 0, 1); - - const tests = [ - { key: publicKey, expiry: expiry, expected: true }, - { key: publicKey, expiry: expired, expected: "signature has expired" }, - { key: invalidPublicKey, expiry: expiry, expected: "invalid signature" }, - { key: invalidPublicKey, expiry: expired, expected: "signature has expired" }, - ]; - - for (const test of tests) { - setSystemTime(test.expiry); - const { signature } = auth.sign(secretKey, 0); - - setSystemTime(); - if (typeof test.expected === "boolean") { - expect(auth.verify(signature, test.expiry.getTime(), test.key)).toBe(test.expected); - } else { - expect(() => auth.verify(signature, test.expiry.getTime(), test.key)).toThrow(test.expected); - } - } -}); - -test("verify cache", () => { - setSystemTime(new Date("2000-01-01T00:00:00.000Z")); - - const { signature, expirationTime, publicKey } = auth.sign(secretKey, 60); - const verifyMessageSpy = spyOn(auth, "verify"); - - expect(cachedVerify(signature, expirationTime, publicKey)).toBeTrue(); - expect(verifyMessageSpy).toHaveBeenCalledTimes(1); - - // This signature is already known, we do not need to revalidate it - expect(cachedVerify(signature, expirationTime, publicKey)).toBeTrue(); - expect(verifyMessageSpy).toHaveBeenCalledTimes(1); - - // This signature expires in 1s, but it is still valid. We do not need to revalide it. - setSystemTime(new Date("2000-01-01T00:00:59.000Z")); - expect(cachedVerify(signature, expirationTime, publicKey)).toBeTrue(); - expect(verifyMessageSpy).toHaveBeenCalledTimes(1); - - // This signature is expired, it should be removed from the cache. - setSystemTime(new Date("2000-01-01T00:01:00.000Z")); - expect(cachedVerify(signature, expirationTime, publicKey)).toBeInstanceOf(Error); - expect(verifyMessageSpy).toHaveBeenCalledTimes(1); -}); diff --git a/src/ping.ts b/src/ping.ts index 57aee4f..1b5db38 100644 --- a/src/ping.ts +++ b/src/ping.ts @@ -1,20 +1,20 @@ import { keyPair } from "./auth/ed25519.js"; import { postWebhook } from "./postWebhook.js"; -export async function ping(url: string, secretKey: string, expiryTime: number) { +export async function ping(url: string, secretKey: string) { const body = JSON.stringify({ message: "PING" }); const invalidSecretKey = keyPair().secretKey; // send valid signature (must respond with 200) try { - await postWebhook(url, body, secretKey, expiryTime, { maximumAttempts: 0 }); + await postWebhook(url, body, secretKey, { maximumAttempts: 0 }); } catch (_e) { return false; } // send invalid signature (must NOT respond with 200) try { - await postWebhook(url, body, invalidSecretKey, expiryTime, { maximumAttempts: 0 }); + await postWebhook(url, body, invalidSecretKey, { maximumAttempts: 0 }); return false; } catch (_e) { return true; diff --git a/src/postWebhook.ts b/src/postWebhook.ts index 8db959d..79e7103 100644 --- a/src/postWebhook.ts +++ b/src/postWebhook.ts @@ -1,5 +1,5 @@ import { logger } from "substreams-sink"; -import { cachedSign } from "./auth/cached.js"; +import { createTimestamp, sign } from "./auth/ed25519.js"; function awaitSetTimeout(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); @@ -7,20 +7,16 @@ function awaitSetTimeout(ms: number) { interface PostWebhookOptions { maximumAttempts?: number; + disableSignature?: string; } -export async function postWebhook( - url: string, - body: string, - secretKey: string, - expiryTime: number, - options: PostWebhookOptions = {}, -) { +export async function postWebhook(url: string, body: string, secretKey: string, options: PostWebhookOptions = {}) { // Retry Policy const initialInterval = 1000; // 1s const maximumAttempts = options.maximumAttempts ?? 100 * initialInterval; const maximumInterval = 100 * initialInterval; const backoffCoefficient = 2; + const disableSignature = options.disableSignature === "true"; let attempts = 0; while (true) { @@ -42,7 +38,8 @@ export async function postWebhook( } try { - const { signature, expirationTime, publicKey } = cachedSign(secretKey, expiryTime); + const timestamp = createTimestamp(); + const signature = disableSignature ? '' : sign(timestamp, body, secretKey); const response = await fetch(url, { body, @@ -50,8 +47,7 @@ export async function postWebhook( headers: { "content-type": "application/json", "x-signature-ed25519": signature, - "x-signature-ed25519-expiry": expirationTime.toString(), - "x-signature-ed25519-public-key": publicKey, + "x-signature-timestamp": String(timestamp), }, }); diff --git a/tsconfig.json b/tsconfig.json index 3b24b14..5f6214c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,7 +17,6 @@ }, "include": [ "index.ts", - "bin/cli.ts", - "src/**/*" + "bin/cli.ts" ] } \ No newline at end of file From 70ed625a6e60c5603d488b63037705aba3f97962 Mon Sep 17 00:00:00 2001 From: Denis Carriere Date: Mon, 12 Feb 2024 22:06:43 -0500 Subject: [PATCH 2/6] remove build --- .github/workflows/bun-build.yml | 23 ----------------------- README.md | 3 +-- bin/cli.ts | 6 +----- package.json | 3 +-- src/postWebhook.ts | 2 +- 5 files changed, 4 insertions(+), 33 deletions(-) delete mode 100644 .github/workflows/bun-build.yml diff --git a/.github/workflows/bun-build.yml b/.github/workflows/bun-build.yml deleted file mode 100644 index 7395fdf..0000000 --- a/.github/workflows/bun-build.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Generate standalone Bun executable -on: - release: - types: [published] - -permissions: - contents: write - -jobs: - build-and-push-release: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: oven-sh/setup-bun@v1 - with: - bun-version: latest - - run: bun install - - run: bun run test - - run: bun run build - - uses: softprops/action-gh-release@v1 - with: - files: | - substreams-sink-webhook \ No newline at end of file diff --git a/README.md b/README.md index 29bcdac..5173eb0 100644 --- a/README.md +++ b/README.md @@ -145,9 +145,8 @@ Options: --webhook-url Webhook URL to send POST (env: WEBHOOK_URL) --secret-key TweetNaCl Secret-key to sign POST data payload (env: SECRET_KEY) --disable-ping Disable ping on init (choices: "true", "false", default: false, env: DISABLE_PING) - --maximum-attempts Maximum attempts to retry POST (default: 100, env: MAXIMUM_ATTEMPTS) --disable-signature Disable Ed25519 signature (choices: "true", "false", default: false, env: DISABLE_SIGNATURE) - --expiry-time Time before a transmission becomes invalid (in seconds) (default: 40, env: EXPIRY_TIME) + --maximum-attempts Maximum attempts to retry POST (default: 100, env: MAXIMUM_ATTEMPTS) -h, --help display help for command ``` diff --git a/bin/cli.ts b/bin/cli.ts index 72c4437..a55abbc 100755 --- a/bin/cli.ts +++ b/bin/cli.ts @@ -16,17 +16,14 @@ export interface WebhookRunOptions extends commander.RunOptions { disableSignature: string; } -const expirationOption = new Option("--expiry-time ", "Time before a transmission becomes invalid (in seconds)").env("EXPIRY_TIME").default(40); - // Run Webhook Sink const program = commander.program(pkg); const command = commander.run(program, pkg); command.addOption(new Option("--webhook-url ", "Webhook URL to send POST").makeOptionMandatory().env("WEBHOOK_URL")); command.addOption(new Option("--secret-key ", "TweetNaCl Secret-key to sign POST data payload").makeOptionMandatory().env("SECRET_KEY")); command.addOption(new Option("--disable-ping ", "Disable ping on init").choices(["true", "false"]).env("DISABLE_PING").default(false)); -command.addOption(new Option("--maximum-attempts ", "Maximum attempts to retry POST").env("MAXIMUM_ATTEMPTS").default(100)); command.addOption(new Option("--disable-signature ", "Disable Ed25519 signature").choices(["true", "false"]).env("DISABLE_SIGNATURE").default(false)); -command.addOption(expirationOption); +command.addOption(new Option("--maximum-attempts ", "Maximum attempts to retry POST").env("MAXIMUM_ATTEMPTS").default(100)); command.action(action); program @@ -43,7 +40,6 @@ program .description("Ping Webhook URL") .addOption(new Option("--webhook-url ", "Webhook URL to send POST").makeOptionMandatory().env("WEBHOOK_URL")) .addOption(new Option("--secret-key ", "TweetNaCl Secret-key to sign POST data payload").makeOptionMandatory().env("SECRET_KEY")) - .addOption(expirationOption) .action(async (options: WebhookRunOptions) => { logger.settings.type = "hidden"; const response = await ping(options.webhookUrl, options.secretKey); diff --git a/package.json b/package.json index 169a95b..ddd6904 100644 --- a/package.json +++ b/package.json @@ -28,13 +28,12 @@ } }, "scripts": { - "start": "tsc && node ./dist/bin/cli.js run --help", + "start": "tsc && node ./dist/bin/cli.js run", "pretest": "tsc --noEmit", "test": "bun test", "posttest": "bunx @biomejs/biome check .", "prepublishOnly": "tsc", "format": "bunx @biomejs/biome format --write ./", - "build": "bun build --compile ./index.ts --outfile substreams-sink-webhook", "bench": "bun ./src/**/*.bench.ts" }, "dependencies": { diff --git a/src/postWebhook.ts b/src/postWebhook.ts index 79e7103..55b0fcf 100644 --- a/src/postWebhook.ts +++ b/src/postWebhook.ts @@ -39,7 +39,7 @@ export async function postWebhook(url: string, body: string, secretKey: string, try { const timestamp = createTimestamp(); - const signature = disableSignature ? '' : sign(timestamp, body, secretKey); + const signature = disableSignature ? "" : sign(timestamp, body, secretKey); const response = await fetch(url, { body, From 455b0d622a0e4f0ce19325c2b4191f3c3266e121 Mon Sep 17 00:00:00 2001 From: Denis Carriere Date: Mon, 12 Feb 2024 22:09:25 -0500 Subject: [PATCH 3/6] remove hono & viem --- package-lock.json | 155 +--------------------------------------------- package.json | 2 - 2 files changed, 1 insertion(+), 156 deletions(-) diff --git a/package-lock.json b/package-lock.json index 985bdd3..f110e0a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,11 +10,9 @@ "dependencies": { "@substreams/sink-database-changes": "^0.3.2", "@substreams/sink-entity-changes": "^0.3.4", - "hono": "^4.0.1", "p-queue": "latest", "substreams-sink": "^0.14.0", "tweetnacl": "latest", - "viem": "^2.7.8", "zod": "latest" }, "bin": { @@ -27,11 +25,6 @@ "typescript": "latest" } }, - "node_modules/@adraffy/ens-normalize": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz", - "integrity": "sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==" - }, "node_modules/@biomejs/biome": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.5.3.tgz", @@ -361,28 +354,6 @@ "node": ">=14" } }, - "node_modules/@noble/curves": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", - "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", - "dependencies": { - "@noble/hashes": "1.3.2" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/hashes": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", - "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@opentelemetry/api": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.7.0.tgz", @@ -391,39 +362,6 @@ "node": ">=8.0.0" } }, - "node_modules/@scure/base": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.5.tgz", - "integrity": "sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==", - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip32": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.2.tgz", - "integrity": "sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA==", - "dependencies": { - "@noble/curves": "~1.2.0", - "@noble/hashes": "~1.3.2", - "@scure/base": "~1.1.2" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip39": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", - "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", - "dependencies": { - "@noble/hashes": "~1.3.0", - "@scure/base": "~1.1.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@sinclair/typebox": { "version": "0.32.14", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.32.14.tgz", @@ -507,26 +445,6 @@ "@types/node": "*" } }, - "node_modules/abitype": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.0.tgz", - "integrity": "sha512-NMeMah//6bJ56H5XRj8QCV4AwuW6hB6zqz2LnhhLdcWVQOsXki6/Pn3APeqxCma62nXIcmZWdu1DlHWS74umVQ==", - "funding": { - "url": "https://github.com/sponsors/wevm" - }, - "peerDependencies": { - "typescript": ">=5.0.4", - "zod": "^3 >=3.22.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, "node_modules/bintrees": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", @@ -594,28 +512,6 @@ "node": ">=8.0.0" } }, - "node_modules/hono": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.0.1.tgz", - "integrity": "sha512-S9cREGPJIAK437RhroOf1PGlJPIlt5itl69OmQ6onPLo5pdCbSHGL8v4uAKxrdHjcTyuoyvKPqWm5jv0dGkdFA==", - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/isows": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.3.tgz", - "integrity": "sha512-2cKei4vlmg2cxEjm3wVSqn8pcoRF/LX/wpifuuNquFO4SQmPwarClT+SUCA2lt+l581tTeZIPIZuIDo2jWN1fg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/wagmi-dev" - } - ], - "peerDependencies": { - "ws": "*" - } - }, "node_modules/mitata": { "version": "0.1.9", "resolved": "https://registry.npmjs.org/mitata/-/mitata-0.1.9.tgz", @@ -721,7 +617,7 @@ "version": "5.3.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", - "devOptional": true, + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -747,55 +643,6 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, - "node_modules/viem": { - "version": "2.7.8", - "resolved": "https://registry.npmjs.org/viem/-/viem-2.7.8.tgz", - "integrity": "sha512-5r5pkBDBmihCvMx4b3MqtP0FoZCRWE2ML1DssU80+vhJQur0PKd4yHdLbbvoiGGVD6bYiA394juhfdSvXIGgFA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/wevm" - } - ], - "dependencies": { - "@adraffy/ens-normalize": "1.10.0", - "@noble/curves": "1.2.0", - "@noble/hashes": "1.3.2", - "@scure/bip32": "1.3.2", - "@scure/bip39": "1.2.1", - "abitype": "1.0.0", - "isows": "1.0.3", - "ws": "8.13.0" - }, - "peerDependencies": { - "typescript": ">=5.0.4" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/yaml": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", diff --git a/package.json b/package.json index ddd6904..3b57490 100644 --- a/package.json +++ b/package.json @@ -39,11 +39,9 @@ "dependencies": { "@substreams/sink-database-changes": "^0.3.2", "@substreams/sink-entity-changes": "^0.3.4", - "hono": "^4.0.1", "p-queue": "latest", "substreams-sink": "^0.14.0", "tweetnacl": "latest", - "viem": "^2.7.8", "zod": "latest" }, "devDependencies": { From b7f0ff7db1aee9a6ea48003dd413fe6e86859265 Mon Sep 17 00:00:00 2001 From: Denis Carriere Date: Mon, 12 Feb 2024 22:29:56 -0500 Subject: [PATCH 4/6] update .env --- .env.example | 2 ++ README.md | 2 ++ index.ts | 7 +++++++ 3 files changed, 11 insertions(+) diff --git a/.env.example b/.env.example index def1213..5cea668 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,7 @@ # Webhook SECRET_KEY="" WEBHOOK_URL=http://127.0.0.1:3000 +PORT=9102 # Get Substreams API Key # https://app.pinax.network @@ -12,6 +13,7 @@ SUBSTREAMS_ENDPOINT=https://eth.substreams.pinax.network:443 MANIFEST=https://github.com/pinax-network/substreams/releases/download/blocks-v0.1.0/blocks-v0.1.0.spkg MODULE_NAME=map_blocks START_BLOCK=-10 +PRODUCTION_MODE=true # Webhook (Optional) DISABLE_PING=false diff --git a/README.md b/README.md index 5173eb0..68171f8 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,7 @@ if (!isVerified) { # Webhook SECRET_KEY="" WEBHOOK_URL=http://127.0.0.1:3000 +PORT=9102 # Get Substreams API Key # https://app.pinax.network @@ -105,6 +106,7 @@ SUBSTREAMS_API_KEY="" MANIFEST=https://github.com/pinax-network/substreams/releases/download/blocks-v0.1.0/blocks-v0.1.0.spkg MODULE_NAME=map_blocks START_BLOCK=-10 +PRODUCTION_MODE=true # Webhook (Optional) DISABLE_PING=false diff --git a/index.ts b/index.ts index 3474436..90749e8 100644 --- a/index.ts +++ b/index.ts @@ -65,8 +65,15 @@ export async function action(options: WebhookRunOptions) { }); }); emitter.start(); + + // HTTP Server http.listen(options); http.server.on("request", (req, res) => { if (req.url === "/") return toText(res, banner()); }); + + emitter.on("close", () => { + logger.info("stream closed"); + http.server.close() + }) } From 8da9a26adf61b8f2ef1ca0273dd13d69806e3d75 Mon Sep 17 00:00:00 2001 From: Denis Carriere Date: Mon, 12 Feb 2024 22:32:25 -0500 Subject: [PATCH 5/6] format --- index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.ts b/index.ts index 90749e8..a7e6a6b 100644 --- a/index.ts +++ b/index.ts @@ -74,6 +74,6 @@ export async function action(options: WebhookRunOptions) { emitter.on("close", () => { logger.info("stream closed"); - http.server.close() - }) + http.server.close(); + }); } From 529477b998c3152beb440a8e2f8881b312693714 Mon Sep 17 00:00:00 2001 From: Denis Carriere Date: Tue, 13 Feb 2024 15:24:35 -0500 Subject: [PATCH 6/6] rename secretKey => privateKey --- .env.example | 4 +- .gitignore | 2 + README.md | 6 +- bin/cli.ts | 19 +- biome.json | 2 +- examples/bun/bun.lockb | Bin 1424 -> 0 bytes examples/bun/http.ts | 10 +- examples/bun/package.json | 2 +- examples/express/http.ts | 2 +- examples/express/package-lock.json | 1265 ---------------------------- examples/express/package.json | 2 +- examples/node/package-lock.json | 535 ------------ examples/node/package.json | 2 +- index.ts | 7 +- package-lock.json | 28 +- package.json | 3 +- src/auth/ed25519.bench.ts | 19 +- src/auth/ed25519.spec.ts | 14 +- src/auth/ed25519.ts | 68 +- src/ping.ts | 9 +- src/postWebhook.ts | 3 +- 21 files changed, 124 insertions(+), 1878 deletions(-) delete mode 100755 examples/bun/bun.lockb delete mode 100644 examples/express/package-lock.json delete mode 100644 examples/node/package-lock.json diff --git a/.env.example b/.env.example index 5cea668..73732da 100644 --- a/.env.example +++ b/.env.example @@ -1,12 +1,12 @@ # Webhook -SECRET_KEY="" +PRIVATE_KEY= WEBHOOK_URL=http://127.0.0.1:3000 PORT=9102 # Get Substreams API Key # https://app.pinax.network # https://app.streamingfast.io/ -SUBSTREAMS_API_KEY="" +SUBSTREAMS_API_KEY= SUBSTREAMS_ENDPOINT=https://eth.substreams.pinax.network:443 # Substreams Package (*.spkg) diff --git a/.gitignore b/.gitignore index 0874804..a215835 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ docker-compose.yml substreams-sink-webhook bun.lockb .vscode +examples/**/bun.lockb +examples/**/package-lock.json \ No newline at end of file diff --git a/README.md b/README.md index 68171f8..26a9fb2 100644 --- a/README.md +++ b/README.md @@ -93,14 +93,14 @@ if (!isVerified) { ```env # Webhook -SECRET_KEY="" +PRIVATE_KEY= WEBHOOK_URL=http://127.0.0.1:3000 PORT=9102 # Get Substreams API Key # https://app.pinax.network # https://app.streamingfast.io/ -SUBSTREAMS_API_KEY="" +SUBSTREAMS_API_KEY= # Substreams Package (*.spkg) MANIFEST=https://github.com/pinax-network/substreams/releases/download/blocks-v0.1.0/blocks-v0.1.0.spkg @@ -145,7 +145,7 @@ Options: --final-blocks-only Only process blocks that have pass finality, to prevent any reorg and undo signal by staying further away from the chain HEAD (default: "false", env: FINAL_BLOCKS_ONLY) --verbose Enable verbose logging (default: "false", env: VERBOSE) --webhook-url Webhook URL to send POST (env: WEBHOOK_URL) - --secret-key TweetNaCl Secret-key to sign POST data payload (env: SECRET_KEY) + --private-key Ed25519 private key to sign POST data payload (env: PRIVATE_KEY) --disable-ping Disable ping on init (choices: "true", "false", default: false, env: DISABLE_PING) --disable-signature Disable Ed25519 signature (choices: "true", "false", default: false, env: DISABLE_SIGNATURE) --maximum-attempts Maximum attempts to retry POST (default: 100, env: MAXIMUM_ATTEMPTS) diff --git a/bin/cli.ts b/bin/cli.ts index a55abbc..ad086c3 100755 --- a/bin/cli.ts +++ b/bin/cli.ts @@ -9,18 +9,21 @@ import { ping } from "../src/ping.js"; export interface WebhookRunOptions extends commander.RunOptions { webhookUrl: string; - secretKey: string; + privateKey: string; expiryTime: number; maximumAttempts: number; disablePing: string; disableSignature: string; } +const webhookUrlOption = new Option("--webhook-url ", "Webhook URL to send POST").makeOptionMandatory().env("WEBHOOK_URL"); +const privateKeyOption = new Option("--private-key ", "Ed25519 private key to sign POST data payload").makeOptionMandatory().env("PRIVATE_KEY"); + // Run Webhook Sink const program = commander.program(pkg); const command = commander.run(program, pkg); -command.addOption(new Option("--webhook-url ", "Webhook URL to send POST").makeOptionMandatory().env("WEBHOOK_URL")); -command.addOption(new Option("--secret-key ", "TweetNaCl Secret-key to sign POST data payload").makeOptionMandatory().env("SECRET_KEY")); +command.addOption(webhookUrlOption); +command.addOption(privateKeyOption); command.addOption(new Option("--disable-ping ", "Disable ping on init").choices(["true", "false"]).env("DISABLE_PING").default(false)); command.addOption(new Option("--disable-signature ", "Disable Ed25519 signature").choices(["true", "false"]).env("DISABLE_SIGNATURE").default(false)); command.addOption(new Option("--maximum-attempts ", "Maximum attempts to retry POST").env("MAXIMUM_ATTEMPTS").default(100)); @@ -30,19 +33,19 @@ program .command("keypair") .description("Generate TweetNaCl keypair") .action(() => { - const { publicKey, secretKey } = keyPair(); + const { publicKey, privateKey } = keyPair(); console.log(`PUBLIC_KEY=${publicKey}`); - console.log(`SECRET_KEY=${secretKey}`); + console.log(`PRIVATE_KEY=${privateKey}`); }); program .command("ping") .description("Ping Webhook URL") - .addOption(new Option("--webhook-url ", "Webhook URL to send POST").makeOptionMandatory().env("WEBHOOK_URL")) - .addOption(new Option("--secret-key ", "TweetNaCl Secret-key to sign POST data payload").makeOptionMandatory().env("SECRET_KEY")) + .addOption(webhookUrlOption) + .addOption(privateKeyOption) .action(async (options: WebhookRunOptions) => { logger.settings.type = "hidden"; - const response = await ping(options.webhookUrl, options.secretKey); + const response = await ping(options.webhookUrl, options.privateKey); if (response) console.log("✅ OK"); else console.log("⁉️ ERROR"); }); diff --git a/biome.json b/biome.json index 5e600f8..ee7b58b 100644 --- a/biome.json +++ b/biome.json @@ -39,6 +39,6 @@ } }, "organizeImports": { - "enabled": true + "enabled": false } } diff --git a/examples/bun/bun.lockb b/examples/bun/bun.lockb deleted file mode 100755 index 9c871c0682f2950483b8608f8a8a0cc7680196af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1424 zcmY#Z)GsYA(of3F(@)JSQ%EY!;{sycoc!eMw9K4T-L(9o+{6;yG6OCq1_lPDDHE=o zy4U)bIVjTi-jOTfXYck|-~Jq_&vPTlO2Cd!kbxDb2nd)V6axp8W{1)ZQ2GhTFbu%S zz|fGAUjlRoLh7U+hth#l0ut35VM zfcC-k!(0s0kBf%scLpjj1?m?7(jYTfaB09I4l)B~4vYpV!!G`h{~yeeSj-|-ze zF0#=quXfkD+`DC^n=Wyt>0#MU-fK#`Jj((TW*1C<`?jOd^?{p%-8qKy{P{9g38yU9 zrze>>*ob(pyPw84|JK7SMa$Db$H2@*_7}_Sd4XH}88&?HWSiU}vSOi7)|8^w^g+6?~l)p4aoO_|^MG{EGt@<%eynoFF_eksxC4hrrIeO87LHIR;A{r=_r^eB<5tM=jEqyL2UZ> z9|Ax;kpD8E<`uxrEj2c>18T!z04S^%K-I1wQV+-;kiHvG^*qpo#$^+2WENX$0d%k# z+&)8ugJJmymT#DtauQ2Yi%Wn;0Rbqd!EzQtCJbsej7ANo;?&%-)FM6Cijvf#yu_T~ zlA_GKbUOt@g!LA1>l>gZ{eZg` { // get headers and body from POST request const signature = String(request.headers["x-signature-ed25519"]); - const timestamp = Number(request.headers["x-signature-timestamp"]); + const timestamp = String(request.headers["x-signature-timestamp"]); const body = await request.body; if (!signature) return new Response("missing required signature in headers", { status: 400 }); diff --git a/examples/express/package-lock.json b/examples/express/package-lock.json deleted file mode 100644 index 54fad18..0000000 --- a/examples/express/package-lock.json +++ /dev/null @@ -1,1265 +0,0 @@ -{ - "name": "express", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "dotenv": "latest", - "express": "latest", - "tweetnacl": "latest" - }, - "devDependencies": { - "@types/express": "^4.17.21", - "@types/node": "latest", - "tsx": "latest" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dev": true, - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "dev": true, - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.43", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz", - "integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.11.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz", - "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/qs": { - "version": "6.9.11", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", - "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==", - "dev": true - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true - }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dev": true, - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", - "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", - "dev": true, - "dependencies": { - "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.6.tgz", - "integrity": "sha512-Mj50FLHtlsoVfRfnHaZvyrooHcrlceNZdL/QBvJJVd9Ta55qCQK0gs4ss2oZDeV9zFCs6ewzYgVE5yfVmfFpVg==", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.3", - "set-function-length": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/define-data-property": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.3.tgz", - "integrity": "sha512-h3GBouC+RPtNX2N0hHVLo2ZwPYurq8mLmXpOLTsw71gr7lHt5VaI4vVkDUNOfiWmm48JEXe3VM7PmLX45AMmmg==", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/dotenv": { - "version": "16.4.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.3.tgz", - "integrity": "sha512-II98GFrje5psQTSve0E7bnwMFybNLqT8Vu8JIFWRjsE3khyNUm/loZupuy5DVzG2IXf/ysxvrixYOQnM6mjD3A==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.12", - "@esbuild/android-arm": "0.19.12", - "@esbuild/android-arm64": "0.19.12", - "@esbuild/android-x64": "0.19.12", - "@esbuild/darwin-arm64": "0.19.12", - "@esbuild/darwin-x64": "0.19.12", - "@esbuild/freebsd-arm64": "0.19.12", - "@esbuild/freebsd-x64": "0.19.12", - "@esbuild/linux-arm": "0.19.12", - "@esbuild/linux-arm64": "0.19.12", - "@esbuild/linux-ia32": "0.19.12", - "@esbuild/linux-loong64": "0.19.12", - "@esbuild/linux-mips64el": "0.19.12", - "@esbuild/linux-ppc64": "0.19.12", - "@esbuild/linux-riscv64": "0.19.12", - "@esbuild/linux-s390x": "0.19.12", - "@esbuild/linux-x64": "0.19.12", - "@esbuild/netbsd-x64": "0.19.12", - "@esbuild/openbsd-x64": "0.19.12", - "@esbuild/sunos-x64": "0.19.12", - "@esbuild/win32-arm64": "0.19.12", - "@esbuild/win32-ia32": "0.19.12", - "@esbuild/win32-x64": "0.19.12" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-tsconfig": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", - "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", - "dev": true, - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", - "dependencies": { - "get-intrinsic": "^1.2.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", - "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-function-length": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", - "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", - "dependencies": { - "define-data-property": "^1.1.2", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/side-channel": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", - "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tsx": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.7.1.tgz", - "integrity": "sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==", - "dev": true, - "dependencies": { - "esbuild": "~0.19.10", - "get-tsconfig": "^4.7.2" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, - "node_modules/tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - } - } -} diff --git a/examples/express/package.json b/examples/express/package.json index c97f7df..5a1b10a 100644 --- a/examples/express/package.json +++ b/examples/express/package.json @@ -9,7 +9,7 @@ "tweetnacl": "latest" }, "devDependencies": { - "@types/express": "^4.17.21", + "@types/express": "latest", "@types/node": "latest", "tsx": "latest" } diff --git a/examples/node/package-lock.json b/examples/node/package-lock.json deleted file mode 100644 index 30d50af..0000000 --- a/examples/node/package-lock.json +++ /dev/null @@ -1,535 +0,0 @@ -{ - "name": "node", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "dotenv": "latest", - "tweetnacl": "^1.0.3" - }, - "devDependencies": { - "@types/node": "latest", - "tsx": "latest" - } - }, - "node_modules/@esbuild-kit/cjs-loader": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@esbuild-kit/cjs-loader/-/cjs-loader-2.4.2.tgz", - "integrity": "sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==", - "dev": true, - "dependencies": { - "@esbuild-kit/core-utils": "^3.0.0", - "get-tsconfig": "^4.4.0" - } - }, - "node_modules/@esbuild-kit/core-utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.1.0.tgz", - "integrity": "sha512-Uuk8RpCg/7fdHSceR1M6XbSZFSuMrxcePFuGgyvsBn+u339dk5OeL4jv2EojwTN2st/unJGsVm4qHWjWNmJ/tw==", - "dev": true, - "dependencies": { - "esbuild": "~0.17.6", - "source-map-support": "^0.5.21" - } - }, - "node_modules/@esbuild-kit/esm-loader": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.5.5.tgz", - "integrity": "sha512-Qwfvj/qoPbClxCRNuac1Du01r9gvNOT+pMYtJDapfB1eoGN1YlJ1BixLyL9WVENRx5RXgNLdfYdx/CuswlGhMw==", - "dev": true, - "dependencies": { - "@esbuild-kit/core-utils": "^3.0.0", - "get-tsconfig": "^4.4.0" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", - "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", - "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", - "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", - "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", - "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", - "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", - "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", - "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", - "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", - "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", - "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", - "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", - "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", - "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", - "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", - "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", - "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", - "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", - "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", - "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", - "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", - "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@types/node": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.0.tgz", - "integrity": "sha512-cumHmIAf6On83X7yP+LrsEyUOf/YlociZelmpRYaGFydoaPdxdt80MAbu6vWerQT2COCp2nPvHdsbD7tHn/YlQ==", - "dev": true - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/dotenv": { - "version": "16.1.4", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.1.4.tgz", - "integrity": "sha512-m55RtE8AsPeJBpOIFKihEmqUcoVncQIwo7x9U8ZwLEZw9ZpXboz2c+rvog+jUaJvVrZ5kBOeYQBX5+8Aa/OZQw==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/motdotla/dotenv?sponsor=1" - } - }, - "node_modules/esbuild": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", - "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.17.19", - "@esbuild/android-arm64": "0.17.19", - "@esbuild/android-x64": "0.17.19", - "@esbuild/darwin-arm64": "0.17.19", - "@esbuild/darwin-x64": "0.17.19", - "@esbuild/freebsd-arm64": "0.17.19", - "@esbuild/freebsd-x64": "0.17.19", - "@esbuild/linux-arm": "0.17.19", - "@esbuild/linux-arm64": "0.17.19", - "@esbuild/linux-ia32": "0.17.19", - "@esbuild/linux-loong64": "0.17.19", - "@esbuild/linux-mips64el": "0.17.19", - "@esbuild/linux-ppc64": "0.17.19", - "@esbuild/linux-riscv64": "0.17.19", - "@esbuild/linux-s390x": "0.17.19", - "@esbuild/linux-x64": "0.17.19", - "@esbuild/netbsd-x64": "0.17.19", - "@esbuild/openbsd-x64": "0.17.19", - "@esbuild/sunos-x64": "0.17.19", - "@esbuild/win32-arm64": "0.17.19", - "@esbuild/win32-ia32": "0.17.19", - "@esbuild/win32-x64": "0.17.19" - } - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/get-tsconfig": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.6.0.tgz", - "integrity": "sha512-lgbo68hHTQnFddybKbbs/RDRJnJT5YyGy2kQzVwbq+g67X73i+5MVTval34QxGkOe9X5Ujf1UYpCaphLyltjEg==", - "dev": true, - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/tsx": { - "version": "3.12.7", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-3.12.7.tgz", - "integrity": "sha512-C2Ip+jPmqKd1GWVQDvz/Eyc6QJbGfE7NrR3fx5BpEHMZsEHoIxHL1j+lKdGobr8ovEyqeNkPLSKp6SCSOt7gmw==", - "dev": true, - "dependencies": { - "@esbuild-kit/cjs-loader": "^2.4.2", - "@esbuild-kit/core-utils": "^3.0.0", - "@esbuild-kit/esm-loader": "^2.5.5" - }, - "bin": { - "tsx": "dist/cli.js" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" - } - } -} diff --git a/examples/node/package.json b/examples/node/package.json index bdf5d3a..8c6c4e0 100644 --- a/examples/node/package.json +++ b/examples/node/package.json @@ -5,7 +5,7 @@ }, "dependencies": { "dotenv": "latest", - "tweetnacl": "^1.0.3" + "tweetnacl": "latest" }, "devDependencies": { "@types/node": "latest", diff --git a/index.ts b/index.ts index a7e6a6b..cbd75a1 100644 --- a/index.ts +++ b/index.ts @@ -7,6 +7,7 @@ import type { WebhookRunOptions } from "./bin/cli.js"; import { banner } from "./src/banner.js"; import { toText } from "./src/http.js"; import { ping } from "./src/ping.js"; +import { checkKey } from "./index.js"; export * from "./src/auth/ed25519.js"; export * from "./src/schemas.js"; @@ -19,8 +20,10 @@ export async function action(options: WebhookRunOptions) { const queue = new PQueue({ concurrency: 1 }); // all messages are sent in block order, no need to parallelize // Ping URL to check if it's valid + const privateKey = options.privateKey; + checkKey(privateKey, "private"); if (options.disablePing === "false") { - if (!(await ping(options.webhookUrl, options.secretKey))) { + if (!(await ping(options.webhookUrl, privateKey))) { logger.error("exiting from invalid PING response"); process.exit(1); } @@ -60,7 +63,7 @@ export async function action(options: WebhookRunOptions) { // Queue POST queue.add(async () => { - const response = await postWebhook(options.webhookUrl, body, options.secretKey, options); + const response = await postWebhook(options.webhookUrl, body, privateKey, options); logger.info("POST", response, metadata); }); }); diff --git a/package-lock.json b/package-lock.json index f110e0a..297a396 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,11 +8,11 @@ "name": "substreams-sink-webhook", "version": "0.8.0", "dependencies": { + "@noble/curves": "^1.3.0", "@substreams/sink-database-changes": "^0.3.2", "@substreams/sink-entity-changes": "^0.3.4", "p-queue": "latest", "substreams-sink": "^0.14.0", - "tweetnacl": "latest", "zod": "latest" }, "bin": { @@ -22,6 +22,7 @@ "@biomejs/biome": "latest", "bun-types": "latest", "mitata": "latest", + "tweetnacl": "latest", "typescript": "latest" } }, @@ -354,6 +355,28 @@ "node": ">=14" } }, + "node_modules/@noble/curves": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz", + "integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==", + "dependencies": { + "@noble/hashes": "1.3.3" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", + "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@opentelemetry/api": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.7.0.tgz", @@ -611,7 +634,8 @@ "node_modules/tweetnacl": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "dev": true }, "node_modules/typescript": { "version": "5.3.3", diff --git a/package.json b/package.json index 3b57490..6834e28 100644 --- a/package.json +++ b/package.json @@ -37,17 +37,18 @@ "bench": "bun ./src/**/*.bench.ts" }, "dependencies": { + "@noble/curves": "^1.3.0", "@substreams/sink-database-changes": "^0.3.2", "@substreams/sink-entity-changes": "^0.3.4", "p-queue": "latest", "substreams-sink": "^0.14.0", - "tweetnacl": "latest", "zod": "latest" }, "devDependencies": { "@biomejs/biome": "latest", "bun-types": "latest", "mitata": "latest", + "tweetnacl": "latest", "typescript": "latest" } } diff --git a/src/auth/ed25519.bench.ts b/src/auth/ed25519.bench.ts index dfe4748..8b5033b 100644 --- a/src/auth/ed25519.bench.ts +++ b/src/auth/ed25519.bench.ts @@ -1,15 +1,20 @@ +import { ed25519 } from "@noble/curves/ed25519.js"; import { bench, group, run } from "mitata"; -import { createTimestamp, sign, verify } from "./ed25519.js"; +import nacl from "tweetnacl"; -const secretKey = "3faae992336ea6599fbee55bb2605f1a1297c7288b860725cdfc8794413559dba3cb7366ee8ca77225b4d41772e270e4e831d171d1de71d91707c42e7ba82cc9"; +const secretKey = "3faae992336ea6599fbee55bb2605f1a1297c7288b860725cdfc8794413559db"; const publicKey = "a3cb7366ee8ca77225b4d41772e270e4e831d171d1de71d91707c42e7ba82cc9"; const body = "hello world"; -const timestamp = createTimestamp(); -const signature = sign(timestamp, body, secretKey); +const signature = "fb8b513516baa2ded8cc1d9745560b60843798a35fe6d7fc99bfcf61845f44ded8e31a175cea1abe649c9663e7f1e98f8cde3a9a53dae4158a69d3b86a753c04"; -group("ed25519", () => { - bench("sign", () => sign(timestamp, body, secretKey)); - bench("verify", () => verify(timestamp, body, signature, publicKey)); +group("sign", () => { + bench("tweetnacl", () => nacl.sign.detached(Buffer.from(body), Buffer.from(secretKey + publicKey, "hex"))); + bench("@noble/curves", () => ed25519.sign(Buffer.from(body), secretKey)); +}); + +group("verify", () => { + bench("tweetnacl", () => nacl.sign.detached.verify(Buffer.from(body), Buffer.from(signature, "hex"), Buffer.from(publicKey, "hex"))); + bench("@noble/curves", () => ed25519.verify(signature, Buffer.from(body), publicKey)); }); await run({ avg: true, json: false, colors: true, min_max: true, collect: false, percentiles: false }); diff --git a/src/auth/ed25519.spec.ts b/src/auth/ed25519.spec.ts index a2044d2..f32ff10 100644 --- a/src/auth/ed25519.spec.ts +++ b/src/auth/ed25519.spec.ts @@ -1,7 +1,7 @@ import { describe, expect, test } from "bun:test"; import * as auth from "./ed25519.js"; -const secretKey = "3faae992336ea6599fbee55bb2605f1a1297c7288b860725cdfc8794413559dba3cb7366ee8ca77225b4d41772e270e4e831d171d1de71d91707c42e7ba82cc9"; +const privateKey = "3faae992336ea6599fbee55bb2605f1a1297c7288b860725cdfc8794413559db"; const publicKey = "a3cb7366ee8ca77225b4d41772e270e4e831d171d1de71d91707c42e7ba82cc9"; const body = `{"status":200,"cursor":"OJGbpO9ZnZcwvxW38_FO8KWwLpcyA1lrUQPgKRFL04Py8yCW35v1VTB1O0-Elami3RztQlOp2tmcHC9y9ZQFuoDrxLpj6yU-FXorwoHr_OfqLPumMQwTJ-hgWeuKYNLeWDjTagn4ersEtNGzbvLaY0UxZZUhK2G62z1VptdXJfEWuiJmyjmrIZrRhK-WoNAS_rEkQ7L1xCmhDzJ4K0dTPcSDNPKZuDR2","session":{"traceId":"3cbb0a1c772a47a72995d95f4c6d2cff","resolvedStartBlock":53448515},"clock":{"timestamp":"2024-02-12T22:23:51.000Z","number":53448530,"id":"f843bc26cea0cbd50b09699546a8a97de6a1727646c17a857c5d8d868fc26142"},"manifest":{"substreamsEndpoint":"https://polygon.substreams.pinax.network:443","chain":"polygon","finalBlockOnly":"false","moduleName":"map_blocks","type":"sf.substreams.v1.Clock","moduleHash":"44c506941d5f30db6cca01692624395d1ac40cd1"},"data":{"id":"f843bc26cea0cbd50b09699546a8a97de6a1727646c17a857c5d8d868fc26142","number":"53448530","timestamp":"2024-02-12T22:23:51Z"}}`; const signature = "8f01c66ccda5b987c43d913290419572ea586dbef2077fa166c4a84797e1d2c76b305bc67ed43efb1fc841562620a61cb59c4d8a13de689a2e98ead19190f80c"; @@ -9,21 +9,21 @@ const timestamp = 1707776632; describe("sign", () => { test("sign", () => { - const signature = auth.sign(timestamp, body, secretKey); + const signature = auth.sign(timestamp, body, privateKey); expect(signature).toBe(signature); }); test("error - missing body", () => { - expect(() => auth.sign(timestamp, "", secretKey)).toThrow("missing body"); + expect(() => auth.sign(timestamp, "", privateKey)).toThrow("missing body"); }); - test("error - invalid secret key length", () => { - expect(() => auth.sign(timestamp, body, "")).toThrow("invalid secret key length"); + test("error - invalid private key length", () => { + expect(() => auth.sign(timestamp, body, "")).toThrow("invalid private key length"); }); test("error - invalid timestamp", () => { - expect(() => auth.sign(0, body, secretKey)).toThrow("invalid timestamp"); - expect(() => auth.sign(Number("abc"), body, secretKey)).toThrow("invalid timestamp"); + expect(() => auth.sign(0, body, privateKey)).toThrow("invalid timestamp"); + expect(() => auth.sign(Number("abc"), body, privateKey)).toThrow("invalid timestamp"); }); }); diff --git a/src/auth/ed25519.ts b/src/auth/ed25519.ts index 67cabf1..c0a4020 100644 --- a/src/auth/ed25519.ts +++ b/src/auth/ed25519.ts @@ -1,50 +1,55 @@ -import nacl from "tweetnacl"; +import { Hex } from "@noble/curves/abstract/utils"; +import { ed25519 } from "@noble/curves/ed25519"; export function createTimestamp() { return Math.floor(Date.now() / 1000); } -export function sign(timestamp: number, body: string, secretKey: string) { - const secretKeyHex = Buffer.from(secretKey, "hex"); +export function createMessage(timestamp: number, body: Hex): Hex { + return Buffer.from(`${timestamp}${body}`); +} - // input validation - if (secretKeyHex.length !== nacl.sign.secretKeyLength) { - throw Error("invalid secret key length"); - } - if (!body) { - throw Error("missing body"); - } - if (!(timestamp > 0)) { - throw Error("invalid timestamp"); +export function checkKey(key: Hex, type: "public" | "private") { + const length = typeof key === "string" ? 64 : 32; + if (key.length !== length) { + throw Error(`invalid ${type} key length`); } - - // sign message - const msg = Buffer.from(timestamp + body); - const signedBuffer = nacl.sign.detached(msg, secretKeyHex); - return Buffer.from(signedBuffer).toString("hex"); } -export function verify(timestamp: number, body: string, signature: string, publicKey: string): true { - const signatureHex = Buffer.from(signature, "hex"); - const publicKeyHex = Buffer.from(publicKey, "hex"); - - // input validation - if (signatureHex.length !== nacl.sign.signatureLength) { +export function checkSignature(signature: Hex) { + const length = typeof signature === "string" ? 128 : 64; + if (signature.length !== length) { throw Error("invalid signature length"); } +} + +export function checkMessage(timestamp: number, body: string) { if (!body) { throw Error("missing body"); } if (!(timestamp > 0)) { throw Error("invalid timestamp"); } - if (publicKeyHex.length !== nacl.sign.publicKeyLength) { - throw Error("invalid public key length"); - } +} + +export function toHex(arrayBuffer: Uint8Array) { + return Buffer.from(arrayBuffer).toString("hex"); +} + +export function sign(timestamp: number, body: string, privateKey: Hex) { + checkKey(privateKey, "private"); + checkMessage(timestamp, body); + const message = createMessage(timestamp, body); + const signedBuffer = ed25519.sign(message, privateKey); + return Buffer.from(signedBuffer).toString("hex"); +} - // verify signature - const msg = Buffer.from(timestamp + body); - const isVerified = nacl.sign.detached.verify(msg, signatureHex, publicKeyHex); +export function verify(timestamp: number, body: string, signature: Hex, publicKey: Hex): true { + checkKey(publicKey, "public"); + checkMessage(timestamp, body); + checkSignature(signature); + const message = createMessage(timestamp, body); + const isVerified = ed25519.verify(signature, message, publicKey); if (!isVerified) { throw Error("invalid signature"); @@ -53,9 +58,10 @@ export function verify(timestamp: number, body: string, signature: string, publi } export function keyPair() { - const { secretKey, publicKey } = nacl.sign.keyPair(); + const privateKey = ed25519.utils.randomPrivateKey(); + const publicKey = ed25519.getPublicKey(privateKey); return { - secretKey: Buffer.from(secretKey).toString("hex"), + privateKey: Buffer.from(privateKey).toString("hex"), publicKey: Buffer.from(publicKey).toString("hex"), }; } diff --git a/src/ping.ts b/src/ping.ts index 1b5db38..b2573d7 100644 --- a/src/ping.ts +++ b/src/ping.ts @@ -1,20 +1,21 @@ +import { Hex } from "@noble/curves/abstract/utils"; import { keyPair } from "./auth/ed25519.js"; import { postWebhook } from "./postWebhook.js"; -export async function ping(url: string, secretKey: string) { +export async function ping(url: string, privateKey: Hex) { const body = JSON.stringify({ message: "PING" }); - const invalidSecretKey = keyPair().secretKey; + const invalidprivateKey = keyPair().privateKey; // send valid signature (must respond with 200) try { - await postWebhook(url, body, secretKey, { maximumAttempts: 0 }); + await postWebhook(url, body, privateKey, { maximumAttempts: 0 }); } catch (_e) { return false; } // send invalid signature (must NOT respond with 200) try { - await postWebhook(url, body, invalidSecretKey, { maximumAttempts: 0 }); + await postWebhook(url, body, invalidprivateKey, { maximumAttempts: 0 }); return false; } catch (_e) { return true; diff --git a/src/postWebhook.ts b/src/postWebhook.ts index 55b0fcf..9bb19e0 100644 --- a/src/postWebhook.ts +++ b/src/postWebhook.ts @@ -1,3 +1,4 @@ +import { Hex } from "@noble/curves/abstract/utils"; import { logger } from "substreams-sink"; import { createTimestamp, sign } from "./auth/ed25519.js"; @@ -10,7 +11,7 @@ interface PostWebhookOptions { disableSignature?: string; } -export async function postWebhook(url: string, body: string, secretKey: string, options: PostWebhookOptions = {}) { +export async function postWebhook(url: string, body: string, secretKey: Hex, options: PostWebhookOptions = {}) { // Retry Policy const initialInterval = 1000; // 1s const maximumAttempts = options.maximumAttempts ?? 100 * initialInterval;