diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2ec0892f..457e62e3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: denoland/deno:2.0.0-rc.3 +image: denoland/deno:2.0.0 default: interruptible: true diff --git a/.tool-versions b/.tool-versions index 900b9e20..18a2eb04 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -deno 1.46.3 \ No newline at end of file +deno 2.0.0 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 636a94fa..90475c15 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM denoland/deno:1.44.2 +FROM denoland/deno:2.0.0 ENV PORT 5000 WORKDIR /app diff --git a/deno.json b/deno.json index df470e32..57d90ab9 100644 --- a/deno.json +++ b/deno.json @@ -10,7 +10,7 @@ "nostr:pull": "deno run -A scripts/nostr-pull.ts", "debug": "deno run -A --inspect src/server.ts", "test": "deno test -A --junit-path=./deno-test.xml", - "check": "deno check src/server.ts", + "check": "deno check --allow-import src/server.ts", "nsec": "deno run scripts/nsec.ts", "admin:event": "deno run -A scripts/admin-event.ts", "admin:role": "deno run -A scripts/admin-role.ts", @@ -37,13 +37,14 @@ "@b-fuze/deno-dom": "jsr:@b-fuze/deno-dom@^0.1.47", "@bradenmacdonald/s3-lite-client": "jsr:@bradenmacdonald/s3-lite-client@^0.7.4", "@electric-sql/pglite": "npm:@electric-sql/pglite@^0.2.8", + "@esroyo/scoped-performance": "jsr:@esroyo/scoped-performance@^3.1.0", "@gfx/canvas-wasm": "jsr:@gfx/canvas-wasm@^0.4.2", "@hono/hono": "jsr:@hono/hono@^4.4.6", "@isaacs/ttlcache": "npm:@isaacs/ttlcache@^1.4.1", "@lambdalisue/async": "jsr:@lambdalisue/async@^2.1.1", "@negrel/webpush": "jsr:@negrel/webpush@^0.3.0", "@noble/secp256k1": "npm:@noble/secp256k1@^2.0.0", - "@nostrify/db": "jsr:@nostrify/db@^0.35.0", + "@nostrify/db": "jsr:@nostrify/db@^0.36.1", "@nostrify/nostrify": "jsr:@nostrify/nostrify@^0.36.0", "@nostrify/policies": "jsr:@nostrify/policies@^0.35.0", "@scure/base": "npm:@scure/base@^1.1.6", @@ -69,7 +70,7 @@ "formdata-helper": "npm:formdata-helper@^0.3.0", "hono-rate-limiter": "npm:hono-rate-limiter@^0.3.0", "iso-639-1": "npm:iso-639-1@2.1.15", - "isomorphic-dompurify": "npm:isomorphic-dompurify@^2.11.0", + "isomorphic-dompurify": "npm:isomorphic-dompurify@^2.16.0", "kysely": "npm:kysely@^0.27.4", "kysely-postgres-js": "npm:kysely-postgres-js@2.0.0", "lande": "npm:lande@^1.0.10", diff --git a/deno.lock b/deno.lock index 98f51ada..24c4ed15 100644 --- a/deno.lock +++ b/deno.lock @@ -1,1555 +1,1476 @@ { - "version": "3", - "packages": { - "specifiers": { - "jsr:@b-fuze/deno-dom@^0.1.47": "jsr:@b-fuze/deno-dom@0.1.48", - "jsr:@bradenmacdonald/s3-lite-client@^0.7.4": "jsr:@bradenmacdonald/s3-lite-client@0.7.6", - "jsr:@denosaurs/plug@1.0.3": "jsr:@denosaurs/plug@1.0.3", - "jsr:@gfx/canvas-wasm@^0.4.2": "jsr:@gfx/canvas-wasm@0.4.2", - "jsr:@gleasonator/policy": "jsr:@gleasonator/policy@0.2.0", - "jsr:@gleasonator/policy@0.2.0": "jsr:@gleasonator/policy@0.2.0", - "jsr:@gleasonator/policy@0.4.0": "jsr:@gleasonator/policy@0.4.0", - "jsr:@gleasonator/policy@0.4.1": "jsr:@gleasonator/policy@0.4.1", - "jsr:@gleasonator/policy@0.4.2": "jsr:@gleasonator/policy@0.4.2", - "jsr:@gleasonator/policy@0.5.0": "jsr:@gleasonator/policy@0.5.0", - "jsr:@gleasonator/policy@0.5.1": "jsr:@gleasonator/policy@0.5.1", - "jsr:@gleasonator/policy@0.5.2": "jsr:@gleasonator/policy@0.5.2", - "jsr:@gleasonator/policy@0.6.0": "jsr:@gleasonator/policy@0.6.0", - "jsr:@gleasonator/policy@0.6.1": "jsr:@gleasonator/policy@0.6.1", - "jsr:@gleasonator/policy@0.6.3": "jsr:@gleasonator/policy@0.6.3", - "jsr:@gleasonator/policy@0.6.4": "jsr:@gleasonator/policy@0.6.4", - "jsr:@gleasonator/policy@0.7.0": "jsr:@gleasonator/policy@0.7.0", - "jsr:@gleasonator/policy@0.7.1": "jsr:@gleasonator/policy@0.7.1", - "jsr:@gleasonator/policy@0.8.0": "jsr:@gleasonator/policy@0.8.0", - "jsr:@hono/hono@^4.4.6": "jsr:@hono/hono@4.6.2", - "jsr:@lambdalisue/async@^2.1.1": "jsr:@lambdalisue/async@2.1.1", - "jsr:@negrel/http-ece@0.6.0": "jsr:@negrel/http-ece@0.6.0", - "jsr:@negrel/webpush": "jsr:@negrel/webpush@0.3.0", - "jsr:@negrel/webpush@^0.3.0": "jsr:@negrel/webpush@0.3.0", - "jsr:@nostrify/db@^0.35.0": "jsr:@nostrify/db@0.35.0", - "jsr:@nostrify/nostrify@^0.22.1": "jsr:@nostrify/nostrify@0.22.5", - "jsr:@nostrify/nostrify@^0.22.4": "jsr:@nostrify/nostrify@0.22.4", - "jsr:@nostrify/nostrify@^0.22.5": "jsr:@nostrify/nostrify@0.22.5", - "jsr:@nostrify/nostrify@^0.31.0": "jsr:@nostrify/nostrify@0.31.0", - "jsr:@nostrify/nostrify@^0.32.0": "jsr:@nostrify/nostrify@0.32.0", - "jsr:@nostrify/nostrify@^0.35.0": "jsr:@nostrify/nostrify@0.35.0", - "jsr:@nostrify/nostrify@^0.36.0": "jsr:@nostrify/nostrify@0.36.0", - "jsr:@nostrify/policies@^0.33.0": "jsr:@nostrify/policies@0.33.0", - "jsr:@nostrify/policies@^0.33.1": "jsr:@nostrify/policies@0.33.1", - "jsr:@nostrify/policies@^0.34.0": "jsr:@nostrify/policies@0.34.0", - "jsr:@nostrify/policies@^0.35.0": "jsr:@nostrify/policies@0.35.0", - "jsr:@nostrify/policies@^0.36.0": "jsr:@nostrify/policies@0.36.0", - "jsr:@nostrify/types@^0.30.0": "jsr:@nostrify/types@0.30.1", - "jsr:@nostrify/types@^0.30.1": "jsr:@nostrify/types@0.30.1", - "jsr:@nostrify/types@^0.35.0": "jsr:@nostrify/types@0.35.0", - "jsr:@soapbox/kysely-pglite@^1.0.0": "jsr:@soapbox/kysely-pglite@1.0.0", - "jsr:@soapbox/stickynotes@^0.4.0": "jsr:@soapbox/stickynotes@0.4.0", - "jsr:@std/assert@^0.213.1": "jsr:@std/assert@0.213.1", - "jsr:@std/assert@^0.223.0": "jsr:@std/assert@0.223.0", - "jsr:@std/assert@^0.224.0": "jsr:@std/assert@0.224.0", - "jsr:@std/assert@^0.225.1": "jsr:@std/assert@0.225.3", - "jsr:@std/bytes@0.224.0": "jsr:@std/bytes@0.224.0", - "jsr:@std/bytes@^0.223.0": "jsr:@std/bytes@0.223.0", - "jsr:@std/bytes@^0.224.0": "jsr:@std/bytes@0.224.0", - "jsr:@std/bytes@^1.0.0-rc.3": "jsr:@std/bytes@1.0.0", - "jsr:@std/bytes@^1.0.1-rc.3": "jsr:@std/bytes@1.0.2", - "jsr:@std/bytes@^1.0.2": "jsr:@std/bytes@1.0.2", - "jsr:@std/bytes@^1.0.2-rc.3": "jsr:@std/bytes@1.0.2", - "jsr:@std/cli@^0.223.0": "jsr:@std/cli@0.223.0", - "jsr:@std/crypto@^0.224.0": "jsr:@std/crypto@0.224.0", - "jsr:@std/dotenv@^0.224.0": "jsr:@std/dotenv@0.224.2", - "jsr:@std/encoding@0.213.1": "jsr:@std/encoding@0.213.1", - "jsr:@std/encoding@0.224.0": "jsr:@std/encoding@0.224.0", - "jsr:@std/encoding@1.0.5": "jsr:@std/encoding@1.0.5", - "jsr:@std/encoding@^0.224.0": "jsr:@std/encoding@0.224.3", - "jsr:@std/encoding@^0.224.1": "jsr:@std/encoding@0.224.3", - "jsr:@std/fmt@0.213.1": "jsr:@std/fmt@0.213.1", - "jsr:@std/fs@0.213.1": "jsr:@std/fs@0.213.1", - "jsr:@std/fs@^0.229.3": "jsr:@std/fs@0.229.3", - "jsr:@std/internal@^1.0.0": "jsr:@std/internal@1.0.4", - "jsr:@std/io@^0.223.0": "jsr:@std/io@0.223.0", - "jsr:@std/io@^0.224": "jsr:@std/io@0.224.8", - "jsr:@std/json@^0.223.0": "jsr:@std/json@0.223.0", - "jsr:@std/media-types@0.224.0": "jsr:@std/media-types@0.224.0", - "jsr:@std/media-types@^0.224.1": "jsr:@std/media-types@0.224.1", - "jsr:@std/path@0.213.1": "jsr:@std/path@0.213.1", - "jsr:@std/path@0.224.0": "jsr:@std/path@0.224.0", - "jsr:@std/path@1.0.0-rc.1": "jsr:@std/path@1.0.0-rc.1", - "jsr:@std/path@^0.213.1": "jsr:@std/path@0.213.1", - "jsr:@std/streams@^0.223.0": "jsr:@std/streams@0.223.0", - "npm:@electric-sql/pglite@^0.2.8": "npm:@electric-sql/pglite@0.2.8", - "npm:@isaacs/ttlcache@^1.4.1": "npm:@isaacs/ttlcache@1.4.1", - "npm:@noble/hashes@^1.4.0": "npm:@noble/hashes@1.4.0", - "npm:@noble/secp256k1@^2.0.0": "npm:@noble/secp256k1@2.1.0", - "npm:@scure/base@^1.1.6": "npm:@scure/base@1.1.6", - "npm:@scure/bip32@^1.4.0": "npm:@scure/bip32@1.4.0", - "npm:@scure/bip39@^1.3.0": "npm:@scure/bip39@1.3.0", - "npm:@types/node": "npm:@types/node@18.16.19", - "npm:comlink-async-generator": "npm:comlink-async-generator@0.0.1", - "npm:comlink-async-generator@^0.0.1": "npm:comlink-async-generator@0.0.1", - "npm:comlink@^4.4.1": "npm:comlink@4.4.1", - "npm:commander@12.1.0": "npm:commander@12.1.0", - "npm:entities@^4.5.0": "npm:entities@4.5.0", - "npm:fast-stable-stringify@^1.0.0": "npm:fast-stable-stringify@1.0.0", - "npm:formdata-helper@^0.3.0": "npm:formdata-helper@0.3.0", - "npm:hono-rate-limiter@^0.3.0": "npm:hono-rate-limiter@0.3.0_hono@4.2.5", - "npm:iso-639-1@2.1.15": "npm:iso-639-1@2.1.15", - "npm:isomorphic-dompurify@^2.11.0": "npm:isomorphic-dompurify@2.11.0", - "npm:kysely-postgres-js@2.0.0": "npm:kysely-postgres-js@2.0.0_kysely@0.27.3_postgres@3.4.4", - "npm:kysely@^0.27.2": "npm:kysely@0.27.4", - "npm:kysely@^0.27.3": "npm:kysely@0.27.4", - "npm:kysely@^0.27.4": "npm:kysely@0.27.4", - "npm:lande@^1.0.10": "npm:lande@1.0.10", - "npm:light-bolt11-decoder": "npm:light-bolt11-decoder@3.1.1", - "npm:linkify-plugin-hashtag@^4.1.1": "npm:linkify-plugin-hashtag@4.1.3_linkifyjs@4.1.3", - "npm:linkify-string@^4.1.1": "npm:linkify-string@4.1.3_linkifyjs@4.1.3", - "npm:linkifyjs@^4.1.1": "npm:linkifyjs@4.1.3", - "npm:lint-staged": "npm:lint-staged@15.2.2", - "npm:lru-cache@^10.2.0": "npm:lru-cache@10.2.2", - "npm:lru-cache@^10.2.2": "npm:lru-cache@10.2.2", - "npm:nostr-tools@2.5.1": "npm:nostr-tools@2.5.1", - "npm:nostr-tools@^2.5.0": "npm:nostr-tools@2.5.1", - "npm:nostr-tools@^2.7.0": "npm:nostr-tools@2.7.0", - "npm:nostr-wasm@^0.1.0": "npm:nostr-wasm@0.1.0", - "npm:path-to-regexp@^7.1.0": "npm:path-to-regexp@7.1.0", - "npm:png-to-ico@^2.1.8": "npm:png-to-ico@2.1.8", - "npm:postgres@3.4.4": "npm:postgres@3.4.4", - "npm:prom-client@^15.1.2": "npm:prom-client@15.1.2", - "npm:tldts@^6.0.14": "npm:tldts@6.1.18", - "npm:tseep@^1.2.1": "npm:tseep@1.2.1", - "npm:type-fest@^4.3.0": "npm:type-fest@4.18.2", - "npm:unfurl.js@^6.4.0": "npm:unfurl.js@6.4.0", - "npm:websocket-ts@^2.1.5": "npm:websocket-ts@2.1.5", - "npm:zod@^3.23.8": "npm:zod@3.23.8" + "version": "4", + "specifiers": { + "jsr:@b-fuze/deno-dom@~0.1.47": "0.1.48", + "jsr:@bradenmacdonald/s3-lite-client@~0.7.4": "0.7.6", + "jsr:@denosaurs/plug@1.0.3": "1.0.3", + "jsr:@esroyo/scoped-performance@^3.1.0": "3.1.0", + "jsr:@gfx/canvas-wasm@~0.4.2": "0.4.2", + "jsr:@gleasonator/policy@*": "0.2.0", + "jsr:@gleasonator/policy@0.2.0": "0.2.0", + "jsr:@gleasonator/policy@0.4.0": "0.4.0", + "jsr:@gleasonator/policy@0.4.1": "0.4.1", + "jsr:@gleasonator/policy@0.4.2": "0.4.2", + "jsr:@gleasonator/policy@0.5.0": "0.5.0", + "jsr:@gleasonator/policy@0.5.1": "0.5.1", + "jsr:@gleasonator/policy@0.5.2": "0.5.2", + "jsr:@gleasonator/policy@0.6.0": "0.6.0", + "jsr:@gleasonator/policy@0.6.1": "0.6.1", + "jsr:@gleasonator/policy@0.6.3": "0.6.3", + "jsr:@gleasonator/policy@0.6.4": "0.6.4", + "jsr:@gleasonator/policy@0.7.0": "0.7.0", + "jsr:@gleasonator/policy@0.7.1": "0.7.1", + "jsr:@gleasonator/policy@0.8.0": "0.8.0", + "jsr:@gleasonator/policy@0.8.1": "0.8.1", + "jsr:@gleasonator/policy@0.9.0": "0.9.0", + "jsr:@hono/hono@^4.4.6": "4.6.2", + "jsr:@lambdalisue/async@^2.1.1": "2.1.1", + "jsr:@negrel/http-ece@0.6.0": "0.6.0", + "jsr:@negrel/webpush@0.3": "0.3.0", + "jsr:@nostrify/db@~0.36.1": "0.36.1", + "jsr:@nostrify/nostrify@0.31": "0.31.0", + "jsr:@nostrify/nostrify@0.32": "0.32.0", + "jsr:@nostrify/nostrify@0.35": "0.35.0", + "jsr:@nostrify/nostrify@0.36": "0.36.0", + "jsr:@nostrify/nostrify@~0.22.1": "0.22.5", + "jsr:@nostrify/nostrify@~0.22.4": "0.22.4", + "jsr:@nostrify/nostrify@~0.22.5": "0.22.5", + "jsr:@nostrify/policies@0.33": "0.33.0", + "jsr:@nostrify/policies@0.34": "0.34.0", + "jsr:@nostrify/policies@0.35": "0.35.0", + "jsr:@nostrify/policies@0.36": "0.36.0", + "jsr:@nostrify/policies@~0.33.1": "0.33.1", + "jsr:@nostrify/policies@~0.36.1": "0.36.1", + "jsr:@nostrify/types@0.30": "0.30.1", + "jsr:@nostrify/types@0.35": "0.35.0", + "jsr:@nostrify/types@~0.30.1": "0.30.1", + "jsr:@soapbox/kysely-pglite@1": "1.0.0", + "jsr:@soapbox/stickynotes@0.4": "0.4.0", + "jsr:@std/assert@0.223": "0.223.0", + "jsr:@std/assert@0.224": "0.224.0", + "jsr:@std/assert@~0.213.1": "0.213.1", + "jsr:@std/assert@~0.225.1": "0.225.3", + "jsr:@std/bytes@0.223": "0.223.0", + "jsr:@std/bytes@0.224": "0.224.0", + "jsr:@std/bytes@0.224.0": "0.224.0", + "jsr:@std/bytes@^1.0.0-rc.3": "1.0.0", + "jsr:@std/bytes@^1.0.1-rc.3": "1.0.2", + "jsr:@std/bytes@^1.0.2": "1.0.2", + "jsr:@std/bytes@^1.0.2-rc.3": "1.0.2", + "jsr:@std/cli@0.223": "0.223.0", + "jsr:@std/crypto@0.224": "0.224.0", + "jsr:@std/dotenv@0.224": "0.224.2", + "jsr:@std/encoding@0.213.1": "0.213.1", + "jsr:@std/encoding@0.224": "0.224.3", + "jsr:@std/encoding@0.224.0": "0.224.0", + "jsr:@std/encoding@1.0.5": "1.0.5", + "jsr:@std/encoding@~0.224.1": "0.224.3", + "jsr:@std/fmt@0.213.1": "0.213.1", + "jsr:@std/fs@0.213.1": "0.213.1", + "jsr:@std/fs@~0.229.3": "0.229.3", + "jsr:@std/internal@1": "1.0.4", + "jsr:@std/io@0.223": "0.223.0", + "jsr:@std/io@0.224": "0.224.8", + "jsr:@std/json@0.223": "0.223.0", + "jsr:@std/media-types@0.224.0": "0.224.0", + "jsr:@std/media-types@~0.224.1": "0.224.1", + "jsr:@std/path@0.213.1": "0.213.1", + "jsr:@std/path@0.224.0": "0.224.0", + "jsr:@std/path@1.0.0-rc.1": "1.0.0-rc.1", + "jsr:@std/path@~0.213.1": "0.213.1", + "jsr:@std/streams@0.223": "0.223.0", + "npm:@electric-sql/pglite@~0.2.8": "0.2.8", + "npm:@isaacs/ttlcache@^1.4.1": "1.4.1", + "npm:@noble/hashes@^1.4.0": "1.4.0", + "npm:@noble/secp256k1@2": "2.1.0", + "npm:@scure/base@^1.1.6": "1.1.6", + "npm:@scure/bip32@^1.4.0": "1.4.0", + "npm:@scure/bip39@^1.3.0": "1.3.0", + "npm:@types/node@*": "18.16.19", + "npm:comlink-async-generator@*": "0.0.1", + "npm:comlink-async-generator@^0.0.1": "0.0.1", + "npm:comlink@^4.4.1": "4.4.1", + "npm:commander@12.1.0": "12.1.0", + "npm:entities@^4.5.0": "4.5.0", + "npm:fast-stable-stringify@1": "1.0.0", + "npm:formdata-helper@0.3": "0.3.0", + "npm:hono-rate-limiter@0.3": "0.3.0_hono@4.2.5", + "npm:iso-639-1@2.1.15": "2.1.15", + "npm:isomorphic-dompurify@^2.16.0": "2.16.0", + "npm:kysely-postgres-js@2.0.0": "2.0.0_kysely@0.27.3_postgres@3.4.4", + "npm:kysely@~0.27.2": "0.27.4", + "npm:kysely@~0.27.3": "0.27.4", + "npm:kysely@~0.27.4": "0.27.4", + "npm:lande@^1.0.10": "1.0.10", + "npm:light-bolt11-decoder@*": "3.1.1", + "npm:linkify-plugin-hashtag@^4.1.1": "4.1.3_linkifyjs@4.1.3", + "npm:linkify-string@^4.1.1": "4.1.3_linkifyjs@4.1.3", + "npm:linkifyjs@^4.1.1": "4.1.3", + "npm:lint-staged@*": "15.2.2", + "npm:lru-cache@^10.2.0": "10.2.2", + "npm:lru-cache@^10.2.2": "10.2.2", + "npm:nostr-tools@2.5.1": "2.5.1", + "npm:nostr-tools@^2.5.0": "2.5.1", + "npm:nostr-tools@^2.7.0": "2.7.0", + "npm:nostr-wasm@0.1": "0.1.0", + "npm:path-to-regexp@^7.1.0": "7.1.0", + "npm:png-to-ico@^2.1.8": "2.1.8", + "npm:postgres@3.4.4": "3.4.4", + "npm:prom-client@^15.1.2": "15.1.2", + "npm:tldts@^6.0.14": "6.1.18", + "npm:tseep@^1.2.1": "1.2.1", + "npm:type-fest@^4.3.0": "4.18.2", + "npm:unfurl.js@^6.4.0": "6.4.0", + "npm:websocket-ts@^2.1.5": "2.1.5", + "npm:zod@^3.23.8": "3.23.8" + }, + "jsr": { + "@b-fuze/deno-dom@0.1.47": { + "integrity": "270a888de91329f8ce3849211ece0ad97ce1e8b9a8a774f2bed2f43c8b0ffe8e", + "dependencies": [ + "jsr:@denosaurs/plug" + ] }, - "jsr": { - "@b-fuze/deno-dom@0.1.47": { - "integrity": "270a888de91329f8ce3849211ece0ad97ce1e8b9a8a774f2bed2f43c8b0ffe8e", - "dependencies": [ - "jsr:@denosaurs/plug@1.0.3" - ] - }, - "@b-fuze/deno-dom@0.1.48": { - "integrity": "bf5b591aef2e9e9c59adfcbb93a9ecd45bab5b7c8263625beafa5c8f1662e7da" - }, - "@bradenmacdonald/s3-lite-client@0.7.6": { - "integrity": "2b5976dca95d207dc88e23f9807e3eecbc441b0cf547dcda5784afe6668404d1", - "dependencies": [ - "jsr:@std/io@^0.224" - ] - }, - "@denosaurs/plug@1.0.3": { - "integrity": "b010544e386bea0ff3a1d05e0c88f704ea28cbd4d753439c2f1ee021a85d4640", - "dependencies": [ - "jsr:@std/encoding@0.213.1", - "jsr:@std/fmt@0.213.1", - "jsr:@std/fs@0.213.1", - "jsr:@std/path@0.213.1" - ] - }, - "@gfx/canvas-wasm@0.4.2": { - "integrity": "d653be3bd12cb2fa9bbe5d1b1f041a81b91d80b68502761204aaf60e4592532a", - "dependencies": [ - "jsr:@std/encoding@1.0.5" - ] - }, - "@gleasonator/policy@0.2.0": { - "integrity": "3fe58b853ab203b2b67e65b64391dbcf5c07bc1caaf46e97b2f8ed5b14f30fdf", - "dependencies": [ - "jsr:@nostrify/nostrify@^0.22.1" - ] - }, - "@gleasonator/policy@0.4.0": { - "integrity": "59c2f3ab1dc663e99a3e10b7eb69bf9fe581ce5d428fe56653e38f7f961da5ea", - "dependencies": [ - "jsr:@nostrify/nostrify@^0.22.1" - ] - }, - "@gleasonator/policy@0.4.1": { - "integrity": "4d42d11d2e9b5f183cec1ca73dbb6f5cd50475efb2bef9495857fa8e5e5d8251", - "dependencies": [ - "jsr:@nostrify/nostrify@^0.22.1" - ] - }, - "@gleasonator/policy@0.4.2": { - "integrity": "704527346b35a1ef799c58ba365fea30d1d4bb8e5291937183223d27b24b0f27", - "dependencies": [ - "jsr:@nostrify/nostrify@^0.22.1" - ] - }, - "@gleasonator/policy@0.5.0": { - "integrity": "c2882eb3b4147dfe96b6ec2870b012b5a614f686770d1d4b2f778fdc44e8b1f5", - "dependencies": [ - "jsr:@nostrify/nostrify@^0.31.0", - "jsr:@nostrify/policies@^0.33.0" - ] - }, - "@gleasonator/policy@0.5.1": { - "integrity": "2d687c5166556ce13ac05c4542f61ef8a47d8b96b57f6e43d52035805f895551", - "dependencies": [ - "jsr:@nostrify/nostrify@^0.31.0", - "jsr:@nostrify/policies@^0.33.0" - ] - }, - "@gleasonator/policy@0.5.2": { - "integrity": "cdd3add87be3132eb05736bca640dfb3bbb1aa79928a44d3563cde20bab7c0d3", - "dependencies": [ - "jsr:@nostrify/nostrify@^0.31.0", - "jsr:@nostrify/policies@^0.33.1" - ] - }, - "@gleasonator/policy@0.6.0": { - "integrity": "77f52bb245255a61070a4970c50e2ea8e82345c1de2fef12b9d8887a20b46e6d", - "dependencies": [ - "jsr:@nostrify/nostrify@^0.32.0", - "jsr:@nostrify/policies@^0.34.0" - ] - }, - "@gleasonator/policy@0.6.1": { - "integrity": "ba763d69332a736678b068b4063709874bc64010dfc3f974818218a41deb2291", - "dependencies": [ - "jsr:@nostrify/nostrify@^0.32.0", - "jsr:@nostrify/policies@^0.34.0" - ] - }, - "@gleasonator/policy@0.6.3": { - "integrity": "7126c52edd3de21488714e66ec71f31ba9b14f8afc761ab73ac7c3ecc936625c", - "dependencies": [ - "jsr:@nostrify/nostrify@^0.32.0", - "jsr:@nostrify/policies@^0.34.0" - ] - }, - "@gleasonator/policy@0.6.4": { - "integrity": "fd91c94546edd1de1faa80cb3248699b2f010ef1bdd89818dbc4a03e7606e0bb", - "dependencies": [ - "jsr:@nostrify/nostrify@^0.32.0", - "jsr:@nostrify/policies@^0.34.0" - ] - }, - "@gleasonator/policy@0.7.0": { - "integrity": "22cad69f6c0eaa20ccd45fcbd0a3990c9e395f23181669ebbf397b8c501d14cf", - "dependencies": [ - "jsr:@nostrify/nostrify@^0.36.0", - "jsr:@nostrify/policies@^0.36.0" - ] - }, - "@gleasonator/policy@0.7.1": { - "integrity": "411c106ec8594f6b1c6aa716895600f2483f72367862bd80add91a0bfec94c28", - "dependencies": [ - "jsr:@nostrify/nostrify@^0.36.0", - "jsr:@nostrify/policies@^0.36.0" - ] - }, - "@gleasonator/policy@0.8.0": { - "integrity": "139611066eb60f15ec40686f9c9b8bad13eb631fdd069fd6eaae3ccf27157b0d", - "dependencies": [ - "jsr:@nostrify/nostrify@^0.36.0", - "jsr:@nostrify/policies@^0.36.0" - ] - }, - "@hono/hono@4.4.6": { - "integrity": "aa557ca9930787ee86b9ca1730691f1ce1c379174c2cb244d5934db2b6314453" - }, - "@hono/hono@4.5.0": { - "integrity": "4a410f7773ac4b5b0eb4520b26c7ab7795a271d57a9df7fa1953ded6b90ccaf7" - }, - "@hono/hono@4.5.1": { - "integrity": "459748ed4d4146c6e4bdff0213ff1ac44749904066ae02e7550d6c7f28c9bc4c" - }, - "@hono/hono@4.5.11": { - "integrity": "5bd6b1a3a503efb746fdcf0aae3ac536dd09229d372988bde5db0798ef64ae4f" - }, - "@hono/hono@4.5.3": { - "integrity": "429923b2b3c6586a1450862328d61a1346fee5841e8ae86c494250475057213c" - }, - "@hono/hono@4.5.4": { - "integrity": "3792780b8460d5df0959b07c059db9325e4fa1a49f8b5aff7ab9bc870bdec8e3" - }, - "@hono/hono@4.5.5": { - "integrity": "e5a63b5f535475cd80974b65fed23a138d0cbb91fe1cc9a17a7c7278e835c308" - }, - "@hono/hono@4.5.9": { - "integrity": "47f561e67aedbd6d1e21e3a1ae26c1b80ffdb62a51c161d502e75bee17ca40af" - }, - "@hono/hono@4.6.2": { - "integrity": "35fcf3be4687825080b01bed7bbe2ac66f8d8b8939f0bad459661bf3b46d916f" - }, - "@lambdalisue/async@2.1.1": { - "integrity": "1fc9bc6f4ed50215cd2f7217842b18cea80f81c25744f88f8c5eb4be5a1c9ab4" - }, - "@negrel/http-ece@0.6.0": { - "integrity": "7afdd81b86ea5b21a9677b323c01c3338705e11cc2bfed250870f5349d8f86f7", - "dependencies": [ - "jsr:@std/bytes@0.224.0", - "jsr:@std/encoding@0.224.0" - ] - }, - "@negrel/webpush@0.3.0": { - "integrity": "5200a56e81668f2debadea228fbeabfe0eda2ee85a56786611dd97950bc51b23", - "dependencies": [ - "jsr:@negrel/http-ece@0.6.0", - "jsr:@std/bytes@0.224.0", - "jsr:@std/encoding@0.224.0", - "jsr:@std/media-types@0.224.0", - "jsr:@std/path@0.224.0" - ] - }, - "@nostrify/db@0.35.0": { - "integrity": "637191c41812544e361b7997dc44ea098f8bd7efebb28f37a8a7142a0ecada8d", - "dependencies": [ - "jsr:@nostrify/nostrify@^0.35.0", - "jsr:@nostrify/types@^0.35.0", - "npm:kysely@^0.27.3", - "npm:nostr-tools@^2.7.0" - ] - }, - "@nostrify/nostrify@0.22.4": { - "integrity": "1c8a7847e5773213044b491e85fd7cafae2ad194ce59da4d957d2b27c776b42d", - "dependencies": [ - "jsr:@std/encoding@^0.224.1", - "npm:@noble/hashes@^1.4.0", - "npm:@scure/base@^1.1.6", - "npm:@scure/bip32@^1.4.0", - "npm:@scure/bip39@^1.3.0", - "npm:kysely@^0.27.3", - "npm:lru-cache@^10.2.0", - "npm:nostr-tools@^2.5.0", - "npm:websocket-ts@^2.1.5", - "npm:zod@^3.23.8" - ] - }, - "@nostrify/nostrify@0.22.5": { - "integrity": "5b9c17325cc02e37c71e14ac0103b40446b0402fe183e5f5362af23e9ea162bf", - "dependencies": [ - "jsr:@std/encoding@^0.224.1", - "npm:@scure/base@^1.1.6", - "npm:@scure/bip32@^1.4.0", - "npm:@scure/bip39@^1.3.0", - "npm:kysely@^0.27.3", - "npm:lru-cache@^10.2.0", - "npm:nostr-tools@^2.7.0", - "npm:websocket-ts@^2.1.5", - "npm:zod@^3.23.8" - ] - }, - "@nostrify/nostrify@0.30.0": { - "integrity": "7c29e7d8b5a0a81e238170ac1e7ad708bc72dd8f478d8d82c30598fb4eff9b9c", - "dependencies": [ - "jsr:@nostrify/types@^0.30.0", - "jsr:@std/crypto@^0.224.0", - "jsr:@std/encoding@^0.224.1", - "npm:@scure/base@^1.1.6", - "npm:@scure/bip32@^1.4.0", - "npm:@scure/bip39@^1.3.0", - "npm:lru-cache@^10.2.0", - "npm:nostr-tools@^2.7.0", - "npm:websocket-ts@^2.1.5", - "npm:zod@^3.23.8" - ] - }, - "@nostrify/nostrify@0.31.0": { - "integrity": "1c1b686bb9ca3ad8d19807e3b96ef3793a65d70fd0f433fe6ef8b3fdb9f45557", - "dependencies": [ - "jsr:@nostrify/types@^0.30.1", - "jsr:@std/encoding@^0.224.1", - "npm:@scure/bip32@^1.4.0", - "npm:@scure/bip39@^1.3.0", - "npm:lru-cache@^10.2.0", - "npm:nostr-tools@^2.7.0", - "npm:websocket-ts@^2.1.5", - "npm:zod@^3.23.8" - ] - }, - "@nostrify/nostrify@0.32.0": { - "integrity": "2d3b7a9cce275c150355f8e566c11f14044afd0b889afcb48e883da9467bdaa9", - "dependencies": [ - "jsr:@nostrify/types@^0.30.1", - "jsr:@std/encoding@^0.224.1", - "npm:@scure/bip32@^1.4.0", - "npm:@scure/bip39@^1.3.0", - "npm:lru-cache@^10.2.0", - "npm:nostr-tools@^2.7.0", - "npm:websocket-ts@^2.1.5", - "npm:zod@^3.23.8" - ] - }, - "@nostrify/nostrify@0.35.0": { - "integrity": "9bfef4883838b8b4cb2e2b28a60b72de95391ca5b789bc7206a2baea054dea55", - "dependencies": [ - "jsr:@nostrify/types@^0.35.0", - "jsr:@std/encoding@^0.224.1", - "npm:@scure/bip32@^1.4.0", - "npm:@scure/bip39@^1.3.0", - "npm:lru-cache@^10.2.0", - "npm:nostr-tools@^2.7.0", - "npm:websocket-ts@^2.1.5", - "npm:zod@^3.23.8" - ] - }, - "@nostrify/nostrify@0.36.0": { - "integrity": "f00dbff1f02a2c496c5e85eeeb7a84101b7dd874d87456449dc71b6d037e40fc", - "dependencies": [ - "jsr:@nostrify/types@^0.35.0", - "jsr:@std/crypto@^0.224.0", - "jsr:@std/encoding@^0.224.1", - "npm:@scure/base@^1.1.6", - "npm:@scure/bip32@^1.4.0", - "npm:@scure/bip39@^1.3.0", - "npm:lru-cache@^10.2.0", - "npm:nostr-tools@^2.7.0", - "npm:websocket-ts@^2.1.5", - "npm:zod@^3.23.8" - ] - }, - "@nostrify/policies@0.33.0": { - "integrity": "c946b06d0527298b4d7c9819d142a10f522ba09eee76c37525aa4acfc5d87aee", - "dependencies": [ - "jsr:@nostrify/types@^0.30.1", - "npm:nostr-tools@^2.7.0" - ] - }, - "@nostrify/policies@0.33.1": { - "integrity": "381e1f9406a6da22da03a254e46b1aa07d5491b9761961cda3a4aeb5bf3f5286", - "dependencies": [ - "jsr:@nostrify/types@^0.30.1", - "npm:nostr-tools@^2.7.0" - ] - }, - "@nostrify/policies@0.34.0": { - "integrity": "27eb8fb36106a29e982ec7fc6bbb91bd6989f8ce11113a3ef6c528b4c2deceee", - "dependencies": [ - "jsr:@nostrify/nostrify@^0.32.0", - "jsr:@nostrify/types@^0.30.1", - "npm:nostr-tools@^2.7.0" - ] - }, - "@nostrify/policies@0.35.0": { - "integrity": "b828fac9f253e460a9587c05588b7dae6a0a32c5a9c9083e449219887b9e8e20", - "dependencies": [ - "jsr:@nostrify/nostrify@^0.35.0", - "jsr:@nostrify/types@^0.35.0", - "npm:nostr-tools@^2.7.0" - ] - }, - "@nostrify/policies@0.36.0": { - "integrity": "ad1930de48ce03cdf34da456af1563b487581d1d86683cd416ad760ae40b1fb3", - "dependencies": [ - "jsr:@nostrify/nostrify@^0.36.0", - "jsr:@nostrify/types@^0.35.0", - "npm:nostr-tools@^2.7.0" - ] - }, - "@nostrify/types@0.30.0": { - "integrity": "1f38fa849cff930bd709edbf94ef9ac02f46afb8b851f86c8736517b354616da" - }, - "@nostrify/types@0.30.1": { - "integrity": "245da176f6893a43250697db51ad32bfa29bf9b1cdc1ca218043d9abf6de5ae5" - }, - "@nostrify/types@0.35.0": { - "integrity": "b8d515563d467072694557d5626fa1600f74e83197eef45dd86a9a99c64f7fe6" - }, - "@soapbox/kysely-pglite@1.0.0": { - "integrity": "0954b1bf3deab051c479cba966b1e6ed5a0a966aa21d1f40143ec8f5efcd475d", - "dependencies": [ - "npm:kysely@^0.27.4" - ] - }, - "@soapbox/stickynotes@0.4.0": { - "integrity": "60bfe61ab3d7e04bf708273b1e2d391a59534bdf29e54160e98d7afd328ca1ec" - }, - "@std/assert@0.213.1": { - "integrity": "24c28178b30c8e0782c18e8e94ea72b16282207569cdd10ffb9d1d26f2edebfe" - }, - "@std/assert@0.223.0": { - "integrity": "eb8d6d879d76e1cc431205bd346ed4d88dc051c6366365b1af47034b0670be24" - }, - "@std/assert@0.224.0": { - "integrity": "8643233ec7aec38a940a8264a6e3eed9bfa44e7a71cc6b3c8874213ff401967f" - }, - "@std/assert@0.225.3": { - "integrity": "b3c2847aecf6955b50644cdb9cf072004ea3d1998dd7579fc0acb99dbb23bd4f", - "dependencies": [ - "jsr:@std/internal@^1.0.0" - ] - }, - "@std/bytes@0.223.0": { - "integrity": "84b75052cd8680942c397c2631318772b295019098f40aac5c36cead4cba51a8" - }, - "@std/bytes@0.224.0": { - "integrity": "a2250e1d0eb7d1c5a426f21267ab9bdeac2447fa87a3d0d1a467d3f7a6058e49" - }, - "@std/bytes@1.0.0": { - "integrity": "9392e72af80adccaa1197912fa19990ed091cb98d5c9c4344b0c301b22d7c632" - }, - "@std/bytes@1.0.2": { - "integrity": "fbdee322bbd8c599a6af186a1603b3355e59a5fb1baa139f8f4c3c9a1b3e3d57" - }, - "@std/cli@0.223.0": { - "integrity": "2feb7970f2028904c3edc22ea916ce9538113dfc170844f3eae03578c333c356", - "dependencies": [ - "jsr:@std/assert@^0.223.0" - ] - }, - "@std/crypto@0.224.0": { - "integrity": "154ef3ff08ef535562ef1a718718c5b2c5fc3808f0f9100daad69e829bfcdf2d", - "dependencies": [ - "jsr:@std/assert@^0.224.0", - "jsr:@std/encoding@^0.224.0" - ] - }, - "@std/dotenv@0.224.0": { - "integrity": "d9234cdf551507dcda60abb6c474289843741d8c07ee8ce540c60f5c1b220a1d" - }, - "@std/dotenv@0.224.2": { - "integrity": "29081695357e4534696c9e986b2560be29c141ccf52daa32b6c20ff5b5c64ab9" - }, - "@std/encoding@0.213.1": { - "integrity": "fcbb6928713dde941a18ca5db88ca1544d0755ec8fb20fe61e2dc8144b390c62" - }, - "@std/encoding@0.224.0": { - "integrity": "efb6dca97d3e9c31392bd5c8cfd9f9fc9decf5a1f4d1f78af7900a493bcf89b5" - }, - "@std/encoding@0.224.3": { - "integrity": "5e861b6d81be5359fad4155e591acf17c0207b595112d1840998bb9f476dbdaf" - }, - "@std/encoding@1.0.5": { - "integrity": "ecf363d4fc25bd85bd915ff6733a7e79b67e0e7806334af15f4645c569fefc04" - }, - "@std/fmt@0.213.1": { - "integrity": "a06d31777566d874b9c856c10244ac3e6b660bdec4c82506cd46be052a1082c3" - }, - "@std/fs@0.213.1": { - "integrity": "fbcaf099f8a85c27ab0712b666262cda8fe6d02e9937bf9313ecaea39a22c501", - "dependencies": [ - "jsr:@std/assert@^0.213.1", - "jsr:@std/path@^0.213.1" - ] - }, - "@std/fs@0.229.3": { - "integrity": "783bca21f24da92e04c3893c9e79653227ab016c48e96b3078377ebd5222e6eb", - "dependencies": [ - "jsr:@std/path@1.0.0-rc.1" - ] - }, - "@std/internal@1.0.0": { - "integrity": "ac6a6dfebf838582c4b4f61a6907374e27e05bedb6ce276e0f1608fe84e7cd9a" - }, - "@std/internal@1.0.1": { - "integrity": "6f8c7544d06a11dd256c8d6ba54b11ed870aac6c5aeafff499892662c57673e6" - }, - "@std/internal@1.0.3": { - "integrity": "208e9b94a3d5649bd880e9ca38b885ab7651ab5b5303a56ed25de4755fb7b11e" - }, - "@std/internal@1.0.4": { - "integrity": "62e8e4911527e5e4f307741a795c0b0a9e6958d0b3790716ae71ce085f755422" - }, - "@std/io@0.223.0": { - "integrity": "2d8c3c2ab3a515619b90da2c6ff5ea7b75a94383259ef4d02116b228393f84f1", - "dependencies": [ - "jsr:@std/assert@^0.223.0", - "jsr:@std/bytes@^0.223.0" - ] - }, - "@std/io@0.224.0": { - "integrity": "0aff885d21d829c050b8a08b1d71b54aed5841aecf227f8d77e99ec529a11e8e", - "dependencies": [ - "jsr:@std/bytes@^0.224.0" - ] - }, - "@std/io@0.224.1": { - "integrity": "73de242551a5c0965eb33e36b1fc7df4834ffbc836a1a643a410ccd11253d6be", - "dependencies": [ - "jsr:@std/bytes@^1.0.0-rc.3" - ] - }, - "@std/io@0.224.3": { - "integrity": "b402edeb99c6b3778d9ae3e9927bc9085b170b41e5a09bbb7064ab2ee394ae2f", - "dependencies": [ - "jsr:@std/bytes@^1.0.1-rc.3" - ] - }, - "@std/io@0.224.4": { - "integrity": "bce1151765e4e70e376039fd72c71672b4d4aae363878a5ee3e58361b81197ec", - "dependencies": [ - "jsr:@std/bytes@^1.0.2-rc.3" - ] - }, - "@std/io@0.224.6": { - "integrity": "eefe034a370be34daf066c8634dd645635d099bb21eccf110f0bdc28d9040891", - "dependencies": [ - "jsr:@std/bytes@^1.0.2" - ] - }, - "@std/io@0.224.7": { - "integrity": "a70848793c44a7c100926571a8c9be68ba85487bfcd4d0540d86deabe1123dc9", - "dependencies": [ - "jsr:@std/bytes@^1.0.2" - ] - }, - "@std/io@0.224.8": { - "integrity": "f525d05d51fd873de6352b9afcf35cab9ab5dc448bf3c20e0c8b521ded9be392", - "dependencies": [ - "jsr:@std/bytes@^1.0.2" - ] - }, - "@std/json@0.223.0": { - "integrity": "9a4a255931dd0397924c6b10bb6a72fe3e28ddd876b981ada2e3b8dd0764163f", - "dependencies": [ - "jsr:@std/streams@^0.223.0" - ] - }, - "@std/media-types@0.224.0": { - "integrity": "5ac87989393f8cb1c81bee02aef6f5d4c8289b416deabc04f9ad25dff292d0b0" - }, - "@std/media-types@0.224.1": { - "integrity": "9e69a5daed37c5b5c6d3ce4731dc191f80e67f79bed392b0957d1d03b87f11e1" - }, - "@std/path@0.213.1": { - "integrity": "f187bf278a172752e02fcbacf6bd78a335ed320d080a7ed3a5a59c3e88abc673", - "dependencies": [ - "jsr:@std/assert@^0.213.1" - ] - }, - "@std/path@0.224.0": { - "integrity": "55bca6361e5a6d158b9380e82d4981d82d338ec587de02951e2b7c3a24910ee6", - "dependencies": [ - "jsr:@std/assert@^0.224.0" - ] - }, - "@std/path@1.0.0-rc.1": { - "integrity": "b8c00ae2f19106a6bb7cbf1ab9be52aa70de1605daeb2dbdc4f87a7cbaf10ff6" - }, - "@std/streams@0.223.0": { - "integrity": "d6b28e498ced3960b04dc5d251f2dcfc1df244b5ec5a48dc23a8f9b490be3b99", - "dependencies": [ - "jsr:@std/assert@^0.223.0", - "jsr:@std/bytes@^0.223.0", - "jsr:@std/io@^0.223.0" - ] - } + "@b-fuze/deno-dom@0.1.48": { + "integrity": "bf5b591aef2e9e9c59adfcbb93a9ecd45bab5b7c8263625beafa5c8f1662e7da" }, - "npm": { - "@electric-sql/pglite@0.2.8": { - "integrity": "sha512-0wSmQu22euBRzR5ghqyIHnBH4MfwlkL5WstOrrA3KOsjEWEglvoL/gH92JajEUA6Ufei/+qbkB2hVloC/K/RxQ==", - "dependencies": {} - }, - "@isaacs/ttlcache@1.4.1": { - "integrity": "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==", - "dependencies": {} - }, - "@noble/ciphers@0.5.3": { - "integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==", - "dependencies": {} - }, - "@noble/curves@1.1.0": { - "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", - "dependencies": { - "@noble/hashes": "@noble/hashes@1.3.1" - } - }, - "@noble/curves@1.2.0": { - "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", - "dependencies": { - "@noble/hashes": "@noble/hashes@1.3.2" - } - }, - "@noble/curves@1.4.0": { - "integrity": "sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==", - "dependencies": { - "@noble/hashes": "@noble/hashes@1.4.0" - } - }, - "@noble/hashes@1.3.1": { - "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", - "dependencies": {} - }, - "@noble/hashes@1.3.2": { - "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", - "dependencies": {} - }, - "@noble/hashes@1.4.0": { - "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", - "dependencies": {} - }, - "@noble/secp256k1@2.1.0": { - "integrity": "sha512-XLEQQNdablO0XZOIniFQimiXsZDNwaYgL96dZwC54Q30imSbAOFf3NKtepc+cXyuZf5Q1HCgbqgZ2UFFuHVcEw==", - "dependencies": {} - }, - "@opentelemetry/api@1.9.0": { - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", - "dependencies": {} - }, - "@scure/base@1.1.1": { - "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==", - "dependencies": {} - }, - "@scure/base@1.1.6": { - "integrity": "sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==", - "dependencies": {} - }, - "@scure/bip32@1.3.1": { - "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", - "dependencies": { - "@noble/curves": "@noble/curves@1.1.0", - "@noble/hashes": "@noble/hashes@1.3.2", - "@scure/base": "@scure/base@1.1.6" - } - }, - "@scure/bip32@1.4.0": { - "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", - "dependencies": { - "@noble/curves": "@noble/curves@1.4.0", - "@noble/hashes": "@noble/hashes@1.4.0", - "@scure/base": "@scure/base@1.1.6" - } - }, - "@scure/bip39@1.2.1": { - "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", - "dependencies": { - "@noble/hashes": "@noble/hashes@1.3.2", - "@scure/base": "@scure/base@1.1.6" - } - }, - "@scure/bip39@1.3.0": { - "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", - "dependencies": { - "@noble/hashes": "@noble/hashes@1.4.0", - "@scure/base": "@scure/base@1.1.6" - } - }, - "@types/dompurify@3.0.5": { - "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", - "dependencies": { - "@types/trusted-types": "@types/trusted-types@2.0.7" - } - }, - "@types/node@17.0.45": { - "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", - "dependencies": {} - }, - "@types/node@18.16.19": { - "integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==", - "dependencies": {} - }, - "@types/trusted-types@2.0.7": { - "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", - "dependencies": {} - }, - "agent-base@7.1.1": { - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dependencies": { - "debug": "debug@4.3.4" - } - }, - "ansi-escapes@6.2.0": { - "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", - "dependencies": { - "type-fest": "type-fest@3.13.1" - } - }, - "ansi-regex@6.0.1": { - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dependencies": {} - }, - "ansi-styles@6.2.1": { - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dependencies": {} - }, - "asynckit@0.4.0": { - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dependencies": {} - }, - "bintrees@1.0.2": { - "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==", - "dependencies": {} - }, - "braces@3.0.2": { - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "fill-range@7.0.1" - } - }, - "chalk@5.3.0": { - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dependencies": {} - }, - "cli-cursor@4.0.0": { - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", - "dependencies": { - "restore-cursor": "restore-cursor@4.0.0" - } - }, - "cli-truncate@4.0.0": { - "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", - "dependencies": { - "slice-ansi": "slice-ansi@5.0.0", - "string-width": "string-width@7.1.0" - } - }, - "colorette@2.0.20": { - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dependencies": {} - }, - "combined-stream@1.0.8": { - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "delayed-stream@1.0.0" - } - }, - "comlink-async-generator@0.0.1": { - "integrity": "sha512-RjOPv6Tb7cL9FiIgwanUJuFG9aW4myAFyyzxZoEkEegeDQrZqr92d1Njv2WIgi7nbGpTiyy5GdNTUubDaNgZ6A==", - "dependencies": { - "comlink": "comlink@4.4.1" - } - }, - "comlink@4.4.1": { - "integrity": "sha512-+1dlx0aY5Jo1vHy/tSsIGpSkN4tS9rZSW8FIhG0JH/crs9wwweswIo/POr451r7bZww3hFbPAKnTpimzL/mm4Q==", - "dependencies": {} - }, - "commander@11.1.0": { - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", - "dependencies": {} - }, - "commander@12.1.0": { - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dependencies": {} - }, - "cross-spawn@7.0.3": { - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "path-key@3.1.1", - "shebang-command": "shebang-command@2.0.0", - "which": "which@2.0.2" - } - }, - "cssstyle@4.0.1": { - "integrity": "sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==", - "dependencies": { - "rrweb-cssom": "rrweb-cssom@0.6.0" - } - }, - "data-urls@5.0.0": { - "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", - "dependencies": { - "whatwg-mimetype": "whatwg-mimetype@4.0.0", - "whatwg-url": "whatwg-url@14.0.0" - } - }, - "debug@3.2.7": { - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "ms@2.1.3" - } - }, - "debug@4.3.4": { - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "ms@2.1.2" - } - }, - "decimal.js@10.4.3": { - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", - "dependencies": {} - }, - "delayed-stream@1.0.0": { - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dependencies": {} - }, - "dom-serializer@2.0.0": { - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dependencies": { - "domelementtype": "domelementtype@2.3.0", - "domhandler": "domhandler@5.0.3", - "entities": "entities@4.5.0" - } - }, - "domelementtype@2.3.0": { - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dependencies": {} - }, - "domhandler@5.0.3": { - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dependencies": { - "domelementtype": "domelementtype@2.3.0" - } - }, - "dompurify@3.1.4": { - "integrity": "sha512-2gnshi6OshmuKil8rMZuQCGiUF3cUxHY3NGDzUAdUx/NPEe5DVnO8BDoAQouvgwnx0R/+a6jUn36Z0FSdq8vww==", - "dependencies": {} - }, - "domutils@3.1.0": { - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dependencies": { - "dom-serializer": "dom-serializer@2.0.0", - "domelementtype": "domelementtype@2.3.0", - "domhandler": "domhandler@5.0.3" - } - }, - "emoji-regex@10.3.0": { - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", - "dependencies": {} - }, - "entities@4.5.0": { - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dependencies": {} - }, - "eventemitter3@5.0.1": { - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "dependencies": {} - }, - "execa@8.0.1": { - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dependencies": { - "cross-spawn": "cross-spawn@7.0.3", - "get-stream": "get-stream@8.0.1", - "human-signals": "human-signals@5.0.0", - "is-stream": "is-stream@3.0.0", - "merge-stream": "merge-stream@2.0.0", - "npm-run-path": "npm-run-path@5.3.0", - "onetime": "onetime@6.0.0", - "signal-exit": "signal-exit@4.1.0", - "strip-final-newline": "strip-final-newline@3.0.0" - } - }, - "fast-stable-stringify@1.0.0": { - "integrity": "sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==", - "dependencies": {} - }, - "fill-range@7.0.1": { - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "to-regex-range@5.0.1" - } - }, - "form-data@4.0.0": { - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "asynckit@0.4.0", - "combined-stream": "combined-stream@1.0.8", - "mime-types": "mime-types@2.1.35" - } - }, - "formdata-helper@0.3.0": { - "integrity": "sha512-QkRUFbNgWSu9lkc5TKLWri0ilTFowo950w13I5pRhj4cUxzMLuz0MIhGbE/gIRyfsZQoFeMNN0h06OCSOgfhUg==", - "dependencies": {} - }, - "get-east-asian-width@1.2.0": { - "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", - "dependencies": {} - }, - "get-stream@8.0.1": { - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dependencies": {} - }, - "he@1.2.0": { - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dependencies": {} - }, - "hono-rate-limiter@0.3.0_hono@4.2.5": { - "integrity": "sha512-QUS1N+DCZs8CKjpoHugvEvAp/+e+LUllPPnaIlATK9GyT26niQxn4H4V+O5kHwcsXjD3P3JOc0jMb7fnW6gpFQ==", - "dependencies": { - "hono": "hono@4.2.5" - } - }, - "hono@4.2.5": { - "integrity": "sha512-uonJD3i/yy005kQ7bPZRVfG3rejYJwyPqBmPoUGijS4UB/qM+YlrZ7xzSWy+ByDu9buGHUG+f+SKzz03Y6V1Kw==", - "dependencies": {} - }, - "html-encoding-sniffer@4.0.0": { - "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", - "dependencies": { - "whatwg-encoding": "whatwg-encoding@3.1.1" - } - }, - "htmlparser2@8.0.2": { - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "dependencies": { - "domelementtype": "domelementtype@2.3.0", - "domhandler": "domhandler@5.0.3", - "domutils": "domutils@3.1.0", - "entities": "entities@4.5.0" - } - }, - "http-proxy-agent@7.0.2": { - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dependencies": { - "agent-base": "agent-base@7.1.1", - "debug": "debug@4.3.4" - } - }, - "https-proxy-agent@7.0.4": { - "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", - "dependencies": { - "agent-base": "agent-base@7.1.1", - "debug": "debug@4.3.4" - } - }, - "human-signals@5.0.0": { - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dependencies": {} - }, - "iconv-lite@0.4.24": { - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": "safer-buffer@2.1.2" - } - }, - "iconv-lite@0.6.3": { - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dependencies": { - "safer-buffer": "safer-buffer@2.1.2" - } - }, - "is-fullwidth-code-point@4.0.0": { - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", - "dependencies": {} - }, - "is-fullwidth-code-point@5.0.0": { - "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", - "dependencies": { - "get-east-asian-width": "get-east-asian-width@1.2.0" - } - }, - "is-number@7.0.0": { - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dependencies": {} - }, - "is-potential-custom-element-name@1.0.1": { - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dependencies": {} - }, - "is-stream@3.0.0": { - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dependencies": {} - }, - "isexe@2.0.0": { - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dependencies": {} - }, - "iso-639-1@2.1.15": { - "integrity": "sha512-7c7mBznZu2ktfvyT582E2msM+Udc1EjOyhVRE/0ZsjD9LBtWSm23h3PtiRh2a35XoUsTQQjJXaJzuLjXsOdFDg==", - "dependencies": {} - }, - "isomorphic-dompurify@2.11.0": { - "integrity": "sha512-PNGGCbbSH7+zF45UKu4Kh+yI8hm1bWA8kIZQow4KMImnjYQtrqJA0ZmwHamYUU7+M5tQ84z7xXMWmZF/v5t5eA==", - "dependencies": { - "@types/dompurify": "@types/dompurify@3.0.5", - "dompurify": "dompurify@3.1.4", - "jsdom": "jsdom@24.0.0" - } - }, - "jsdom@24.0.0": { - "integrity": "sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==", - "dependencies": { - "cssstyle": "cssstyle@4.0.1", - "data-urls": "data-urls@5.0.0", - "decimal.js": "decimal.js@10.4.3", - "form-data": "form-data@4.0.0", - "html-encoding-sniffer": "html-encoding-sniffer@4.0.0", - "http-proxy-agent": "http-proxy-agent@7.0.2", - "https-proxy-agent": "https-proxy-agent@7.0.4", - "is-potential-custom-element-name": "is-potential-custom-element-name@1.0.1", - "nwsapi": "nwsapi@2.2.10", - "parse5": "parse5@7.1.2", - "rrweb-cssom": "rrweb-cssom@0.6.0", - "saxes": "saxes@6.0.0", - "symbol-tree": "symbol-tree@3.2.4", - "tough-cookie": "tough-cookie@4.1.4", - "w3c-xmlserializer": "w3c-xmlserializer@5.0.0", - "webidl-conversions": "webidl-conversions@7.0.0", - "whatwg-encoding": "whatwg-encoding@3.1.1", - "whatwg-mimetype": "whatwg-mimetype@4.0.0", - "whatwg-url": "whatwg-url@14.0.0", - "ws": "ws@8.17.0", - "xml-name-validator": "xml-name-validator@5.0.0" - } - }, - "kysely-postgres-js@2.0.0_kysely@0.27.3_postgres@3.4.4": { - "integrity": "sha512-R1tWx6/x3tSatWvsmbHJxpBZYhNNxcnMw52QzZaHKg7ZOWtHib4iZyEaw4gb2hNKVctWQ3jfMxZT/ZaEMK6kBQ==", - "dependencies": { - "kysely": "kysely@0.27.3", - "postgres": "postgres@3.4.4" - } - }, - "kysely@0.27.3": { - "integrity": "sha512-lG03Ru+XyOJFsjH3OMY6R/9U38IjDPfnOfDgO3ynhbDr+Dz8fak+X6L62vqu3iybQnj+lG84OttBuU9KY3L9kA==", - "dependencies": {} - }, - "kysely@0.27.4": { - "integrity": "sha512-dyNKv2KRvYOQPLCAOCjjQuCk4YFd33BvGdf/o5bC7FiW+BB6snA81Zt+2wT9QDFzKqxKa5rrOmvlK/anehCcgA==", - "dependencies": {} - }, - "lande@1.0.10": { - "integrity": "sha512-yT52DQh+UV2pEp08jOYrA4drDv0DbjpiRyZYgl25ak9G2cVR2AimzrqkYQWrD9a7Ud+qkAcaiDDoNH9DXfHPmw==", - "dependencies": { - "toygrad": "toygrad@2.6.0" - } - }, - "light-bolt11-decoder@3.1.1": { - "integrity": "sha512-sLg/KCwYkgsHWkefWd6KqpCHrLFWWaXTOX3cf6yD2hAzL0SLpX+lFcaFK2spkjbgzG6hhijKfORDc9WoUHwX0A==", - "dependencies": { - "@scure/base": "@scure/base@1.1.1" - } - }, - "lilconfig@3.0.0": { - "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", - "dependencies": {} - }, - "linkify-plugin-hashtag@4.1.3_linkifyjs@4.1.3": { - "integrity": "sha512-sq627UTrmmDhVnYoUbj/EFfSrhGBvAZYIUdUCjtLeW/AWBV7g9NX9JXEglAuJ7DIyJ84Ged0EHOe+xCXRe2Gmw==", - "dependencies": { - "linkifyjs": "linkifyjs@4.1.3" - } - }, - "linkify-string@4.1.3_linkifyjs@4.1.3": { - "integrity": "sha512-6dAgx4MiTcvEX87OS5aNpAioO7cSELUXp61k7azOvMYOLSmREx0w4yM1Uf0+O3JLC08YdkUyZhAX+YkasRt/mw==", - "dependencies": { - "linkifyjs": "linkifyjs@4.1.3" - } - }, - "linkifyjs@4.1.3": { - "integrity": "sha512-auMesunaJ8yfkHvK4gfg1K0SaKX/6Wn9g2Aac/NwX+l5VdmFZzo/hdPGxEOETj+ryRa4/fiOPjeeKURSAJx1sg==", - "dependencies": {} - }, - "lint-staged@15.2.2": { - "integrity": "sha512-TiTt93OPh1OZOsb5B7k96A/ATl2AjIZo+vnzFZ6oHK5FuTk63ByDtxGQpHm+kFETjEWqgkF95M8FRXKR/LEBcw==", - "dependencies": { - "chalk": "chalk@5.3.0", - "commander": "commander@11.1.0", - "debug": "debug@4.3.4", - "execa": "execa@8.0.1", - "lilconfig": "lilconfig@3.0.0", - "listr2": "listr2@8.0.1", - "micromatch": "micromatch@4.0.5", - "pidtree": "pidtree@0.6.0", - "string-argv": "string-argv@0.3.2", - "yaml": "yaml@2.3.4" - } - }, - "listr2@8.0.1": { - "integrity": "sha512-ovJXBXkKGfq+CwmKTjluEqFi3p4h8xvkxGQQAQan22YCgef4KZ1mKGjzfGh6PL6AW5Csw0QiQPNuQyH+6Xk3hA==", - "dependencies": { - "cli-truncate": "cli-truncate@4.0.0", - "colorette": "colorette@2.0.20", - "eventemitter3": "eventemitter3@5.0.1", - "log-update": "log-update@6.0.0", - "rfdc": "rfdc@1.3.1", - "wrap-ansi": "wrap-ansi@9.0.0" - } - }, - "log-update@6.0.0": { - "integrity": "sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==", - "dependencies": { - "ansi-escapes": "ansi-escapes@6.2.0", - "cli-cursor": "cli-cursor@4.0.0", - "slice-ansi": "slice-ansi@7.1.0", - "strip-ansi": "strip-ansi@7.1.0", - "wrap-ansi": "wrap-ansi@9.0.0" - } - }, - "lru-cache@10.2.2": { - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", - "dependencies": {} - }, - "merge-stream@2.0.0": { - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dependencies": {} - }, - "micromatch@4.0.5": { - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "braces@3.0.2", - "picomatch": "picomatch@2.3.1" - } - }, - "mime-db@1.52.0": { - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dependencies": {} - }, - "mime-types@2.1.35": { - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "mime-db@1.52.0" - } - }, - "mimic-fn@2.1.0": { - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dependencies": {} - }, - "mimic-fn@4.0.0": { - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dependencies": {} - }, - "minimist@1.2.8": { - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dependencies": {} - }, - "ms@2.1.2": { - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dependencies": {} - }, - "ms@2.1.3": { - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dependencies": {} - }, - "node-fetch@2.7.0": { - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "whatwg-url@5.0.0" - } - }, - "nostr-tools@2.5.1": { - "integrity": "sha512-bpkhGGAhdiCN0irfV+xoH3YP5CQeOXyXzUq7SYeM6D56xwTXZCPEmBlUGqFVfQidvRsoVeVxeAiOXW2c2HxoRQ==", - "dependencies": { - "@noble/ciphers": "@noble/ciphers@0.5.3", - "@noble/curves": "@noble/curves@1.2.0", - "@noble/hashes": "@noble/hashes@1.3.1", - "@scure/base": "@scure/base@1.1.1", - "@scure/bip32": "@scure/bip32@1.3.1", - "@scure/bip39": "@scure/bip39@1.2.1", - "nostr-wasm": "nostr-wasm@0.1.0" - } - }, - "nostr-tools@2.7.0": { - "integrity": "sha512-jJoL2J1CBiKDxaXZww27nY/Wsuxzx7AULxmGKFce4sskDu1tohNyfnzYQ8BvDyvkstU8kNZUAXPL32tre33uig==", - "dependencies": { - "@noble/ciphers": "@noble/ciphers@0.5.3", - "@noble/curves": "@noble/curves@1.2.0", - "@noble/hashes": "@noble/hashes@1.3.1", - "@scure/base": "@scure/base@1.1.1", - "@scure/bip32": "@scure/bip32@1.3.1", - "@scure/bip39": "@scure/bip39@1.2.1", - "nostr-wasm": "nostr-wasm@0.1.0" - } - }, - "nostr-wasm@0.1.0": { - "integrity": "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==", - "dependencies": {} - }, - "npm-run-path@5.3.0": { - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "dependencies": { - "path-key": "path-key@4.0.0" - } - }, - "nwsapi@2.2.10": { - "integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==", - "dependencies": {} - }, - "onetime@5.1.2": { - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "mimic-fn@2.1.0" - } - }, - "onetime@6.0.0": { - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dependencies": { - "mimic-fn": "mimic-fn@4.0.0" - } - }, - "parse5@7.1.2": { - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dependencies": { - "entities": "entities@4.5.0" - } - }, - "path-key@3.1.1": { - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dependencies": {} - }, - "path-key@4.0.0": { - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dependencies": {} - }, - "path-to-regexp@7.1.0": { - "integrity": "sha512-ZToe+MbUF4lBqk6dV8GKot4DKfzrxXsplOddH8zN3YK+qw9/McvP7+4ICjZvOne0jQhN4eJwHsX6tT0Ns19fvw==", - "dependencies": {} - }, - "picomatch@2.3.1": { - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dependencies": {} - }, - "pidtree@0.6.0": { - "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", - "dependencies": {} - }, - "png-to-ico@2.1.8": { - "integrity": "sha512-Nf+IIn/cZ/DIZVdGveJp86NG5uNib1ZXMiDd/8x32HCTeKSvgpyg6D/6tUBn1QO/zybzoMK0/mc3QRgAyXdv9w==", - "dependencies": { - "@types/node": "@types/node@17.0.45", - "minimist": "minimist@1.2.8", - "pngjs": "pngjs@6.0.0" - } - }, - "pngjs@6.0.0": { - "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==", - "dependencies": {} - }, - "postgres@3.4.4": { - "integrity": "sha512-IbyN+9KslkqcXa8AO9fxpk97PA4pzewvpi2B3Dwy9u4zpV32QicaEdgmF3eSQUzdRk7ttDHQejNgAEr4XoeH4A==", - "dependencies": {} - }, - "prom-client@15.1.2": { - "integrity": "sha512-on3h1iXb04QFLLThrmVYg1SChBQ9N1c+nKAjebBjokBqipddH3uxmOUcEkTnzmJ8Jh/5TSUnUqS40i2QB2dJHQ==", - "dependencies": { - "@opentelemetry/api": "@opentelemetry/api@1.9.0", - "tdigest": "tdigest@0.1.2" - } - }, - "psl@1.9.0": { - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dependencies": {} - }, - "punycode@2.3.1": { - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dependencies": {} - }, - "querystringify@2.2.0": { - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dependencies": {} - }, - "requires-port@1.0.0": { - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dependencies": {} - }, - "restore-cursor@4.0.0": { - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", - "dependencies": { - "onetime": "onetime@5.1.2", - "signal-exit": "signal-exit@3.0.7" - } - }, - "rfdc@1.3.1": { - "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", - "dependencies": {} - }, - "rrweb-cssom@0.6.0": { - "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==", - "dependencies": {} - }, - "safer-buffer@2.1.2": { - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dependencies": {} - }, - "saxes@6.0.0": { - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "dependencies": { - "xmlchars": "xmlchars@2.2.0" - } - }, - "shebang-command@2.0.0": { - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": { - "shebang-regex": "shebang-regex@3.0.0" - } - }, - "shebang-regex@3.0.0": { - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dependencies": {} - }, - "signal-exit@3.0.7": { - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dependencies": {} - }, - "signal-exit@4.1.0": { - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dependencies": {} - }, - "slice-ansi@5.0.0": { - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", - "dependencies": { - "ansi-styles": "ansi-styles@6.2.1", - "is-fullwidth-code-point": "is-fullwidth-code-point@4.0.0" - } - }, - "slice-ansi@7.1.0": { - "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", - "dependencies": { - "ansi-styles": "ansi-styles@6.2.1", - "is-fullwidth-code-point": "is-fullwidth-code-point@5.0.0" - } - }, - "string-argv@0.3.2": { - "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", - "dependencies": {} - }, - "string-width@7.1.0": { - "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==", - "dependencies": { - "emoji-regex": "emoji-regex@10.3.0", - "get-east-asian-width": "get-east-asian-width@1.2.0", - "strip-ansi": "strip-ansi@7.1.0" - } - }, - "strip-ansi@7.1.0": { - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dependencies": { - "ansi-regex": "ansi-regex@6.0.1" - } - }, - "strip-final-newline@3.0.0": { - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dependencies": {} - }, - "symbol-tree@3.2.4": { - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dependencies": {} - }, - "tdigest@0.1.2": { - "integrity": "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==", - "dependencies": { - "bintrees": "bintrees@1.0.2" - } - }, - "tldts-core@6.1.18": { - "integrity": "sha512-e4wx32F/7dMBSZyKAx825Yte3U0PQtZZ0bkWxYQiwLteRVnQ5zM40fEbi0IyNtwQssgJAk3GCr7Q+w39hX0VKA==", - "dependencies": {} - }, - "tldts@6.1.18": { - "integrity": "sha512-F+6zjPFnFxZ0h6uGb8neQWwHQm8u3orZVFribsGq4eBgEVrzSkHxzWS2l6aKr19T1vXiOMFjqfff4fQt+WgJFg==", - "dependencies": { - "tldts-core": "tldts-core@6.1.18" - } - }, - "to-regex-range@5.0.1": { - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "is-number@7.0.0" - } - }, - "tough-cookie@4.1.4": { - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", - "dependencies": { - "psl": "psl@1.9.0", - "punycode": "punycode@2.3.1", - "universalify": "universalify@0.2.0", - "url-parse": "url-parse@1.5.10" - } - }, - "toygrad@2.6.0": { - "integrity": "sha512-g4zBmlSbvzOE5FOILxYkAybTSxijKLkj1WoNqVGnbMcWDyj4wWQ+eYSr3ik7XOpIgMq/7eBcPRTJX3DM2E0YMg==", - "dependencies": {} - }, - "tr46@0.0.3": { - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dependencies": {} - }, - "tr46@5.0.0": { - "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", - "dependencies": { - "punycode": "punycode@2.3.1" - } - }, - "tseep@1.2.1": { - "integrity": "sha512-VFnsNcPGC4qFJ1nxbIPSjTmtRZOhlqLmtwRqtLVos8mbRHki8HO9cy9Z1e89EiWyxFmq6LBviI9TQjijxw/mEw==", - "dependencies": {} - }, - "type-fest@3.13.1": { - "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", - "dependencies": {} - }, - "type-fest@4.18.2": { - "integrity": "sha512-+suCYpfJLAe4OXS6+PPXjW3urOS4IoP9waSiLuXfLgqZODKw/aWwASvzqE886wA0kQgGy0mIWyhd87VpqIy6Xg==", - "dependencies": {} - }, - "unfurl.js@6.4.0": { - "integrity": "sha512-DogJFWPkOWMcu2xPdpmbcsL+diOOJInD3/jXOv6saX1upnWmMK8ndAtDWUfJkuInqNI9yzADud4ID9T+9UeWCw==", - "dependencies": { - "debug": "debug@3.2.7", - "he": "he@1.2.0", - "htmlparser2": "htmlparser2@8.0.2", - "iconv-lite": "iconv-lite@0.4.24", - "node-fetch": "node-fetch@2.7.0" - } - }, - "universalify@0.2.0": { - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dependencies": {} - }, - "url-parse@1.5.10": { - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dependencies": { - "querystringify": "querystringify@2.2.0", - "requires-port": "requires-port@1.0.0" - } - }, - "w3c-xmlserializer@5.0.0": { - "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", - "dependencies": { - "xml-name-validator": "xml-name-validator@5.0.0" - } - }, - "webidl-conversions@3.0.1": { - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dependencies": {} - }, - "webidl-conversions@7.0.0": { - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dependencies": {} - }, - "websocket-ts@2.1.5": { - "integrity": "sha512-rCNl9w6Hsir1azFm/pbjBEFzLD/gi7Th5ZgOxMifB6STUfTSovYAzryWw0TRvSZ1+Qu1Z5Plw4z42UfTNA9idA==", - "dependencies": {} - }, - "whatwg-encoding@3.1.1": { - "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", - "dependencies": { - "iconv-lite": "iconv-lite@0.6.3" - } - }, - "whatwg-mimetype@4.0.0": { - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", - "dependencies": {} - }, - "whatwg-url@14.0.0": { - "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", - "dependencies": { - "tr46": "tr46@5.0.0", - "webidl-conversions": "webidl-conversions@7.0.0" - } - }, - "whatwg-url@5.0.0": { - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "tr46@0.0.3", - "webidl-conversions": "webidl-conversions@3.0.1" - } - }, - "which@2.0.2": { - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "isexe@2.0.0" - } - }, - "wrap-ansi@9.0.0": { - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", - "dependencies": { - "ansi-styles": "ansi-styles@6.2.1", - "string-width": "string-width@7.1.0", - "strip-ansi": "strip-ansi@7.1.0" - } - }, - "ws@8.17.0": { - "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", - "dependencies": {} - }, - "xml-name-validator@5.0.0": { - "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", - "dependencies": {} - }, - "xmlchars@2.2.0": { - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dependencies": {} - }, - "yaml@2.3.4": { - "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", - "dependencies": {} - }, - "zod@3.23.8": { - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", - "dependencies": {} - } + "@bradenmacdonald/s3-lite-client@0.7.6": { + "integrity": "2b5976dca95d207dc88e23f9807e3eecbc441b0cf547dcda5784afe6668404d1", + "dependencies": [ + "jsr:@std/io@0.224" + ] + }, + "@denosaurs/plug@1.0.3": { + "integrity": "b010544e386bea0ff3a1d05e0c88f704ea28cbd4d753439c2f1ee021a85d4640", + "dependencies": [ + "jsr:@std/encoding@0.213.1", + "jsr:@std/fmt", + "jsr:@std/fs@0.213.1", + "jsr:@std/path@0.213.1" + ] + }, + "@esroyo/scoped-performance@3.1.0": { + "integrity": "e6a12a1d4edb32cbea7afce005123c1ef684e1815bf9b6caadfbf3e89fe66222" + }, + "@gfx/canvas-wasm@0.4.2": { + "integrity": "d653be3bd12cb2fa9bbe5d1b1f041a81b91d80b68502761204aaf60e4592532a", + "dependencies": [ + "jsr:@std/encoding@1.0.5" + ] + }, + "@gleasonator/policy@0.2.0": { + "integrity": "3fe58b853ab203b2b67e65b64391dbcf5c07bc1caaf46e97b2f8ed5b14f30fdf", + "dependencies": [ + "jsr:@nostrify/nostrify@~0.22.1" + ] + }, + "@gleasonator/policy@0.4.0": { + "integrity": "59c2f3ab1dc663e99a3e10b7eb69bf9fe581ce5d428fe56653e38f7f961da5ea", + "dependencies": [ + "jsr:@nostrify/nostrify@~0.22.1" + ] + }, + "@gleasonator/policy@0.4.1": { + "integrity": "4d42d11d2e9b5f183cec1ca73dbb6f5cd50475efb2bef9495857fa8e5e5d8251", + "dependencies": [ + "jsr:@nostrify/nostrify@~0.22.1" + ] + }, + "@gleasonator/policy@0.4.2": { + "integrity": "704527346b35a1ef799c58ba365fea30d1d4bb8e5291937183223d27b24b0f27", + "dependencies": [ + "jsr:@nostrify/nostrify@~0.22.1" + ] + }, + "@gleasonator/policy@0.5.0": { + "integrity": "c2882eb3b4147dfe96b6ec2870b012b5a614f686770d1d4b2f778fdc44e8b1f5", + "dependencies": [ + "jsr:@nostrify/nostrify@0.31", + "jsr:@nostrify/policies@0.33" + ] + }, + "@gleasonator/policy@0.5.1": { + "integrity": "2d687c5166556ce13ac05c4542f61ef8a47d8b96b57f6e43d52035805f895551", + "dependencies": [ + "jsr:@nostrify/nostrify@0.31", + "jsr:@nostrify/policies@0.33" + ] + }, + "@gleasonator/policy@0.5.2": { + "integrity": "cdd3add87be3132eb05736bca640dfb3bbb1aa79928a44d3563cde20bab7c0d3", + "dependencies": [ + "jsr:@nostrify/nostrify@0.31", + "jsr:@nostrify/policies@~0.33.1" + ] + }, + "@gleasonator/policy@0.6.0": { + "integrity": "77f52bb245255a61070a4970c50e2ea8e82345c1de2fef12b9d8887a20b46e6d", + "dependencies": [ + "jsr:@nostrify/nostrify@0.32", + "jsr:@nostrify/policies@0.34" + ] + }, + "@gleasonator/policy@0.6.1": { + "integrity": "ba763d69332a736678b068b4063709874bc64010dfc3f974818218a41deb2291", + "dependencies": [ + "jsr:@nostrify/nostrify@0.32", + "jsr:@nostrify/policies@0.34" + ] + }, + "@gleasonator/policy@0.6.3": { + "integrity": "7126c52edd3de21488714e66ec71f31ba9b14f8afc761ab73ac7c3ecc936625c", + "dependencies": [ + "jsr:@nostrify/nostrify@0.32", + "jsr:@nostrify/policies@0.34" + ] + }, + "@gleasonator/policy@0.6.4": { + "integrity": "fd91c94546edd1de1faa80cb3248699b2f010ef1bdd89818dbc4a03e7606e0bb", + "dependencies": [ + "jsr:@nostrify/nostrify@0.32", + "jsr:@nostrify/policies@0.34" + ] + }, + "@gleasonator/policy@0.7.0": { + "integrity": "22cad69f6c0eaa20ccd45fcbd0a3990c9e395f23181669ebbf397b8c501d14cf", + "dependencies": [ + "jsr:@nostrify/nostrify@0.36", + "jsr:@nostrify/policies@0.36" + ] + }, + "@gleasonator/policy@0.7.1": { + "integrity": "411c106ec8594f6b1c6aa716895600f2483f72367862bd80add91a0bfec94c28", + "dependencies": [ + "jsr:@nostrify/nostrify@0.36", + "jsr:@nostrify/policies@0.36" + ] + }, + "@gleasonator/policy@0.8.0": { + "integrity": "139611066eb60f15ec40686f9c9b8bad13eb631fdd069fd6eaae3ccf27157b0d", + "dependencies": [ + "jsr:@nostrify/nostrify@0.36", + "jsr:@nostrify/policies@0.36" + ] + }, + "@gleasonator/policy@0.8.1": { + "integrity": "32bc3da39f9fa0f8c4ab1d1d6f7ddf44c3c19ffbca34a7344b8ae9233e852fdb", + "dependencies": [ + "jsr:@nostrify/nostrify@0.36", + "jsr:@nostrify/policies@~0.36.1" + ] + }, + "@gleasonator/policy@0.9.0": { + "integrity": "483f87c3a18fb39f795495d3388453193f1115ab7e130981121f7828ce6b51bb", + "dependencies": [ + "jsr:@nostrify/nostrify@0.36", + "jsr:@nostrify/policies@~0.36.1" + ] + }, + "@hono/hono@4.4.6": { + "integrity": "aa557ca9930787ee86b9ca1730691f1ce1c379174c2cb244d5934db2b6314453" + }, + "@hono/hono@4.5.0": { + "integrity": "4a410f7773ac4b5b0eb4520b26c7ab7795a271d57a9df7fa1953ded6b90ccaf7" + }, + "@hono/hono@4.5.1": { + "integrity": "459748ed4d4146c6e4bdff0213ff1ac44749904066ae02e7550d6c7f28c9bc4c" + }, + "@hono/hono@4.5.3": { + "integrity": "429923b2b3c6586a1450862328d61a1346fee5841e8ae86c494250475057213c" + }, + "@hono/hono@4.5.4": { + "integrity": "3792780b8460d5df0959b07c059db9325e4fa1a49f8b5aff7ab9bc870bdec8e3" + }, + "@hono/hono@4.5.5": { + "integrity": "e5a63b5f535475cd80974b65fed23a138d0cbb91fe1cc9a17a7c7278e835c308" + }, + "@hono/hono@4.5.9": { + "integrity": "47f561e67aedbd6d1e21e3a1ae26c1b80ffdb62a51c161d502e75bee17ca40af" + }, + "@hono/hono@4.5.11": { + "integrity": "5bd6b1a3a503efb746fdcf0aae3ac536dd09229d372988bde5db0798ef64ae4f" + }, + "@hono/hono@4.6.2": { + "integrity": "35fcf3be4687825080b01bed7bbe2ac66f8d8b8939f0bad459661bf3b46d916f" + }, + "@lambdalisue/async@2.1.1": { + "integrity": "1fc9bc6f4ed50215cd2f7217842b18cea80f81c25744f88f8c5eb4be5a1c9ab4" + }, + "@negrel/http-ece@0.6.0": { + "integrity": "7afdd81b86ea5b21a9677b323c01c3338705e11cc2bfed250870f5349d8f86f7", + "dependencies": [ + "jsr:@std/bytes@0.224.0", + "jsr:@std/encoding@0.224.0" + ] + }, + "@negrel/webpush@0.3.0": { + "integrity": "5200a56e81668f2debadea228fbeabfe0eda2ee85a56786611dd97950bc51b23", + "dependencies": [ + "jsr:@negrel/http-ece", + "jsr:@std/bytes@0.224.0", + "jsr:@std/encoding@0.224.0", + "jsr:@std/media-types@0.224.0", + "jsr:@std/path@0.224.0" + ] + }, + "@nostrify/db@0.36.1": { + "integrity": "b65b89ca6fe98d9dbcc0402b5c9c07b8430c2c91f84ba4128ff2eeed70c3d49f", + "dependencies": [ + "jsr:@nostrify/nostrify@0.36", + "jsr:@nostrify/types@0.35", + "npm:kysely@~0.27.3", + "npm:nostr-tools@^2.7.0" + ] + }, + "@nostrify/nostrify@0.22.4": { + "integrity": "1c8a7847e5773213044b491e85fd7cafae2ad194ce59da4d957d2b27c776b42d", + "dependencies": [ + "jsr:@std/encoding@~0.224.1", + "npm:@noble/hashes", + "npm:@scure/base", + "npm:@scure/bip32", + "npm:@scure/bip39", + "npm:kysely@~0.27.3", + "npm:lru-cache@^10.2.0", + "npm:nostr-tools@^2.5.0", + "npm:websocket-ts", + "npm:zod" + ] + }, + "@nostrify/nostrify@0.22.5": { + "integrity": "5b9c17325cc02e37c71e14ac0103b40446b0402fe183e5f5362af23e9ea162bf", + "dependencies": [ + "jsr:@std/encoding@~0.224.1", + "npm:@scure/base", + "npm:@scure/bip32", + "npm:@scure/bip39", + "npm:kysely@~0.27.3", + "npm:lru-cache@^10.2.0", + "npm:nostr-tools@^2.7.0", + "npm:websocket-ts", + "npm:zod" + ] + }, + "@nostrify/nostrify@0.30.0": { + "integrity": "7c29e7d8b5a0a81e238170ac1e7ad708bc72dd8f478d8d82c30598fb4eff9b9c", + "dependencies": [ + "jsr:@nostrify/types@0.30", + "jsr:@std/crypto", + "jsr:@std/encoding@~0.224.1", + "npm:@scure/base", + "npm:@scure/bip32", + "npm:@scure/bip39", + "npm:lru-cache@^10.2.0", + "npm:nostr-tools@^2.7.0", + "npm:websocket-ts", + "npm:zod" + ] + }, + "@nostrify/nostrify@0.31.0": { + "integrity": "1c1b686bb9ca3ad8d19807e3b96ef3793a65d70fd0f433fe6ef8b3fdb9f45557", + "dependencies": [ + "jsr:@nostrify/types@~0.30.1", + "jsr:@std/encoding@~0.224.1", + "npm:@scure/bip32", + "npm:@scure/bip39", + "npm:lru-cache@^10.2.0", + "npm:nostr-tools@^2.7.0", + "npm:websocket-ts", + "npm:zod" + ] + }, + "@nostrify/nostrify@0.32.0": { + "integrity": "2d3b7a9cce275c150355f8e566c11f14044afd0b889afcb48e883da9467bdaa9", + "dependencies": [ + "jsr:@nostrify/types@~0.30.1", + "jsr:@std/encoding@~0.224.1", + "npm:@scure/bip32", + "npm:@scure/bip39", + "npm:lru-cache@^10.2.0", + "npm:nostr-tools@^2.7.0", + "npm:websocket-ts", + "npm:zod" + ] + }, + "@nostrify/nostrify@0.35.0": { + "integrity": "9bfef4883838b8b4cb2e2b28a60b72de95391ca5b789bc7206a2baea054dea55", + "dependencies": [ + "jsr:@nostrify/types@0.35", + "jsr:@std/encoding@~0.224.1", + "npm:@scure/bip32", + "npm:@scure/bip39", + "npm:lru-cache@^10.2.0", + "npm:nostr-tools@^2.7.0", + "npm:websocket-ts", + "npm:zod" + ] + }, + "@nostrify/nostrify@0.36.0": { + "integrity": "f00dbff1f02a2c496c5e85eeeb7a84101b7dd874d87456449dc71b6d037e40fc", + "dependencies": [ + "jsr:@nostrify/types@0.35", + "jsr:@std/crypto", + "jsr:@std/encoding@~0.224.1", + "npm:@scure/base", + "npm:@scure/bip32", + "npm:@scure/bip39", + "npm:lru-cache@^10.2.0", + "npm:nostr-tools@^2.7.0", + "npm:websocket-ts", + "npm:zod" + ] + }, + "@nostrify/policies@0.33.0": { + "integrity": "c946b06d0527298b4d7c9819d142a10f522ba09eee76c37525aa4acfc5d87aee", + "dependencies": [ + "jsr:@nostrify/types@~0.30.1", + "npm:nostr-tools@^2.7.0" + ] + }, + "@nostrify/policies@0.33.1": { + "integrity": "381e1f9406a6da22da03a254e46b1aa07d5491b9761961cda3a4aeb5bf3f5286", + "dependencies": [ + "jsr:@nostrify/types@~0.30.1", + "npm:nostr-tools@^2.7.0" + ] + }, + "@nostrify/policies@0.34.0": { + "integrity": "27eb8fb36106a29e982ec7fc6bbb91bd6989f8ce11113a3ef6c528b4c2deceee", + "dependencies": [ + "jsr:@nostrify/nostrify@0.32", + "jsr:@nostrify/types@~0.30.1", + "npm:nostr-tools@^2.7.0" + ] + }, + "@nostrify/policies@0.35.0": { + "integrity": "b828fac9f253e460a9587c05588b7dae6a0a32c5a9c9083e449219887b9e8e20", + "dependencies": [ + "jsr:@nostrify/nostrify@0.35", + "jsr:@nostrify/types@0.35", + "npm:nostr-tools@^2.7.0" + ] + }, + "@nostrify/policies@0.36.0": { + "integrity": "ad1930de48ce03cdf34da456af1563b487581d1d86683cd416ad760ae40b1fb3", + "dependencies": [ + "jsr:@nostrify/nostrify@0.36", + "jsr:@nostrify/types@0.35", + "npm:nostr-tools@^2.7.0" + ] + }, + "@nostrify/policies@0.36.1": { + "integrity": "6d59af115a687fcd18b6caebab0e4f50ee6cdb0aafa2aacd0aec2065021275b4", + "dependencies": [ + "jsr:@nostrify/nostrify@0.36", + "jsr:@nostrify/types@0.35", + "npm:nostr-tools@^2.7.0" + ] + }, + "@nostrify/types@0.30.0": { + "integrity": "1f38fa849cff930bd709edbf94ef9ac02f46afb8b851f86c8736517b354616da" + }, + "@nostrify/types@0.30.1": { + "integrity": "245da176f6893a43250697db51ad32bfa29bf9b1cdc1ca218043d9abf6de5ae5" + }, + "@nostrify/types@0.35.0": { + "integrity": "b8d515563d467072694557d5626fa1600f74e83197eef45dd86a9a99c64f7fe6" + }, + "@soapbox/kysely-pglite@1.0.0": { + "integrity": "0954b1bf3deab051c479cba966b1e6ed5a0a966aa21d1f40143ec8f5efcd475d", + "dependencies": [ + "npm:kysely@~0.27.4" + ] + }, + "@soapbox/stickynotes@0.4.0": { + "integrity": "60bfe61ab3d7e04bf708273b1e2d391a59534bdf29e54160e98d7afd328ca1ec" + }, + "@std/assert@0.213.1": { + "integrity": "24c28178b30c8e0782c18e8e94ea72b16282207569cdd10ffb9d1d26f2edebfe" + }, + "@std/assert@0.223.0": { + "integrity": "eb8d6d879d76e1cc431205bd346ed4d88dc051c6366365b1af47034b0670be24" + }, + "@std/assert@0.224.0": { + "integrity": "8643233ec7aec38a940a8264a6e3eed9bfa44e7a71cc6b3c8874213ff401967f" + }, + "@std/assert@0.225.3": { + "integrity": "b3c2847aecf6955b50644cdb9cf072004ea3d1998dd7579fc0acb99dbb23bd4f", + "dependencies": [ + "jsr:@std/internal" + ] + }, + "@std/bytes@0.223.0": { + "integrity": "84b75052cd8680942c397c2631318772b295019098f40aac5c36cead4cba51a8" + }, + "@std/bytes@0.224.0": { + "integrity": "a2250e1d0eb7d1c5a426f21267ab9bdeac2447fa87a3d0d1a467d3f7a6058e49" + }, + "@std/bytes@1.0.0": { + "integrity": "9392e72af80adccaa1197912fa19990ed091cb98d5c9c4344b0c301b22d7c632" + }, + "@std/bytes@1.0.2": { + "integrity": "fbdee322bbd8c599a6af186a1603b3355e59a5fb1baa139f8f4c3c9a1b3e3d57" + }, + "@std/cli@0.223.0": { + "integrity": "2feb7970f2028904c3edc22ea916ce9538113dfc170844f3eae03578c333c356", + "dependencies": [ + "jsr:@std/assert@0.223" + ] + }, + "@std/crypto@0.224.0": { + "integrity": "154ef3ff08ef535562ef1a718718c5b2c5fc3808f0f9100daad69e829bfcdf2d", + "dependencies": [ + "jsr:@std/assert@0.224", + "jsr:@std/encoding@0.224" + ] + }, + "@std/dotenv@0.224.0": { + "integrity": "d9234cdf551507dcda60abb6c474289843741d8c07ee8ce540c60f5c1b220a1d" + }, + "@std/dotenv@0.224.2": { + "integrity": "29081695357e4534696c9e986b2560be29c141ccf52daa32b6c20ff5b5c64ab9" + }, + "@std/encoding@0.213.1": { + "integrity": "fcbb6928713dde941a18ca5db88ca1544d0755ec8fb20fe61e2dc8144b390c62" + }, + "@std/encoding@0.224.0": { + "integrity": "efb6dca97d3e9c31392bd5c8cfd9f9fc9decf5a1f4d1f78af7900a493bcf89b5" + }, + "@std/encoding@0.224.3": { + "integrity": "5e861b6d81be5359fad4155e591acf17c0207b595112d1840998bb9f476dbdaf" + }, + "@std/encoding@1.0.5": { + "integrity": "ecf363d4fc25bd85bd915ff6733a7e79b67e0e7806334af15f4645c569fefc04" + }, + "@std/fmt@0.213.1": { + "integrity": "a06d31777566d874b9c856c10244ac3e6b660bdec4c82506cd46be052a1082c3" + }, + "@std/fs@0.213.1": { + "integrity": "fbcaf099f8a85c27ab0712b666262cda8fe6d02e9937bf9313ecaea39a22c501", + "dependencies": [ + "jsr:@std/assert@~0.213.1", + "jsr:@std/path@~0.213.1" + ] + }, + "@std/fs@0.229.3": { + "integrity": "783bca21f24da92e04c3893c9e79653227ab016c48e96b3078377ebd5222e6eb", + "dependencies": [ + "jsr:@std/path@1.0.0-rc.1" + ] + }, + "@std/internal@1.0.0": { + "integrity": "ac6a6dfebf838582c4b4f61a6907374e27e05bedb6ce276e0f1608fe84e7cd9a" + }, + "@std/internal@1.0.1": { + "integrity": "6f8c7544d06a11dd256c8d6ba54b11ed870aac6c5aeafff499892662c57673e6" + }, + "@std/internal@1.0.3": { + "integrity": "208e9b94a3d5649bd880e9ca38b885ab7651ab5b5303a56ed25de4755fb7b11e" + }, + "@std/internal@1.0.4": { + "integrity": "62e8e4911527e5e4f307741a795c0b0a9e6958d0b3790716ae71ce085f755422" + }, + "@std/io@0.223.0": { + "integrity": "2d8c3c2ab3a515619b90da2c6ff5ea7b75a94383259ef4d02116b228393f84f1", + "dependencies": [ + "jsr:@std/assert@0.223", + "jsr:@std/bytes@0.223" + ] + }, + "@std/io@0.224.0": { + "integrity": "0aff885d21d829c050b8a08b1d71b54aed5841aecf227f8d77e99ec529a11e8e", + "dependencies": [ + "jsr:@std/bytes@0.224" + ] + }, + "@std/io@0.224.1": { + "integrity": "73de242551a5c0965eb33e36b1fc7df4834ffbc836a1a643a410ccd11253d6be", + "dependencies": [ + "jsr:@std/bytes@^1.0.0-rc.3" + ] + }, + "@std/io@0.224.3": { + "integrity": "b402edeb99c6b3778d9ae3e9927bc9085b170b41e5a09bbb7064ab2ee394ae2f", + "dependencies": [ + "jsr:@std/bytes@^1.0.1-rc.3" + ] + }, + "@std/io@0.224.4": { + "integrity": "bce1151765e4e70e376039fd72c71672b4d4aae363878a5ee3e58361b81197ec", + "dependencies": [ + "jsr:@std/bytes@^1.0.2-rc.3" + ] + }, + "@std/io@0.224.6": { + "integrity": "eefe034a370be34daf066c8634dd645635d099bb21eccf110f0bdc28d9040891", + "dependencies": [ + "jsr:@std/bytes@^1.0.2" + ] + }, + "@std/io@0.224.7": { + "integrity": "a70848793c44a7c100926571a8c9be68ba85487bfcd4d0540d86deabe1123dc9", + "dependencies": [ + "jsr:@std/bytes@^1.0.2" + ] + }, + "@std/io@0.224.8": { + "integrity": "f525d05d51fd873de6352b9afcf35cab9ab5dc448bf3c20e0c8b521ded9be392", + "dependencies": [ + "jsr:@std/bytes@^1.0.2" + ] + }, + "@std/json@0.223.0": { + "integrity": "9a4a255931dd0397924c6b10bb6a72fe3e28ddd876b981ada2e3b8dd0764163f", + "dependencies": [ + "jsr:@std/streams" + ] + }, + "@std/media-types@0.224.0": { + "integrity": "5ac87989393f8cb1c81bee02aef6f5d4c8289b416deabc04f9ad25dff292d0b0" + }, + "@std/media-types@0.224.1": { + "integrity": "9e69a5daed37c5b5c6d3ce4731dc191f80e67f79bed392b0957d1d03b87f11e1" + }, + "@std/path@0.213.1": { + "integrity": "f187bf278a172752e02fcbacf6bd78a335ed320d080a7ed3a5a59c3e88abc673", + "dependencies": [ + "jsr:@std/assert@~0.213.1" + ] + }, + "@std/path@0.224.0": { + "integrity": "55bca6361e5a6d158b9380e82d4981d82d338ec587de02951e2b7c3a24910ee6", + "dependencies": [ + "jsr:@std/assert@0.224" + ] + }, + "@std/path@1.0.0-rc.1": { + "integrity": "b8c00ae2f19106a6bb7cbf1ab9be52aa70de1605daeb2dbdc4f87a7cbaf10ff6" + }, + "@std/streams@0.223.0": { + "integrity": "d6b28e498ced3960b04dc5d251f2dcfc1df244b5ec5a48dc23a8f9b490be3b99", + "dependencies": [ + "jsr:@std/assert@0.223", + "jsr:@std/bytes@0.223", + "jsr:@std/io@0.223" + ] + } + }, + "npm": { + "@electric-sql/pglite@0.2.8": { + "integrity": "sha512-0wSmQu22euBRzR5ghqyIHnBH4MfwlkL5WstOrrA3KOsjEWEglvoL/gH92JajEUA6Ufei/+qbkB2hVloC/K/RxQ==" + }, + "@isaacs/ttlcache@1.4.1": { + "integrity": "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==" + }, + "@noble/ciphers@0.5.3": { + "integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==" + }, + "@noble/curves@1.1.0": { + "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", + "dependencies": [ + "@noble/hashes@1.3.1" + ] + }, + "@noble/curves@1.2.0": { + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "dependencies": [ + "@noble/hashes@1.3.2" + ] + }, + "@noble/curves@1.4.0": { + "integrity": "sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==", + "dependencies": [ + "@noble/hashes@1.4.0" + ] + }, + "@noble/hashes@1.3.1": { + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==" + }, + "@noble/hashes@1.3.2": { + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==" + }, + "@noble/hashes@1.4.0": { + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==" + }, + "@noble/secp256k1@2.1.0": { + "integrity": "sha512-XLEQQNdablO0XZOIniFQimiXsZDNwaYgL96dZwC54Q30imSbAOFf3NKtepc+cXyuZf5Q1HCgbqgZ2UFFuHVcEw==" + }, + "@opentelemetry/api@1.9.0": { + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==" + }, + "@scure/base@1.1.1": { + "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==" + }, + "@scure/base@1.1.6": { + "integrity": "sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==" + }, + "@scure/bip32@1.3.1": { + "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", + "dependencies": [ + "@noble/curves@1.1.0", + "@noble/hashes@1.3.2", + "@scure/base@1.1.6" + ] + }, + "@scure/bip32@1.4.0": { + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", + "dependencies": [ + "@noble/curves@1.4.0", + "@noble/hashes@1.4.0", + "@scure/base@1.1.6" + ] + }, + "@scure/bip39@1.2.1": { + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "dependencies": [ + "@noble/hashes@1.3.2", + "@scure/base@1.1.6" + ] + }, + "@scure/bip39@1.3.0": { + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", + "dependencies": [ + "@noble/hashes@1.4.0", + "@scure/base@1.1.6" + ] + }, + "@types/dompurify@3.0.5": { + "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", + "dependencies": [ + "@types/trusted-types" + ] + }, + "@types/node@17.0.45": { + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==" + }, + "@types/node@18.16.19": { + "integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==" + }, + "@types/trusted-types@2.0.7": { + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" + }, + "agent-base@7.1.1": { + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dependencies": [ + "debug@4.3.4" + ] + }, + "ansi-escapes@6.2.0": { + "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", + "dependencies": [ + "type-fest@3.13.1" + ] + }, + "ansi-regex@6.0.1": { + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + }, + "ansi-styles@6.2.1": { + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==" + }, + "asynckit@0.4.0": { + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "bintrees@1.0.2": { + "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==" + }, + "braces@3.0.2": { + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": [ + "fill-range" + ] + }, + "chalk@5.3.0": { + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==" + }, + "cli-cursor@4.0.0": { + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "dependencies": [ + "restore-cursor" + ] + }, + "cli-truncate@4.0.0": { + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dependencies": [ + "slice-ansi@5.0.0", + "string-width" + ] + }, + "colorette@2.0.20": { + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + }, + "combined-stream@1.0.8": { + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": [ + "delayed-stream" + ] + }, + "comlink-async-generator@0.0.1": { + "integrity": "sha512-RjOPv6Tb7cL9FiIgwanUJuFG9aW4myAFyyzxZoEkEegeDQrZqr92d1Njv2WIgi7nbGpTiyy5GdNTUubDaNgZ6A==", + "dependencies": [ + "comlink" + ] + }, + "comlink@4.4.1": { + "integrity": "sha512-+1dlx0aY5Jo1vHy/tSsIGpSkN4tS9rZSW8FIhG0JH/crs9wwweswIo/POr451r7bZww3hFbPAKnTpimzL/mm4Q==" + }, + "commander@11.1.0": { + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==" + }, + "commander@12.1.0": { + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==" + }, + "cross-spawn@7.0.3": { + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": [ + "path-key@3.1.1", + "shebang-command", + "which" + ] + }, + "cssstyle@4.1.0": { + "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==", + "dependencies": [ + "rrweb-cssom" + ] + }, + "data-urls@5.0.0": { + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dependencies": [ + "whatwg-mimetype", + "whatwg-url@14.0.0" + ] + }, + "debug@3.2.7": { + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": [ + "ms@2.1.3" + ] + }, + "debug@4.3.4": { + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": [ + "ms@2.1.2" + ] + }, + "decimal.js@10.4.3": { + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + }, + "delayed-stream@1.0.0": { + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "dom-serializer@2.0.0": { + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": [ + "domelementtype", + "domhandler", + "entities" + ] + }, + "domelementtype@2.3.0": { + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + }, + "domhandler@5.0.3": { + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": [ + "domelementtype" + ] + }, + "dompurify@3.1.7": { + "integrity": "sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==" + }, + "domutils@3.1.0": { + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dependencies": [ + "dom-serializer", + "domelementtype", + "domhandler" + ] + }, + "emoji-regex@10.3.0": { + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==" + }, + "entities@4.5.0": { + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" + }, + "eventemitter3@5.0.1": { + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, + "execa@8.0.1": { + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dependencies": [ + "cross-spawn", + "get-stream", + "human-signals", + "is-stream", + "merge-stream", + "npm-run-path", + "onetime@6.0.0", + "signal-exit@4.1.0", + "strip-final-newline" + ] + }, + "fast-stable-stringify@1.0.0": { + "integrity": "sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==" + }, + "fill-range@7.0.1": { + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": [ + "to-regex-range" + ] + }, + "form-data@4.0.0": { + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": [ + "asynckit", + "combined-stream", + "mime-types" + ] + }, + "formdata-helper@0.3.0": { + "integrity": "sha512-QkRUFbNgWSu9lkc5TKLWri0ilTFowo950w13I5pRhj4cUxzMLuz0MIhGbE/gIRyfsZQoFeMNN0h06OCSOgfhUg==" + }, + "get-east-asian-width@1.2.0": { + "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==" + }, + "get-stream@8.0.1": { + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==" + }, + "he@1.2.0": { + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "hono-rate-limiter@0.3.0_hono@4.2.5": { + "integrity": "sha512-QUS1N+DCZs8CKjpoHugvEvAp/+e+LUllPPnaIlATK9GyT26niQxn4H4V+O5kHwcsXjD3P3JOc0jMb7fnW6gpFQ==", + "dependencies": [ + "hono" + ] + }, + "hono@4.2.5": { + "integrity": "sha512-uonJD3i/yy005kQ7bPZRVfG3rejYJwyPqBmPoUGijS4UB/qM+YlrZ7xzSWy+ByDu9buGHUG+f+SKzz03Y6V1Kw==" + }, + "html-encoding-sniffer@4.0.0": { + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dependencies": [ + "whatwg-encoding" + ] + }, + "htmlparser2@8.0.2": { + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "dependencies": [ + "domelementtype", + "domhandler", + "domutils", + "entities" + ] + }, + "http-proxy-agent@7.0.2": { + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dependencies": [ + "agent-base", + "debug@4.3.4" + ] + }, + "https-proxy-agent@7.0.5": { + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dependencies": [ + "agent-base", + "debug@4.3.4" + ] + }, + "human-signals@5.0.0": { + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==" + }, + "iconv-lite@0.4.24": { + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": [ + "safer-buffer" + ] + }, + "iconv-lite@0.6.3": { + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": [ + "safer-buffer" + ] + }, + "is-fullwidth-code-point@4.0.0": { + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==" + }, + "is-fullwidth-code-point@5.0.0": { + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dependencies": [ + "get-east-asian-width" + ] + }, + "is-number@7.0.0": { + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-potential-custom-element-name@1.0.1": { + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, + "is-stream@3.0.0": { + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==" + }, + "isexe@2.0.0": { + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "iso-639-1@2.1.15": { + "integrity": "sha512-7c7mBznZu2ktfvyT582E2msM+Udc1EjOyhVRE/0ZsjD9LBtWSm23h3PtiRh2a35XoUsTQQjJXaJzuLjXsOdFDg==" + }, + "isomorphic-dompurify@2.16.0": { + "integrity": "sha512-cXhX2owp8rPxafCr0ywqy2CGI/4ceLNgWkWBEvUz64KTbtg3oRL2ZRqq/zW0pzt4YtDjkHLbwcp/lozpKzAQjg==", + "dependencies": [ + "@types/dompurify", + "dompurify", + "jsdom" + ] + }, + "jsdom@25.0.1": { + "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", + "dependencies": [ + "cssstyle", + "data-urls", + "decimal.js", + "form-data", + "html-encoding-sniffer", + "http-proxy-agent", + "https-proxy-agent", + "is-potential-custom-element-name", + "nwsapi", + "parse5", + "rrweb-cssom", + "saxes", + "symbol-tree", + "tough-cookie", + "w3c-xmlserializer", + "webidl-conversions@7.0.0", + "whatwg-encoding", + "whatwg-mimetype", + "whatwg-url@14.0.0", + "ws", + "xml-name-validator" + ] + }, + "kysely-postgres-js@2.0.0_kysely@0.27.3_postgres@3.4.4": { + "integrity": "sha512-R1tWx6/x3tSatWvsmbHJxpBZYhNNxcnMw52QzZaHKg7ZOWtHib4iZyEaw4gb2hNKVctWQ3jfMxZT/ZaEMK6kBQ==", + "dependencies": [ + "kysely@0.27.3", + "postgres" + ] + }, + "kysely@0.27.3": { + "integrity": "sha512-lG03Ru+XyOJFsjH3OMY6R/9U38IjDPfnOfDgO3ynhbDr+Dz8fak+X6L62vqu3iybQnj+lG84OttBuU9KY3L9kA==" + }, + "kysely@0.27.4": { + "integrity": "sha512-dyNKv2KRvYOQPLCAOCjjQuCk4YFd33BvGdf/o5bC7FiW+BB6snA81Zt+2wT9QDFzKqxKa5rrOmvlK/anehCcgA==" + }, + "lande@1.0.10": { + "integrity": "sha512-yT52DQh+UV2pEp08jOYrA4drDv0DbjpiRyZYgl25ak9G2cVR2AimzrqkYQWrD9a7Ud+qkAcaiDDoNH9DXfHPmw==", + "dependencies": [ + "toygrad" + ] + }, + "light-bolt11-decoder@3.1.1": { + "integrity": "sha512-sLg/KCwYkgsHWkefWd6KqpCHrLFWWaXTOX3cf6yD2hAzL0SLpX+lFcaFK2spkjbgzG6hhijKfORDc9WoUHwX0A==", + "dependencies": [ + "@scure/base@1.1.1" + ] + }, + "lilconfig@3.0.0": { + "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==" + }, + "linkify-plugin-hashtag@4.1.3_linkifyjs@4.1.3": { + "integrity": "sha512-sq627UTrmmDhVnYoUbj/EFfSrhGBvAZYIUdUCjtLeW/AWBV7g9NX9JXEglAuJ7DIyJ84Ged0EHOe+xCXRe2Gmw==", + "dependencies": [ + "linkifyjs" + ] + }, + "linkify-string@4.1.3_linkifyjs@4.1.3": { + "integrity": "sha512-6dAgx4MiTcvEX87OS5aNpAioO7cSELUXp61k7azOvMYOLSmREx0w4yM1Uf0+O3JLC08YdkUyZhAX+YkasRt/mw==", + "dependencies": [ + "linkifyjs" + ] + }, + "linkifyjs@4.1.3": { + "integrity": "sha512-auMesunaJ8yfkHvK4gfg1K0SaKX/6Wn9g2Aac/NwX+l5VdmFZzo/hdPGxEOETj+ryRa4/fiOPjeeKURSAJx1sg==" + }, + "lint-staged@15.2.2": { + "integrity": "sha512-TiTt93OPh1OZOsb5B7k96A/ATl2AjIZo+vnzFZ6oHK5FuTk63ByDtxGQpHm+kFETjEWqgkF95M8FRXKR/LEBcw==", + "dependencies": [ + "chalk", + "commander@11.1.0", + "debug@4.3.4", + "execa", + "lilconfig", + "listr2", + "micromatch", + "pidtree", + "string-argv", + "yaml" + ] + }, + "listr2@8.0.1": { + "integrity": "sha512-ovJXBXkKGfq+CwmKTjluEqFi3p4h8xvkxGQQAQan22YCgef4KZ1mKGjzfGh6PL6AW5Csw0QiQPNuQyH+6Xk3hA==", + "dependencies": [ + "cli-truncate", + "colorette", + "eventemitter3", + "log-update", + "rfdc", + "wrap-ansi" + ] + }, + "log-update@6.0.0": { + "integrity": "sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==", + "dependencies": [ + "ansi-escapes", + "cli-cursor", + "slice-ansi@7.1.0", + "strip-ansi", + "wrap-ansi" + ] + }, + "lru-cache@10.2.2": { + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==" + }, + "merge-stream@2.0.0": { + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "micromatch@4.0.5": { + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": [ + "braces", + "picomatch" + ] + }, + "mime-db@1.52.0": { + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types@2.1.35": { + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": [ + "mime-db" + ] + }, + "mimic-fn@2.1.0": { + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "mimic-fn@4.0.0": { + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==" + }, + "minimist@1.2.8": { + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + }, + "ms@2.1.2": { + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "ms@2.1.3": { + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node-fetch@2.7.0": { + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": [ + "whatwg-url@5.0.0" + ] + }, + "nostr-tools@2.5.1": { + "integrity": "sha512-bpkhGGAhdiCN0irfV+xoH3YP5CQeOXyXzUq7SYeM6D56xwTXZCPEmBlUGqFVfQidvRsoVeVxeAiOXW2c2HxoRQ==", + "dependencies": [ + "@noble/ciphers", + "@noble/curves@1.2.0", + "@noble/hashes@1.3.1", + "@scure/base@1.1.1", + "@scure/bip32@1.3.1", + "@scure/bip39@1.2.1", + "nostr-wasm" + ] + }, + "nostr-tools@2.7.0": { + "integrity": "sha512-jJoL2J1CBiKDxaXZww27nY/Wsuxzx7AULxmGKFce4sskDu1tohNyfnzYQ8BvDyvkstU8kNZUAXPL32tre33uig==", + "dependencies": [ + "@noble/ciphers", + "@noble/curves@1.2.0", + "@noble/hashes@1.3.1", + "@scure/base@1.1.1", + "@scure/bip32@1.3.1", + "@scure/bip39@1.2.1", + "nostr-wasm" + ] + }, + "nostr-wasm@0.1.0": { + "integrity": "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==" + }, + "npm-run-path@5.3.0": { + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dependencies": [ + "path-key@4.0.0" + ] + }, + "nwsapi@2.2.13": { + "integrity": "sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==" + }, + "onetime@5.1.2": { + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": [ + "mimic-fn@2.1.0" + ] + }, + "onetime@6.0.0": { + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dependencies": [ + "mimic-fn@4.0.0" + ] + }, + "parse5@7.1.2": { + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dependencies": [ + "entities" + ] + }, + "path-key@3.1.1": { + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "path-key@4.0.0": { + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==" + }, + "path-to-regexp@7.1.0": { + "integrity": "sha512-ZToe+MbUF4lBqk6dV8GKot4DKfzrxXsplOddH8zN3YK+qw9/McvP7+4ICjZvOne0jQhN4eJwHsX6tT0Ns19fvw==" + }, + "picomatch@2.3.1": { + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + }, + "pidtree@0.6.0": { + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==" + }, + "png-to-ico@2.1.8": { + "integrity": "sha512-Nf+IIn/cZ/DIZVdGveJp86NG5uNib1ZXMiDd/8x32HCTeKSvgpyg6D/6tUBn1QO/zybzoMK0/mc3QRgAyXdv9w==", + "dependencies": [ + "@types/node@17.0.45", + "minimist", + "pngjs" + ] + }, + "pngjs@6.0.0": { + "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==" + }, + "postgres@3.4.4": { + "integrity": "sha512-IbyN+9KslkqcXa8AO9fxpk97PA4pzewvpi2B3Dwy9u4zpV32QicaEdgmF3eSQUzdRk7ttDHQejNgAEr4XoeH4A==" + }, + "prom-client@15.1.2": { + "integrity": "sha512-on3h1iXb04QFLLThrmVYg1SChBQ9N1c+nKAjebBjokBqipddH3uxmOUcEkTnzmJ8Jh/5TSUnUqS40i2QB2dJHQ==", + "dependencies": [ + "@opentelemetry/api", + "tdigest" + ] + }, + "punycode@2.3.1": { + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" + }, + "restore-cursor@4.0.0": { + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dependencies": [ + "onetime@5.1.2", + "signal-exit@3.0.7" + ] + }, + "rfdc@1.3.1": { + "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==" + }, + "rrweb-cssom@0.7.1": { + "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==" + }, + "safer-buffer@2.1.2": { + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "saxes@6.0.0": { + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dependencies": [ + "xmlchars" + ] + }, + "shebang-command@2.0.0": { + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": [ + "shebang-regex" + ] + }, + "shebang-regex@3.0.0": { + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "signal-exit@3.0.7": { + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "signal-exit@4.1.0": { + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==" + }, + "slice-ansi@5.0.0": { + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dependencies": [ + "ansi-styles", + "is-fullwidth-code-point@4.0.0" + ] + }, + "slice-ansi@7.1.0": { + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dependencies": [ + "ansi-styles", + "is-fullwidth-code-point@5.0.0" + ] + }, + "string-argv@0.3.2": { + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==" + }, + "string-width@7.1.0": { + "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==", + "dependencies": [ + "emoji-regex", + "get-east-asian-width", + "strip-ansi" + ] + }, + "strip-ansi@7.1.0": { + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": [ + "ansi-regex" + ] + }, + "strip-final-newline@3.0.0": { + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==" + }, + "symbol-tree@3.2.4": { + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "tdigest@0.1.2": { + "integrity": "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==", + "dependencies": [ + "bintrees" + ] + }, + "tldts-core@6.1.18": { + "integrity": "sha512-e4wx32F/7dMBSZyKAx825Yte3U0PQtZZ0bkWxYQiwLteRVnQ5zM40fEbi0IyNtwQssgJAk3GCr7Q+w39hX0VKA==" + }, + "tldts-core@6.1.49": { + "integrity": "sha512-ctRO/wzBasOCxAStJG/60Qe8/QpGmaVPsE8djdk0vioxN4uCOgKoveH71Qc2EOmVMIjVf0BjigI5p9ZDuLOygg==" + }, + "tldts@6.1.18": { + "integrity": "sha512-F+6zjPFnFxZ0h6uGb8neQWwHQm8u3orZVFribsGq4eBgEVrzSkHxzWS2l6aKr19T1vXiOMFjqfff4fQt+WgJFg==", + "dependencies": [ + "tldts-core@6.1.18" + ] + }, + "tldts@6.1.49": { + "integrity": "sha512-E5se9HuCyfwWvmc0JiXiocOw+Cm4tlJCKewdB5RKMH8MmtiTsQCc+yu5BBYB5ZN4lNbz8Xg65bqJ1odS9+RhIA==", + "dependencies": [ + "tldts-core@6.1.49" + ] + }, + "to-regex-range@5.0.1": { + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": [ + "is-number" + ] + }, + "tough-cookie@5.0.0": { + "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", + "dependencies": [ + "tldts@6.1.49" + ] + }, + "toygrad@2.6.0": { + "integrity": "sha512-g4zBmlSbvzOE5FOILxYkAybTSxijKLkj1WoNqVGnbMcWDyj4wWQ+eYSr3ik7XOpIgMq/7eBcPRTJX3DM2E0YMg==" + }, + "tr46@0.0.3": { + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "tr46@5.0.0": { + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "dependencies": [ + "punycode" + ] + }, + "tseep@1.2.1": { + "integrity": "sha512-VFnsNcPGC4qFJ1nxbIPSjTmtRZOhlqLmtwRqtLVos8mbRHki8HO9cy9Z1e89EiWyxFmq6LBviI9TQjijxw/mEw==" + }, + "type-fest@3.13.1": { + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==" + }, + "type-fest@4.18.2": { + "integrity": "sha512-+suCYpfJLAe4OXS6+PPXjW3urOS4IoP9waSiLuXfLgqZODKw/aWwASvzqE886wA0kQgGy0mIWyhd87VpqIy6Xg==" + }, + "unfurl.js@6.4.0": { + "integrity": "sha512-DogJFWPkOWMcu2xPdpmbcsL+diOOJInD3/jXOv6saX1upnWmMK8ndAtDWUfJkuInqNI9yzADud4ID9T+9UeWCw==", + "dependencies": [ + "debug@3.2.7", + "he", + "htmlparser2", + "iconv-lite@0.4.24", + "node-fetch" + ] + }, + "w3c-xmlserializer@5.0.0": { + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dependencies": [ + "xml-name-validator" + ] + }, + "webidl-conversions@3.0.1": { + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "webidl-conversions@7.0.0": { + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" + }, + "websocket-ts@2.1.5": { + "integrity": "sha512-rCNl9w6Hsir1azFm/pbjBEFzLD/gi7Th5ZgOxMifB6STUfTSovYAzryWw0TRvSZ1+Qu1Z5Plw4z42UfTNA9idA==" + }, + "whatwg-encoding@3.1.1": { + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dependencies": [ + "iconv-lite@0.6.3" + ] + }, + "whatwg-mimetype@4.0.0": { + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==" + }, + "whatwg-url@14.0.0": { + "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "dependencies": [ + "tr46@5.0.0", + "webidl-conversions@7.0.0" + ] + }, + "whatwg-url@5.0.0": { + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": [ + "tr46@0.0.3", + "webidl-conversions@3.0.1" + ] + }, + "which@2.0.2": { + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": [ + "isexe" + ] + }, + "wrap-ansi@9.0.0": { + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dependencies": [ + "ansi-styles", + "string-width", + "strip-ansi" + ] + }, + "ws@8.18.0": { + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==" + }, + "xml-name-validator@5.0.0": { + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==" + }, + "xmlchars@2.2.0": { + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "yaml@2.3.4": { + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==" + }, + "zod@3.23.8": { + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==" } }, "redirects": { @@ -2165,49 +2086,50 @@ }, "workspace": { "dependencies": [ - "jsr:@b-fuze/deno-dom@^0.1.47", - "jsr:@bradenmacdonald/s3-lite-client@^0.7.4", - "jsr:@gfx/canvas-wasm@^0.4.2", + "jsr:@b-fuze/deno-dom@~0.1.47", + "jsr:@bradenmacdonald/s3-lite-client@~0.7.4", + "jsr:@esroyo/scoped-performance@^3.1.0", + "jsr:@gfx/canvas-wasm@~0.4.2", "jsr:@hono/hono@^4.4.6", "jsr:@lambdalisue/async@^2.1.1", - "jsr:@negrel/webpush@^0.3.0", - "jsr:@nostrify/db@^0.35.0", - "jsr:@nostrify/nostrify@^0.36.0", - "jsr:@nostrify/policies@^0.35.0", - "jsr:@soapbox/kysely-pglite@^1.0.0", - "jsr:@soapbox/stickynotes@^0.4.0", - "jsr:@std/assert@^0.225.1", - "jsr:@std/cli@^0.223.0", - "jsr:@std/crypto@^0.224.0", - "jsr:@std/dotenv@^0.224.0", - "jsr:@std/encoding@^0.224.0", - "jsr:@std/fs@^0.229.3", - "jsr:@std/json@^0.223.0", - "jsr:@std/media-types@^0.224.1", - "jsr:@std/streams@^0.223.0", - "npm:@electric-sql/pglite@^0.2.8", + "jsr:@negrel/webpush@0.3", + "jsr:@nostrify/db@~0.36.1", + "jsr:@nostrify/nostrify@0.36", + "jsr:@nostrify/policies@0.35", + "jsr:@soapbox/kysely-pglite@1", + "jsr:@soapbox/stickynotes@0.4", + "jsr:@std/assert@~0.225.1", + "jsr:@std/cli@0.223", + "jsr:@std/crypto@0.224", + "jsr:@std/dotenv@0.224", + "jsr:@std/encoding@0.224", + "jsr:@std/fs@~0.229.3", + "jsr:@std/json@0.223", + "jsr:@std/media-types@~0.224.1", + "jsr:@std/streams@0.223", + "npm:@electric-sql/pglite@~0.2.8", "npm:@isaacs/ttlcache@^1.4.1", - "npm:@noble/secp256k1@^2.0.0", + "npm:@noble/secp256k1@2", "npm:@scure/base@^1.1.6", "npm:comlink-async-generator@^0.0.1", "npm:comlink@^4.4.1", "npm:commander@12.1.0", "npm:entities@^4.5.0", - "npm:fast-stable-stringify@^1.0.0", - "npm:formdata-helper@^0.3.0", - "npm:hono-rate-limiter@^0.3.0", + "npm:fast-stable-stringify@1", + "npm:formdata-helper@0.3", + "npm:hono-rate-limiter@0.3", "npm:iso-639-1@2.1.15", - "npm:isomorphic-dompurify@^2.11.0", + "npm:isomorphic-dompurify@^2.16.0", "npm:kysely-postgres-js@2.0.0", - "npm:kysely@^0.27.4", + "npm:kysely@~0.27.4", "npm:lande@^1.0.10", - "npm:light-bolt11-decoder", + "npm:light-bolt11-decoder@*", "npm:linkify-plugin-hashtag@^4.1.1", "npm:linkify-string@^4.1.1", "npm:linkifyjs@^4.1.1", "npm:lru-cache@^10.2.2", "npm:nostr-tools@2.5.1", - "npm:nostr-wasm@^0.1.0", + "npm:nostr-wasm@0.1", "npm:path-to-regexp@^7.1.0", "npm:png-to-ico@^2.1.8", "npm:prom-client@^15.1.2", diff --git a/grafana/Ditto-Dashboard.json b/grafana/Ditto-Dashboard.json index d722d5d9..3c03f23d 100644 --- a/grafana/Ditto-Dashboard.json +++ b/grafana/Ditto-Dashboard.json @@ -879,7 +879,7 @@ "uid": "${prometheus}" }, "editorMode": "code", - "expr": "increase(ditto_db_query_duration_ms_sum[$__rate_interval])", + "expr": "increase(ditto_db_query_duration_seconds_sum[$__rate_interval])", "instant": false, "legendFormat": "Time", "range": true, diff --git a/src/app.ts b/src/app.ts index 0cb17d1c..c1b901b4 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,4 +1,4 @@ -import { Context, Env as HonoEnv, Handler, Hono, Input as HonoInput, MiddlewareHandler } from '@hono/hono'; +import { type Context, Env as HonoEnv, Handler, Hono, Input as HonoInput, MiddlewareHandler } from '@hono/hono'; import { cors } from '@hono/hono/cors'; import { serveStatic } from '@hono/hono/deno'; import { logger } from '@hono/hono/logger'; @@ -113,6 +113,7 @@ import { trendingStatusesController, trendingTagsController, } from '@/controllers/api/trends.ts'; +import { translateController } from '@/controllers/api/translate.ts'; import { errorHandler } from '@/controllers/error.ts'; import { frontendController } from '@/controllers/frontend.ts'; import { metricsController } from '@/controllers/metrics.ts'; @@ -120,6 +121,7 @@ import { indexController } from '@/controllers/site.ts'; import { manifestController } from '@/controllers/manifest.ts'; import { nodeInfoController, nodeInfoSchemaController } from '@/controllers/well-known/nodeinfo.ts'; import { nostrController } from '@/controllers/well-known/nostr.ts'; +import { DittoTranslator } from '@/interfaces/DittoTranslator.ts'; import { auth98Middleware, requireProof, requireRole } from '@/middleware/auth98Middleware.ts'; import { cspMiddleware } from '@/middleware/cspMiddleware.ts'; import { metricsMiddleware } from '@/middleware/metricsMiddleware.ts'; @@ -129,6 +131,7 @@ import { requireSigner } from '@/middleware/requireSigner.ts'; import { signerMiddleware } from '@/middleware/signerMiddleware.ts'; import { storeMiddleware } from '@/middleware/storeMiddleware.ts'; import { uploaderMiddleware } from '@/middleware/uploaderMiddleware.ts'; +import { translatorMiddleware } from '@/middleware/translatorMiddleware.ts'; interface AppEnv extends HonoEnv { Variables: { @@ -144,6 +147,8 @@ interface AppEnv extends HonoEnv { pagination: { since?: number; until?: number; limit: number }; /** Normalized list pagination params. */ listPagination: { offset: number; limit: number }; + /** Translation service. */ + translator?: DittoTranslator; }; } @@ -223,6 +228,13 @@ app.post('/api/v1/statuses/:id{[0-9a-f]{64}}/bookmark', requireSigner, bookmarkC app.post('/api/v1/statuses/:id{[0-9a-f]{64}}/unbookmark', requireSigner, unbookmarkController); app.post('/api/v1/statuses/:id{[0-9a-f]{64}}/pin', requireSigner, pinController); app.post('/api/v1/statuses/:id{[0-9a-f]{64}}/unpin', requireSigner, unpinController); +app.post( + '/api/v1/statuses/:id{[0-9a-f]{64}}/translate', + requireSigner, + rateLimitMiddleware(15, Time.minutes(1)), + translatorMiddleware, + translateController, +); app.post('/api/v1/statuses/:id{[0-9a-f]{64}}/reblog', requireSigner, reblogStatusController); app.post('/api/v1/statuses/:id{[0-9a-f]{64}}/unreblog', requireSigner, unreblogStatusController); app.post('/api/v1/statuses', requireSigner, createStatusController); diff --git a/src/caches/translationCache.ts b/src/caches/translationCache.ts new file mode 100644 index 00000000..7bd27946 --- /dev/null +++ b/src/caches/translationCache.ts @@ -0,0 +1,11 @@ +import { LanguageCode } from 'iso-639-1'; +import { LRUCache } from 'lru-cache'; + +import { Conf } from '@/config.ts'; +import { MastodonTranslation } from '@/entities/MastodonTranslation.ts'; + +/** Translations LRU cache. */ +export const translationCache = new LRUCache<`${LanguageCode}-${string}`, MastodonTranslation>({ + max: Conf.caches.translation.max, + ttl: Conf.caches.translation.ttl, +}); diff --git a/src/config.ts b/src/config.ts index bdeb8ec2..1aa4adfb 100644 --- a/src/config.ts +++ b/src/config.ts @@ -296,6 +296,26 @@ class Conf { static get preferredLanguages(): LanguageCode[] | undefined { return Deno.env.get('DITTO_LANGUAGES')?.split(',')?.filter(ISO6391.validate) as LanguageCode[]; } + /** Translation provider used to translate posts. */ + static get translationProvider(): string | undefined { + return Deno.env.get('TRANSLATION_PROVIDER'); + } + /** DeepL URL endpoint. */ + static get deeplBaseUrl(): string | undefined { + return Deno.env.get('DEEPL_BASE_URL'); + } + /** DeepL API KEY. */ + static get deeplApiKey(): string | undefined { + return Deno.env.get('DEEPL_API_KEY'); + } + /** LibreTranslate URL endpoint. */ + static get libretranslateBaseUrl(): string | undefined { + return Deno.env.get('LIBRETRANSLATE_BASE_URL'); + } + /** LibreTranslate API KEY. */ + static get libretranslateApiKey(): string | undefined { + return Deno.env.get('LIBRETRANSLATE_API_KEY'); + } /** Cache settings. */ static caches = { /** NIP-05 cache settings. */ @@ -319,6 +339,13 @@ class Conf { ttl: Number(Deno.env.get('DITTO_CACHE_LINK_PREVIEW_TTL') || 12 * 60 * 60 * 1000), }; }, + /** Translation cache settings. */ + get translation(): { max: number; ttl: number } { + return { + max: Number(Deno.env.get('DITTO_CACHE_TRANSLATION_MAX') || 1000), + ttl: Number(Deno.env.get('DITTO_CACHE_TRANSLATION_TTL') || 6 * 60 * 60 * 1000), + }; + }, }; } diff --git a/src/controllers/api/accounts.ts b/src/controllers/api/accounts.ts index c4a2bc40..b3c75ae5 100644 --- a/src/controllers/api/accounts.ts +++ b/src/controllers/api/accounts.ts @@ -51,7 +51,7 @@ const verifyCredentialsController: AppController = async (c) => { const store = await Storages.db(); - const [author, [settingsStore], [captcha]] = await Promise.all([ + const [author, [settingsEvent]] = await Promise.all([ getAuthor(pubkey, { signal: AbortSignal.timeout(5000) }), store.query([{ @@ -60,32 +60,18 @@ const verifyCredentialsController: AppController = async (c) => { '#d': ['pub.ditto.pleroma_settings_store'], limit: 1, }]), - - store.query([{ - kinds: [1985], - authors: [Conf.pubkey], - '#L': ['pub.ditto.captcha'], - '#l': ['solved'], - '#p': [pubkey], - limit: 1, - }]), ]); + let settingsStore: Record | undefined; + try { + settingsStore = n.json().pipe(z.record(z.string(), z.unknown())).parse(settingsEvent?.content); + } catch { + // Do nothing + } + const account = author - ? await renderAccount(author, { withSource: true }) - : await accountFromPubkey(pubkey, { withSource: true }); - - if (settingsStore) { - try { - account.pleroma.settings_store = JSON.parse(settingsStore.content); - } catch { - // Ignore - } - } - - if (captcha && account.source) { - account.source.ditto.captcha_solved = true; - } + ? await renderAccount(author, { withSource: true, settingsStore }) + : await accountFromPubkey(pubkey, { withSource: true, settingsStore }); return c.json(account); }; @@ -280,7 +266,7 @@ const updateCredentialsSchema = z.object({ bot: z.boolean().optional(), discoverable: z.boolean().optional(), nip05: z.string().email().or(z.literal('')).optional(), - pleroma_settings_store: z.unknown().optional(), + pleroma_settings_store: z.record(z.string(), z.unknown()).optional(), lud16: z.string().email().or(z.literal('')).optional(), website: z.string().url().or(z.literal('')).optional(), }); @@ -339,8 +325,8 @@ const updateCredentialsController: AppController = async (c) => { c, ); - const account = await renderAccount(event, { withSource: true }); const settingsStore = result.data.pleroma_settings_store; + const account = await renderAccount(event, { withSource: true, settingsStore }); if (settingsStore) { await createEvent({ @@ -350,8 +336,6 @@ const updateCredentialsController: AppController = async (c) => { }, c); } - account.pleroma.settings_store = settingsStore; - return c.json(account); }; diff --git a/src/controllers/api/captcha.ts b/src/controllers/api/captcha.ts index ef266745..1bb92118 100644 --- a/src/controllers/api/captcha.ts +++ b/src/controllers/api/captcha.ts @@ -4,7 +4,7 @@ import { z } from 'zod'; import { AppController } from '@/app.ts'; import { Conf } from '@/config.ts'; -import { createAdminEvent } from '@/utils/api.ts'; +import { updateUser } from '@/utils/api.ts'; interface Point { x: number; @@ -169,16 +169,7 @@ export const captchaVerifyController: AppController = async (c) => { if (solved) { captchas.delete(id); - - await createAdminEvent({ - kind: 1985, - tags: [ - ['L', 'pub.ditto.captcha'], - ['l', 'solved', 'pub.ditto.captcha'], - ['p', pubkey, Conf.relay], - ], - }, c); - + await updateUser(pubkey, { captcha_solved: true }, c); return new Response(null, { status: 204 }); } diff --git a/src/controllers/api/instance.ts b/src/controllers/api/instance.ts index da92fe53..78a72dd4 100644 --- a/src/controllers/api/instance.ts +++ b/src/controllers/api/instance.ts @@ -129,7 +129,7 @@ const instanceV2Controller: AppController = async (c) => { max_expiration: 2629746, }, translation: { - enabled: false, + enabled: Boolean(Conf.translationProvider), }, }, nostr: { @@ -142,7 +142,7 @@ const instanceV2Controller: AppController = async (c) => { }, }, registrations: { - enabled: false, + enabled: true, approval_required: false, message: null, url: null, diff --git a/src/controllers/api/translate.ts b/src/controllers/api/translate.ts new file mode 100644 index 00000000..d763c713 --- /dev/null +++ b/src/controllers/api/translate.ts @@ -0,0 +1,147 @@ +import { LanguageCode } from 'iso-639-1'; +import { z } from 'zod'; + +import { AppController } from '@/app.ts'; +import { translationCache } from '@/caches/translationCache.ts'; +import { MastodonTranslation } from '@/entities/MastodonTranslation.ts'; +import { cachedTranslationsSizeGauge } from '@/metrics.ts'; +import { getEvent } from '@/queries.ts'; +import { localeSchema } from '@/schema.ts'; +import { parseBody } from '@/utils/api.ts'; +import { renderStatus } from '@/views/mastodon/statuses.ts'; + +const translateSchema = z.object({ + lang: localeSchema, +}); + +const translateController: AppController = async (c) => { + const result = translateSchema.safeParse(await parseBody(c.req.raw)); + const { signal } = c.req.raw; + + if (!result.success) { + return c.json({ error: 'Bad request.', schema: result.error }, 422); + } + + const translator = c.get('translator'); + if (!translator) { + return c.json({ error: 'No translator configured.' }, 500); + } + + const lang = result.data.lang.language.slice(0, 2) as LanguageCode; + + const id = c.req.param('id'); + + const event = await getEvent(id, { signal }); + if (!event) { + return c.json({ error: 'Record not found' }, 400); + } + + const viewerPubkey = await c.get('signer')?.getPublicKey(); + + if (lang.toLowerCase() === event?.language?.toLowerCase()) { + return c.json({ error: 'Source and target languages are the same. No translation needed.' }, 400); + } + + const status = await renderStatus(event, { viewerPubkey }); + if (!status?.content) { + return c.json({ error: 'Bad request.', schema: result.error }, 400); + } + + const cacheKey: `${LanguageCode}-${string}` = `${lang}-${id}`; + const cached = translationCache.get(cacheKey); + + if (cached) { + return c.json(cached, 200); + } + + const mediaAttachments = status?.media_attachments.map((value) => { + return { + id: value.id, + description: value.description ?? '', + }; + }) ?? []; + + try { + const texts: string[] = []; + + const mastodonTranslation: MastodonTranslation = { + content: '', + spoiler_text: '', + media_attachments: [], + poll: null, + detected_source_language: event.language ?? 'en', + provider: translator.provider, + }; + + if ((status?.poll as MastodonTranslation['poll'])?.options) { + mastodonTranslation.poll = { id: (status?.poll as MastodonTranslation['poll'])?.id!, options: [] }; + } + + type TranslationIndex = { + [key: number]: 'content' | 'spoilerText' | 'poll' | { type: 'media'; id: string }; + }; + const translationIndex: TranslationIndex = {}; + let index = 0; + + // Content + translationIndex[index] = 'content'; + texts.push(status.content); + index++; + + // Spoiler text + if (status.spoiler_text) { + translationIndex[index] = 'spoilerText'; + texts.push(status.spoiler_text); + index++; + } + + // Media description + for (const [mediaIndex, value] of mediaAttachments.entries()) { + translationIndex[index + mediaIndex] = { type: 'media', id: value.id }; + texts.push(mediaAttachments[mediaIndex].description); + index += mediaIndex; + } + + // Poll title + if (status?.poll) { + for (const [pollIndex] of (status?.poll as MastodonTranslation['poll'])!.options.entries()) { + translationIndex[index + pollIndex] = 'poll'; + texts.push((status.poll as MastodonTranslation['poll'])!.options[pollIndex].title); + index += pollIndex; + } + } + + const data = await translator.translate(texts, event.language, lang, { signal }); + const translatedTexts = data.results; + + for (let i = 0; i < texts.length; i++) { + if (translationIndex[i] === 'content') { + mastodonTranslation.content = translatedTexts[i]; + } else if (translationIndex[i] === 'spoilerText') { + mastodonTranslation.spoiler_text = translatedTexts[i]; + } else if (translationIndex[i] === 'poll') { + mastodonTranslation.poll?.options.push({ title: translatedTexts[i] }); + } else { + const media = translationIndex[i] as { type: 'media'; id: string }; + mastodonTranslation.media_attachments.push({ + id: media.id, + description: translatedTexts[i], + }); + } + } + + mastodonTranslation.detected_source_language = data.source_lang; + + translationCache.set(cacheKey, mastodonTranslation); + cachedTranslationsSizeGauge.set(translationCache.size); + + return c.json(mastodonTranslation, 200); + } catch (e) { + if (e instanceof Error && e.message.includes('not supported')) { + return c.json({ error: `Translation of source language '${event.language}' not supported` }, 422); + } + return c.json({ error: 'Service Unavailable' }, 503); + } +}; + +export { translateController }; diff --git a/src/controllers/nostr/relay.ts b/src/controllers/nostr/relay.ts index 2a38e751..f62ad76b 100644 --- a/src/controllers/nostr/relay.ts +++ b/src/controllers/nostr/relay.ts @@ -18,6 +18,7 @@ import * as pipeline from '@/pipeline.ts'; import { RelayError } from '@/RelayError.ts'; import { Storages } from '@/storages.ts'; import { Time } from '@/utils/time.ts'; +import { purifyEvent } from '@/utils/purify.ts'; /** Limit of initial events returned for a subscription. */ const FILTER_LIMIT = 100; @@ -105,7 +106,7 @@ function connectStream(socket: WebSocket, ip: string | undefined) { try { for (const event of await store.query(filters, { limit: FILTER_LIMIT, timeout: Conf.db.timeouts.relay })) { - send(['EVENT', subId, event]); + send(['EVENT', subId, purifyEvent(event)]); } } catch (e: any) { if (e instanceof RelayError) { @@ -137,7 +138,7 @@ function connectStream(socket: WebSocket, ip: string | undefined) { relayEventsCounter.inc({ kind: event.kind.toString() }); try { // This will store it (if eligible) and run other side-effects. - await pipeline.handleEvent(event, AbortSignal.timeout(1000)); + await pipeline.handleEvent(purifyEvent(event), AbortSignal.timeout(1000)); send(['OK', event.id, true, '']); } catch (e) { if (e instanceof RelayError) { diff --git a/src/db/DittoTables.ts b/src/db/DittoTables.ts index 85336452..6046cf71 100644 --- a/src/db/DittoTables.ts +++ b/src/db/DittoTables.ts @@ -1,4 +1,4 @@ -import { Generated, Nullable } from 'kysely'; +import { Generated } from 'kysely'; import { NPostgresSchema } from '@nostrify/db'; @@ -13,7 +13,7 @@ export interface DittoTables extends NPostgresSchema { } type NostrEventsRow = NPostgresSchema['nostr_events'] & { - language: Nullable; + language: string | null; }; interface AuthorStatsRow { diff --git a/src/db/KyselyLogger.ts b/src/db/KyselyLogger.ts index 72167e21..514f44a4 100644 --- a/src/db/KyselyLogger.ts +++ b/src/db/KyselyLogger.ts @@ -9,12 +9,14 @@ export const KyselyLogger: Logger = (event) => { const { query, queryDurationMillis } = event; const { sql, parameters } = query; + const queryDurationSeconds = queryDurationMillis / 1000; + dbQueriesCounter.inc(); - dbQueryDurationHistogram.observe(queryDurationMillis); + dbQueryDurationHistogram.observe(queryDurationSeconds); console.debug( sql, JSON.stringify(parameters), - `\x1b[90m(${(queryDurationMillis / 1000).toFixed(2)}s)\x1b[0m`, + `\x1b[90m(${(queryDurationSeconds / 1000).toFixed(2)}s)\x1b[0m`, ); }; diff --git a/src/entities/MastodonAccount.ts b/src/entities/MastodonAccount.ts index b713d70c..99409c6a 100644 --- a/src/entities/MastodonAccount.ts +++ b/src/entities/MastodonAccount.ts @@ -54,7 +54,7 @@ export interface MastodonAccount { is_moderator: boolean; is_suggested: boolean; is_local: boolean; - settings_store: unknown; + settings_store?: Record; tags: string[]; }; nostr: { diff --git a/src/entities/MastodonTranslation.ts b/src/entities/MastodonTranslation.ts new file mode 100644 index 00000000..d59b9aad --- /dev/null +++ b/src/entities/MastodonTranslation.ts @@ -0,0 +1,17 @@ +import { LanguageCode } from 'iso-639-1'; + +/** https://docs.joinmastodon.org/entities/Translation/ */ +export interface MastodonTranslation { + /** HTML-encoded translated content of the status. */ + content: string; + /** The translated spoiler warning of the status. */ + spoiler_text: string; + /** The translated media descriptions of the status. */ + media_attachments: { id: string; description: string }[]; + /** The translated poll of the status. */ + poll: { id: string; options: { title: string }[] } | null; + //** The language of the source text, as auto-detected by the machine translation provider. */ + detected_source_language: LanguageCode; + /** The service that provided the machine translation. */ + provider: string; +} diff --git a/src/interfaces/DittoEvent.ts b/src/interfaces/DittoEvent.ts index dcaec6ae..cca7c0ca 100644 --- a/src/interfaces/DittoEvent.ts +++ b/src/interfaces/DittoEvent.ts @@ -1,4 +1,5 @@ import { NostrEvent } from '@nostrify/nostrify'; +import { LanguageCode } from 'iso-639-1'; /** Ditto internal stats for the event's author. */ export interface AuthorStats { @@ -43,4 +44,6 @@ export interface DittoEvent extends NostrEvent { zap_sender?: DittoEvent | string; zap_amount?: number; zap_message?: string; + /** Language of the event (kind 1s are more accurate). */ + language?: LanguageCode; } diff --git a/src/interfaces/DittoTranslator.ts b/src/interfaces/DittoTranslator.ts new file mode 100644 index 00000000..7e5e1d50 --- /dev/null +++ b/src/interfaces/DittoTranslator.ts @@ -0,0 +1,18 @@ +import type { LanguageCode } from 'iso-639-1'; + +/** DittoTranslator class, used for status translation. */ +export interface DittoTranslator { + /** Provider name, eg `DeepL.com` */ + provider: string; + /** Translate the 'content' into 'targetLanguage'. */ + translate( + /** Texts to translate. */ + texts: string[], + /** The language of the source texts. */ + sourceLanguage: LanguageCode | undefined, + /** The texts will be translated into this language. */ + targetLanguage: LanguageCode, + /** Custom options. */ + opts?: { signal?: AbortSignal }, + ): Promise<{ results: string[]; source_lang: LanguageCode }>; +} diff --git a/src/metrics.ts b/src/metrics.ts index 633d72f0..2cb3eb2d 100644 --- a/src/metrics.ts +++ b/src/metrics.ts @@ -12,6 +12,12 @@ export const httpResponsesCounter = new Counter({ labelNames: ['method', 'path', 'status'], }); +export const httpResponseDurationHistogram = new Histogram({ + name: 'ditto_http_response_duration_seconds', + help: 'Histogram of HTTP response times in seconds', + labelNames: ['method', 'path', 'status'], +}); + export const streamingConnectionsGauge = new Gauge({ name: 'ditto_streaming_connections', help: 'Number of active connections to the streaming API', @@ -91,7 +97,7 @@ export const dbAvailableConnectionsGauge = new Gauge({ }); export const dbQueryDurationHistogram = new Histogram({ - name: 'ditto_db_query_duration_ms', + name: 'ditto_db_query_duration_seconds', help: 'Duration of database queries', }); @@ -115,6 +121,11 @@ export const cachedLinkPreviewSizeGauge = new Gauge({ help: 'Number of link previews in cache', }); +export const cachedTranslationsSizeGauge = new Gauge({ + name: 'ditto_cached_translations_size', + help: 'Number of translated statuses in cache', +}); + export const internalSubscriptionsSizeGauge = new Gauge({ name: 'ditto_internal_subscriptions_size', help: "Number of active subscriptions to Ditto's internal relay", diff --git a/src/middleware/cacheMiddleware.ts b/src/middleware/cacheMiddleware.ts deleted file mode 100644 index baa4976d..00000000 --- a/src/middleware/cacheMiddleware.ts +++ /dev/null @@ -1,28 +0,0 @@ -import Debug from '@soapbox/stickynotes/debug'; -import { type MiddlewareHandler } from 'hono'; - -import ExpiringCache from '@/utils/expiring-cache.ts'; - -const debug = Debug('ditto:middleware:cache'); - -export const cacheMiddleware = (options: { - cacheName: string; - expires?: number; -}): MiddlewareHandler => { - return async (c, next) => { - const key = c.req.url.replace('http://', 'https://'); - const cache = new ExpiringCache(await caches.open(options.cacheName)); - const response = await cache.match(key); - if (!response) { - debug('Building cache for page', c.req.url); - await next(); - const response = c.res.clone(); - if (response.status < 500) { - await cache.putExpiring(key, response, options.expires ?? 0); - } - } else { - debug('Serving page from cache', c.req.url); - return response; - } - }; -}; diff --git a/src/middleware/metricsMiddleware.ts b/src/middleware/metricsMiddleware.ts index e8a30972..0b213b82 100644 --- a/src/middleware/metricsMiddleware.ts +++ b/src/middleware/metricsMiddleware.ts @@ -1,9 +1,14 @@ +import { ScopedPerformance } from '@esroyo/scoped-performance'; import { MiddlewareHandler } from '@hono/hono'; -import { httpRequestsCounter, httpResponsesCounter } from '@/metrics.ts'; +import { httpRequestsCounter, httpResponseDurationHistogram, httpResponsesCounter } from '@/metrics.ts'; /** Prometheus metrics middleware that tracks HTTP requests by methods and responses by status code. */ export const metricsMiddleware: MiddlewareHandler = async (c, next) => { + // Start a timer to measure the duration of the response. + using perf = new ScopedPerformance(); + perf.mark('start'); + // HTTP Request. const { method } = c.req; httpRequestsCounter.inc({ method }); @@ -17,4 +22,8 @@ export const metricsMiddleware: MiddlewareHandler = async (c, next) => { // Tries to find actual route names first before falling back on potential middleware handlers like `app.use('*')`. const path = c.req.matchedRoutes.find((r) => r.method !== 'ALL')?.path ?? c.req.routePath; httpResponsesCounter.inc({ method, status, path }); + + // Measure the duration of the response. + const { duration } = perf.measure('total', 'start'); + httpResponseDurationHistogram.observe({ method, status, path }, duration / 1000); }; diff --git a/src/middleware/translatorMiddleware.ts b/src/middleware/translatorMiddleware.ts new file mode 100644 index 00000000..f5a6baa2 --- /dev/null +++ b/src/middleware/translatorMiddleware.ts @@ -0,0 +1,28 @@ +import { AppMiddleware } from '@/app.ts'; +import { Conf } from '@/config.ts'; +import { fetchWorker } from '@/workers/fetch.ts'; +import { DeepLTranslator } from '@/translators/DeepLTranslator.ts'; +import { LibreTranslateTranslator } from '@/translators/LibreTranslateTranslator.ts'; + +/** Set the translator used for translating posts. */ +export const translatorMiddleware: AppMiddleware = async (c, next) => { + switch (Conf.translationProvider) { + case 'deepl': { + const { deeplApiKey: apiKey, deeplBaseUrl: baseUrl } = Conf; + if (apiKey) { + c.set('translator', new DeepLTranslator({ baseUrl, apiKey, fetch: fetchWorker })); + } + break; + } + + case 'libretranslate': { + const { libretranslateApiKey: apiKey, libretranslateBaseUrl: baseUrl } = Conf; + if (apiKey) { + c.set('translator', new LibreTranslateTranslator({ baseUrl, apiKey, fetch: fetchWorker })); + } + break; + } + } + + await next(); +}; diff --git a/src/pipeline.ts b/src/pipeline.ts index 12d377f8..29498417 100644 --- a/src/pipeline.ts +++ b/src/pipeline.ts @@ -1,8 +1,6 @@ import { NKinds, NostrEvent, NSchema as n } from '@nostrify/nostrify'; import { Stickynotes } from '@soapbox/stickynotes'; -import ISO6391 from 'iso-639-1'; import { Kysely, sql } from 'kysely'; -import lande from 'lande'; import { LRUCache } from 'lru-cache'; import { nip19 } from 'nostr-tools'; import { z } from 'zod'; @@ -19,6 +17,7 @@ import { Storages } from '@/storages.ts'; import { MastodonPush } from '@/types/MastodonPush.ts'; import { eventAge, parseNip05, Time } from '@/utils.ts'; import { getAmount } from '@/utils/bolt11.ts'; +import { detectLanguage } from '@/utils/language.ts'; import { nip05Cache } from '@/utils/nip05.ts'; import { purifyEvent } from '@/utils/purify.ts'; import { updateStats } from '@/utils/stats.ts'; @@ -205,23 +204,19 @@ async function parseMetadata(event: NostrEvent, signal: AbortSignal): Promise { - const [topResult] = lande(event.content); + if (event.kind !== 1) return; - if (topResult) { - const [iso6393, confidence] = topResult; - const locale = new Intl.Locale(iso6393); + const language = detectLanguage(event.content, 0.90); + if (!language) return; - if (confidence >= 0.95 && ISO6391.validate(locale.language)) { - const kysely = await Storages.kysely(); - try { - await kysely.updateTable('nostr_events') - .set('language', locale.language) - .where('id', '=', event.id) - .execute(); - } catch { - // do nothing - } - } + const kysely = await Storages.kysely(); + try { + await kysely.updateTable('nostr_events') + .set('language', language) + .where('id', '=', event.id) + .execute(); + } catch { + // do nothing } } diff --git a/src/schema.ts b/src/schema.ts index 5efa7769..a9dd56e3 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -1,4 +1,4 @@ -import ISO6391 from 'iso-639-1'; +import ISO6391, { LanguageCode } from 'iso-639-1'; import { z } from 'zod'; /** Validates individual items in an array, dropping any that aren't valid. */ @@ -41,7 +41,8 @@ const fileSchema = z.custom((value) => value instanceof File); const percentageSchema = z.coerce.number().int().gte(1).lte(100); -const languageSchema = z.string().transform((val, ctx) => { +const languageSchema = z.string().transform((val, ctx) => { + val = val.toLowerCase(); if (!ISO6391.validate(val)) { ctx.addIssue({ code: z.ZodIssueCode.custom, @@ -49,7 +50,19 @@ const languageSchema = z.string().transform((val, ctx) => { }); return z.NEVER; } - return val; + return val as LanguageCode; +}); + +const localeSchema = z.string().transform((val, ctx) => { + try { + return new Intl.Locale(val); + } catch { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: 'Invalid locale', + }); + return z.NEVER; + } }); export { @@ -59,6 +72,7 @@ export { filteredArray, hashtagSchema, languageSchema, + localeSchema, percentageSchema, safeUrlSchema, }; diff --git a/src/storages/EventsDB.test.ts b/src/storages/EventsDB.test.ts index b24032aa..e19fe775 100644 --- a/src/storages/EventsDB.test.ts +++ b/src/storages/EventsDB.test.ts @@ -7,7 +7,7 @@ import { Conf } from '@/config.ts'; import { createTestDB } from '@/test.ts'; Deno.test('count filters', async () => { - await using db = await createTestDB(); + await using db = await createTestDB({ pure: true }); const { store } = db; const event1 = await eventFixture('event-1'); @@ -18,7 +18,7 @@ Deno.test('count filters', async () => { }); Deno.test('insert and filter events', async () => { - await using db = await createTestDB(); + await using db = await createTestDB({ pure: true }); const { store } = db; const event1 = await eventFixture('event-1'); @@ -35,7 +35,7 @@ Deno.test('insert and filter events', async () => { }); Deno.test('query events with domain search filter', async () => { - await using db = await createTestDB(); + await using db = await createTestDB({ pure: true }); const { store, kysely } = db; const event1 = await eventFixture('event-1'); @@ -55,7 +55,7 @@ Deno.test('query events with domain search filter', async () => { }); Deno.test('query events with language search filter', async () => { - await using db = await createTestDB(); + await using db = await createTestDB({ pure: true }); const { store, kysely } = db; const en = genEvent({ kind: 1, content: 'hello world!' }); @@ -72,7 +72,7 @@ Deno.test('query events with language search filter', async () => { }); Deno.test('delete events', async () => { - await using db = await createTestDB(); + await using db = await createTestDB({ pure: true }); const { store } = db; const sk = generateSecretKey(); @@ -96,7 +96,7 @@ Deno.test('delete events', async () => { }); Deno.test("user cannot delete another user's event", async () => { - await using db = await createTestDB(); + await using db = await createTestDB({ pure: true }); const { store } = db; const event = genEvent({ kind: 1, content: 'hello world', created_at: 1 }); @@ -113,7 +113,7 @@ Deno.test("user cannot delete another user's event", async () => { }); Deno.test('admin can delete any event', async () => { - await using db = await createTestDB(); + await using db = await createTestDB({ pure: true }); const { store } = db; const sk = generateSecretKey(); @@ -137,7 +137,7 @@ Deno.test('admin can delete any event', async () => { }); Deno.test('throws a RelayError when inserting an event deleted by the admin', async () => { - await using db = await createTestDB(); + await using db = await createTestDB({ pure: true }); const { store } = db; const event = genEvent(); @@ -154,7 +154,7 @@ Deno.test('throws a RelayError when inserting an event deleted by the admin', as }); Deno.test('throws a RelayError when inserting an event deleted by a user', async () => { - await using db = await createTestDB(); + await using db = await createTestDB({ pure: true }); const { store } = db; const sk = generateSecretKey(); @@ -173,7 +173,7 @@ Deno.test('throws a RelayError when inserting an event deleted by a user', async }); Deno.test('inserting replaceable events', async () => { - await using db = await createTestDB(); + await using db = await createTestDB({ pure: true }); const { store } = db; const sk = generateSecretKey(); @@ -190,7 +190,7 @@ Deno.test('inserting replaceable events', async () => { }); Deno.test("throws a RelayError when querying an event with a large 'since'", async () => { - await using db = await createTestDB(); + await using db = await createTestDB({ pure: true }); const { store } = db; await assertRejects( @@ -201,7 +201,7 @@ Deno.test("throws a RelayError when querying an event with a large 'since'", asy }); Deno.test("throws a RelayError when querying an event with a large 'until'", async () => { - await using db = await createTestDB(); + await using db = await createTestDB({ pure: true }); const { store } = db; await assertRejects( @@ -212,7 +212,7 @@ Deno.test("throws a RelayError when querying an event with a large 'until'", asy }); Deno.test("throws a RelayError when querying an event with a large 'kind'", async () => { - await using db = await createTestDB(); + await using db = await createTestDB({ pure: true }); const { store } = db; await assertRejects( diff --git a/src/storages/EventsDB.ts b/src/storages/EventsDB.ts index 1bf3cd86..b303dad0 100644 --- a/src/storages/EventsDB.ts +++ b/src/storages/EventsDB.ts @@ -1,5 +1,6 @@ // deno-lint-ignore-file require-await +import { LanguageCode } from 'iso-639-1'; import { NPostgres, NPostgresSchema } from '@nostrify/db'; import { NIP50, NKinds, NostrEvent, NostrFilter, NSchema as n } from '@nostrify/nostrify'; import { Stickynotes } from '@soapbox/stickynotes'; @@ -12,6 +13,7 @@ import { RelayError } from '@/RelayError.ts'; import { isNostrId, isURL } from '@/utils.ts'; import { abortError } from '@/utils/abort.ts'; import { purifyEvent } from '@/utils/purify.ts'; +import { DittoEvent } from '@/interfaces/DittoEvent.ts'; /** Function to decide whether or not to index a tag. */ type TagCondition = ({ event, count, value }: { @@ -28,6 +30,8 @@ interface EventsDBOpts { pubkey: string; /** Timeout in milliseconds for database queries. */ timeout: number; + /** Whether the event returned should be a Nostr event or a Ditto event. Defaults to false. */ + pure?: boolean; } /** SQL database storage adapter for Nostr events. */ @@ -151,7 +155,7 @@ class EventsDB extends NPostgres { let query = super.getFilterQuery(trx, { ...filter, search: tokens.filter((t) => typeof t === 'string').join(' '), - }) as SelectQueryBuilder>; + }) as SelectQueryBuilder; const languages = new Set(); @@ -175,7 +179,7 @@ class EventsDB extends NPostgres { override async query( filters: NostrFilter[], opts: { signal?: AbortSignal; timeout?: number; limit?: number } = {}, - ): Promise { + ): Promise { filters = await this.expandFilters(filters); for (const filter of filters) { @@ -199,6 +203,29 @@ class EventsDB extends NPostgres { return super.query(filters, { ...opts, timeout: opts.timeout ?? this.opts.timeout }); } + /** Parse an event row from the database. */ + protected override parseEventRow(row: DittoTables['nostr_events']): DittoEvent { + const event: DittoEvent = { + id: row.id, + kind: row.kind, + pubkey: row.pubkey, + content: row.content, + created_at: Number(row.created_at), + tags: row.tags, + sig: row.sig, + }; + + if (this.opts.pure) { + return event; + } + + if (row.language) { + event.language = row.language as LanguageCode; + } + + return event; + } + /** Delete events based on filters from the database. */ override async remove(filters: NostrFilter[], opts: { signal?: AbortSignal; timeout?: number } = {}): Promise { this.console.debug('DELETE', JSON.stringify(filters)); diff --git a/src/test.ts b/src/test.ts index f4e720e1..3f2d1c38 100644 --- a/src/test.ts +++ b/src/test.ts @@ -1,3 +1,5 @@ +import ISO6391, { LanguageCode } from 'iso-639-1'; +import lande from 'lande'; import { NostrEvent } from '@nostrify/nostrify'; import { finalizeEvent, generateSecretKey } from 'nostr-tools'; @@ -33,7 +35,7 @@ export function genEvent(t: Partial = {}, sk: Uint8Array = generateS } /** Create a database for testing. It uses `TEST_DATABASE_URL`, or creates an in-memory database by default. */ -export async function createTestDB() { +export async function createTestDB(opts?: { pure?: boolean }) { const { testDatabaseUrl } = Conf; const { kysely } = DittoDB.create(testDatabaseUrl, { poolSize: 1 }); @@ -43,6 +45,7 @@ export async function createTestDB() { kysely, timeout: Conf.db.timeouts.default, pubkey: Conf.pubkey, + pure: opts?.pure ?? false, }); return { @@ -65,3 +68,15 @@ export async function createTestDB() { export function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } + +export function getLanguage(text: string): LanguageCode | undefined { + const [topResult] = lande(text); + if (topResult) { + const [iso6393] = topResult; + const locale = new Intl.Locale(iso6393); + if (ISO6391.validate(locale.language)) { + return locale.language as LanguageCode; + } + } + return; +} diff --git a/src/translators/DeepLTranslator.test.ts b/src/translators/DeepLTranslator.test.ts new file mode 100644 index 00000000..d78c0a0a --- /dev/null +++ b/src/translators/DeepLTranslator.test.ts @@ -0,0 +1,55 @@ +import { assertEquals } from '@std/assert'; + +import { Conf } from '@/config.ts'; +import { DeepLTranslator } from '@/translators/DeepLTranslator.ts'; +import { getLanguage } from '@/test.ts'; + +const { + deeplBaseUrl: baseUrl, + deeplApiKey: apiKey, + translationProvider, +} = Conf; + +const deepl = 'deepl'; + +Deno.test('DeepL translation with source language omitted', { + ignore: !(translationProvider === deepl && apiKey), +}, async () => { + const translator = new DeepLTranslator({ fetch: fetch, baseUrl, apiKey: apiKey! }); + + const data = await translator.translate( + [ + 'Bom dia amigos', + 'Meu nome é Patrick', + 'Eu irei morar na America, eu prometo. Mas antes, eu devo mencionar que o lande está interpretando este texto como italiano, que estranho.', + ], + undefined, + 'en', + ); + + assertEquals(data.source_lang, 'pt'); + assertEquals(getLanguage(data.results[0]), 'en'); + assertEquals(getLanguage(data.results[1]), 'en'); + assertEquals(getLanguage(data.results[2]), 'en'); +}); + +Deno.test('DeepL translation with source language set', { + ignore: !(translationProvider === deepl && apiKey), +}, async () => { + const translator = new DeepLTranslator({ fetch: fetch, baseUrl, apiKey: apiKey as string }); + + const data = await translator.translate( + [ + 'Bom dia amigos', + 'Meu nome é Patrick', + 'Eu irei morar na America, eu prometo. Mas antes, eu devo mencionar que o lande está interpretando este texto como italiano, que estranho.', + ], + 'pt', + 'en', + ); + + assertEquals(data.source_lang, 'pt'); + assertEquals(getLanguage(data.results[0]), 'en'); + assertEquals(getLanguage(data.results[1]), 'en'); + assertEquals(getLanguage(data.results[2]), 'en'); +}); diff --git a/src/translators/DeepLTranslator.ts b/src/translators/DeepLTranslator.ts new file mode 100644 index 00000000..b856d1ec --- /dev/null +++ b/src/translators/DeepLTranslator.ts @@ -0,0 +1,94 @@ +import { LanguageCode } from 'iso-639-1'; +import { z } from 'zod'; + +import { DittoTranslator } from '@/interfaces/DittoTranslator.ts'; +import { languageSchema } from '@/schema.ts'; + +interface DeepLTranslatorOpts { + /** DeepL base URL to use. Default: 'https://api.deepl.com' */ + baseUrl?: string; + /** DeepL API key. */ + apiKey: string; + /** Custom fetch implementation. */ + fetch?: typeof fetch; +} + +export class DeepLTranslator implements DittoTranslator { + private readonly baseUrl: string; + private readonly apiKey: string; + private readonly fetch: typeof fetch; + + readonly provider = 'DeepL.com'; + + constructor(opts: DeepLTranslatorOpts) { + this.baseUrl = opts.baseUrl ?? 'https://api.deepl.com'; + this.fetch = opts.fetch ?? globalThis.fetch; + this.apiKey = opts.apiKey; + } + + async translate( + texts: string[], + source: LanguageCode | undefined, + dest: LanguageCode, + opts?: { signal?: AbortSignal }, + ) { + const { translations } = await this.translateMany(texts, source, dest, opts); + + return { + results: translations.map((value) => value.text), + source_lang: translations[0]?.detected_source_language as LanguageCode, + }; + } + + /** DeepL translate request. */ + private async translateMany( + texts: string[], + source: LanguageCode | undefined, + targetLanguage: LanguageCode, + opts?: { signal?: AbortSignal }, + ) { + const body: any = { + text: texts, + target_lang: targetLanguage.toUpperCase(), + tag_handling: 'html', + split_sentences: '1', + }; + if (source) { + body.source_lang = source.toUpperCase(); + } + + const url = new URL('/v2/translate', this.baseUrl); + + const request = new Request(url, { + method: 'POST', + body: JSON.stringify(body), + headers: { + 'Authorization': `DeepL-Auth-Key ${this.apiKey}`, + 'Content-Type': 'application/json', + }, + signal: opts?.signal, + }); + + const response = await this.fetch(request); + const json = await response.json(); + + if (!response.ok) { + throw new Error(json['message']); + } + + return DeepLTranslator.schema().parse(json); + } + + /** DeepL response schema. + * https://developers.deepl.com/docs/api-reference/translate/openapi-spec-for-text-translation */ + private static schema() { + return z.object({ + translations: z.array( + z.object({ + detected_source_language: languageSchema, + text: z.string(), + }), + ), + }); + } +} diff --git a/src/translators/LibreTranslateTranslator.test.ts b/src/translators/LibreTranslateTranslator.test.ts new file mode 100644 index 00000000..edda3039 --- /dev/null +++ b/src/translators/LibreTranslateTranslator.test.ts @@ -0,0 +1,55 @@ +import { assertEquals } from '@std/assert'; + +import { Conf } from '@/config.ts'; +import { LibreTranslateTranslator } from '@/translators/LibreTranslateTranslator.ts'; +import { getLanguage } from '@/test.ts'; + +const { + libretranslateBaseUrl: baseUrl, + libretranslateApiKey: apiKey, + translationProvider, +} = Conf; + +const libretranslate = 'libretranslate'; + +Deno.test('LibreTranslate translation with source language omitted', { + ignore: !(translationProvider === libretranslate && apiKey), +}, async () => { + const translator = new LibreTranslateTranslator({ fetch: fetch, baseUrl, apiKey: apiKey! }); + + const data = await translator.translate( + [ + 'Bom dia amigos', + 'Meu nome é Patrick, um nome belo ou feio? A questão é mais profunda do que parece.', + 'A respiração é mais importante do que comer e tomar agua.', + ], + undefined, + 'ca', + ); + + assertEquals(data.source_lang, 'pt'); + assertEquals(getLanguage(data.results[0]), 'ca'); + assertEquals(getLanguage(data.results[1]), 'ca'); + assertEquals(getLanguage(data.results[2]), 'ca'); +}); + +Deno.test('LibreTranslate translation with source language set', { + ignore: !(translationProvider === libretranslate && apiKey), +}, async () => { + const translator = new LibreTranslateTranslator({ fetch: fetch, baseUrl, apiKey: apiKey! }); + + const data = await translator.translate( + [ + 'Bom dia amigos', + 'Meu nome é Patrick, um nome belo ou feio? A questão é mais profunda do que parece.', + 'A respiração é mais importante do que comer e tomar agua.', + ], + 'pt', + 'ca', + ); + + assertEquals(data.source_lang, 'pt'); + assertEquals(getLanguage(data.results[0]), 'ca'); + assertEquals(getLanguage(data.results[1]), 'ca'); + assertEquals(getLanguage(data.results[2]), 'ca'); +}); diff --git a/src/translators/LibreTranslateTranslator.ts b/src/translators/LibreTranslateTranslator.ts new file mode 100644 index 00000000..ef7fb1f8 --- /dev/null +++ b/src/translators/LibreTranslateTranslator.ts @@ -0,0 +1,92 @@ +import { LanguageCode } from 'iso-639-1'; +import { z } from 'zod'; + +import { DittoTranslator } from '@/interfaces/DittoTranslator.ts'; +import { languageSchema } from '@/schema.ts'; + +interface LibreTranslateTranslatorOpts { + /** Libretranslate endpoint to use. Default: 'https://libretranslate.com' */ + baseUrl?: string; + /** Libretranslate API key. */ + apiKey: string; + /** Custom fetch implementation. */ + fetch?: typeof fetch; +} + +export class LibreTranslateTranslator implements DittoTranslator { + private readonly baseUrl: string; + private readonly apiKey: string; + private readonly fetch: typeof fetch; + + readonly provider = 'libretranslate.com'; + + constructor(opts: LibreTranslateTranslatorOpts) { + this.baseUrl = opts.baseUrl ?? 'https://libretranslate.com'; + this.fetch = opts.fetch ?? globalThis.fetch; + this.apiKey = opts.apiKey; + } + + async translate( + texts: string[], + source: LanguageCode | undefined, + dest: LanguageCode, + opts?: { signal?: AbortSignal }, + ) { + const translations = await Promise.all( + texts.map((text) => this.translateOne(text, source, dest, 'html', { signal: opts?.signal })), + ); + + return { + results: translations.map((value) => value.translatedText), + source_lang: (translations[0]?.detectedLanguage?.language ?? source) as LanguageCode, // cast is ok + }; + } + + private async translateOne( + q: string, + sourceLanguage: string | undefined, + targetLanguage: string, + format: 'html' | 'text', + opts?: { signal?: AbortSignal }, + ) { + const body = { + q, + source: sourceLanguage?.toLowerCase() ?? 'auto', + target: targetLanguage.toLowerCase(), + format, + api_key: this.apiKey, + }; + + const url = new URL('/translate', this.baseUrl); + + const request = new Request(url, { + method: 'POST', + body: JSON.stringify(body), + headers: { + 'Content-Type': 'application/json', + }, + signal: opts?.signal, + }); + + const response = await this.fetch(request); + const json = await response.json(); + if (!response.ok) { + throw new Error(json['error']); + } + const data = LibreTranslateTranslator.schema().parse(json); + + return data; + } + + /** Libretranslate response schema. + * https://libretranslate.com/docs/#/translate/post_translate */ + private static schema() { + return z.object({ + translatedText: z.string(), + /** This field is only available if the 'source' is set to 'auto' */ + detectedLanguage: z.object({ + language: languageSchema, + }).optional(), + }); + } +} diff --git a/src/utils/api.ts b/src/utils/api.ts index c6d3c6b6..e7766979 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -1,4 +1,4 @@ -import { Context } from '@hono/hono'; +import { type Context } from '@hono/hono'; import { HTTPException } from '@hono/hono/http-exception'; import { NostrEvent, NostrFilter } from '@nostrify/nostrify'; import Debug from '@soapbox/stickynotes/debug'; diff --git a/src/utils/expiring-cache.test.ts b/src/utils/expiring-cache.test.ts deleted file mode 100644 index 8c6d7b18..00000000 --- a/src/utils/expiring-cache.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { assert } from '@std/assert'; - -import ExpiringCache from './expiring-cache.ts'; - -Deno.test('ExpiringCache', async () => { - const cache = new ExpiringCache(await caches.open('test')); - - await cache.putExpiring('http://mostr.local/1', new Response('hello world'), 300); - await cache.putExpiring('http://mostr.local/2', new Response('hello world'), -1); - - // const resp1 = await cache.match('http://mostr.local/1'); - const resp2 = await cache.match('http://mostr.local/2'); - - // assert(resp1!.headers.get('Expires')); - assert(!resp2); - - // await resp1!.text(); -}); diff --git a/src/utils/expiring-cache.ts b/src/utils/expiring-cache.ts deleted file mode 100644 index ebb5d2ee..00000000 --- a/src/utils/expiring-cache.ts +++ /dev/null @@ -1,68 +0,0 @@ -class ExpiringCache implements Cache { - #cache: Cache; - - constructor(cache: Cache) { - this.#cache = cache; - } - - add(request: RequestInfo | URL): Promise { - return this.#cache.add(request); - } - - addAll(requests: RequestInfo[]): Promise { - return this.#cache.addAll(requests); - } - - keys(request?: RequestInfo | URL | undefined, options?: CacheQueryOptions | undefined): Promise { - return this.#cache.keys(request, options); - } - - matchAll( - request?: RequestInfo | URL | undefined, - options?: CacheQueryOptions | undefined, - ): Promise { - return this.#cache.matchAll(request, options); - } - - put(request: RequestInfo | URL, response: Response): Promise { - return this.#cache.put(request, response); - } - - putExpiring(request: RequestInfo | URL, response: Response, expiresIn: number): Promise { - const expires = Date.now() + expiresIn; - - const clone = new Response(response.body, { - status: response.status, - headers: { - expires: new Date(expires).toUTCString(), - ...Object.fromEntries(response.headers.entries()), - }, - }); - - return this.#cache.put(request, clone); - } - - async match(request: RequestInfo | URL, options?: CacheQueryOptions | undefined): Promise { - const response = await this.#cache.match(request, options); - const expires = response?.headers.get('Expires'); - - if (response && expires) { - if (new Date(expires).getTime() > Date.now()) { - return response; - } else { - await Promise.all([ - this.delete(request), - response.text(), // Prevent memory leaks - ]); - } - } else if (response) { - return response; - } - } - - delete(request: RequestInfo | URL, options?: CacheQueryOptions | undefined): Promise { - return this.#cache.delete(request, options); - } -} - -export default ExpiringCache; diff --git a/src/utils/language.test.ts b/src/utils/language.test.ts new file mode 100644 index 00000000..255f6b58 --- /dev/null +++ b/src/utils/language.test.ts @@ -0,0 +1,28 @@ +import { detectLanguage } from '@/utils/language.ts'; +import { assertEquals } from '@std/assert'; + +Deno.test('Detect English language', () => { + assertEquals(detectLanguage(``, 0.90), undefined); + assertEquals(detectLanguage(`Good morning my fellow friends`, 0.90), 'en'); + assertEquals( + detectLanguage( + `Would you listen to Michael Jackson's songs?\n\nnostr:nevent1qvzqqqqqqypzqprpljlvcnpnw3pejvkkhrc3y6wvmd7vjuad0fg2ud3dky66gaxaqyvhwumn8ghj7cm0vfexzen4d4sjucm0d5hhyetvv9usqg8htx8xcjq7ffrzxu7nrhlr8vljcv6gpmet0auy87mpj6djxk4myqha02kp`, + 0.90, + ), + 'en', + ); + assertEquals( + detectLanguage( + `https://youtu.be/FxppefYTA2I?si=grgEpbEhFu_-3V_uhttps://youtu.be/FxppefYTA2I?si=grgEpbEhFu_-3V_uhttps://youtu.be/FxppefYTA2I?si=grgEpbEhFu_-3V_uhttps://youtu.be/FxppefYTA2I?si=grgEpbEhFu_-3V_uWould you listen to Michael Jackson's songs?\n\nnostr:nevent1qvzqqqqqqypzqprpljlvcnpnw3pejvkkhrc3y6wvmd7vjuad0fg2ud3dky66gaxaqyvhwumn8ghj7cm0vfexzen4d4sjucm0d5hhyetvv9usqg8htx8xcjq7ffrzxu7nrhlr8vljcv6gpmet0auy87mpj6djxk4myqha02kp`, + 0.90, + ), + 'en', + ); + assertEquals( + detectLanguage( + `https://youtu.be/FxppefYTA2I?si=grgEpbEhFu_-3V_u 😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎😂💯♡⌨︎ https://youtu.be/FxppefYTA2I?si=grgEpbEhFu_-3V_uhttps://youtu.be/FxppefYTA2I?si=grgEpbEhFu_-3V_uhttps://youtu.be/FxppefYTA2I?si=grgEpbEhFu_-3V_u Would you listen to Michael Jackson's songs?\n\nnostr:nevent1qvzqqqqqqypzqprpljlvcnpnw3pejvkkhrc3y6wvmd7vjuad0fg2ud3dky66gaxaqyvhwumn8ghj7cm0vfexzen4d4sjucm0d5hhyetvv9usqg8htx8xcjq7ffrzxu7nrhlr8vljcv6gpmet0auy87mpj6djxk4myqha02kp`, + 0.90, + ), + 'en', + ); +}); diff --git a/src/utils/language.ts b/src/utils/language.ts new file mode 100644 index 00000000..8af8ddf9 --- /dev/null +++ b/src/utils/language.ts @@ -0,0 +1,34 @@ +import ISO6391, { type LanguageCode } from 'iso-639-1'; +import lande from 'lande'; +import linkify from 'linkifyjs'; + +linkify.registerCustomProtocol('nostr', true); + +/** Returns the detected language if the confidence is greater or equal than 'minConfidence' + * 'minConfidence' must be a number between 0 and 1, such as 0.95 + */ +export function detectLanguage(text: string, minConfidence: number): LanguageCode | undefined { + // It's better to remove the emojis first + const sanitizedText = linkify.tokenize( + text + .replaceAll(/\p{Extended_Pictographic}/gu, '') + .replaceAll(/[\s\uFEFF\u00A0\u200B-\u200D\u{0FE0E}]+/gu, ' '), + ).reduce((acc, { t, v }) => t === 'text' ? acc + v : acc, '').trim(); + + if (sanitizedText.length < 10) { // heuristics + return; + } + + const [topResult] = lande( + sanitizedText, + ); + if (topResult) { + const [iso6393, confidence] = topResult; + const locale = new Intl.Locale(iso6393); + + if (confidence >= minConfidence && ISO6391.validate(locale.language)) { + return locale.language as LanguageCode; + } + } + return; +} diff --git a/src/views/mastodon/accounts.ts b/src/views/mastodon/accounts.ts index 1e10c4ff..626203b8 100644 --- a/src/views/mastodon/accounts.ts +++ b/src/views/mastodon/accounts.ts @@ -12,16 +12,18 @@ import { faviconCache } from '@/utils/favicon.ts'; import { nostrDate, nostrNow } from '@/utils.ts'; import { renderEmojis } from '@/views/mastodon/emojis.ts'; -interface ToAccountOpts { - withSource?: boolean; -} +type ToAccountOpts = { + withSource: true; + settingsStore: Record | undefined; +} | { + withSource?: false; +}; async function renderAccount( event: Omit, opts: ToAccountOpts = {}, signal = AbortSignal.timeout(3000), ): Promise { - const { withSource = false } = opts; const { pubkey } = event; const names = getTagSet(event.user?.tags ?? [], 'n'); @@ -76,7 +78,7 @@ async function renderAccount( locked: false, note: about ? escape(about) : '', roles: [], - source: withSource + source: opts.withSource ? { fields: [], language: '', @@ -88,7 +90,7 @@ async function renderAccount( nip05, }, ditto: { - captcha_solved: false, + captcha_solved: names.has('captcha_solved'), }, } : undefined, @@ -107,7 +109,7 @@ async function renderAccount( is_moderator: names.has('admin') || names.has('moderator'), is_suggested: names.has('suggested'), is_local: parsed05?.domain === Conf.url.host, - settings_store: undefined as unknown, + settings_store: opts.withSource ? opts.settingsStore : undefined, tags: [...getTagSet(event.user?.tags ?? [], 't')], favicon: favicon?.toString(), }, diff --git a/src/views/mastodon/statuses.ts b/src/views/mastodon/statuses.ts index e21c9e1c..48d8e099 100644 --- a/src/views/mastodon/statuses.ts +++ b/src/views/mastodon/statuses.ts @@ -113,7 +113,7 @@ async function renderStatus(event: DittoEvent, opts: RenderStatusOpts): Promise< sensitive: !!cw, spoiler_text: (cw ? cw[1] : subject?.[1]) || '', visibility: 'public', - language: event.tags.find((tag) => tag[0] === 'l' && tag[2] === 'ISO-639-1')?.[1] || null, + language: event.language ?? null, replies_count: event.event_stats?.replies_count ?? 0, reblogs_count: event.event_stats?.reposts_count ?? 0, favourites_count: event.event_stats?.reactions['+'] ?? 0, diff --git a/src/workers/fetch.ts b/src/workers/fetch.ts index 4fbc57bb..bb5588ed 100644 --- a/src/workers/fetch.ts +++ b/src/workers/fetch.ts @@ -5,7 +5,7 @@ import './handlers/abortsignal.ts'; import { fetchResponsesCounter } from '@/metrics.ts'; -const worker = new Worker(new URL('./fetch.worker.ts', import.meta.url), { type: 'module' }); +const worker = new Worker(new URL('./fetch.worker.ts', import.meta.url), { type: 'module', name: 'fetchWorker' }); const client = Comlink.wrap(worker); // Wait for the worker to be ready before we start using it. diff --git a/src/workers/policy.ts b/src/workers/policy.ts index debfe242..a396468f 100644 --- a/src/workers/policy.ts +++ b/src/workers/policy.ts @@ -20,14 +20,17 @@ class PolicyWorker implements NPolicy { new URL('./policy.worker.ts', import.meta.url), { type: 'module', - deno: { - permissions: { - read: [Conf.denoDir, Conf.policy, Conf.dataDir], - write: [Conf.dataDir], - net: 'inherit', - env: false, - }, - }, + name: 'PolicyWorker', + // FIXME: Disabled until Deno 2.0 adds support for `import` permission here. + // https://github.com/denoland/deno/issues/26074 + // deno: { + // permissions: { + // read: [Conf.denoDir, Conf.policy, Conf.dataDir], + // write: [Conf.dataDir], + // net: 'inherit', + // env: false, + // }, + // }, }, ), ); diff --git a/src/workers/policy.worker.ts b/src/workers/policy.worker.ts index 86cea87c..c5b4129d 100644 --- a/src/workers/policy.worker.ts +++ b/src/workers/policy.worker.ts @@ -37,7 +37,7 @@ export class CustomPolicy implements NPolicy { const store = new EventsDB({ kysely, pubkey, - timeout: 1_000, + timeout: 5_000, }); this.policy = new Policy({ store, pubkey }); diff --git a/src/workers/verify.ts b/src/workers/verify.ts index 15ad783a..c0a2fea3 100644 --- a/src/workers/verify.ts +++ b/src/workers/verify.ts @@ -4,7 +4,7 @@ import * as Comlink from 'comlink'; import type { VerifyWorker } from './verify.worker.ts'; const worker = Comlink.wrap( - new Worker(new URL('./verify.worker.ts', import.meta.url), { type: 'module' }), + new Worker(new URL('./verify.worker.ts', import.meta.url), { type: 'module', name: 'verifyEventWorker' }), ); function verifyEventWorker(event: NostrEvent): Promise {