diff --git a/deno.json b/deno.json index 517f3784..430d46be 100644 --- a/deno.json +++ b/deno.json @@ -66,7 +66,7 @@ "@noble/secp256k1": "npm:@noble/secp256k1@^2.0.0", "@nostrify/db": "jsr:@nostrify/db@^0.39.4", "@nostrify/nostrify": "jsr:@nostrify/nostrify@^0.39.1", - "@nostrify/policies": "https://gitlab.com/soapbox-pub/nostrify/-/raw/58c20b3ee23d8be4987d2c0055d86960e7db763d/packages/policies/mod.ts", + "@nostrify/policies": "jsr:@nostrify/policies@^0.36.1", "@nostrify/types": "jsr:@nostrify/types@^0.36.0", "@scure/base": "npm:@scure/base@^1.1.6", "@sentry/deno": "https://deno.land/x/sentry@7.112.2/index.mjs", diff --git a/deno.lock b/deno.lock index ffa3e2a1..8cbff313 100644 --- a/deno.lock +++ b/deno.lock @@ -4,49 +4,71 @@ "jsr:@b-fuze/deno-dom@~0.1.47": "0.1.49", "jsr:@bradenmacdonald/s3-lite-client@~0.7.4": "0.7.6", "jsr:@core/asyncutil@^1.2.0": "1.2.0", + "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:@hono/hono@^4.4.6": "4.7.5", "jsr:@negrel/http-ece@0.6.0": "0.6.0", "jsr:@negrel/webpush@0.3": "0.3.0", "jsr:@nostrify/db@~0.39.4": "0.39.4", + "jsr:@nostrify/nostrify@0.36": "0.36.2", "jsr:@nostrify/nostrify@0.39": "0.39.1", "jsr:@nostrify/nostrify@~0.39.1": "0.39.1", + "jsr:@nostrify/policies@~0.36.1": "0.36.1", + "jsr:@nostrify/types@0.35": "0.35.0", "jsr:@nostrify/types@0.36": "0.36.0", "jsr:@soapbox/kysely-pglite@1": "1.0.0", "jsr:@soapbox/logi@0.3": "0.3.0", "jsr:@soapbox/safe-fetch@2": "2.0.0", + "jsr:@std/assert@0.223": "0.223.0", "jsr:@std/assert@0.224": "0.224.0", "jsr:@std/assert@^1.0.12": "1.0.12", + "jsr:@std/assert@~0.213.1": "0.213.1", "jsr:@std/assert@~0.225.1": "0.225.3", "jsr:@std/async@^1.0.10": "1.0.12", + "jsr:@std/bytes@0.223": "0.223.0", "jsr:@std/bytes@0.224.0": "0.224.0", "jsr:@std/bytes@^1.0.2": "1.0.5", + "jsr:@std/cli@0.223": "0.223.0", "jsr:@std/crypto@0.224": "0.224.0", + "jsr:@std/data-structures@^1.0.6": "1.0.6", + "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@^1.0.15": "1.0.15", + "jsr:@std/fs@~0.229.3": "0.229.3", "jsr:@std/internal@1": "1.0.6", "jsr:@std/internal@^1.0.6": "1.0.6", + "jsr:@std/io@0.223": "0.223.0", "jsr:@std/io@0.224": "0.224.9", "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@^1.0.8": "1.0.8", + "jsr:@std/path@~0.213.1": "0.213.1", "jsr:@std/streams@0.223": "0.223.0", "jsr:@std/testing@^1.0.9": "1.0.10", "npm:@cashu/cashu-ts@^2.2.0": "2.4.1", "npm:@electric-sql/pglite@~0.2.8": "0.2.17", "npm:@isaacs/ttlcache@^1.4.1": "1.4.1", + "npm:@noble/secp256k1@2": "2.2.3", "npm:@scure/base@^1.1.6": "1.2.4", "npm:@scure/bip32@^1.4.0": "1.6.2", "npm:@scure/bip39@^1.3.0": "1.5.4", "npm:@types/node@*": "22.5.4", "npm:blurhash@2.0.5": "2.0.5", + "npm:comlink-async-generator@^0.0.1": "0.0.1", "npm:comlink@^4.4.1": "4.4.2", "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.7.5", "npm:iso-639-1@^3.1.5": "3.1.5", @@ -63,30 +85,45 @@ "npm:lru-cache@^10.2.2": "10.4.3", "npm:nostr-tools@2.5.1": "2.5.1", "npm:nostr-tools@^2.10.4": "2.11.0", + "npm:nostr-tools@^2.7.0": "2.11.0", "npm:nostr-wasm@0.1": "0.1.0", "npm:path-to-regexp@^7.1.0": "7.2.0", "npm:prom-client@^15.1.2": "15.1.3", "npm:sharp@~0.33.5": "0.33.5", "npm:tldts@^6.0.14": "6.1.85", "npm:tldts@^6.1.61": "6.1.85", + "npm:tseep@^1.2.1": "1.3.1", "npm:type-fest@^4.3.0": "4.38.0", "npm:unfurl.js@^6.4.0": "6.4.0", + "npm:websocket-ts@^2.1.5": "2.2.1", "npm:websocket-ts@^2.2.1": "2.2.1", "npm:zod@^3.23.8": "3.24.2" }, "jsr": { "@b-fuze/deno-dom@0.1.49": { - "integrity": "45c40175fdd1e74ab2d4b54c4fdaabbad6e5b76ee28df12a48e076b3fa7901a9" + "integrity": "45c40175fdd1e74ab2d4b54c4fdaabbad6e5b76ee28df12a48e076b3fa7901a9", + "dependencies": [ + "jsr:@denosaurs/plug" + ] }, "@bradenmacdonald/s3-lite-client@0.7.6": { "integrity": "2b5976dca95d207dc88e23f9807e3eecbc441b0cf547dcda5784afe6668404d1", "dependencies": [ - "jsr:@std/io" + "jsr:@std/io@0.224" ] }, "@core/asyncutil@1.2.0": { "integrity": "9967f15190c60df032c13f72ce5ac73d185c34f31c53dc918d8800025854c118" }, + "@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" }, @@ -113,23 +150,36 @@ "jsr:@std/bytes@0.224.0", "jsr:@std/encoding@0.224.0", "jsr:@std/media-types@0.224.0", - "jsr:@std/path" + "jsr:@std/path@0.224.0" ] }, "@nostrify/db@0.39.4": { "integrity": "53fecea3b67394cf4f52795f89d1d065bdeb0627b8655cc7fc3a89d6b21adf01", "dependencies": [ "jsr:@nostrify/nostrify@0.39", - "jsr:@nostrify/types", + "jsr:@nostrify/types@0.36", "npm:kysely@~0.27.3", "npm:nostr-tools@^2.10.4" ] }, + "@nostrify/nostrify@0.36.2": { + "integrity": "cc4787ca170b623a2e5dfed1baa4426077daa6143af728ea7dd325d58f4d04d6", + "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@^2.1.5", + "npm:zod" + ] + }, "@nostrify/nostrify@0.39.1": { "integrity": "84f98c815a07f4151bd02188a3525e438c416e9de632c79c9da9edbfca580d7f", "dependencies": [ "jsr:@nostrify/nostrify@~0.39.1", - "jsr:@nostrify/types", + "jsr:@nostrify/types@0.36", "jsr:@std/crypto", "jsr:@std/encoding@~0.224.1", "npm:@scure/base", @@ -137,10 +187,21 @@ "npm:@scure/bip39", "npm:lru-cache@^10.2.0", "npm:nostr-tools@^2.10.4", - "npm:websocket-ts", + "npm:websocket-ts@^2.2.1", "npm:zod" ] }, + "@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.35.0": { + "integrity": "b8d515563d467072694557d5626fa1600f74e83197eef45dd86a9a99c64f7fe6" + }, "@nostrify/types@0.36.0": { "integrity": "b3413467debcbd298d217483df4e2aae6c335a34765c90ac7811cf7c637600e7" }, @@ -159,6 +220,12 @@ "npm:tldts@^6.1.61" ] }, + "@std/assert@0.213.1": { + "integrity": "24c28178b30c8e0782c18e8e94ea72b16282207569cdd10ffb9d1d26f2edebfe" + }, + "@std/assert@0.223.0": { + "integrity": "eb8d6d879d76e1cc431205bd346ed4d88dc051c6366365b1af47034b0670be24" + }, "@std/assert@0.224.0": { "integrity": "8643233ec7aec38a940a8264a6e3eed9bfa44e7a71cc6b3c8874213ff401967f" }, @@ -180,12 +247,21 @@ "@std/async@1.0.12": { "integrity": "d1bfcec459e8012846fe4e38dfc4241ab23240ecda3d8d6dfcf6d81a632e803d" }, + "@std/bytes@0.223.0": { + "integrity": "84b75052cd8680942c397c2631318772b295019098f40aac5c36cead4cba51a8" + }, "@std/bytes@0.224.0": { "integrity": "a2250e1d0eb7d1c5a426f21267ab9bdeac2447fa87a3d0d1a467d3f7a6058e49" }, "@std/bytes@1.0.5": { "integrity": "4465dd739d7963d964c809202ebea6d5c6b8e3829ef25c6a224290fbb8a1021e" }, + "@std/cli@0.223.0": { + "integrity": "2feb7970f2028904c3edc22ea916ce9538113dfc170844f3eae03578c333c356", + "dependencies": [ + "jsr:@std/assert@0.223" + ] + }, "@std/crypto@0.224.0": { "integrity": "154ef3ff08ef535562ef1a718718c5b2c5fc3808f0f9100daad69e829bfcdf2d", "dependencies": [ @@ -193,6 +269,12 @@ "jsr:@std/encoding@0.224" ] }, + "@std/data-structures@1.0.6": { + "integrity": "76a7fd8080c66604c0496220a791860492ab21a04a63a969c0b9a0609bbbb760" + }, + "@std/encoding@0.213.1": { + "integrity": "fcbb6928713dde941a18ca5db88ca1544d0755ec8fb20fe61e2dc8144b390c62" + }, "@std/encoding@0.224.0": { "integrity": "efb6dca97d3e9c31392bd5c8cfd9f9fc9decf5a1f4d1f78af7900a493bcf89b5" }, @@ -202,12 +284,41 @@ "@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/fs@1.0.15": { + "integrity": "c083fb479889d6440d768e498195c3fc499d426fbf9a6592f98f53884d1d3f41", + "dependencies": [ + "jsr:@std/path@^1.0.8" + ] + }, "@std/internal@1.0.4": { "integrity": "62e8e4911527e5e4f307741a795c0b0a9e6958d0b3790716ae71ce085f755422" }, "@std/internal@1.0.6": { "integrity": "9533b128f230f73bd209408bb07a4b12f8d4255ab2a4d22a1fd6d87304aca9a4" }, + "@std/io@0.223.0": { + "integrity": "2d8c3c2ab3a515619b90da2c6ff5ea7b75a94383259ef4d02116b228393f84f1", + "dependencies": [ + "jsr:@std/assert@0.223", + "jsr:@std/bytes@0.223" + ] + }, "@std/io@0.224.9": { "integrity": "4414664b6926f665102e73c969cfda06d2c4c59bd5d0c603fd4f1b1c840d6ee3", "dependencies": [ @@ -226,14 +337,31 @@ "@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/path@1.0.8": { + "integrity": "548fa456bb6a04d3c1a1e7477986b6cffbce95102d0bb447c67c4ee70e0364be" + }, "@std/streams@0.223.0": { - "integrity": "d6b28e498ced3960b04dc5d251f2dcfc1df244b5ec5a48dc23a8f9b490be3b99" + "integrity": "d6b28e498ced3960b04dc5d251f2dcfc1df244b5ec5a48dc23a8f9b490be3b99", + "dependencies": [ + "jsr:@std/assert@0.223", + "jsr:@std/bytes@0.223", + "jsr:@std/io@0.223" + ] }, "@std/testing@1.0.9": { "integrity": "9bdd4ac07cb13e7594ac30e90f6ceef7254ac83a9aeaa089be0008f33aab5cd4" @@ -241,7 +369,11 @@ "@std/testing@1.0.10": { "integrity": "8997bd0b0df020b81bf5eae103c66622918adeff7e45e96291c92a29dbf82cc1", "dependencies": [ - "jsr:@std/assert@^1.0.12" + "jsr:@std/assert@^1.0.12", + "jsr:@std/data-structures", + "jsr:@std/fs@^1.0.15", + "jsr:@std/internal@^1.0.6", + "jsr:@std/path@^1.0.8" ] } }, @@ -429,6 +561,9 @@ "@noble/hashes@1.7.1": { "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==" }, + "@noble/secp256k1@2.2.3": { + "integrity": "sha512-l7r5oEQym9Us7EAigzg30/PQAvynhMt2uoYtT3t26eGDVm9Yii5mZ5jWSWmZ/oSIR2Et0xfc6DXrG0bZ787V3w==" + }, "@opentelemetry/api@1.9.0": { "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==" }, @@ -535,6 +670,12 @@ "delayed-stream" ] }, + "comlink-async-generator@0.0.1": { + "integrity": "sha512-RjOPv6Tb7cL9FiIgwanUJuFG9aW4myAFyyzxZoEkEegeDQrZqr92d1Njv2WIgi7nbGpTiyy5GdNTUubDaNgZ6A==", + "dependencies": [ + "comlink" + ] + }, "comlink@4.4.2": { "integrity": "sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==" }, @@ -639,6 +780,9 @@ "hasown" ] }, + "fast-stable-stringify@1.0.0": { + "integrity": "sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==" + }, "form-data@4.0.2": { "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", "dependencies": [ @@ -989,6 +1133,9 @@ "punycode" ] }, + "tseep@1.3.1": { + "integrity": "sha512-ZPtfk1tQnZVyr7BPtbJ93qaAh2lZuIOpTMjhrYa4XctT8xe7t4SAW9LIxrySDuYMsfNNayE51E/WNGrNVgVicQ==" + }, "tslib@2.8.1": { "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, @@ -1327,6 +1474,9 @@ "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.80.0/encoding/utf8.ts": "1b7e77db9a12363c67872f8a208886ca1329f160c1ca9133b13d2ed399688b99", + "https://deno.land/x/keypress@0.0.7/dep.ts": "feeb0056d332c126343249b79fe86cb0bf3abd03ea4c270cd39575c38d37a911", + "https://deno.land/x/keypress@0.0.7/mod.ts": "1130570c2397118a3a301b1137400a8e55486716cc3557b3bd5e9947b6b9c035", "https://deno.land/x/sentry@7.112.2/index.mjs": "04382d5c2f4e233ba389611db46f77943b2a7f6efbeaaf31193f6e586f4366ef", "https://gitlab.com/soapbox-pub/nostrify/-/raw/58c20b3ee23d8be4987d2c0055d86960e7db763d/packages/nostrify/NSchema.ts": "b6f72b3e8a24f8c29f84fea03f5d5dc1926196be751aec598457358846e9b3d5", "https://gitlab.com/soapbox-pub/nostrify/-/raw/58c20b3ee23d8be4987d2c0055d86960e7db763d/packages/policies/AntiDuplicationPolicy.ts": "8586878d762312a326e468ec6de948f3ca7c23bc7fe807ccb7b5dfbd90d3041e", @@ -1387,7 +1537,17 @@ "https://gitlab.com/soapbox-pub/postgres.js/-/raw/e79d7d2039446fbf7a37d4eca0d17e94a94b8b53/deno/src/queue.js": "709624843223ea842bf095f6934080f19f1a059a51cbbf82e9827f3bb1bf2ca7", "https://gitlab.com/soapbox-pub/postgres.js/-/raw/e79d7d2039446fbf7a37d4eca0d17e94a94b8b53/deno/src/result.js": "001ff5e0c8d634674f483d07fbcd620a797e3101f842d6c20ca3ace936260465", "https://gitlab.com/soapbox-pub/postgres.js/-/raw/e79d7d2039446fbf7a37d4eca0d17e94a94b8b53/deno/src/subscribe.js": "9e4d0c3e573a6048e77ee2f15abbd5bcd17da9ca85a78c914553472c6d6c169b", - "https://gitlab.com/soapbox-pub/postgres.js/-/raw/e79d7d2039446fbf7a37d4eca0d17e94a94b8b53/deno/src/types.js": "471f4a6c35412aa202a7c177c0a7e5a7c3bd225f01bbde67c947894c1b8bf6ed" + "https://gitlab.com/soapbox-pub/postgres.js/-/raw/e79d7d2039446fbf7a37d4eca0d17e94a94b8b53/deno/src/types.js": "471f4a6c35412aa202a7c177c0a7e5a7c3bd225f01bbde67c947894c1b8bf6ed", + "https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/KeyCombo.ts": "a370b2dca76faa416d00e45479c8ce344971b5b86b44b4d0b213245c4bd2f8a3", + "https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/checkbox.ts": "e337ee7396aaefe6cc8c6349a445542fe7f0760311773369c9012b3fa278d21e", + "https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/config.ts": "a94a022c757f63ee7c410e29b97d3bfab1811889fb4483f56395cf376a911d1b", + "https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/confirm.ts": "dde395f351e14ebff9dd882c49c1376f0c648a5978d17dfad997f9958df01d1c", + "https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/input.ts": "5b7a3e194e6a5b74bb26d5ef1363d78619769772ad01244fd6f95b9c66cc6f0d", + "https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/list.ts": "3b96403b043b5b5bc417d8d07b34828961c1f9d2bdbc9f24e6ef40fb9c95438e", + "https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/mod.ts": "cb10c598652cf7edf600af17f73bcadcdedf6900d9f5b5647e89ba2ea378b7d5", + "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/util.ts": "a8285450db7b56a3e507f478aaad68927ecb1ee545449cb869ccc4aace13fada" }, "workspace": { "dependencies": [ @@ -1400,6 +1560,7 @@ "jsr:@negrel/webpush@0.3", "jsr:@nostrify/db@~0.39.4", "jsr:@nostrify/nostrify@~0.39.1", + "jsr:@nostrify/policies@~0.36.1", "jsr:@nostrify/types@0.36", "jsr:@soapbox/kysely-pglite@1", "jsr:@soapbox/logi@0.3", diff --git a/packages/ditto/app.ts b/packages/ditto/app.ts index c1afb960..7331181b 100644 --- a/packages/ditto/app.ts +++ b/packages/ditto/app.ts @@ -156,7 +156,7 @@ import { adminListPoliciesController, adminUpdatePolicyController, } from '@/controllers/api/policies.ts'; -import { createPolicyEvent, DEFAULT_POLICY_SPEC } from '@/utils/policies.ts'; +import { createPolicyEvent, DEFAULT_POLICY_SPEC } from '@/utils/policies/mod.ts'; export interface AppEnv extends DittoEnv { Variables: DittoEnv['Variables'] & { diff --git a/packages/ditto/controllers/api/instance.ts b/packages/ditto/controllers/api/instance.ts index 201aef0d..c5a43316 100644 --- a/packages/ditto/controllers/api/instance.ts +++ b/packages/ditto/controllers/api/instance.ts @@ -30,7 +30,7 @@ const cache = (f: () => Promise, interval: number) => { logi({ level: 'error', ns: 'ditto.routes.instanceV1', - message: `Error fetching cached value: ${error}` + message: `Error fetching cached value: ${error}`, }); value = undefined; // Ensure we retry next time } diff --git a/packages/ditto/controllers/api/policies.ts b/packages/ditto/controllers/api/policies.ts index 20db3c16..2995d756 100644 --- a/packages/ditto/controllers/api/policies.ts +++ b/packages/ditto/controllers/api/policies.ts @@ -1,6 +1,6 @@ import { type AppController } from '@/app.ts'; -import { createPolicyEvent } from '@/utils/policies.ts'; -import { DEFAULT_POLICY_SPEC, policyRegistry } from '@/utils/policies.ts'; +import { createPolicyEvent } from '@/utils/policies/mod.ts'; +import { DEFAULT_POLICY_SPEC, policyRegistry } from '@/utils/policies/mod.ts'; import { z } from 'zod'; export const adminListPoliciesController: AppController = (c) => { diff --git a/packages/ditto/utils/policies.ts b/packages/ditto/utils/policies.ts deleted file mode 100644 index c987900d..00000000 --- a/packages/ditto/utils/policies.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { nostrNow } from '@/utils.ts'; -import type { DittoConf } from '@ditto/conf'; -import { PolicyRegistry } from '@nostrify/policies'; -import { MockRelay } from '@nostrify/nostrify/test'; - -type ParamValue = string | number | boolean; - -export const policyRegistry = new PolicyRegistry({ - antiDuplicationPolicyStore: { - get: (key: Deno.KvKey) => Promise.resolve({ key, value: null, versionstamp: null }), - set: () => Promise.resolve({ ok: true, versionstamp: '00000000000000000000' }), - }, - store: new MockRelay(), -}); - -interface PolicySpecItem { - name: keyof typeof policyRegistry.available; - params?: Record; -} - -export interface PolicySpec { - policies: PolicySpecItem[]; -} - -export const DEFAULT_POLICY_SPEC: PolicySpec = { - policies: [ - { 'name': 'AntiDuplicationPolicy' }, - { 'name': 'HellthreadPolicy' }, - { 'name': 'ReplyBotPolicy' }, - { 'name': 'SizePolicy' }, - { 'name': 'HashtagPolicy', 'params': { 'hashtags': ['NSFW', 'explicit', 'violence', 'cp', 'porn'] } }, - ], -}; - -export const createPolicyEvent = async (conf: DittoConf, policies: PolicySpec) => { - return await conf.signer.signEvent({ - kind: 11984, - content: JSON.stringify(policies), - created_at: nostrNow(), - tags: [], - }); -}; diff --git a/packages/ditto/utils/policies/mod.ts b/packages/ditto/utils/policies/mod.ts new file mode 100644 index 00000000..374be9ee --- /dev/null +++ b/packages/ditto/utils/policies/mod.ts @@ -0,0 +1,50 @@ +import { nostrNow } from '@/utils.ts'; +import type { DittoConf } from '@ditto/conf'; +import { PolicyRegistry } from './registry.ts'; +import { MockRelay } from '@nostrify/nostrify/test'; +import { nip19 } from 'nostr-tools'; + +type ParamValue = string | number | boolean; + +export { PolicyRegistry }; + +export const policyRegistry = new PolicyRegistry({ + antiDuplicationPolicyStore: { + get: (key: Deno.KvKey) => Promise.resolve({ key, value: null, versionstamp: null }), + set: () => Promise.resolve({ ok: true, versionstamp: '00000000000000000000' }), + }, + store: new MockRelay(), +}); + +interface PolicySpecItem { + name: keyof typeof policyRegistry.available; + params?: Record; +} + +export interface PolicySpec { + policies: PolicySpecItem[]; +} + +export const normalizeNpub = (itm: string) => { + if (!itm.startsWith('npub1')) return itm; + return nip19.decode(itm as `npub1${string}`).data; +}; + +export const DEFAULT_POLICY_SPEC: PolicySpec = { + policies: [ + { 'name': 'AntiDuplicationPolicy' }, + { 'name': 'HellthreadPolicy' }, + { 'name': 'ReplyBotPolicy' }, + { 'name': 'SizePolicy' }, + { 'name': 'HashtagPolicy', 'params': { 'hashtags': ['NSFW', 'explicit', 'violence', 'cp', 'porn'] } }, + ], +}; + +export const createPolicyEvent = async (conf: DittoConf, policies: PolicySpec) => { + return await conf.signer.signEvent({ + kind: 11984, + content: JSON.stringify(policies), + created_at: nostrNow(), + tags: [], + }); +}; diff --git a/packages/ditto/utils/policies/parameters.test.ts b/packages/ditto/utils/policies/parameters.test.ts new file mode 100644 index 00000000..3bcd7f6c --- /dev/null +++ b/packages/ditto/utils/policies/parameters.test.ts @@ -0,0 +1,105 @@ +import { assertEquals } from '@std/assert'; +import { z } from 'zod'; +import { zodSchemaToFields } from './parameters.ts'; + +Deno.test('zodSchemaToFields - basic types', () => { + const schema = z.object({ + name: z.string(), + age: z.number(), + }); + + const result = zodSchemaToFields(schema); + assertEquals(result, { + name: { type: 'string' }, + age: { type: 'number' }, + }); +}); + +Deno.test('zodSchemaToFields - array types', () => { + const schema = z.object({ + tags: z.array(z.string()), + scores: z.array(z.number()), + }); + + const result = zodSchemaToFields(schema); + assertEquals(result, { + tags: { type: 'multi_string' }, + scores: { type: 'multi_number' }, + }); +}); + +Deno.test('zodSchemaToFields - special-case NIP-01 filters', () => { + const schema = z.object({ + filters: z.array(z.string()), + keywords: z.array(z.string()), + }); + + const result = zodSchemaToFields(schema); + assertEquals(result, { + filters: { type: 'multi_string' }, + keywords: { type: 'multi_string' }, + }); +}); + +Deno.test('zodSchemaToFields - mixed types', () => { + const schema = z.object({ + id: z.string(), + values: z.array(z.number()), + flags: z.array(z.string()).describe('Test description'), + }); + + const result = zodSchemaToFields(schema); + assertEquals(result, { + id: { type: 'string' }, + values: { type: 'multi_number' }, + flags: { type: 'multi_string', description: 'Test description' }, + }); +}); + +Deno.test('zodSchemaToFields - optional fields', () => { + const schema = z.object({ + name: z.string().optional(), + age: z.number().optional(), + }); + + const result = zodSchemaToFields(schema); + assertEquals(result, { + name: { type: 'string', optional: true }, + age: { type: 'number', optional: true }, + }); +}); + +Deno.test('zodSchemaToFields - default values', () => { + const schema = z.object({ + name: z.string().default('John Doe'), + age: z.number().default(30), + }); + + const result = zodSchemaToFields(schema); + assertEquals(result, { + name: { type: 'string', default: 'John Doe' }, + age: { type: 'number', default: 30 }, + }); +}); + +Deno.test('zodSchemaToFields - boolean fields', () => { + const schema = z.object({ + active: z.boolean(), + }); + + const result = zodSchemaToFields(schema); + assertEquals(result, { + active: { type: 'boolean' }, + }); +}); + +Deno.test('zodSchemaToFields - invalid schema', () => { + const schema = z.object({ + invalid: z.any(), + }); + + const result = zodSchemaToFields(schema); + assertEquals(result, { + invalid: { type: 'unknown' }, + }); +}); diff --git a/packages/ditto/utils/policies/parameters.ts b/packages/ditto/utils/policies/parameters.ts new file mode 100644 index 00000000..ab9fec7a --- /dev/null +++ b/packages/ditto/utils/policies/parameters.ts @@ -0,0 +1,90 @@ +import { z } from 'zod'; + +type FieldType = 'string' | 'multi_string' | 'number' | 'multi_number' | 'boolean' | 'unknown'; + +export interface FieldItem { + type: FieldType; + description?: string; + optional?: boolean; + default?: any; +} + +interface UnwrappedZodType { + baseType: z.ZodTypeAny; + optional?: boolean; + defaultValue?: any; + description?: string; +} + +/** + * Extracts the base type from wrapped Zod types like ZodOptional and ZodDefault. + */ +function unwrapZodType(field: z.ZodTypeAny): UnwrappedZodType { + let optional = false; + let defaultValue: any = undefined; + let description: string | undefined = undefined; + + description = field.description; + + while (field instanceof z.ZodOptional || field instanceof z.ZodDefault) { + if (field instanceof z.ZodOptional) optional = true; + if (field instanceof z.ZodDefault) defaultValue = field._def.defaultValue(); + if (!description) description = field.description; + + field = field._def.innerType; + } + + const result: UnwrappedZodType = { baseType: field }; + + if (optional) result.optional = true; + if (typeof defaultValue !== 'undefined') { + if ( + typeof defaultValue === 'string' && defaultValue.length > 0 || + (typeof defaultValue === 'number') || + (typeof defaultValue === 'object' && Object.keys(defaultValue).length > 0) || + (Array.isArray(defaultValue) && defaultValue.length > 0) + ) { + result.defaultValue = defaultValue; + } + } + if (description) result.description = description; + return result; +} + +/** + * Serializes a Zod schema into a record of field types. NOT meant to be used + * as a general-purpose serializer - this is specifically for generating policy + * parameter descriptions! This function also parses internal Zod properties, + * but there is precedent for this: + * https://github.com/colinhacks/zod/discussions/1953 + * + * With version pinning and the Zod maintainers' knowledge of this kind of + * usage, it should be fine for it to be this way. + * + * Special-cases NIP-01 filters as `multi_string`. + */ +export function zodSchemaToFields(schema: z.ZodObject): Record { + const result: Record = {}; + + for (const [key, field] of Object.entries(schema.shape) as [string, z.ZodTypeAny][]) { + const { baseType, optional, defaultValue, description } = unwrapZodType(field); + result[key] = { type: 'unknown' }; + if (optional) result[key].optional = optional; + if (defaultValue) result[key].default = defaultValue; + if (description) result[key].description = description; + + if (key === 'filters') { + result[key].type = 'multi_string'; + } else if (baseType instanceof z.ZodArray) { + const elementType = unwrapZodType(baseType._def.type).baseType._def.typeName; + if (elementType === 'ZodNumber') result[key].type = 'multi_number'; + else result[key].type = 'multi_string'; + } else if (baseType instanceof z.ZodNumber) result[key].type = 'number'; + else if (baseType instanceof z.ZodBoolean) result[key].type = 'boolean'; + else if (baseType instanceof z.ZodString) result[key].type = 'string'; + + if (baseType.description && !result[key].description) result[key].description = baseType.description; + } + + return result; +} diff --git a/packages/ditto/utils/policies/registry.ts b/packages/ditto/utils/policies/registry.ts new file mode 100644 index 00000000..9e1638db --- /dev/null +++ b/packages/ditto/utils/policies/registry.ts @@ -0,0 +1,218 @@ +import { + AntiDuplicationPolicy, + AuthorPolicy, + DomainPolicy, + FiltersPolicy, + HashtagPolicy, + HellthreadPolicy, + KeywordPolicy, + OpenAIPolicy, + PowPolicy, + PubkeyBanPolicy, + RegexPolicy, + ReplyBotPolicy, + SizePolicy, + WhitelistPolicy, + WoTPolicy, +} from '@nostrify/policies'; +import { FieldItem, zodSchemaToFields } from './parameters.ts'; +import { NPolicy, NStore } from '@nostrify/types'; +import { + AntiDuplicationPolicyOpts, + AntiDuplicationPolicyOptsSchema, + DomainPolicyOptsSchema, + FiltersPolicyOptsSchema, + HashtagPolicyOptsSchema, + HellthreadPolicyOptsSchema, + KeywordPolicyOptsSchema, + OpenAIPolicyOptsSchema, + PowPolicyOptsSchema, + PubkeyBanPolicyOptsSchema, + RegexPolicyOptsSchema, + ReplyBotPolicyOptsSchema, + SizePolicyOptsSchema, + WhitelistPolicyOptsSchema, + WoTPolicyOptsSchema, +} from '@/utils/policies/schemas.ts'; +import { normalizeNpub } from '@/utils/policies/mod.ts'; + +export interface PolicyItem { + instantiate: (params: Record) => NPolicy; + name: string; + parameters: Record; + description: string; +} + +export interface PolicyRegistryOpts { + antiDuplicationPolicyStore?: AntiDuplicationPolicyOpts['kv']; + store: NStore; +} + +export class PolicyRegistry { + constructor(private opts: PolicyRegistryOpts) {} + + available: Record = { + 'AntiDuplicationPolicy': { + instantiate: (params) => { + const parsed = AntiDuplicationPolicyOptsSchema.parse(params); + if (!this.opts.antiDuplicationPolicyStore) { + throw new Error('AntiDuplicationPolicy: tried to instantiate store but no store supplied!'); + } + return new AntiDuplicationPolicy({ + kv: this.opts.antiDuplicationPolicyStore, + ...parsed, + }); + }, + description: 'Prevent messages with the exact same content from being submitted repeatedly.', + name: 'Deduplicate messages', + parameters: zodSchemaToFields(AntiDuplicationPolicyOptsSchema), + }, + 'AuthorPolicy': { + instantiate: () => { + return new AuthorPolicy(this.opts.store); + }, + description: 'Rejects events by authors without a kind 0 event associated with their pubkey.', + name: 'Block events without associated profiles', + parameters: {}, + }, + 'DomainPolicy': { + instantiate: (params) => { + const parsed = DomainPolicyOptsSchema.parse(params); + return new DomainPolicy(this.opts.store, parsed); + }, + description: 'Ban events by pubkeys without a valid NIP-05 domain. Domains can also be whitelisted/blacklisted', + name: 'Filter by NIP-05', + parameters: zodSchemaToFields(DomainPolicyOptsSchema), + }, + 'FiltersPolicy': { + instantiate: (params) => { + if (!params.filters || !Array.isArray(params.filters)) throw new Error('Invalid params to FiltersPolicy'); + const filters = params.filters.map((item) => { + try { + return JSON.parse(item); + } catch { + return undefined; + } + }).filter(Boolean); + const parsed = FiltersPolicyOptsSchema.parse({ filters }); + return new FiltersPolicy(parsed.filters); + }, + description: 'Only allow events matching a given Nostr filter', + name: 'Filter by Nostr filter', + parameters: zodSchemaToFields(FiltersPolicyOptsSchema), + }, + 'HashtagPolicy': { + instantiate: (params) => { + const parsed = HashtagPolicyOptsSchema.parse(params); + return new HashtagPolicy(parsed.hashtags); + }, + description: 'Ban events containing the specified hashtags', + name: 'Ban hashtags', + parameters: zodSchemaToFields(HashtagPolicyOptsSchema), + }, + 'HellthreadPolicy': { + instantiate: (params) => { + const parsed = HellthreadPolicyOptsSchema.parse(params); + return new HellthreadPolicy({ + ...parsed, + }); + }, + description: "Prevent 'hellthreads' - notes that tag hundreds of people to cause a nuisance and server load.", + name: 'Limit events with excessive mentions', + parameters: zodSchemaToFields(HellthreadPolicyOptsSchema), + }, + 'KeywordPolicy': { + instantiate: (params) => { + const parsed = KeywordPolicyOptsSchema.parse(params); + return new KeywordPolicy(parsed.keywords); + }, + description: 'Ban events that contain specified keywords.', + name: 'Block certain words', + parameters: zodSchemaToFields(KeywordPolicyOptsSchema), + }, + 'OpenAIPolicy': { + instantiate: (params) => { + const parsed = OpenAIPolicyOptsSchema.parse(params); + return new OpenAIPolicy({ + ...parsed, + }); + }, + description: "Use OpenAI moderation integration to block posts that don't meet your community guidelines.", + name: 'Use OpenAI moderation', + parameters: zodSchemaToFields(OpenAIPolicyOptsSchema), + }, + 'PowPolicy': { + instantiate: (params) => { + const parsed = PowPolicyOptsSchema.parse(params); + return new PowPolicy({ + ...parsed, + }); + }, + description: 'Use proof-of-work to limit events from spammers.', + name: 'Require proof-of-work for events', + parameters: zodSchemaToFields(PowPolicyOptsSchema), + }, + 'PubkeyBanPolicy': { + instantiate: (params) => { + const parsed = PubkeyBanPolicyOptsSchema.parse(params); + return new PubkeyBanPolicy(parsed.pubkeys.map(normalizeNpub)); + }, + description: 'Ban events from certain pubkeys', + name: 'Ban certain pubkeys', + parameters: zodSchemaToFields(PubkeyBanPolicyOptsSchema), + }, + 'RegexPolicy': { + instantiate: (params) => { + const parsed = RegexPolicyOptsSchema.parse(params); + return new RegexPolicy(new RegExp(parsed.expr, parsed.flags)); + }, + description: 'Ban events that match a certain regular expression.', + name: 'Filter by regex', + parameters: zodSchemaToFields(RegexPolicyOptsSchema), + }, + 'ReplyBotPolicy': { + instantiate: (params) => { + const parsed = ReplyBotPolicyOptsSchema.parse(params); + return new ReplyBotPolicy({ + store: this.opts.store, + ...parsed, + }); + }, + description: 'Block events that reply too quickly to other events.', + name: 'Block reply spambots', + parameters: zodSchemaToFields(ReplyBotPolicyOptsSchema), + }, + 'SizePolicy': { + instantiate: (params) => { + const parsed = SizePolicyOptsSchema.parse(params); + return new SizePolicy({ + ...parsed, + }); + }, + description: 'Restrict events that are too big in size.', + name: 'Block events by size', + parameters: zodSchemaToFields(SizePolicyOptsSchema), + }, + 'WhitelistPolicy': { + instantiate: (params) => { + const parsed = WhitelistPolicyOptsSchema.parse(params); + return new WhitelistPolicy(parsed.pubkeys.map(normalizeNpub)); + }, + description: 'Allow only whitelisted pubkeys to post. All other events are rejected.', + name: 'Whitelist people', + parameters: zodSchemaToFields(WhitelistPolicyOptsSchema), + }, + 'WoTPolicy': { + instantiate: (params) => { + const parsed = WoTPolicyOptsSchema.parse(params); + return new WoTPolicy({ + store: this.opts.store, + ...parsed, + }); + }, + description: 'Use a web-of-trust to only allow users a certain distance from trusted users to publish posts.', + name: 'Build a web-of-trust', + parameters: zodSchemaToFields(WoTPolicyOptsSchema), + }, + }; +} diff --git a/packages/ditto/utils/policies/schemas.ts b/packages/ditto/utils/policies/schemas.ts new file mode 100644 index 00000000..1c827274 --- /dev/null +++ b/packages/ditto/utils/policies/schemas.ts @@ -0,0 +1,150 @@ +import { NostrEvent, NProfilePointer, NStore } from '@nostrify/types'; +import { z } from 'zod'; +import { NSchema as n } from '@nostrify/nostrify'; + +/** Options for the `WoTPolicy`. */ +export const WoTPolicyOptsSchema = z.object({ + pubkeys: z.array(z.string()), + depth: z.number().default(2), +}); + +export interface WoTPolicyOpts extends z.TypeOf { + store: NStore; +} + +/** Options for `FiltersPolicy`. */ +export const FiltersPolicyOptsSchema = z.object({ + filters: z.array(n.filter()), +}); + +interface FiltersPolicyOpts extends z.TypeOf { } + +/** Options for `HashtagPolicy`. */ +export const HashtagPolicyOptsSchema = z.object({ + hashtags: z.array(z.string()).describe('Banned hashtags (case-insensitive)'), +}); + +export interface HashtagPolicyOpts extends z.TypeOf { } + +/** Options for `WhitelistPolicy`. */ +export const WhitelistPolicyOptsSchema = z.object({ + pubkeys: z.array(z.string()).describe('Allowed pubkeys'), +}); + +export interface WhitelistPolicyOpts extends z.TypeOf { } + +/** Options for `RegexPolicy`. */ +export const RegexPolicyOptsSchema = z.object({ + expr: z.string().describe('Expression'), + flags: z.string().optional().describe('Additional RegExp flags. Leave blank if unsure.'), +}); + +export interface RegexPolicyOpts extends Partial> { + expr: string; +} + +/** Options for `KeywordPolicy`. */ +export const KeywordPolicyOptsSchema = z.object({ + keywords: z.array(z.string()).describe('Banned keywords (case-insensitive)'), +}); + +export interface KeywordPolicyOpts extends z.TypeOf { } + +/** Options for `DomainPolicy`. */ +export const DomainPolicyOptsSchema = z.object({ + blacklist: z.array(z.string()).default([]).describe('Domains to blacklist'), + whitelist: z.array(z.string()).optional().describe('Domains to whitelist'), +}); + +export interface DomainPolicyOpts extends Partial> { + lookup?(nip05: string, signal?: AbortSignal): Promise; + store: NStore; +} + +/** Options for `PubkeyBanPolicy`. */ +export const PubkeyBanPolicyOptsSchema = z.object({ + pubkeys: z.array(z.string()).describe('Banned pubkeys'), +}); + +export interface PubkeyBanPolicyOpts extends z.TypeOf { } + +/** Options for `OpenAIPolicy`. */ +export const OpenAIPolicyOptsSchema = z.object({ + endpoint: z.string().default('https://api.openai.com/v1/moderations').describe('OpenAI API endpoint'), + kinds: z.array(z.number()).default([1, 30023]).describe('Event kinds to filter through the policy'), + apiKey: z.string().describe('OpenAI API key'), +}); + +interface OpenAIModerationResult { + id: string; + model: string; + results: { + categories: { + 'hate': boolean; + 'hate/threatening': boolean; + 'self-harm': boolean; + 'sexual': boolean; + 'sexual/minors': boolean; + 'violence': boolean; + 'violence/graphic': boolean; + }; + category_scores: { + 'hate': number; + 'hate/threatening': number; + 'self-harm': number; + 'sexual': number; + 'sexual/minors': number; + 'violence': number; + 'violence/graphic': number; + }; + flagged: boolean; + }[]; +} + +export interface OpenAIPolicyOpts extends Partial> { + apiKey: string; + handler?(event: NostrEvent, result: OpenAIModerationResult): boolean; + fetch?: typeof fetch; +} + +/** Options for `ReplyBotPolicy`. */ +export const ReplyBotPolicyOptsSchema = z.object({ + threshold: z.number().default(1).describe('Minimum time in seconds between two posts.'), + kinds: z.array(z.number()).default([1]).describe('Event kinds to filter'), +}); + +export interface ReplyBotPolicyOpts extends Partial> { + store: NStore; +} + +/** Options for `SizePolicy`. */ +export const SizePolicyOptsSchema = z.object({ + maxBytes: z.number().default(8 * 1024).describe('Max allowed message size in bytes'), +}); + +export interface SizePolicyOpts extends Partial> { } + +/** Options for `AntiDuplicationPolicy`. */ +export const AntiDuplicationPolicyOptsSchema = z.object({ + expireIn: z.number().default(60000).describe('How long should we wait before an identical message can be reposted?'), + minLength: z.number().default(50).describe('Minimum length for filtered messages'), +}); + +export interface AntiDuplicationPolicyOpts extends Partial> { + kv: Pick; + deobfuscate?(event: NostrEvent): string; +} + +/** Options for `HellthreadPolicy`. */ +export const HellthreadPolicyOptsSchema = z.object({ + limit: z.number().default(100).describe('Maximum number of mentions to allow per post'), +}); + +export interface HellthreadPolicyOpts extends Partial> { } + +/** Options for `PowPolicy`. */ +export const PowPolicyOptsSchema = z.object({ + difficulty: z.number().default(1).describe('Number of bits of proof-of-work to require'), +}); + +export interface PowPolicyOpts extends Partial> { } diff --git a/packages/ditto/workers/policy.worker.ts b/packages/ditto/workers/policy.worker.ts index a2bcadbe..0803b45d 100644 --- a/packages/ditto/workers/policy.worker.ts +++ b/packages/ditto/workers/policy.worker.ts @@ -2,12 +2,12 @@ import { DittoConf } from '@ditto/conf'; import { DittoPolyPg } from '@ditto/db'; import '@soapbox/safe-fetch/load'; import { NostrEvent, NostrRelayOK, NPolicy } from '@nostrify/nostrify'; -import { PipePolicy, PolicyRegistry, ReadOnlyPolicy } from '@nostrify/policies'; +import { PipePolicy, ReadOnlyPolicy } from '@nostrify/policies'; import * as Comlink from 'comlink'; import { ReadOnlySigner } from '@/signers/ReadOnlySigner.ts'; import { DittoPgStore } from '@/storages/DittoPgStore.ts'; -import { DEFAULT_POLICY_SPEC, PolicySpec } from '@/utils/policies.ts'; +import { DEFAULT_POLICY_SPEC, PolicyRegistry, PolicySpec } from '@/utils/policies/mod.ts'; import { logi } from '@soapbox/logi'; // @ts-ignore Don't try to access the env from this worker.