mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29:46 +00:00
Merge branch 'main' into search-nip05-enhance-and-postgres-support-testing
This commit is contained in:
commit
cf4ee051db
24 changed files with 568 additions and 79 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
image: denoland/deno:1.44.2
|
image: denoland/deno:1.45.2
|
||||||
|
|
||||||
default:
|
default:
|
||||||
interruptible: true
|
interruptible: true
|
||||||
|
|
@ -20,7 +20,10 @@ check:
|
||||||
|
|
||||||
test:
|
test:
|
||||||
stage: test
|
stage: test
|
||||||
script: deno task test
|
script:
|
||||||
|
- deno task test --coverage=cov_profile
|
||||||
|
- deno coverage cov_profile
|
||||||
|
coverage: /All files[^\|]*\|[^\|]*\s+([\d\.]+)/
|
||||||
variables:
|
variables:
|
||||||
DITTO_NSEC: nsec1zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs4rm7hz
|
DITTO_NSEC: nsec1zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs4rm7hz
|
||||||
artifacts:
|
artifacts:
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
deno 1.44.2
|
deno 1.45.2
|
||||||
16
CHANGELOG.md
16
CHANGELOG.md
|
|
@ -7,9 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.1.0] - 2024-07-15
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Prometheus support (`/metrics` endpoint).
|
||||||
|
- Sort zaps by amount; add pagination.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Added IP rate-limiting of HTTP requests and WebSocket messages.
|
||||||
|
- Added database query timeouts.
|
||||||
|
- Fixed nos2x compatibility.
|
||||||
|
|
||||||
## [1.0.0] - 2024-06-14
|
## [1.0.0] - 2024-06-14
|
||||||
|
|
||||||
- Initial release
|
- Initial release
|
||||||
|
|
||||||
[unreleased]: https://gitlab.com/soapbox-pub/ditto/-/compare/v1.0.0...HEAD
|
[unreleased]: https://gitlab.com/soapbox-pub/ditto/-/compare/v1.1.0...HEAD
|
||||||
|
[1.1.0]: https://gitlab.com/soapbox-pub/ditto/-/compare/v1.0.0...v1.1.0
|
||||||
[1.0.0]: https://gitlab.com/soapbox-pub/ditto/-/tags/v1.0.0
|
[1.0.0]: https://gitlab.com/soapbox-pub/ditto/-/tags/v1.0.0
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://deno.land/x/deno@v1.41.0/cli/schemas/config-file.v1.json",
|
"$schema": "https://deno.land/x/deno@v1.41.0/cli/schemas/config-file.v1.json",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"start": "deno run -A src/server.ts",
|
"start": "deno run -A src/server.ts",
|
||||||
"dev": "deno run -A --watch src/server.ts",
|
"dev": "deno run -A --watch src/server.ts",
|
||||||
|
|
@ -51,7 +51,8 @@
|
||||||
"iso-639-1": "npm:iso-639-1@2.1.15",
|
"iso-639-1": "npm:iso-639-1@2.1.15",
|
||||||
"isomorphic-dompurify": "npm:isomorphic-dompurify@^2.11.0",
|
"isomorphic-dompurify": "npm:isomorphic-dompurify@^2.11.0",
|
||||||
"kysely": "npm:kysely@^0.27.3",
|
"kysely": "npm:kysely@^0.27.3",
|
||||||
"kysely_deno_postgres": "https://gitlab.com/soapbox-pub/kysely-deno-postgres/-/raw/c6869b9e12d74af78a846ad503d84493f5db9df4/mod.ts",
|
"postgres": "https://raw.githubusercontent.com/xyzshantaram/postgres.js/17469a9e5f025d112206c583a29275e93dfc1431/deno/mod.js",
|
||||||
|
"kysely-postgres-js": "npm:kysely-postgres-js@2.0.0",
|
||||||
"light-bolt11-decoder": "npm:light-bolt11-decoder",
|
"light-bolt11-decoder": "npm:light-bolt11-decoder",
|
||||||
"linkify-plugin-hashtag": "npm:linkify-plugin-hashtag@^4.1.1",
|
"linkify-plugin-hashtag": "npm:linkify-plugin-hashtag@^4.1.1",
|
||||||
"linkify-string": "npm:linkify-string@^4.1.1",
|
"linkify-string": "npm:linkify-string@^4.1.1",
|
||||||
|
|
@ -60,7 +61,6 @@
|
||||||
"nostr-relaypool": "npm:nostr-relaypool2@0.6.34",
|
"nostr-relaypool": "npm:nostr-relaypool2@0.6.34",
|
||||||
"nostr-tools": "npm:nostr-tools@2.5.1",
|
"nostr-tools": "npm:nostr-tools@2.5.1",
|
||||||
"nostr-wasm": "npm:nostr-wasm@^0.1.0",
|
"nostr-wasm": "npm:nostr-wasm@^0.1.0",
|
||||||
"postgres": "https://deno.land/x/postgres@v0.19.0/mod.ts",
|
|
||||||
"prom-client": "npm:prom-client@^15.1.2",
|
"prom-client": "npm:prom-client@^15.1.2",
|
||||||
"question-deno": "https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/mod.ts",
|
"question-deno": "https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/mod.ts",
|
||||||
"tldts": "npm:tldts@^6.0.14",
|
"tldts": "npm:tldts@^6.0.14",
|
||||||
|
|
|
||||||
309
deno.lock
generated
309
deno.lock
generated
|
|
@ -47,6 +47,7 @@
|
||||||
"npm:hono-rate-limiter@^0.3.0": "npm:hono-rate-limiter@0.3.0_hono@4.2.5",
|
"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: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: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.3": "npm:kysely@0.27.3",
|
"npm:kysely@0.27.3": "npm:kysely@0.27.3",
|
||||||
"npm:kysely@^0.27.2": "npm:kysely@0.27.3",
|
"npm:kysely@^0.27.2": "npm:kysely@0.27.3",
|
||||||
"npm:kysely@^0.27.3": "npm:kysely@0.27.3",
|
"npm:kysely@^0.27.3": "npm:kysely@0.27.3",
|
||||||
|
|
@ -62,6 +63,7 @@
|
||||||
"npm:nostr-tools@^2.5.0": "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-tools@^2.7.0": "npm:nostr-tools@2.7.0",
|
||||||
"npm:nostr-wasm@^0.1.0": "npm:nostr-wasm@0.1.0",
|
"npm:nostr-wasm@^0.1.0": "npm:nostr-wasm@0.1.0",
|
||||||
|
"npm:postgres@3.4.4": "npm:postgres@3.4.4",
|
||||||
"npm:prom-client@^15.1.2": "npm:prom-client@15.1.2",
|
"npm:prom-client@^15.1.2": "npm:prom-client@15.1.2",
|
||||||
"npm:tldts@^6.0.14": "npm:tldts@6.1.18",
|
"npm:tldts@^6.0.14": "npm:tldts@6.1.18",
|
||||||
"npm:type-fest@^4.3.0": "npm:type-fest@4.18.2",
|
"npm:type-fest@^4.3.0": "npm:type-fest@4.18.2",
|
||||||
|
|
@ -139,6 +141,7 @@
|
||||||
"@nostrify/nostrify@0.25.0": {
|
"@nostrify/nostrify@0.25.0": {
|
||||||
"integrity": "98f26f44e95ac87fc91b3f3809d38432e1a7f6aebf10380b2554b6f9526313c6",
|
"integrity": "98f26f44e95ac87fc91b3f3809d38432e1a7f6aebf10380b2554b6f9526313c6",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
|
"jsr:@std/crypto@^0.224.0",
|
||||||
"jsr:@std/encoding@^0.224.1",
|
"jsr:@std/encoding@^0.224.1",
|
||||||
"npm:@scure/base@^1.1.6",
|
"npm:@scure/base@^1.1.6",
|
||||||
"npm:@scure/bip32@^1.4.0",
|
"npm:@scure/bip32@^1.4.0",
|
||||||
|
|
@ -664,6 +667,13 @@
|
||||||
"xml-name-validator": "xml-name-validator@5.0.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": {
|
"kysely@0.27.3": {
|
||||||
"integrity": "sha512-lG03Ru+XyOJFsjH3OMY6R/9U38IjDPfnOfDgO3ynhbDr+Dz8fak+X6L62vqu3iybQnj+lG84OttBuU9KY3L9kA==",
|
"integrity": "sha512-lG03Ru+XyOJFsjH3OMY6R/9U38IjDPfnOfDgO3ynhbDr+Dz8fak+X6L62vqu3iybQnj+lG84OttBuU9KY3L9kA==",
|
||||||
"dependencies": {}
|
"dependencies": {}
|
||||||
|
|
@ -868,6 +878,10 @@
|
||||||
"integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
|
"integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
|
||||||
"dependencies": {}
|
"dependencies": {}
|
||||||
},
|
},
|
||||||
|
"postgres@3.4.4": {
|
||||||
|
"integrity": "sha512-IbyN+9KslkqcXa8AO9fxpk97PA4pzewvpi2B3Dwy9u4zpV32QicaEdgmF3eSQUzdRk7ttDHQejNgAEr4XoeH4A==",
|
||||||
|
"dependencies": {}
|
||||||
|
},
|
||||||
"prom-client@15.1.2": {
|
"prom-client@15.1.2": {
|
||||||
"integrity": "sha512-on3h1iXb04QFLLThrmVYg1SChBQ9N1c+nKAjebBjokBqipddH3uxmOUcEkTnzmJ8Jh/5TSUnUqS40i2QB2dJHQ==",
|
"integrity": "sha512-on3h1iXb04QFLLThrmVYg1SChBQ9N1c+nKAjebBjokBqipddH3uxmOUcEkTnzmJ8Jh/5TSUnUqS40i2QB2dJHQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -1126,7 +1140,277 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"redirects": {
|
||||||
|
"https://github.com/xyzshantaram/postgres.js/raw/master/deno/mod.js": "https://raw.githubusercontent.com/xyzshantaram/postgres.js/master/deno/mod.js"
|
||||||
|
},
|
||||||
"remote": {
|
"remote": {
|
||||||
|
"https://deno.land/std@0.132.0/_deno_unstable.ts": "23a1a36928f1b6d3b0170aaa67de09af12aa998525f608ff7331b9fb364cbde6",
|
||||||
|
"https://deno.land/std@0.132.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74",
|
||||||
|
"https://deno.land/std@0.132.0/_util/os.ts": "49b92edea1e82ba295ec946de8ffd956ed123e2948d9bd1d3e901b04e4307617",
|
||||||
|
"https://deno.land/std@0.132.0/_wasm_crypto/crypto.mjs": "3b383eb715e8bfe61b4450ef0644b2653429c88d494807c86c5235979f62e56b",
|
||||||
|
"https://deno.land/std@0.132.0/_wasm_crypto/crypto.wasm.mjs": "0ad9ecc0d03ca8a083d9109db22e7507f019f63cf55b82ea618ab58855617577",
|
||||||
|
"https://deno.land/std@0.132.0/_wasm_crypto/mod.ts": "30a93c8b6b6c5b269e96a3e95d2c045d86a496814a8737443b77cad941d6a0b5",
|
||||||
|
"https://deno.land/std@0.132.0/async/abortable.ts": "87aa7230be8360c24ad437212311c9e8d4328854baec27b4c7abb26e85515c06",
|
||||||
|
"https://deno.land/std@0.132.0/async/deadline.ts": "48ac998d7564969f3e6ec6b6f9bf0217ebd00239b1b2292feba61272d5dd58d0",
|
||||||
|
"https://deno.land/std@0.132.0/async/debounce.ts": "564273ef242bcfcda19a439132f940db8694173abffc159ea34f07d18fc42620",
|
||||||
|
"https://deno.land/std@0.132.0/async/deferred.ts": "bc18e28108252c9f67dfca2bbc4587c3cbf3aeb6e155f8c864ca8ecff992b98a",
|
||||||
|
"https://deno.land/std@0.132.0/async/delay.ts": "cbbdf1c87d1aed8edc7bae13592fb3e27e3106e0748f089c263390d4f49e5f6c",
|
||||||
|
"https://deno.land/std@0.132.0/async/mod.ts": "2240c6841157738414331f47dee09bb8c0482c5b1980b6e3234dd03515c8132f",
|
||||||
|
"https://deno.land/std@0.132.0/async/mux_async_iterator.ts": "f4d1d259b0c694d381770ddaaa4b799a94843eba80c17f4a2ec2949168e52d1e",
|
||||||
|
"https://deno.land/std@0.132.0/async/pool.ts": "97b0dd27c69544e374df857a40902e74e39532f226005543eabacb551e277082",
|
||||||
|
"https://deno.land/std@0.132.0/async/tee.ts": "1341feb1f5b1a96f8628d0f8fc07d8c43d3813423f18a63bf1b4785568d21b1f",
|
||||||
|
"https://deno.land/std@0.132.0/bytes/bytes_list.ts": "67eb118e0b7891d2f389dad4add35856f4ad5faab46318ff99653456c23b025d",
|
||||||
|
"https://deno.land/std@0.132.0/bytes/equals.ts": "fc16dff2090cced02497f16483de123dfa91e591029f985029193dfaa9d894c9",
|
||||||
|
"https://deno.land/std@0.132.0/bytes/mod.ts": "d3b455c0dbd4804644159d1e25946ade5ee385d2359894de49e2c6101b18b7a9",
|
||||||
|
"https://deno.land/std@0.132.0/encoding/base64.ts": "c8c16b4adaa60d7a8eee047c73ece26844435e8f7f1328d74593dbb2dd58ea4f",
|
||||||
|
"https://deno.land/std@0.132.0/encoding/base64url.ts": "55f9d13df02efac10c6f96169daa3e702606a64e8aa27c0295f645f198c27130",
|
||||||
|
"https://deno.land/std@0.132.0/encoding/hex.ts": "7f023e1e51cfd6b189682e602e8640939e7be71a300a2fcf3daf8f84dc609bbc",
|
||||||
|
"https://deno.land/std@0.132.0/flags/mod.ts": "430cf2d1c26e00286373b2647ebdca637f7558505e88e9c108a4742cd184c916",
|
||||||
|
"https://deno.land/std@0.132.0/fmt/colors.ts": "30455035d6d728394781c10755351742dd731e3db6771b1843f9b9e490104d37",
|
||||||
|
"https://deno.land/std@0.132.0/fmt/printf.ts": "e2c0f72146aed1efecf0c39ab928b26ae493a2278f670a871a0fbdcf36ff3379",
|
||||||
|
"https://deno.land/std@0.132.0/fs/eol.ts": "b92f0b88036de507e7e6fbedbe8f666835ea9dcbf5ac85917fa1fadc919f83a5",
|
||||||
|
"https://deno.land/std@0.132.0/fs/exists.ts": "cb734d872f8554ea40b8bff77ad33d4143c1187eac621a55bf37781a43c56f6d",
|
||||||
|
"https://deno.land/std@0.132.0/hash/sha256.ts": "803846c7a5a8a5a97f31defeb37d72f519086c880837129934f5d6f72102a8e8",
|
||||||
|
"https://deno.land/std@0.132.0/io/buffer.ts": "bd0c4bf53db4b4be916ca5963e454bddfd3fcd45039041ea161dbf826817822b",
|
||||||
|
"https://deno.land/std@0.132.0/node/_buffer.mjs": "f4a7df481d4eed06dc0151b833177d8ef74fc3a96dd4d2b073e690b6ced9474d",
|
||||||
|
"https://deno.land/std@0.132.0/node/_core.ts": "568d277be2e086af996cbdd599fec569f5280e9a494335ca23ad392b130d7bb9",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/constants.ts": "49011c87be4e45407ef5e99e96bde3f08656ebd8e6dfc99048c703dd0ce53952",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/asn1.js/base/buffer.js": "73beb8294eb29bd61458bbaaeeb51dfad4ec9c9868a62207a061d908f1637261",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/asn1.js/base/node.js": "4b777980d2a23088698fd2ff065bb311a2c713497d359e674cb6ef6baf267a0f",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/asn1.js/base/reporter.js": "8e4886e8ae311c9a92caf58bbbd8670326ceeae97430f4884e558e4acf8e8598",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/asn1.js/constants/der.js": "354b255479bff22a31d25bf08b217a295071700e37d0991cc05cac9f95e5e7ca",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/asn1.js/decoders/der.js": "c6faf66761daa43fbf79221308443893587c317774047b508a04c570713b76fb",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/asn1.js/decoders/pem.js": "8316ef7ce2ce478bc3dc1e9df1b75225d1eb8fb5d1378f8adf0cf19ecea5b501",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/asn1.js/encoders/der.js": "408336c88d17c5605ea64081261cf42267d8f9fda90098cb560aa6635bb00877",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/asn1.js/encoders/pem.js": "42a00c925b68c0858d6de0ba41ab89935b39fae9117bbf72a9abb2f4b755a2e7",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/asn1.js/mod.js": "7b78859707be10a0a1e4faccdd28cd5a4f71ad74a3e7bebda030757da97cd232",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/bn.js/bn.js": "abd1badd659fd0ae54e6a421a573a25aef4e795edc392178360cf716b144286d",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/aes.js": "1cf4c354c5bb341ffc9ab7207f471229835b021947225bce2e1642f26643847a",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/auth_cipher.js": "19b4dbb903e8406eb733176e6318d5e1a3bd382b67b72f7cf8e1c46cc6321ba4",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/decrypter.js": "05c1676942fd8e95837115bc2d1371bcf62e9bf19f6c3348870961fc64ddad0b",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/encrypter.js": "93ec98ab26fbeb5969eae2943e42fb66780f377b9b0ff0ecc32a9ed11201b142",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/ghash.js": "667b64845764a84f0096ef8cf7debed1a5f15ac9af26b379848237be57da399a",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/incr32.js": "4a7f0107753e4390b4ccc4dbd5200c5527d43f894f768e131903df30a09dfd67",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/mod.js": "d8eb88e7a317467831473621f32e60d7db9d981f6a2ae45d2fb2af170eab2d22",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/modes/cbc.js": "9790799cff181a074686c885708cb8eb473aeb3c86ff2e8d0ff911ae6c1e4431",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/modes/cfb.js": "a4e36ede6f26d8559d8f0528a134592761c706145a641bd9ad1100763e831cdb",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/modes/cfb1.js": "c6372f4973a68ca742682e81d1165e8869aaabf0091a8b963d4d60e5ee8e6f6a",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/modes/cfb8.js": "bd29eebb89199b056ff2441f7fb5e0300f458e13dcaaddbb8bc00cbdb199db67",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/modes/ctr.js": "9c2cbac1fc8f9b58334faacb98e6c57e8c3712f673ea4cf2d528a2894998ab2f",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/modes/ecb.js": "9629d193433688f0cfc432eca52838db0fb28d9eb4f45563df952bde50b59763",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/modes/mod.js": "7d8516ef8a20565539eb17cad5bb70add02ac06d1891e8f47cb981c22821787e",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/modes/ofb.js": "c23abaa6f1ec5343e9d7ba61d702acb3d81a0bd3d34dd2004e36975dc043d6ff",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/stream_cipher.js": "a533a03a2214c6b5934ce85a59eb1e04239fd6f429017c7ca3c443ec7e07e68f",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_aes/xor.ts": "4417711c026eb9a07475067cd31fa601e88c2d6ababd606d33d1e74da6fcfd09",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/browserify_rsa.js": "de8c98d2379a70d8c239b4886e2b3a11c7204eec39ae6b65d978d0d516ee6b08",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/cipher_base.js": "f565ad9daf3b3dd3b68381bed848da94fb093a9e4e5a48c92f47e26cc229df39",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/evp_bytes_to_key.ts": "8bd9fa445576b3e39586bdbef7c907f1dfda201bf22602d2ca1c6d760366311e",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/parse_asn1/asn1.js": "4f33b0197ffbe9cff62e5bad266e6b40d55874ea653552bb32ed251ad091f70a",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/parse_asn1/certificate.js": "aab306870830a81ad188db8fa8e037d7f5dd6c5abdabbd9739558245d1a12224",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/parse_asn1/fix_proc.js": "af3052b76f441878e102ffcfc7420692e65777af765e96f786310ae1acf7f76a",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/parse_asn1/mod.js": "e923a13b27089a99eeb578d2ffb9b4cfe8ce690918fec05d0024fa126f3e1ce3",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/public_encrypt/mgf.js": "5b81dc1680829b564fc5a00b842fb9c88916e4639b4fa27fa8bb6b759d272371",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/public_encrypt/mod.js": "eb8b64d7a58ee3823c1b642e799cc7ed1257d99f4d4aefa2b4796dd112ec094a",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/public_encrypt/private_decrypt.js": "0050df879f7c1338132c45056835f64e32140e2a2d5d03c1366ccce64855f632",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/public_encrypt/public_encrypt.js": "0132cb4fb8f72593278474884195b9c52b4e9ba33d8ddd22116d07a07f47005a",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/public_encrypt/with_public.js": "7373dac9b53b8331ccf3521c854a131dcb304a2e4d34cd116649118f7919ed0c",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/public_encrypt/xor.js": "900c6fc8b95e1861d796193c41988f5f70a09c7059e42887a243d0113ecaf0fd",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/crypto_browserify/randombytes.ts": "f465cd8e114a3c110297e0143445b12125d729b25bada5bd88d5b30cf612d7dd",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/hash.ts": "6a84a079412d09ead27b900590f0bede9924bc7ce522b8b7d55183a2aaf63a68",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/pbkdf2.ts": "00af38578729b3060371dfee70dae502a5848b4cc4787c48f634195cab1ce89a",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/randomBytes.ts": "04e276bcbfa55b3502c7169deab3f2bf58bbc5e9727634f8a150eff734338e90",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/randomFill.ts": "019ff2a8330c3ede6e65af28c5a8e3dee9d404975749c8dadf6ba11ccc28528e",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/randomInt.ts": "2db981c2baf4ddac07b6da71f90677f4acf4dc2d93f351563fdd084d645b8413",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/scrypt.ts": "caf07a6b8afa6ac582f80f99ed7dc7fefd5476fcd2aad771b8440e5b883d6d70",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/timingSafeEqual.ts": "4a4ef17e482889d9d82138d5ffc0e787c32c04b1f12b28d076b1a69ceca46af1",
|
||||||
|
"https://deno.land/std@0.132.0/node/_crypto/types.ts": "d3fae758c5b62f63d8126c76eec31a5559a2f34305defb5fe2a7d9034057ff54",
|
||||||
|
"https://deno.land/std@0.132.0/node/_dns/_utils.ts": "42494c8b8fa1c13eb134c5696744f77197717fa857e4d05147f2395a739b0a40",
|
||||||
|
"https://deno.land/std@0.132.0/node/_events.mjs": "d7d56df4b9f69e445064bad5e5558978fb18c18c439bbb62fa13149b40d7fb99",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fixed_queue.ts": "455b3c484de48e810b13bdf95cd1658ecb1ba6bcb8b9315ffe994efcde3ba5f5",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_access.ts": "0700488320d37208d000b94767ab37208d469550767edab69b65b09a330f245d",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_appendFile.ts": "5dca59d7f2ec33316d75da3d6a12d39f5c35b429bddf83f4b2c030b3a289d4b3",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_chmod.ts": "8fc25677b82a2643686e6f8270a8f1bee87dd60986334591450699e199dac7d5",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_chown.ts": "57858c54d376648fc3c8cf5a8ad4f7f19fb153b75fac3ed41df0332d757e7de9",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_close.ts": "785a9d1a6d615e8aa9f5a4ac50c9a131931f8b0e17b3d4671cd1fd25a5c10f2b",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_common.ts": "6a373d1583d9ec5cc7a8ff1072d77dc999e35282a320b7477038a2b209c304d3",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_constants.ts": "5c20b190fc6b7cfdaf12a30ba545fc787db2c7bbe87ed5b890da99578116a339",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_copy.ts": "675eb02a2dfc20dab1186bf6ed0a33b1abae656f440bc0a3ce74f385e0052eef",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_dir.ts": "8a05f72e32dd568b41ef45f8f55f1f54e9a306a7588475fa7014289cd12872d9",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_dirent.ts": "649c0a794e7b8d930cdd7e6a168b404aa0448bf784e0cfbe1bd6d25b99052273",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_exists.ts": "83e9ca6ea1ab3c6c7c3fc45f3c1287ee88839f08140ac11056441537450055bb",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_fdatasync.ts": "bbd078fea6c62c64d898101d697aefbfbb722797a75e328a82c2a4f2e7eb963d",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_fstat.ts": "559ff6ff094337db37b0f3108aeaecf42672795af45b206adbd90105afebf9c6",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_fsync.ts": "590be69ce5363dd4f8867f244cfabe8df89d40f86bbbe44fd00d69411d0b798e",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_ftruncate.ts": "8eb2a9fcf026bd9b85dc07a22bc452c48db4be05ab83f5f2b6a0549e15c1f75f",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_futimes.ts": "c753cb9e9f129a11d1110ed43905b8966ac2a1d362ed69d5a34bb44513b00082",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_link.ts": "3f9ccce31c2e56284fbcf2c65ec2e6fed1d9e67a9997410223486ac5092888e3",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_lstat.ts": "571cea559d270e3b2e7fc585b0eb051899f6d0e54b1786f5e2cee3e9f71e7f27",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_mkdir.ts": "68421a23b6d3c2d0142a6d0b3ccdd87903f9c8f98d6754aba554ab4c6b435bb8",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_mkdtemp.ts": "86eaec96c63ea178c749fa856115a345e9797baecad22297b9ef98e3d62b90e2",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_open.ts": "b1ca72addd2723b2a5a876378e72609fbe168adad2006f5d7b4f1868beef65ca",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_read.ts": "3b4ef96aad20f3f29a859125ebeac8c9461574743f70c2a7ef301b8505f7d036",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_readFile.ts": "3eae6c930e08c1279d400c0f5a008e6d96949ff3a4f5bf7d43e1b94b94ce3854",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_readdir.ts": "a546f01387b7c49ddc1bd78d0e123a9668c710c56cffb4d9577ef46703cab463",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_readlink.ts": "00553cd155f3bea565ffe43d7f0c10d75e895455562e1e8ea153e8f4e7ac04c7",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_realpath.ts": "3ec236e4ad3c171203043422939973b6a948200ec4802425db41fa60c860dde9",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_rename.ts": "3be71e8f43275c349b7abb9343b6e6764df09fabcbd2d316f8ac170ea556c645",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_rm.ts": "a9328f99d925d7c74d31361d466ca33475aa7c6d1d6f037a49ce1ed996f0a0b4",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_rmdir.ts": "b74007891357e709b37e6721eb355a1c4f25575995bb7c961a3c40f03ebc624c",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_stat.ts": "bd47ce0bfc2b867392abc6ec95878ab4f6dddb94af73903d6fa1a02ba3e26af8",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_streams.ts": "0e54bd4e41b462a701d6729ea17db01624aa48109e402fea8eecf13be324cf16",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_symlink.ts": "0bddc37c5092f847634bd41cee0b643b9c03fc541c0e635cf35da1fcb4d0f7fa",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_truncate.ts": "e2d380f7a81f69c4d4db30c442558ba8d8dea561e5097af41022bb5724e494e5",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_unlink.ts": "c537ca98e507972d65f0b113a179b5f5083f0da3e6f9fae29895fd2a9660c18a",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_utimes.ts": "c4446b7e39bf6977eca4364360501a97b96db9ea41e0cdf49abddab73481a175",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_watch.ts": "2338de777458021d39cb9f0a5f3ea1bd9109a7ca2c2ad6ec41029df1753838f8",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_write.mjs": "8c130b8b9522e1e4b08e687eb27939240260c115fda1e38e99c57b4f3af6481f",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_writeFile.ts": "79d176021c8ceae0d956763a33834166ebc3f1691ed9219a21674b2374f115c3",
|
||||||
|
"https://deno.land/std@0.132.0/node/_fs/_fs_writev.mjs": "274df0a109010862c8f8b320dc7784de9bd9425fe2a6afd05f1f06f547a25cba",
|
||||||
|
"https://deno.land/std@0.132.0/node/_next_tick.ts": "64c361f6bca21df2a72dd77b84bd49d80d97a694dd3080703bc78f52146351d1",
|
||||||
|
"https://deno.land/std@0.132.0/node/_options.ts": "27f3c1269a700d330cc046cf748aa9178b8fc39d1473de625688e07cb0eb9d28",
|
||||||
|
"https://deno.land/std@0.132.0/node/_process/exiting.ts": "bc9694769139ffc596f962087155a8bfef10101d03423b9dcbc51ce6e1f88fce",
|
||||||
|
"https://deno.land/std@0.132.0/node/_process/process.ts": "84644b184053835670f79652d1ce3312c9ad079c211e6207ebefeedf159352a3",
|
||||||
|
"https://deno.land/std@0.132.0/node/_process/stdio.mjs": "971c3b086040d8521562155db13f22f9971d5c42c852b2081d4d2f0d8b6ab6bd",
|
||||||
|
"https://deno.land/std@0.132.0/node/_process/streams.mjs": "555062e177ad05f887147651fdda25fa55098475fcf142c8d162b8fe14097bbb",
|
||||||
|
"https://deno.land/std@0.132.0/node/_stream.mjs": "07f6cbabaad0382fb4b9a25e70ac3093a44022b859247f64726746e6373f1c91",
|
||||||
|
"https://deno.land/std@0.132.0/node/_util/_util_callbackify.ts": "79928ad80df3e469f7dcdb198118a7436d18a9f6c08bd7a4382332ad25a718cf",
|
||||||
|
"https://deno.land/std@0.132.0/node/_utils.ts": "c2c352e83c4c96f5ff994b1c8246bff2abcb21bfc3f1c06162cb3af1d201e615",
|
||||||
|
"https://deno.land/std@0.132.0/node/buffer.ts": "fbecbf3f237fa49bec96e97ecf56a7b92d48037b3d11219288e68943cc921600",
|
||||||
|
"https://deno.land/std@0.132.0/node/crypto.ts": "fffbc3fc3dcc16ea986d3e89eed5f70db7dfef2c18d1205a8c8fe5327ee0192d",
|
||||||
|
"https://deno.land/std@0.132.0/node/dns.ts": "ae2abd1bc8ac79543fe4d702f2aa3607101dc788b6eeba06e06436cb42ee3779",
|
||||||
|
"https://deno.land/std@0.132.0/node/events.ts": "a1d40fc0dbccc944379ef968b80ea08f9fce579e88b5057fdb64e4f0812476dd",
|
||||||
|
"https://deno.land/std@0.132.0/node/fs.ts": "21a3189c460bd37ac3f6734e040587125b7c8435c0a9da4e6c57544a3aca81c2",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/assert.mjs": "118327c8866266534b30d3a36ad978204af7336dc2db3158b8167192918d4e06",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/async_hooks.ts": "8eca5b80f58ffb259e9b3a73536dc2fe2e67d07fd24bfe2aee325a4aa435edb3",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/blob.mjs": "52080b2f40b114203df67f8a6650f9fe3c653912b8b3ef2f31f029853df4db53",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/buffer.mjs": "6662fe7fe517329453545be34cea27a24f8ccd6d09afd4f609f11ade2b6dfca7",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/crypto/keys.ts": "16ce7b15a9fc5e4e3dee8fde75dae12f3d722558d5a1a6e65a9b4f86d64a21e9",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/crypto/util.mjs": "1de55a47fdbed6721b467a77ba48fdd1550c10b5eee77bbdb602eaffee365a5e",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/dtrace.ts": "50dd0e77b0269e47ff673bdb9ad0ef0ea3a3c53ac30c1695883ce4748e04ca14",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/error_codes.ts": "ac03c4eae33de3a69d6c98e8678003207eecf75a6900eb847e3fea3c8c9e6d8f",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/errors.ts": "25f91691225b001660e6e64745ecd336fbf562cf0185e8896ff013c2d0226794",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/fs/streams.ts": "c925db185efdf56c35cde8270c07d61698b80603a90e07caf1cb4ff80abf195b",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/fs/utils.mjs": "2a571ecbd169b444f07b7193306f108fdcb4bfd9b394b33716ad05edf30e899e",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/hide_stack_frames.ts": "a91962ec84610bc7ec86022c4593cdf688156a5910c07b5bcd71994225c13a03",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/idna.ts": "a8bdd28431f06630d8aad85d3cb8fd862459107af228c8805373ad2080f1c587",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/net.ts": "1239886cd2508a68624c2dae8abf895e8aa3bb15a748955349f9ac5539032238",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/normalize_encoding.mjs": "3779ec8a7adf5d963b0224f9b85d1bc974a2ec2db0e858396b5d3c2c92138a0a",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/process/per_thread.mjs": "a42b1dcfb009ad5039144a999a35a429e76112f9322febbe353eda9d1879d936",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/querystring.ts": "c3b23674a379f696e505606ddce9c6feabe9fc497b280c56705c340f4028fe74",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/stream_base_commons.ts": "934a9e69f46f2de644956edfa9fb040af7861e326fe5325dab38ef9caf2940bc",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/streams/_utils.ts": "77fceaa766679847e4d4c3c96b2573c00a790298d90551e8e4df1d5e0fdaad3b",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/streams/add-abort-signal.mjs": "5623b83fa64d439cc4a1f09ae47ec1db29512cc03479389614d8f62a37902f5e",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/streams/buffer_list.mjs": "c6a7b29204fae025ff5e9383332acaea5d44bc7c522a407a79b8f7a6bc6c312d",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/streams/compose.mjs": "b522daab35a80ae62296012a4254fd7edfc0366080ffe63ddda4e38fe6b6803e",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/streams/destroy.mjs": "9c9bbeb172a437041d529829f433df72cf0b63ae49f3ee6080a55ffbef7572ad",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/streams/duplex.mjs": "b014087cd04f79b8a4028d8b9423b987e07bbfacf3b5df518cb752ac3657580f",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/streams/end-of-stream.mjs": "38be76eaceac231dfde643e72bc0940625446bf6d1dbd995c91c5ba9fd59b338",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/streams/from.mjs": "134255c698ed63b33199911eb8e042f8f67e9682409bb11552e6120041ed1872",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/streams/legacy.mjs": "6ea28db95d4503447473e62f0b23ff473bfe1751223c33a3c5816652e93b257a",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/streams/passthrough.mjs": "a51074193b959f3103d94de41e23a78dfcff532bdba53af9146b86340d85eded",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/streams/pipeline.mjs": "9890b121759ede869174ef70c011fde964ca94d81f2ed97b8622d7cb17b49285",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/streams/readable.mjs": "a70c41171ae25c556b52785b0c178328014bd33d8c0e4d229d4adaac7414b6ca",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/streams/state.mjs": "9ef917392a9d8005a6e038260c5fd31518d2753aea0bc9e39824c199310434cb",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/streams/transform.mjs": "3b361abad2ac78f7ccb6f305012bafdc0e983dfa4bb6ecddb4626e34a781a5f5",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/streams/utils.mjs": "06c21d0db0d51f1bf1e3225a661c3c29909be80355d268e64ee5922fc5eb6c5e",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/streams/writable.mjs": "ad4e2b176ffdf752c8e678ead3a514679a5a8d652f4acf797115dceb798744d5",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/timers.mjs": "b43e24580cec2dd50f795e4342251a79515c0db21630c25b40fdc380a78b74e7",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/url.ts": "eacef0ace4f4c5394e9818a81499f4871b2a993d1bd3b902047e44a381ef0e22",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/util.mjs": "2f0c8ff553c175ea6e4ed13d7cd7cd6b86dc093dc2f783c6c3dfc63f60a0943e",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/util/comparisons.ts": "680b55fe8bdf1613633bc469fa0440f43162c76dbe36af9aa2966310e1bb9f6e",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/util/debuglog.ts": "99e91bdf26f6c67861031f684817e1705a5bc300e81346585b396f413387edfb",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/util/inspect.mjs": "d1c2569c66a3dab45eec03208f22ad4351482527859c0011a28a6c797288a0aa",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/util/types.ts": "b2dacb8f1f5d28a51c4da5c5b75172b7fcf694073ce95ca141323657e18b0c60",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal/validators.mjs": "a7e82eafb7deb85c332d5f8d9ffef052f46a42d4a121eada4a54232451acc49a",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/_libuv_winerror.ts": "801e05c2742ae6cd42a5f0fd555a255a7308a65732551e962e5345f55eedc519",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/_node.ts": "e4075ba8a37aef4eb5b592c8e3807c39cb49ca8653faf8e01a43421938076c1b",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/_utils.ts": "1c50883b5751a9ea1b38951e62ed63bacfdc9d69ea665292edfa28e1b1c5bd94",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/_winerror.ts": "8811d4be66f918c165370b619259c1f35e8c3e458b8539db64c704fbde0a7cd2",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/async_wrap.ts": "f06b8a403ad871248eb064190d27bf6fefdbe948991e71a18d7077390d5773f9",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/buffer.ts": "722c62b85f966e0777b2d98c021b60e75d7f2c2dabc43413ef37d60dbd13a5d9",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/cares_wrap.ts": "25b7b5d56612b2985260b673021828d6511a1c83b4c1927f5732cad2f2a718af",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/config.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/connection_wrap.ts": "0380444ee94d5bd7b0b09921223d16729c9762a94e80b7f51eda49c7f42e6d0a",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/constants.ts": "aff06aac49eda4234bd3a2b0b8e1fbfc67824e281c532ff9960831ab503014cc",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/contextify.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/credentials.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/crypto.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/errors.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/fs.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/fs_dir.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/fs_event_wrap.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/handle_wrap.ts": "e59df84b1fb1b9823b09774b3e512d9c0029b4557400d09dd02cd7661c2c4830",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/heap_utils.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/http_parser.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/icu.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/inspector.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/js_stream.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/messaging.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/mod.ts": "f68e74e8eed84eaa6b0de24f0f4c47735ed46866d7ee1c5a5e7c0667b4f0540f",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/module_wrap.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/native_module.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/natives.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/node_file.ts": "c96ee0b2af319a3916de950a6c4b0d5fb00d09395c51cd239c54d95d62567aaf",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/node_options.ts": "3cd5706153d28a4f5944b8b162c1c61b7b8e368a448fb1a2cff9f7957d3db360",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/options.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/os.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/performance.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/pipe_wrap.ts": "00e942327f8e1c4b74a5888a82f0e16ba775cd09af804f96b6f6849b7eab1719",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/process_methods.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/report.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/serdes.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/signal_wrap.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/spawn_sync.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/stream_wrap.ts": "d6e96f4b89d82ad5cc9b243c3d3880c9d85086165da54a7d85821a63491e5abf",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/string_decoder.ts": "5cb1863763d1e9b458bc21d6f976f16d9c18b3b3f57eaf0ade120aee38fba227",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/symbols.ts": "51cfca9bb6132d42071d4e9e6b68a340a7f274041cfcba3ad02900886e972a6c",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/task_queue.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/tcp_wrap.ts": "10c64d5e092a8bff99cfe05adea716e4e52f4158662a5821790953e47e2cc21c",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/timers.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/tls_wrap.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/trace_events.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/tty_wrap.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/types.ts": "4c26fb74ba2e45de553c15014c916df6789529a93171e450d5afb016b4c765e7",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/udp_wrap.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/url.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/util.ts": "90364292e2bd598ab5d105b48ca49817b6708f2d1d9cbaf08b2b3ab5ca4c90a7",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/uv.ts": "3821bc5e676d6955d68f581988c961d77dd28190aba5a9c59f16001a4deb34ba",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/v8.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/worker.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/internal_binding/zlib.ts": "e292217d048a33573966b7d25352828d3282921fbcadce8735a20fb3da370cc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/net.ts": "dfcb7e412abb3d5c55edde7d823b0ccb9601f7d40555ae3c862810c78b176185",
|
||||||
|
"https://deno.land/std@0.132.0/node/os.ts": "943d3294a7a00f39491148cd2097cdbf101233a421262223bb20ae702c059df5",
|
||||||
|
"https://deno.land/std@0.132.0/node/path.ts": "c65858e9cbb52dbc0dd348eefcdc41e82906c39cfa7982f2d4d805e828414b8c",
|
||||||
|
"https://deno.land/std@0.132.0/node/path/_constants.ts": "bd26f24a052b7d6b746151f4a236d29ab3c2096883bb6449c2fa499494406672",
|
||||||
|
"https://deno.land/std@0.132.0/node/path/_interface.ts": "6034ee29f6f295460ec82db1a94df9269aecbb0eceb81be72e9d843f8e8a97e6",
|
||||||
|
"https://deno.land/std@0.132.0/node/path/_util.ts": "9d4735fc05f8f1fb94406450e84e23fd201dc3fef5298b009e44cfa4e797b8f0",
|
||||||
|
"https://deno.land/std@0.132.0/node/path/common.ts": "f41a38a0719a1e85aa11c6ba3bea5e37c15dd009d705bd8873f94c833568cbc4",
|
||||||
|
"https://deno.land/std@0.132.0/node/path/glob.ts": "d6b64a24f148855a6e8057a171a2f9910c39e492e4ccec482005205b28eb4533",
|
||||||
|
"https://deno.land/std@0.132.0/node/path/mod.ts": "62e21dc6e1fe2e9742fce85de631a7b067d968544fe66954578e6d73c97369a2",
|
||||||
|
"https://deno.land/std@0.132.0/node/path/posix.ts": "9dd5fc83c4ae0e0b700bef43c88c67e276840c187a66d4d6a661440cf1fecc52",
|
||||||
|
"https://deno.land/std@0.132.0/node/path/separator.ts": "c908c9c28ebe7f1fea67daaccf84b63af90d882fe986f9fa03af9563a852723a",
|
||||||
|
"https://deno.land/std@0.132.0/node/path/win32.ts": "f869ee449b6dee69b13e2d1f8f7f1d01c7ae1e67fa573eab789429929f7a3864",
|
||||||
|
"https://deno.land/std@0.132.0/node/process.ts": "699f47f2f177556e17e2f7d0dcd3705ff5065cbdf72029e534c1540404d6f501",
|
||||||
|
"https://deno.land/std@0.132.0/node/querystring.ts": "967b8a7b00a73ebe373666deb3a7e501f164bac27bb342fde7221ecbb3522689",
|
||||||
|
"https://deno.land/std@0.132.0/node/stream.ts": "d127faa074a9e3886e4a01dcfe9f9a6a4b5641f76f6acc356e8ded7da5dc2c81",
|
||||||
|
"https://deno.land/std@0.132.0/node/stream/promises.mjs": "b263c09f2d6bd715dc514fab3f99cca84f442e2d23e87adbe76e32ea46fc87e6",
|
||||||
|
"https://deno.land/std@0.132.0/node/string_decoder.ts": "51ce85a173d2e36ac580d418bb48b804adb41732fc8bd85f7d5d27b7accbc61f",
|
||||||
|
"https://deno.land/std@0.132.0/node/timers.ts": "2d66fcd21e37acf76c3a699a97230da57cc21382c8e885b3c5377b37efd0f06c",
|
||||||
|
"https://deno.land/std@0.132.0/node/url.ts": "bc0bde2774854b6a377c4c61fa73e5a28283cbeb7f8703479f44e471219c33a8",
|
||||||
|
"https://deno.land/std@0.132.0/node/util.ts": "7fd6933b37af89a8e64d73dc6ee1732455a59e7e6d0965311fbd73cd634ea630",
|
||||||
|
"https://deno.land/std@0.132.0/node/util/types.mjs": "f9288198cacd374b41bae7e92a23179d3160f4c0eaf14e19be3a4e7057219a60",
|
||||||
|
"https://deno.land/std@0.132.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3",
|
||||||
|
"https://deno.land/std@0.132.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09",
|
||||||
|
"https://deno.land/std@0.132.0/path/_util.ts": "c1e9686d0164e29f7d880b2158971d805b6e0efc3110d0b3e24e4b8af2190d2b",
|
||||||
|
"https://deno.land/std@0.132.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633",
|
||||||
|
"https://deno.land/std@0.132.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee",
|
||||||
|
"https://deno.land/std@0.132.0/path/mod.ts": "4275129bb766f0e475ecc5246aa35689eeade419d72a48355203f31802640be7",
|
||||||
|
"https://deno.land/std@0.132.0/path/posix.ts": "663e4a6fe30a145f56aa41a22d95114c4c5582d8b57d2d7c9ed27ad2c47636bb",
|
||||||
|
"https://deno.land/std@0.132.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9",
|
||||||
|
"https://deno.land/std@0.132.0/path/win32.ts": "e7bdf63e8d9982b4d8a01ef5689425c93310ece950e517476e22af10f41a136e",
|
||||||
|
"https://deno.land/std@0.132.0/streams/conversion.ts": "712585bfa0172a97fb68dd46e784ae8ad59d11b88079d6a4ab098ff42e697d21",
|
||||||
|
"https://deno.land/std@0.132.0/testing/_diff.ts": "9d849cd6877694152e01775b2d93f9d6b7aef7e24bfe3bfafc4d7a1ac8e9f392",
|
||||||
|
"https://deno.land/std@0.132.0/testing/asserts.ts": "b0ef969032882b1f7eb1c7571e313214baa1485f7b61cf35807b2434e254365c",
|
||||||
"https://deno.land/std@0.160.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74",
|
"https://deno.land/std@0.160.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74",
|
||||||
"https://deno.land/std@0.160.0/_util/os.ts": "8a33345f74990e627b9dfe2de9b040004b08ea5146c7c9e8fe9a29070d193934",
|
"https://deno.land/std@0.160.0/_util/os.ts": "8a33345f74990e627b9dfe2de9b040004b08ea5146c7c9e8fe9a29070d193934",
|
||||||
"https://deno.land/std@0.160.0/async/abortable.ts": "87aa7230be8360c24ad437212311c9e8d4328854baec27b4c7abb26e85515c06",
|
"https://deno.land/std@0.160.0/async/abortable.ts": "87aa7230be8360c24ad437212311c9e8d4328854baec27b4c7abb26e85515c06",
|
||||||
|
|
@ -1417,6 +1701,30 @@
|
||||||
"https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/password.ts": "3c578bd21e4fd283431aa940357f40fed2e26d3de12ad129a696d7fe38ae744d",
|
"https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/password.ts": "3c578bd21e4fd283431aa940357f40fed2e26d3de12ad129a696d7fe38ae744d",
|
||||||
"https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/text-util.ts": "37c0437d2030c0b6255f10afec7ccfcb6b195e9a0a011bb7956595142c3d7383",
|
"https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/text-util.ts": "37c0437d2030c0b6255f10afec7ccfcb6b195e9a0a011bb7956595142c3d7383",
|
||||||
"https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/util.ts": "a8285450db7b56a3e507f478aaad68927ecb1ee545449cb869ccc4aace13fada",
|
"https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/util.ts": "a8285450db7b56a3e507f478aaad68927ecb1ee545449cb869ccc4aace13fada",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/17469a9e5f025d112206c583a29275e93dfc1431/deno/mod.js": "cb68f17d6d90df318934deccdb469d740be0888e7a597a9e7eea7100ce36a252",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/17469a9e5f025d112206c583a29275e93dfc1431/deno/polyfills.js": "318eb01f2b4cc33a46c59f3ddc11f22a56d6b1db8b7719b2ad7decee63a5bd47",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/17469a9e5f025d112206c583a29275e93dfc1431/deno/src/bytes.js": "f2de43bdc8fa5dc4b169f2c70d5d8b053a3dea8f85ef011d7b27dec69e14ebb7",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/17469a9e5f025d112206c583a29275e93dfc1431/deno/src/connection.js": "c63d53a0f35a7eb2670befef551f23fe914bbe9f0590de974e3e210c50527a29",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/17469a9e5f025d112206c583a29275e93dfc1431/deno/src/errors.js": "85cfbed9a5ab0db41ab8e97b806c881af29807dfe99bc656fdf1a18c1c13b6c6",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/17469a9e5f025d112206c583a29275e93dfc1431/deno/src/index.js": "119a320c6cc8d0e3927ec1384322494613980716f9fb4f67c82e3cb708a23d0a",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/17469a9e5f025d112206c583a29275e93dfc1431/deno/src/large.js": "f3e770cdb7cc695f7b50687b4c6c4b7252129515486ec8def98b7582ee7c54ef",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/17469a9e5f025d112206c583a29275e93dfc1431/deno/src/query.js": "67c45a5151032aa46b587abc15381fe4efd97c696e5c1b53082b8161309c4ee2",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/17469a9e5f025d112206c583a29275e93dfc1431/deno/src/queue.js": "709624843223ea842bf095f6934080f19f1a059a51cbbf82e9827f3bb1bf2ca7",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/17469a9e5f025d112206c583a29275e93dfc1431/deno/src/result.js": "001ff5e0c8d634674f483d07fbcd620a797e3101f842d6c20ca3ace936260465",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/17469a9e5f025d112206c583a29275e93dfc1431/deno/src/subscribe.js": "9e4d0c3e573a6048e77ee2f15abbd5bcd17da9ca85a78c914553472c6d6c169b",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/17469a9e5f025d112206c583a29275e93dfc1431/deno/src/types.js": "471f4a6c35412aa202a7c177c0a7e5a7c3bd225f01bbde67c947894c1b8bf6ed",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/master/deno/mod.js": "cb68f17d6d90df318934deccdb469d740be0888e7a597a9e7eea7100ce36a252",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/master/deno/polyfills.js": "318eb01f2b4cc33a46c59f3ddc11f22a56d6b1db8b7719b2ad7decee63a5bd47",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/master/deno/src/bytes.js": "f2de43bdc8fa5dc4b169f2c70d5d8b053a3dea8f85ef011d7b27dec69e14ebb7",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/master/deno/src/connection.js": "c63d53a0f35a7eb2670befef551f23fe914bbe9f0590de974e3e210c50527a29",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/master/deno/src/errors.js": "85cfbed9a5ab0db41ab8e97b806c881af29807dfe99bc656fdf1a18c1c13b6c6",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/master/deno/src/index.js": "b6fa0bd613aedc45b93ee5be4d133e8f1418b5d25fc041c56b124f28b432ce5a",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/master/deno/src/large.js": "f3e770cdb7cc695f7b50687b4c6c4b7252129515486ec8def98b7582ee7c54ef",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/master/deno/src/query.js": "67c45a5151032aa46b587abc15381fe4efd97c696e5c1b53082b8161309c4ee2",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/master/deno/src/queue.js": "15e6345adb6708bf3b99ad39fc2231c2fb61de5f6cba4b7a7a6be881482a4ec3",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/master/deno/src/result.js": "001ff5e0c8d634674f483d07fbcd620a797e3101f842d6c20ca3ace936260465",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/master/deno/src/subscribe.js": "9e4d0c3e573a6048e77ee2f15abbd5bcd17da9ca85a78c914553472c6d6c169b",
|
||||||
|
"https://raw.githubusercontent.com/xyzshantaram/postgres.js/master/deno/src/types.js": "471f4a6c35412aa202a7c177c0a7e5a7c3bd225f01bbde67c947894c1b8bf6ed",
|
||||||
"https://unpkg.com/nostr-relaypool2@0.6.34/lib/nostr-relaypool.worker.js": "a336e5c58b1e6946ae8943eb4fef21b810dc2a5a233438cff92b883673e29c96"
|
"https://unpkg.com/nostr-relaypool2@0.6.34/lib/nostr-relaypool.worker.js": "a336e5c58b1e6946ae8943eb4fef21b810dc2a5a233438cff92b883673e29c96"
|
||||||
},
|
},
|
||||||
"workspace": {
|
"workspace": {
|
||||||
|
|
@ -1446,6 +1754,7 @@
|
||||||
"npm:hono-rate-limiter@^0.3.0",
|
"npm:hono-rate-limiter@^0.3.0",
|
||||||
"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@^0.27.3",
|
"npm:kysely@^0.27.3",
|
||||||
"npm:light-bolt11-decoder",
|
"npm:light-bolt11-decoder",
|
||||||
"npm:linkify-plugin-hashtag@^4.1.1",
|
"npm:linkify-plugin-hashtag@^4.1.1",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,11 @@
|
||||||
|
import { Conf } from '@/config.ts';
|
||||||
import { DittoDB } from '@/db/DittoDB.ts';
|
import { DittoDB } from '@/db/DittoDB.ts';
|
||||||
|
import { sleep } from '@/test.ts';
|
||||||
|
|
||||||
|
if (Deno.env.get('CI') && Conf.db.dialect === 'postgres') {
|
||||||
|
console.info('Waiting 1 second for postgres to start...');
|
||||||
|
await sleep(1_000);
|
||||||
|
}
|
||||||
|
|
||||||
const kysely = await DittoDB.getInstance();
|
const kysely = await DittoDB.getInstance();
|
||||||
await kysely.destroy();
|
await kysely.destroy();
|
||||||
|
|
|
||||||
23
src/app.ts
23
src/app.ts
|
|
@ -108,6 +108,7 @@ import {
|
||||||
trendingStatusesController,
|
trendingStatusesController,
|
||||||
trendingTagsController,
|
trendingTagsController,
|
||||||
} from '@/controllers/api/trends.ts';
|
} from '@/controllers/api/trends.ts';
|
||||||
|
import { errorHandler } from '@/controllers/error.ts';
|
||||||
import { metricsController } from '@/controllers/metrics.ts';
|
import { metricsController } from '@/controllers/metrics.ts';
|
||||||
import { indexController } from '@/controllers/site.ts';
|
import { indexController } from '@/controllers/site.ts';
|
||||||
import { nodeInfoController, nodeInfoSchemaController } from '@/controllers/well-known/nodeinfo.ts';
|
import { nodeInfoController, nodeInfoSchemaController } from '@/controllers/well-known/nodeinfo.ts';
|
||||||
|
|
@ -151,18 +152,17 @@ if (Conf.cronEnabled) {
|
||||||
|
|
||||||
app.use('*', rateLimitMiddleware(300, Time.minutes(5)));
|
app.use('*', rateLimitMiddleware(300, Time.minutes(5)));
|
||||||
|
|
||||||
app.use('/api/*', logger(debug));
|
app.use('/api/*', metricsMiddleware, logger(debug));
|
||||||
app.use('/.well-known/*', logger(debug));
|
app.use('/.well-known/*', metricsMiddleware, logger(debug));
|
||||||
app.use('/users/*', logger(debug));
|
app.use('/users/*', metricsMiddleware, logger(debug));
|
||||||
app.use('/nodeinfo/*', logger(debug));
|
app.use('/nodeinfo/*', metricsMiddleware, logger(debug));
|
||||||
app.use('/oauth/*', logger(debug));
|
app.use('/oauth/*', metricsMiddleware, logger(debug));
|
||||||
|
|
||||||
app.get('/api/v1/streaming', streamingController);
|
app.get('/api/v1/streaming', metricsMiddleware, streamingController);
|
||||||
app.get('/relay', relayController);
|
app.get('/relay', metricsMiddleware, relayController);
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
'*',
|
'*',
|
||||||
metricsMiddleware,
|
|
||||||
cspMiddleware(),
|
cspMiddleware(),
|
||||||
cors({ origin: '*', exposeHeaders: ['link'] }),
|
cors({ origin: '*', exposeHeaders: ['link'] }),
|
||||||
signerMiddleware,
|
signerMiddleware,
|
||||||
|
|
@ -340,12 +340,7 @@ app.get('/', frontendController, indexController);
|
||||||
// Fallback
|
// Fallback
|
||||||
app.get('*', publicFiles, staticFiles, frontendController);
|
app.get('*', publicFiles, staticFiles, frontendController);
|
||||||
|
|
||||||
app.onError((err, c) => {
|
app.onError(errorHandler);
|
||||||
if (err.message === 'canceling statement due to statement timeout') {
|
|
||||||
return c.json({ error: 'The server was unable to respond in a timely manner' }, 500);
|
|
||||||
}
|
|
||||||
return c.json({ error: 'Something went wrong' }, 500);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default app;
|
export default app;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,21 @@ class Conf {
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
|
/** Database query timeout configurations. */
|
||||||
|
timeouts: {
|
||||||
|
/** Default query timeout when another setting isn't more specific. */
|
||||||
|
get default(): number {
|
||||||
|
return Number(Deno.env.get('DB_TIMEOUT_DEFAULT') || 5_000);
|
||||||
|
},
|
||||||
|
/** Timeout used for queries made through the Nostr relay. */
|
||||||
|
get relay(): number {
|
||||||
|
return Number(Deno.env.get('DB_TIMEOUT_RELAY') || 1_000);
|
||||||
|
},
|
||||||
|
/** Timeout used for timelines such as home, notifications, hashtag, etc. */
|
||||||
|
get timelines(): number {
|
||||||
|
return Number(Deno.env.get('DB_TIMEOUT_TIMELINES') || 15_000);
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
/** Character limit to enforce for posts made through Mastodon API. */
|
/** Character limit to enforce for posts made through Mastodon API. */
|
||||||
static get postCharLimit(): number {
|
static get postCharLimit(): number {
|
||||||
|
|
|
||||||
|
|
@ -209,7 +209,7 @@ const accountStatusesController: AppController = async (c) => {
|
||||||
filter['#t'] = [tagged];
|
filter['#t'] = [tagged];
|
||||||
}
|
}
|
||||||
|
|
||||||
const opts = { signal, limit, timeout: 10_000 };
|
const opts = { signal, limit, timeout: Conf.db.timeouts.timelines };
|
||||||
|
|
||||||
const events = await store.query([filter], opts)
|
const events = await store.query([filter], opts)
|
||||||
.then((events) => hydrateEvents({ events, store, signal }))
|
.then((events) => hydrateEvents({ events, store, signal }))
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ async function renderNotifications(
|
||||||
const store = c.get('store');
|
const store = c.get('store');
|
||||||
const pubkey = await c.get('signer')?.getPublicKey()!;
|
const pubkey = await c.get('signer')?.getPublicKey()!;
|
||||||
const { signal } = c.req.raw;
|
const { signal } = c.req.raw;
|
||||||
const opts = { signal, limit: params.limit, timeout: 15_000 };
|
const opts = { signal, limit: params.limit, timeout: Conf.db.timeouts.timelines };
|
||||||
|
|
||||||
const events = await store
|
const events = await store
|
||||||
.query(filters, opts)
|
.query(filters, opts)
|
||||||
|
|
|
||||||
|
|
@ -97,8 +97,8 @@ const createStatusController: AppController = async (c) => {
|
||||||
|
|
||||||
const root = ancestor.tags.find((tag) => tag[0] === 'e' && tag[3] === 'root')?.[1] ?? ancestor.id;
|
const root = ancestor.tags.find((tag) => tag[0] === 'e' && tag[3] === 'root')?.[1] ?? ancestor.id;
|
||||||
|
|
||||||
tags.push(['e', root, 'root']);
|
tags.push(['e', root, Conf.relay, 'root']);
|
||||||
tags.push(['e', data.in_reply_to_id, 'reply']);
|
tags.push(['e', data.in_reply_to_id, Conf.relay, 'reply']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.quote_id) {
|
if (data.quote_id) {
|
||||||
|
|
@ -202,7 +202,7 @@ const deleteStatusController: AppController = async (c) => {
|
||||||
if (event.pubkey === pubkey) {
|
if (event.pubkey === pubkey) {
|
||||||
await createEvent({
|
await createEvent({
|
||||||
kind: 5,
|
kind: 5,
|
||||||
tags: [['e', id]],
|
tags: [['e', id, Conf.relay]],
|
||||||
}, c);
|
}, c);
|
||||||
|
|
||||||
const author = await getAuthor(event.pubkey);
|
const author = await getAuthor(event.pubkey);
|
||||||
|
|
@ -260,8 +260,8 @@ const favouriteController: AppController = async (c) => {
|
||||||
kind: 7,
|
kind: 7,
|
||||||
content: '+',
|
content: '+',
|
||||||
tags: [
|
tags: [
|
||||||
['e', target.id],
|
['e', target.id, Conf.relay],
|
||||||
['p', target.pubkey],
|
['p', target.pubkey, Conf.relay],
|
||||||
],
|
],
|
||||||
}, c);
|
}, c);
|
||||||
|
|
||||||
|
|
@ -302,7 +302,10 @@ const reblogStatusController: AppController = async (c) => {
|
||||||
|
|
||||||
const reblogEvent = await createEvent({
|
const reblogEvent = await createEvent({
|
||||||
kind: 6,
|
kind: 6,
|
||||||
tags: [['e', event.id], ['p', event.pubkey]],
|
tags: [
|
||||||
|
['e', event.id, Conf.relay],
|
||||||
|
['p', event.pubkey, Conf.relay],
|
||||||
|
],
|
||||||
}, c);
|
}, c);
|
||||||
|
|
||||||
await hydrateEvents({
|
await hydrateEvents({
|
||||||
|
|
@ -337,7 +340,7 @@ const unreblogStatusController: AppController = async (c) => {
|
||||||
|
|
||||||
await createEvent({
|
await createEvent({
|
||||||
kind: 5,
|
kind: 5,
|
||||||
tags: [['e', repostEvent.id]],
|
tags: [['e', repostEvent.id, Conf.relay]],
|
||||||
}, c);
|
}, c);
|
||||||
|
|
||||||
return c.json(await renderStatus(event, { viewerPubkey: pubkey }));
|
return c.json(await renderStatus(event, { viewerPubkey: pubkey }));
|
||||||
|
|
@ -389,7 +392,7 @@ const bookmarkController: AppController = async (c) => {
|
||||||
if (event) {
|
if (event) {
|
||||||
await updateListEvent(
|
await updateListEvent(
|
||||||
{ kinds: [10003], authors: [pubkey], limit: 1 },
|
{ kinds: [10003], authors: [pubkey], limit: 1 },
|
||||||
(tags) => addTag(tags, ['e', eventId]),
|
(tags) => addTag(tags, ['e', eventId, Conf.relay]),
|
||||||
c,
|
c,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -416,7 +419,7 @@ const unbookmarkController: AppController = async (c) => {
|
||||||
if (event) {
|
if (event) {
|
||||||
await updateListEvent(
|
await updateListEvent(
|
||||||
{ kinds: [10003], authors: [pubkey], limit: 1 },
|
{ kinds: [10003], authors: [pubkey], limit: 1 },
|
||||||
(tags) => deleteTag(tags, ['e', eventId]),
|
(tags) => deleteTag(tags, ['e', eventId, Conf.relay]),
|
||||||
c,
|
c,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -443,7 +446,7 @@ const pinController: AppController = async (c) => {
|
||||||
if (event) {
|
if (event) {
|
||||||
await updateListEvent(
|
await updateListEvent(
|
||||||
{ kinds: [10001], authors: [pubkey], limit: 1 },
|
{ kinds: [10001], authors: [pubkey], limit: 1 },
|
||||||
(tags) => addTag(tags, ['e', eventId]),
|
(tags) => addTag(tags, ['e', eventId, Conf.relay]),
|
||||||
c,
|
c,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -472,7 +475,7 @@ const unpinController: AppController = async (c) => {
|
||||||
if (event) {
|
if (event) {
|
||||||
await updateListEvent(
|
await updateListEvent(
|
||||||
{ kinds: [10001], authors: [pubkey], limit: 1 },
|
{ kinds: [10001], authors: [pubkey], limit: 1 },
|
||||||
(tags) => deleteTag(tags, ['e', eventId]),
|
(tags) => deleteTag(tags, ['e', eventId, Conf.relay]),
|
||||||
c,
|
c,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -516,7 +519,7 @@ const zapController: AppController = async (c) => {
|
||||||
lnurl = getLnurl(meta);
|
lnurl = getLnurl(meta);
|
||||||
if (target && lnurl) {
|
if (target && lnurl) {
|
||||||
tags.push(
|
tags.push(
|
||||||
['e', target.id],
|
['e', target.id, Conf.relay],
|
||||||
['p', target.pubkey],
|
['p', target.pubkey],
|
||||||
['amount', amount.toString()],
|
['amount', amount.toString()],
|
||||||
['relays', Conf.relay],
|
['relays', Conf.relay],
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,10 @@ import { MuteListPolicy } from '@/policies/MuteListPolicy.ts';
|
||||||
import { getFeedPubkeys } from '@/queries.ts';
|
import { getFeedPubkeys } from '@/queries.ts';
|
||||||
import { hydrateEvents } from '@/storages/hydrate.ts';
|
import { hydrateEvents } from '@/storages/hydrate.ts';
|
||||||
import { Storages } from '@/storages.ts';
|
import { Storages } from '@/storages.ts';
|
||||||
import { bech32ToPubkey } from '@/utils.ts';
|
import { bech32ToPubkey, Time } from '@/utils.ts';
|
||||||
import { renderReblog, renderStatus } from '@/views/mastodon/statuses.ts';
|
import { renderReblog, renderStatus } from '@/views/mastodon/statuses.ts';
|
||||||
import { renderNotification } from '@/views/mastodon/notifications.ts';
|
import { renderNotification } from '@/views/mastodon/notifications.ts';
|
||||||
|
import TTLCache from '@isaacs/ttlcache';
|
||||||
|
|
||||||
const debug = Debug('ditto:streaming');
|
const debug = Debug('ditto:streaming');
|
||||||
|
|
||||||
|
|
@ -37,6 +38,11 @@ const streamSchema = z.enum([
|
||||||
|
|
||||||
type Stream = z.infer<typeof streamSchema>;
|
type Stream = z.infer<typeof streamSchema>;
|
||||||
|
|
||||||
|
const LIMITER_WINDOW = Time.minutes(5);
|
||||||
|
const LIMITER_LIMIT = 100;
|
||||||
|
|
||||||
|
const limiter = new TTLCache<string, number>();
|
||||||
|
|
||||||
const streamingController: AppController = async (c) => {
|
const streamingController: AppController = async (c) => {
|
||||||
const upgrade = c.req.header('upgrade');
|
const upgrade = c.req.header('upgrade');
|
||||||
const token = c.req.header('sec-websocket-protocol');
|
const token = c.req.header('sec-websocket-protocol');
|
||||||
|
|
@ -52,6 +58,14 @@ const streamingController: AppController = async (c) => {
|
||||||
return c.json({ error: 'Invalid access token' }, 401);
|
return c.json({ error: 'Invalid access token' }, 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ip = c.req.header('x-real-ip');
|
||||||
|
if (ip) {
|
||||||
|
const count = limiter.get(ip) ?? 0;
|
||||||
|
if (count > LIMITER_LIMIT) {
|
||||||
|
return c.json({ error: 'Rate limit exceeded' }, 429);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const { socket, response } = Deno.upgradeWebSocket(c.req.raw, { protocol: token, idleTimeout: 30 });
|
const { socket, response } = Deno.upgradeWebSocket(c.req.raw, { protocol: token, idleTimeout: 30 });
|
||||||
|
|
||||||
const store = await Storages.db();
|
const store = await Storages.db();
|
||||||
|
|
@ -122,6 +136,23 @@ const streamingController: AppController = async (c) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
socket.onmessage = (e) => {
|
||||||
|
if (ip) {
|
||||||
|
const count = limiter.get(ip) ?? 0;
|
||||||
|
limiter.set(ip, count + 1, { ttl: LIMITER_WINDOW });
|
||||||
|
|
||||||
|
if (count > LIMITER_LIMIT) {
|
||||||
|
socket.close(1008, 'Rate limit exceeded');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof e.data !== 'string') {
|
||||||
|
socket.close(1003, 'Invalid message');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
socket.onclose = () => {
|
socket.onclose = () => {
|
||||||
streamingConnectionsGauge.dec();
|
streamingConnectionsGauge.dec();
|
||||||
controller.abort();
|
controller.abort();
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ const suggestedTimelineController: AppController = async (c) => {
|
||||||
async function renderStatuses(c: AppContext, filters: NostrFilter[]) {
|
async function renderStatuses(c: AppContext, filters: NostrFilter[]) {
|
||||||
const { signal } = c.req.raw;
|
const { signal } = c.req.raw;
|
||||||
const store = c.get('store');
|
const store = c.get('store');
|
||||||
const opts = { signal, timeout: 10_000 };
|
const opts = { signal, timeout: Conf.db.timeouts.timelines };
|
||||||
|
|
||||||
const events = await store
|
const events = await store
|
||||||
.query(filters, opts)
|
.query(filters, opts)
|
||||||
|
|
|
||||||
16
src/controllers/error.ts
Normal file
16
src/controllers/error.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { ErrorHandler } from '@hono/hono';
|
||||||
|
import { HTTPException } from '@hono/hono/http-exception';
|
||||||
|
|
||||||
|
export const errorHandler: ErrorHandler = (err, c) => {
|
||||||
|
if (err instanceof HTTPException) {
|
||||||
|
return c.json({ error: err.message }, err.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error(err);
|
||||||
|
|
||||||
|
if (err.message === 'canceling statement due to statement timeout') {
|
||||||
|
return c.json({ error: 'The server was unable to respond in a timely manner' }, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.json({ error: 'Something went wrong' }, 500);
|
||||||
|
};
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import TTLCache from '@isaacs/ttlcache';
|
||||||
import {
|
import {
|
||||||
NostrClientCLOSE,
|
NostrClientCLOSE,
|
||||||
NostrClientCOUNT,
|
NostrClientCOUNT,
|
||||||
|
|
@ -9,17 +10,24 @@ import {
|
||||||
} from '@nostrify/nostrify';
|
} from '@nostrify/nostrify';
|
||||||
|
|
||||||
import { AppController } from '@/app.ts';
|
import { AppController } from '@/app.ts';
|
||||||
|
import { Conf } from '@/config.ts';
|
||||||
import { relayInfoController } from '@/controllers/nostr/relay-info.ts';
|
import { relayInfoController } from '@/controllers/nostr/relay-info.ts';
|
||||||
import { relayConnectionsGauge, relayEventCounter, relayMessageCounter } from '@/metrics.ts';
|
import { relayConnectionsGauge, relayEventCounter, relayMessageCounter } from '@/metrics.ts';
|
||||||
import * as pipeline from '@/pipeline.ts';
|
import * as pipeline from '@/pipeline.ts';
|
||||||
import { RelayError } from '@/RelayError.ts';
|
import { RelayError } from '@/RelayError.ts';
|
||||||
import { Storages } from '@/storages.ts';
|
import { Storages } from '@/storages.ts';
|
||||||
|
import { Time } from '@/utils/time.ts';
|
||||||
|
|
||||||
/** Limit of initial events returned for a subscription. */
|
/** Limit of initial events returned for a subscription. */
|
||||||
const FILTER_LIMIT = 100;
|
const FILTER_LIMIT = 100;
|
||||||
|
|
||||||
|
const LIMITER_WINDOW = Time.minutes(1);
|
||||||
|
const LIMITER_LIMIT = 300;
|
||||||
|
|
||||||
|
const limiter = new TTLCache<string, number>();
|
||||||
|
|
||||||
/** Set up the Websocket connection. */
|
/** Set up the Websocket connection. */
|
||||||
function connectStream(socket: WebSocket) {
|
function connectStream(socket: WebSocket, ip: string | undefined) {
|
||||||
const controllers = new Map<string, AbortController>();
|
const controllers = new Map<string, AbortController>();
|
||||||
|
|
||||||
socket.onopen = () => {
|
socket.onopen = () => {
|
||||||
|
|
@ -27,6 +35,21 @@ function connectStream(socket: WebSocket) {
|
||||||
};
|
};
|
||||||
|
|
||||||
socket.onmessage = (e) => {
|
socket.onmessage = (e) => {
|
||||||
|
if (ip) {
|
||||||
|
const count = limiter.get(ip) ?? 0;
|
||||||
|
limiter.set(ip, count + 1, { ttl: LIMITER_WINDOW });
|
||||||
|
|
||||||
|
if (count > LIMITER_LIMIT) {
|
||||||
|
socket.close(1008, 'Rate limit exceeded');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof e.data !== 'string') {
|
||||||
|
socket.close(1003, 'Invalid message');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const result = n.json().pipe(n.clientMsg()).safeParse(e.data);
|
const result = n.json().pipe(n.clientMsg()).safeParse(e.data);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
relayMessageCounter.inc({ verb: result.data[0] });
|
relayMessageCounter.inc({ verb: result.data[0] });
|
||||||
|
|
@ -73,7 +96,7 @@ function connectStream(socket: WebSocket) {
|
||||||
const pubsub = await Storages.pubsub();
|
const pubsub = await Storages.pubsub();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (const event of await store.query(filters, { limit: FILTER_LIMIT, timeout: 1000 })) {
|
for (const event of await store.query(filters, { limit: FILTER_LIMIT, timeout: Conf.db.timeouts.relay })) {
|
||||||
send(['EVENT', subId, event]);
|
send(['EVENT', subId, event]);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -128,7 +151,7 @@ function connectStream(socket: WebSocket) {
|
||||||
/** Handle COUNT. Return the number of events matching the filters. */
|
/** Handle COUNT. Return the number of events matching the filters. */
|
||||||
async function handleCount([_, subId, ...filters]: NostrClientCOUNT): Promise<void> {
|
async function handleCount([_, subId, ...filters]: NostrClientCOUNT): Promise<void> {
|
||||||
const store = await Storages.db();
|
const store = await Storages.db();
|
||||||
const { count } = await store.count(filters, { timeout: 100 });
|
const { count } = await store.count(filters, { timeout: Conf.db.timeouts.relay });
|
||||||
send(['COUNT', subId, { count, approximate: false }]);
|
send(['COUNT', subId, { count, approximate: false }]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -152,8 +175,16 @@ const relayController: AppController = (c, next) => {
|
||||||
return c.text('Please use a Nostr client to connect.', 400);
|
return c.text('Please use a Nostr client to connect.', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ip = c.req.header('x-real-ip');
|
||||||
|
if (ip) {
|
||||||
|
const count = limiter.get(ip) ?? 0;
|
||||||
|
if (count > LIMITER_LIMIT) {
|
||||||
|
return c.json({ error: 'Rate limit exceeded' }, 429);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const { socket, response } = Deno.upgradeWebSocket(c.req.raw, { idleTimeout: 30 });
|
const { socket, response } = Deno.upgradeWebSocket(c.req.raw, { idleTimeout: 30 });
|
||||||
connectStream(socket);
|
connectStream(socket, ip);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -39,14 +39,14 @@ export class DittoDB {
|
||||||
|
|
||||||
static get poolSize(): number {
|
static get poolSize(): number {
|
||||||
if (Conf.db.dialect === 'postgres') {
|
if (Conf.db.dialect === 'postgres') {
|
||||||
return DittoPostgres.getPool().size;
|
return DittoPostgres.poolSize;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get availableConnections(): number {
|
static get availableConnections(): number {
|
||||||
if (Conf.db.dialect === 'postgres') {
|
if (Conf.db.dialect === 'postgres') {
|
||||||
return DittoPostgres.getPool().available;
|
return DittoPostgres.availableConnections;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { Kysely, PostgresAdapter, PostgresIntrospector, PostgresQueryCompiler } from 'kysely';
|
import { Kysely } from 'kysely';
|
||||||
import { PostgreSQLDriver } from 'kysely_deno_postgres';
|
import { PostgresJSDialect, PostgresJSDialectConfig } from 'kysely-postgres-js';
|
||||||
import { Pool } from 'postgres';
|
import postgres from 'postgres';
|
||||||
|
|
||||||
import { Conf } from '@/config.ts';
|
import { Conf } from '@/config.ts';
|
||||||
import { DittoTables } from '@/db/DittoTables.ts';
|
import { DittoTables } from '@/db/DittoTables.ts';
|
||||||
|
|
@ -8,37 +8,31 @@ import { KyselyLogger } from '@/db/KyselyLogger.ts';
|
||||||
|
|
||||||
export class DittoPostgres {
|
export class DittoPostgres {
|
||||||
static db: Kysely<DittoTables> | undefined;
|
static db: Kysely<DittoTables> | undefined;
|
||||||
static pool: Pool | undefined;
|
static postgres?: postgres.Sql;
|
||||||
|
|
||||||
static getPool(): Pool {
|
|
||||||
if (!this.pool) {
|
|
||||||
this.pool = new Pool(Conf.databaseUrl, Conf.pg.poolSize, true);
|
|
||||||
}
|
|
||||||
return this.pool;
|
|
||||||
}
|
|
||||||
|
|
||||||
// deno-lint-ignore require-await
|
// deno-lint-ignore require-await
|
||||||
static async getInstance(): Promise<Kysely<DittoTables>> {
|
static async getInstance(): Promise<Kysely<DittoTables>> {
|
||||||
|
if (!this.postgres) {
|
||||||
|
this.postgres = postgres(Conf.databaseUrl, { max: Conf.pg.poolSize });
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.db) {
|
if (!this.db) {
|
||||||
this.db = new Kysely({
|
this.db = new Kysely({
|
||||||
dialect: {
|
dialect: new PostgresJSDialect({
|
||||||
createAdapter() {
|
postgres: this.postgres as unknown as PostgresJSDialectConfig['postgres'],
|
||||||
return new PostgresAdapter();
|
}),
|
||||||
},
|
|
||||||
createDriver() {
|
|
||||||
return new PostgreSQLDriver(DittoPostgres.getPool());
|
|
||||||
},
|
|
||||||
createIntrospector(db: Kysely<unknown>) {
|
|
||||||
return new PostgresIntrospector(db);
|
|
||||||
},
|
|
||||||
createQueryCompiler() {
|
|
||||||
return new PostgresQueryCompiler();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
log: KyselyLogger,
|
log: KyselyLogger,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.db;
|
return this.db;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get poolSize() {
|
||||||
|
return this.postgres?.connections.open ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get availableConnections(): number {
|
||||||
|
return this.postgres?.connections.idle ?? 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,12 @@ export const httpRequestCounter = new Counter({
|
||||||
labelNames: ['method'],
|
labelNames: ['method'],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const httpResponseCounter = new Counter({
|
||||||
|
name: 'http_responses_total',
|
||||||
|
help: 'Total number of HTTP responses',
|
||||||
|
labelNames: ['status', 'path'],
|
||||||
|
});
|
||||||
|
|
||||||
export const streamingConnectionsGauge = new Gauge({
|
export const streamingConnectionsGauge = new Gauge({
|
||||||
name: 'streaming_connections',
|
name: 'streaming_connections',
|
||||||
help: 'Number of active connections to the streaming API',
|
help: 'Number of active connections to the streaming API',
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
import { MiddlewareHandler } from '@hono/hono';
|
import { MiddlewareHandler } from '@hono/hono';
|
||||||
|
|
||||||
import { httpRequestCounter } from '@/metrics.ts';
|
import { httpRequestCounter, httpResponseCounter } from '@/metrics.ts';
|
||||||
|
|
||||||
export const metricsMiddleware: MiddlewareHandler = async (c, next) => {
|
export const metricsMiddleware: MiddlewareHandler = async (c, next) => {
|
||||||
const { method } = c.req;
|
const { method } = c.req;
|
||||||
httpRequestCounter.inc({ method });
|
httpRequestCounter.inc({ method });
|
||||||
|
|
||||||
await next();
|
await next();
|
||||||
|
|
||||||
|
const { status } = c.res;
|
||||||
|
const path = c.req.matchedRoutes.find((r) => r.method !== 'ALL')?.path ?? c.req.routePath;
|
||||||
|
httpResponseCounter.inc({ status, path });
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
// deno-lint-ignore-file require-await
|
// deno-lint-ignore-file require-await
|
||||||
|
import { HTTPException } from '@hono/hono/http-exception';
|
||||||
import { NConnectSigner, NostrEvent, NostrSigner } from '@nostrify/nostrify';
|
import { NConnectSigner, NostrEvent, NostrSigner } from '@nostrify/nostrify';
|
||||||
|
|
||||||
import { Storages } from '@/storages.ts';
|
import { Storages } from '@/storages.ts';
|
||||||
|
|
@ -27,30 +28,78 @@ export class ConnectSigner implements NostrSigner {
|
||||||
|
|
||||||
async signEvent(event: Omit<NostrEvent, 'id' | 'pubkey' | 'sig'>): Promise<NostrEvent> {
|
async signEvent(event: Omit<NostrEvent, 'id' | 'pubkey' | 'sig'>): Promise<NostrEvent> {
|
||||||
const signer = await this.signer;
|
const signer = await this.signer;
|
||||||
return signer.signEvent(event);
|
try {
|
||||||
|
return await signer.signEvent(event);
|
||||||
|
} catch (e) {
|
||||||
|
if (e.name === 'AbortError') {
|
||||||
|
throw new HTTPException(408, { message: 'The event was not signed quickly enough' });
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly nip04 = {
|
readonly nip04 = {
|
||||||
encrypt: async (pubkey: string, plaintext: string): Promise<string> => {
|
encrypt: async (pubkey: string, plaintext: string): Promise<string> => {
|
||||||
const signer = await this.signer;
|
const signer = await this.signer;
|
||||||
return signer.nip04.encrypt(pubkey, plaintext);
|
try {
|
||||||
|
return await signer.nip04.encrypt(pubkey, plaintext);
|
||||||
|
} catch (e) {
|
||||||
|
if (e.name === 'AbortError') {
|
||||||
|
throw new HTTPException(408, {
|
||||||
|
message: 'Text was not encrypted quickly enough',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
decrypt: async (pubkey: string, ciphertext: string): Promise<string> => {
|
decrypt: async (pubkey: string, ciphertext: string): Promise<string> => {
|
||||||
const signer = await this.signer;
|
const signer = await this.signer;
|
||||||
return signer.nip04.decrypt(pubkey, ciphertext);
|
try {
|
||||||
|
return await signer.nip04.decrypt(pubkey, ciphertext);
|
||||||
|
} catch (e) {
|
||||||
|
if (e.name === 'AbortError') {
|
||||||
|
throw new HTTPException(408, {
|
||||||
|
message: 'Text was not decrypted quickly enough',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
readonly nip44 = {
|
readonly nip44 = {
|
||||||
encrypt: async (pubkey: string, plaintext: string): Promise<string> => {
|
encrypt: async (pubkey: string, plaintext: string): Promise<string> => {
|
||||||
const signer = await this.signer;
|
const signer = await this.signer;
|
||||||
return signer.nip44.encrypt(pubkey, plaintext);
|
try {
|
||||||
|
return await signer.nip44.encrypt(pubkey, plaintext);
|
||||||
|
} catch (e) {
|
||||||
|
if (e.name === 'AbortError') {
|
||||||
|
throw new HTTPException(408, {
|
||||||
|
message: 'Text was not encrypted quickly enough',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
decrypt: async (pubkey: string, ciphertext: string): Promise<string> => {
|
decrypt: async (pubkey: string, ciphertext: string): Promise<string> => {
|
||||||
const signer = await this.signer;
|
const signer = await this.signer;
|
||||||
return signer.nip44.decrypt(pubkey, ciphertext);
|
try {
|
||||||
|
return await signer.nip44.decrypt(pubkey, ciphertext);
|
||||||
|
} catch (e) {
|
||||||
|
if (e.name === 'AbortError') {
|
||||||
|
throw new HTTPException(408, {
|
||||||
|
message: 'Text was not decrypted quickly enough',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ export class ReadOnlySigner implements NostrSigner {
|
||||||
|
|
||||||
async signEvent(): Promise<NostrEvent> {
|
async signEvent(): Promise<NostrEvent> {
|
||||||
throw new HTTPException(401, {
|
throw new HTTPException(401, {
|
||||||
message: 'Log out and back in',
|
message: 'Log in with Nostr Connect to sign events',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ class EventsDB implements NStore {
|
||||||
await this.deleteEventsAdmin(event);
|
await this.deleteEventsAdmin(event);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.store.event(event, { ...opts, timeout: opts.timeout ?? 1000 });
|
await this.store.event(event, { ...opts, timeout: opts.timeout ?? Conf.db.timeouts.default });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.message === 'Cannot add a deleted event') {
|
if (e.message === 'Cannot add a deleted event') {
|
||||||
throw new RelayError('blocked', 'event deleted by user');
|
throw new RelayError('blocked', 'event deleted by user');
|
||||||
|
|
@ -164,7 +164,7 @@ class EventsDB implements NStore {
|
||||||
|
|
||||||
this.console.debug('REQ', JSON.stringify(filters));
|
this.console.debug('REQ', JSON.stringify(filters));
|
||||||
|
|
||||||
return this.store.query(filters, { ...opts, timeout: opts.timeout ?? 1000 });
|
return this.store.query(filters, { ...opts, timeout: opts.timeout ?? Conf.db.timeouts.default });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Delete events based on filters from the database. */
|
/** Delete events based on filters from the database. */
|
||||||
|
|
@ -172,7 +172,7 @@ class EventsDB implements NStore {
|
||||||
if (!filters.length) return Promise.resolve();
|
if (!filters.length) return Promise.resolve();
|
||||||
this.console.debug('DELETE', JSON.stringify(filters));
|
this.console.debug('DELETE', JSON.stringify(filters));
|
||||||
|
|
||||||
return this.store.remove(filters, { ...opts, timeout: opts.timeout ?? 3000 });
|
return this.store.remove(filters, { ...opts, timeout: opts.timeout ?? Conf.db.timeouts.default });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get number of events that would be returned by filters. */
|
/** Get number of events that would be returned by filters. */
|
||||||
|
|
@ -185,7 +185,7 @@ class EventsDB implements NStore {
|
||||||
|
|
||||||
this.console.debug('COUNT', JSON.stringify(filters));
|
this.console.debug('COUNT', JSON.stringify(filters));
|
||||||
|
|
||||||
return this.store.count(filters, { ...opts, timeout: opts.timeout ?? 500 });
|
return this.store.count(filters, { ...opts, timeout: opts.timeout ?? Conf.db.timeouts.default });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return only the tags that should be indexed. */
|
/** Return only the tags that should be indexed. */
|
||||||
|
|
@ -253,6 +253,8 @@ class EventsDB implements NStore {
|
||||||
|
|
||||||
/** Converts filters to more performant, simpler filters that are better for SQLite. */
|
/** Converts filters to more performant, simpler filters that are better for SQLite. */
|
||||||
async expandFilters(filters: NostrFilter[]): Promise<NostrFilter[]> {
|
async expandFilters(filters: NostrFilter[]): Promise<NostrFilter[]> {
|
||||||
|
filters = structuredClone(filters);
|
||||||
|
|
||||||
for (const filter of filters) {
|
for (const filter of filters) {
|
||||||
if (filter.search) {
|
if (filter.search) {
|
||||||
const tokens = NIP50.parseInput(filter.search);
|
const tokens = NIP50.parseInput(filter.search);
|
||||||
|
|
@ -282,6 +284,12 @@ class EventsDB implements NStore {
|
||||||
|
|
||||||
filter.search = tokens.filter((t) => typeof t === 'string').join(' ');
|
filter.search = tokens.filter((t) => typeof t === 'string').join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (filter.kinds) {
|
||||||
|
// Ephemeral events are not stored, so don't bother querying for them.
|
||||||
|
// If this results in an empty kinds array, NDatabase will remove the filter before querying and return no results.
|
||||||
|
filter.kinds = filter.kinds.filter((kind) => !NKinds.ephemeral(kind));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return filters;
|
return filters;
|
||||||
|
|
|
||||||
|
|
@ -168,3 +168,7 @@ export const createTestDB = async (databaseUrl?: string) => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function sleep(ms: number): Promise<void> {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue