From f33e03740ef2e2e67087171e98fb114d0ca078b3 Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Tue, 17 Dec 2024 10:59:27 -0800 Subject: [PATCH] Remove tsc-multi, re-install gts, apply autoformatting and linting --- .eslintignore | 1 + .eslintrc.json | 22 +- .prettierrc.js | 3 - build_and_link.sh | 2 +- package-lock.json | 528 +++++++----------------- package.json | 7 +- packages/app/package.json | 11 +- packages/app/src/collection.ts | 8 +- packages/app/src/commands.ts | 13 +- packages/app/src/context.ts | 16 +- packages/app/src/core.ts | 52 +-- packages/app/src/feeds.ts | 59 +-- packages/app/src/follows.ts | 17 +- packages/app/src/freshness.ts | 8 +- packages/app/src/handles.ts | 51 ++- packages/app/src/index.ts | 62 +-- packages/app/src/mutes.ts | 17 +- packages/app/src/plaintext.ts | 10 +- packages/app/src/profiles.ts | 20 +- packages/app/src/relaySelections.ts | 33 +- packages/app/src/relays.ts | 144 ++++--- packages/app/src/router.ts | 140 +++---- packages/app/src/search.ts | 37 +- packages/app/src/session.ts | 24 +- packages/app/src/storage.ts | 213 +++++----- packages/app/src/subscribe.ts | 2 +- packages/app/src/sync.ts | 50 +-- packages/app/src/tags.ts | 19 +- packages/app/src/thunk.ts | 44 +- packages/app/src/topics.ts | 6 +- packages/app/src/types.d.ts | 9 - packages/app/src/user.ts | 62 ++- packages/app/src/wot.ts | 56 ++- packages/app/src/zappers.ts | 49 ++- packages/app/tsc-multi.json | 7 - packages/app/tsconfig.json | 11 +- packages/content/package.json | 14 +- packages/content/src/index.ts | 165 ++++---- packages/content/tsc-multi.json | 7 - packages/content/tsconfig.json | 11 +- packages/dvm/README.md | 10 +- packages/dvm/package.json | 12 +- packages/dvm/src/handler.ts | 32 +- packages/dvm/src/index.ts | 4 +- packages/dvm/src/request.ts | 10 +- packages/dvm/tsc-multi.json | 7 - packages/dvm/tsconfig.json | 11 +- packages/feeds/package.json | 11 +- packages/feeds/src/compiler.ts | 116 ++++-- packages/feeds/src/controller.ts | 131 +++--- packages/feeds/src/core.ts | 80 ++-- packages/feeds/src/index.ts | 8 +- packages/feeds/src/utils.ts | 172 +++++--- packages/feeds/tsc-multi.json | 7 - packages/feeds/tsconfig.json | 11 +- packages/lib/package.json | 13 +- packages/lib/src/Context.ts | 2 +- packages/lib/src/Deferred.ts | 7 +- packages/lib/src/Emitter.ts | 4 +- packages/lib/src/LRUCache.ts | 2 +- packages/lib/src/Tools.ts | 84 ++-- packages/lib/src/index.ts | 18 +- packages/lib/src/normalize-url/index.ts | 4 +- packages/lib/src/types.d.ts | 3 - packages/lib/tsc-multi.json | 7 - packages/lib/tsconfig.json | 11 +- packages/net/package.json | 11 +- packages/net/src/Connection.ts | 18 +- packages/net/src/ConnectionAuth.ts | 53 +-- packages/net/src/ConnectionEvent.ts | 18 +- packages/net/src/ConnectionSender.ts | 24 +- packages/net/src/ConnectionState.ts | 38 +- packages/net/src/ConnectionStats.ts | 25 +- packages/net/src/Context.ts | 41 +- packages/net/src/Executor.ts | 60 +-- packages/net/src/Pool.ts | 6 +- packages/net/src/Publish.ts | 15 +- packages/net/src/Socket.ts | 68 ++- packages/net/src/Subscribe.ts | 188 +++++---- packages/net/src/Sync.ts | 63 +-- packages/net/src/Tracker.ts | 12 +- packages/net/src/index.ts | 46 ++- packages/net/src/target/Echo.ts | 4 +- packages/net/src/target/Local.ts | 10 +- packages/net/src/target/Multi.ts | 8 +- packages/net/src/target/Relay.ts | 8 +- packages/net/src/target/Relays.ts | 10 +- packages/net/src/types.d.ts | 8 - packages/net/tsc-multi.json | 7 - packages/net/tsconfig.json | 9 +- packages/signer/package.json | 16 +- packages/signer/src/index.ts | 12 +- packages/signer/src/nip59.ts | 61 ++- packages/signer/src/signers/nip01.ts | 16 +- packages/signer/src/signers/nip07.ts | 10 +- packages/signer/src/signers/nip46.ts | 88 ++-- packages/signer/src/signers/nip55.ts | 17 +- packages/signer/src/util.ts | 23 +- packages/signer/tsc-multi.json | 7 - packages/signer/tsconfig.json | 11 +- packages/store/package.json | 11 +- packages/store/src/index.ts | 197 +++++---- packages/store/tsc-multi.json | 7 - packages/store/tsconfig.json | 11 +- packages/util/package.json | 15 +- packages/util/src/Address.ts | 21 +- packages/util/src/Encryptable.ts | 9 +- packages/util/src/Events.ts | 50 ++- packages/util/src/Filters.ts | 41 +- packages/util/src/Handler.ts | 14 +- packages/util/src/Kinds.ts | 17 +- packages/util/src/Links.ts | 2 +- packages/util/src/List.ts | 23 +- packages/util/src/Profile.ts | 18 +- packages/util/src/Relay.ts | 60 +-- packages/util/src/Repository.ts | 42 +- packages/util/src/Tags.ts | 10 +- packages/util/src/Zaps.ts | 8 +- packages/util/src/index.ts | 26 +- packages/util/tsc-multi.json | 7 - packages/util/tsconfig.json | 8 +- src/index.ts | 26 ++ 122 files changed, 2243 insertions(+), 2178 deletions(-) create mode 100644 .eslintignore delete mode 100644 .prettierrc.js delete mode 100644 packages/app/src/types.d.ts delete mode 100644 packages/app/tsc-multi.json delete mode 100644 packages/content/tsc-multi.json delete mode 100644 packages/dvm/tsc-multi.json delete mode 100644 packages/feeds/tsc-multi.json delete mode 100644 packages/lib/src/types.d.ts delete mode 100644 packages/lib/tsc-multi.json delete mode 100644 packages/net/src/types.d.ts delete mode 100644 packages/net/tsc-multi.json delete mode 100644 packages/signer/tsc-multi.json delete mode 100644 packages/store/tsc-multi.json delete mode 100644 packages/util/tsc-multi.json create mode 100644 src/index.ts diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +build/ diff --git a/.eslintrc.json b/.eslintrc.json index d52f86c..90cc819 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,31 +1,17 @@ { - "env": { - "browser": true, - "es2021": true, - "node": true - }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": "latest", - "sourceType": "module" - }, - "plugins": [ - "@typescript-eslint" - ], + "extends": "./node_modules/gts/", "rules": { "@typescript-eslint/no-non-null-assertion": "off", "@typescript-eslint/no-unused-vars": ["error", {"args": "none", "destructuredArrayIgnorePattern": "^_"}], "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-empty-interface": "off", + "@typescript-eslint/no-floating-promises": "off", "@typescript-eslint/ban-ts-comment": "off", "no-useless-escape": "off", "prefer-const": ["error", {"destructuring": "all"}], "object-curly-spacing": ["error", "never"], "array-bracket-spacing": ["error", "never"], - "semi": ["error", "never"] + "semi": ["error", "never"], + "quotes": ["error", "double"] } } diff --git a/.prettierrc.js b/.prettierrc.js deleted file mode 100644 index ff15483..0000000 --- a/.prettierrc.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - ...require('gts/.prettierrc.json') -} diff --git a/build_and_link.sh b/build_and_link.sh index 3adf1b9..4dba377 100755 --- a/build_and_link.sh +++ b/build_and_link.sh @@ -2,8 +2,8 @@ upstream=$1 +npm run fix -w @welshman/$upstream npm run build -w @welshman/$upstream -npm run lint -w @welshman/$upstream for downstream in $(./get_packages.py); do n=@welshman/$upstream diff --git a/package-lock.json b/package-lock.json index c03878a..da6d19e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,9 +7,10 @@ "workspaces": [ "packages/*" ], - "dependencies": { - "@types/throttle-debounce": "^5.0.2", - "typedoc": "^0.27.4" + "devDependencies": { + "gts": "^6.0.2", + "typedoc": "^0.27.4", + "typescript": "^5.6.3" } }, "node_modules/@ampproject/remapping": { @@ -24,32 +25,11 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.7", + "version": "7.26.2", "dev": true, "license": "MIT", "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", + "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" }, @@ -57,68 +37,12 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", "dev": true, "license": "MIT", "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" + "node": ">=6.9.0" } }, "node_modules/@braintree/sanitize-url": { @@ -134,21 +58,24 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", + "version": "4.4.1", "dev": true, "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.1", + "version": "4.12.1", "dev": true, "license": "MIT", "engines": { @@ -178,7 +105,7 @@ } }, "node_modules/@eslint/js": { - "version": "8.57.0", + "version": "8.57.1", "dev": true, "license": "MIT", "engines": { @@ -187,8 +114,7 @@ }, "node_modules/@gerrit0/mini-shiki": { "version": "1.24.4", - "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-1.24.4.tgz", - "integrity": "sha512-YEHW1QeAg6UmxEmswiQbOVEg1CW22b1XUD/lNTliOsu0LD0wqoyleFMnmbTp697QE0pcadQiR5cVtbbAPncvpw==", + "dev": true, "license": "MIT", "dependencies": { "@shikijs/engine-oniguruma": "^1.24.2", @@ -197,11 +123,11 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", + "version": "0.13.0", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" }, @@ -389,8 +315,7 @@ }, "node_modules/@shikijs/engine-oniguruma": { "version": "1.24.2", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.24.2.tgz", - "integrity": "sha512-ZN6k//aDNWRJs1uKB12pturKHh7GejKugowOFGAuG7TxDRLod1Bd5JhpOikOiFqPmKjKEPtEA6mRCf7q3ulDyQ==", + "dev": true, "license": "MIT", "dependencies": { "@shikijs/types": "1.24.2", @@ -399,8 +324,7 @@ }, "node_modules/@shikijs/types": { "version": "1.24.2", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.24.2.tgz", - "integrity": "sha512-bdeWZiDtajGLG9BudI0AHet0b6e7FbR0EsE4jpGaI0YwHm/XJunI9+3uZnzFtX65gsyJ6ngCIWUfA4NWRPnBkQ==", + "dev": true, "license": "MIT", "dependencies": { "@shikijs/vscode-textmate": "^9.3.0", @@ -409,8 +333,7 @@ }, "node_modules/@shikijs/vscode-textmate": { "version": "9.3.1", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.3.1.tgz", - "integrity": "sha512-79QfK1393x9Ho60QFyLti+QfdJzRQCVLFb97kOIV7Eo9vQU/roINgk7m24uv0a7AUvN//RDH36FLjjK48v0s9g==", + "dev": true, "license": "MIT" }, "node_modules/@types/estree": { @@ -423,8 +346,7 @@ }, "node_modules/@types/hast": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, "license": "MIT", "dependencies": { "@types/unist": "*" @@ -440,6 +362,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/node": { + "version": "22.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", + "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, "node_modules/@types/normalize-package-data": { "version": "2.4.4", "dev": true, @@ -452,16 +383,22 @@ }, "node_modules/@types/throttle-debounce": { "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@types/throttle-debounce/-/throttle-debounce-5.0.2.tgz", - "integrity": "sha512-pDzSNulqooSKvSNcksnV72nk8p7gRqN8As71Sp28nov1IgmPKWbOEIwAWvBME5pPTtaXJAvG3O4oc76HlQ4kqQ==", "license": "MIT" }, "node_modules/@types/unist": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true, "license": "MIT" }, + "node_modules/@types/ws": { + "version": "8.5.13", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", + "integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.62.0", "dev": true, @@ -643,7 +580,7 @@ } }, "node_modules/@ungap/structured-clone": { - "version": "1.2.0", + "version": "1.2.1", "dev": true, "license": "ISC" }, @@ -701,18 +638,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ajv": { "version": "6.12.6", "dev": true, @@ -797,6 +722,7 @@ }, "node_modules/argparse": { "version": "2.0.1", + "dev": true, "license": "Python-2.0" }, "node_modules/aria-query": { @@ -831,6 +757,7 @@ }, "node_modules/balanced-match": { "version": "1.0.2", + "dev": true, "license": "MIT" }, "node_modules/binary-extensions": { @@ -963,14 +890,6 @@ "node": ">= 6" } }, - "node_modules/clean-stack": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/cli-cursor": { "version": "3.1.0", "dev": true, @@ -990,19 +909,6 @@ "node": ">= 10" } }, - "node_modules/cliui": { - "version": "8.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/code-red": { "version": "1.0.4", "license": "MIT", @@ -1036,7 +942,7 @@ "license": "MIT" }, "node_modules/cross-spawn": { - "version": "7.0.3", + "version": "7.0.6", "dev": true, "license": "MIT", "dependencies": { @@ -1148,8 +1054,7 @@ }, "node_modules/entities": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -1186,15 +1091,15 @@ } }, "node_modules/eslint": { - "version": "8.57.0", + "version": "8.57.1", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -1315,12 +1220,12 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.1.3", + "version": "5.2.1", "dev": true, "license": "MIT", "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.6" + "synckit": "^0.9.1" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -1668,7 +1573,7 @@ } }, "node_modules/flatted": { - "version": "3.3.1", + "version": "3.3.2", "dev": true, "license": "ISC" }, @@ -1712,17 +1617,6 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-stdin": { - "version": "8.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-stream": { "version": "6.0.1", "dev": true, @@ -1803,23 +1697,23 @@ "license": "MIT" }, "node_modules/gts": { - "version": "5.3.1", + "version": "6.0.2", "dev": true, "license": "Apache-2.0", "dependencies": { "@typescript-eslint/eslint-plugin": "5.62.0", "@typescript-eslint/parser": "5.62.0", "chalk": "^4.1.2", - "eslint": "8.57.0", + "eslint": "8.57.1", "eslint-config-prettier": "9.1.0", "eslint-plugin-n": "15.7.0", - "eslint-plugin-prettier": "5.1.3", + "eslint-plugin-prettier": "5.2.1", "execa": "^5.0.0", "inquirer": "^7.3.3", "json5": "^2.1.3", "meow": "^9.0.0", "ncp": "^2.0.0", - "prettier": "3.2.5", + "prettier": "3.3.3", "rimraf": "3.0.2", "write-file-atomic": "^4.0.0" }, @@ -1827,10 +1721,10 @@ "gts": "build/src/cli.js" }, "engines": { - "node": ">=14" + "node": ">=18" }, "peerDependencies": { - "typescript": ">=3" + "typescript": ">=5" } }, "node_modules/hard-rejection": { @@ -1995,7 +1889,7 @@ } }, "node_modules/is-core-module": { - "version": "2.15.1", + "version": "2.16.0", "dev": true, "license": "MIT", "dependencies": { @@ -2182,8 +2076,7 @@ }, "node_modules/linkify-it": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, "license": "MIT", "dependencies": { "uc.micro": "^2.0.0" @@ -2245,8 +2138,7 @@ }, "node_modules/lunr": { "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true, "license": "MIT" }, "node_modules/magic-string": { @@ -2269,8 +2161,7 @@ }, "node_modules/markdown-it": { "version": "14.1.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", - "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1", @@ -2290,8 +2181,7 @@ }, "node_modules/mdurl": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, "license": "MIT" }, "node_modules/meow": { @@ -2559,15 +2449,14 @@ }, "node_modules/nostr-signer-capacitor-plugin": { "version": "0.0.3", - "resolved": "https://registry.npmjs.org/nostr-signer-capacitor-plugin/-/nostr-signer-capacitor-plugin-0.0.3.tgz", - "integrity": "sha512-Aa3gmaEsmI80zm6/+Z12CEryzVlSSB+ERUhPTUGg/juQUlYGyeynifm306URUXYF4NkrdBpJua/gmcvhXcZoUA==", + "license": "MIT", "peer": true, "peerDependencies": { "@capacitor/core": "^6.0.0" } }, "node_modules/nostr-tools": { - "version": "2.7.2", + "version": "2.10.4", "license": "Unlicense", "dependencies": { "@noble/ciphers": "^0.5.1", @@ -2578,7 +2467,7 @@ "@scure/bip39": "1.2.1" }, "optionalDependencies": { - "nostr-wasm": "v0.1.0" + "nostr-wasm": "0.1.0" }, "peerDependencies": { "typescript": ">=5.0.0" @@ -2591,8 +2480,6 @@ }, "node_modules/nostr-wasm": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/nostr-wasm/-/nostr-wasm-0.1.0.tgz", - "integrity": "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==", "license": "MIT", "optional": true }, @@ -2653,20 +2540,6 @@ "node": ">=0.10.0" } }, - "node_modules/p-all": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-map": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-limit": { "version": "3.1.0", "dev": true, @@ -2695,20 +2568,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-map": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-try": { "version": "2.2.0", "dev": true, @@ -2792,7 +2651,7 @@ } }, "node_modules/picocolors": { - "version": "1.1.0", + "version": "1.1.1", "dev": true, "license": "ISC" }, @@ -2816,7 +2675,7 @@ } }, "node_modules/prettier": { - "version": "3.2.5", + "version": "3.3.3", "dev": true, "license": "MIT", "bin": { @@ -2850,8 +2709,7 @@ }, "node_modules/punycode.js": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", - "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -3010,19 +2868,6 @@ "node": ">=8" } }, - "node_modules/readable-stream": { - "version": "3.6.2", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/readdirp": { "version": "3.6.0", "dev": true, @@ -3066,11 +2911,11 @@ } }, "node_modules/resolve": { - "version": "1.22.8", + "version": "1.22.9", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -3280,22 +3125,6 @@ "dev": true, "license": "CC0-1.0" }, - "node_modules/string_decoder": { - "version": "1.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-to-stream": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "readable-stream": "^3.4.0" - } - }, "node_modules/string-width": { "version": "4.2.3", "dev": true, @@ -3350,14 +3179,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/superstruct": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/supports-color": { "version": "7.2.0", "dev": true, @@ -3404,7 +3225,7 @@ } }, "node_modules/synckit": { - "version": "0.8.8", + "version": "0.9.2", "dev": true, "license": "MIT", "dependencies": { @@ -3465,32 +3286,6 @@ "node": ">=8" } }, - "node_modules/tsc-multi": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4", - "fast-glob": "^3.2.12", - "get-stdin": "^8.0.0", - "p-all": "^3.0.0", - "picocolors": "^1.0.0", - "signal-exit": "^3.0.7", - "string-to-stream": "^3.0.1", - "superstruct": "^1.0.3", - "tslib": "^2.5.0", - "yargs": "^17.7.1" - }, - "bin": { - "tsc-multi": "bin/tsc-multi.js" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "typescript": ">=4.3.0" - } - }, "node_modules/tslib": { "version": "2.7.0", "license": "0BSD" @@ -3538,8 +3333,7 @@ }, "node_modules/typedoc": { "version": "0.27.4", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.27.4.tgz", - "integrity": "sha512-wXPQs1AYC2Crk+1XFpNuutLIkNWleokZf1UNf/X8w9KsMnirkvT+LzxTXDvfF6ug3TSLf3Xu5ZXRKGfoXPX7IA==", + "dev": true, "license": "Apache-2.0", "dependencies": { "@gerrit0/mini-shiki": "^1.24.0", @@ -3560,8 +3354,7 @@ }, "node_modules/typedoc/node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -3569,8 +3362,7 @@ }, "node_modules/typedoc/node_modules/minimatch": { "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -3583,7 +3375,8 @@ } }, "node_modules/typescript": { - "version": "5.1.6", + "version": "5.7.2", + "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -3595,8 +3388,13 @@ }, "node_modules/uc.micro": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "license": "MIT" }, "node_modules/uri-js": { @@ -3607,11 +3405,6 @@ "punycode": "^2.1.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "dev": true, @@ -3715,8 +3508,7 @@ }, "node_modules/yaml": { "version": "2.6.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz", - "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==", + "dev": true, "license": "ISC", "bin": { "yaml": "bin.mjs" @@ -3725,23 +3517,6 @@ "node": ">= 14" } }, - "node_modules/yargs": { - "version": "17.7.2", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/yargs-parser": { "version": "20.2.9", "dev": true, @@ -3794,14 +3569,6 @@ "node": ">=8" } }, - "node_modules/yargs/node_modules/yargs-parser": { - "version": "21.1.1", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "dev": true, @@ -3815,7 +3582,7 @@ }, "packages/app": { "name": "@welshman/app", - "version": "0.0.33", + "version": "0.0.34", "license": "MIT", "dependencies": { "@types/throttle-debounce": "^5.0.2", @@ -3830,71 +3597,62 @@ "idb": "^8.0.0", "svelte": "^4.2.18", "throttle-debounce": "^5.0.2" - }, - "devDependencies": { - "gts": "^5.0.1", - "tsc-multi": "^1.1.0", - "typescript": "~5.1.6" } }, "packages/content": { "name": "@welshman/content", - "version": "0.0.13", + "version": "0.0.14", "license": "MIT", "dependencies": { "@braintree/sanitize-url": "^7.0.2", "@welshman/lib": "~0.0.33", "nostr-tools": "^2.7.2" - }, - "devDependencies": { - "gts": "^5.0.1", - "tsc-multi": "^1.1.0", - "typescript": "~5.1.6" } }, "packages/dvm": { "name": "@welshman/dvm", - "version": "0.0.11", + "version": "0.0.12", "license": "MIT", "dependencies": { + "@noble/hashes": "^1.6.1", "@welshman/lib": "~0.0.33", "@welshman/net": "~0.0.41", "@welshman/util": "~0.0.50", "nostr-tools": "^2.7.2" - }, - "devDependencies": { - "gts": "^5.0.1", - "tsc-multi": "^1.1.0", - "typescript": "~5.1.6" } }, + "packages/dvm/node_modules/@noble/hashes": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.6.1.tgz", + "integrity": "sha512-pq5D8h10hHBjyqX+cfBm0i8JUXJ0UhczFc4r74zbuT9XgewFo2E3J1cOaGtdZynILNmQ685YWGzGE1Zv6io50w==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/editor": { + "extraneous": true + }, "packages/feeds": { "name": "@welshman/feeds", - "version": "0.0.26", + "version": "0.0.27", "license": "MIT", "dependencies": { "@welshman/lib": "~0.0.33", "@welshman/util": "~0.0.50" - }, - "devDependencies": { - "gts": "^5.0.1", - "tsc-multi": "^1.1.0", - "typescript": "~5.1.6" } }, "packages/lib": { "name": "@welshman/lib", - "version": "0.0.33", + "version": "0.0.34", "license": "MIT", "dependencies": { "@scure/base": "^1.1.6", "@types/events": "^3.0.3", "events": "^3.3.0" - }, - "devDependencies": { - "gts": "^5.0.1", - "tsc-multi": "^1.1.0", - "typescript": "~5.1.6" } }, "packages/lib/node_modules/@scure/base": { @@ -3906,7 +3664,7 @@ }, "packages/net": { "name": "@welshman/net", - "version": "0.0.41", + "version": "0.0.43", "license": "MIT", "dependencies": { "@welshman/lib": "~0.0.33", @@ -3915,58 +3673,88 @@ "ws": "^8.16.0" }, "devDependencies": { - "gts": "^5.0.1", - "mocha": "^10.7.3", - "tsc-multi": "^1.1.0", - "typescript": "~5.1.6" + "mocha": "^10.7.3" } }, "packages/signer": { "name": "@welshman/signer", - "version": "0.0.16", + "version": "0.0.17", "license": "MIT", "dependencies": { + "@noble/curves": "^1.7.0", + "@noble/hashes": "^1.6.1", "@welshman/lib": "~0.0.33", "@welshman/net": "~0.0.41", "@welshman/util": "~0.0.50", "nostr-tools": "^2.7.2" }, - "devDependencies": { - "gts": "^5.0.1", - "tsc-multi": "^1.1.0", - "typescript": "~5.1.6" + "engines": { + "node": ">=10.x" }, "peerDependencies": { "nostr-signer-capacitor-plugin": "^0.0.3" } }, + "packages/signer/node_modules/@noble/curves": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.7.0.tgz", + "integrity": "sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.6.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/signer/node_modules/@noble/curves/node_modules/@noble/hashes": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.6.0.tgz", + "integrity": "sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/signer/node_modules/@noble/hashes": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.6.1.tgz", + "integrity": "sha512-pq5D8h10hHBjyqX+cfBm0i8JUXJ0UhczFc4r74zbuT9XgewFo2E3J1cOaGtdZynILNmQ685YWGzGE1Zv6io50w==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "packages/store": { "name": "@welshman/store", - "version": "0.0.13", + "version": "0.0.14", "license": "MIT", "dependencies": { "@welshman/lib": "~0.0.33", "@welshman/util": "~0.0.50", "svelte": "^4.2.18" - }, - "devDependencies": { - "gts": "^5.0.1", - "tsc-multi": "^1.1.0", - "typescript": "~5.1.6" } }, "packages/util": { "name": "@welshman/util", - "version": "0.0.50", + "version": "0.0.52", "license": "MIT", "dependencies": { + "@types/ws": "^8.5.13", "@welshman/lib": "~0.0.33", "nostr-tools": "^2.7.2" }, - "devDependencies": { - "gts": "^5.0.1", - "tsc-multi": "^1.1.0", - "typescript": "~5.1.6" + "engines": { + "node": ">=10.4.0" } } } diff --git a/package.json b/package.json index 2c04875..5338519 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,9 @@ "type": "git", "url": "https://github.com/coracle-social/skiff.git" }, - "dependencies": { - "@types/throttle-debounce": "^5.0.2", - "typedoc": "^0.27.4" + "devDependencies": { + "gts": "^6.0.2", + "typedoc": "^0.27.4", + "typescript": "^5.6.3" } } diff --git a/packages/app/package.json b/packages/app/package.json index e94b0ab..e575b65 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -15,21 +15,16 @@ "exports": { ".": { "types": "./build/src/index.d.ts", - "import": "./build/src/index.mjs", - "require": "./build/src/index.cjs" + "import": "./build/src/index.js", + "require": "./build/src/index.js" } }, "scripts": { "pub": "npm run lint && npm run build && npm publish", - "build": "gts clean && tsc-multi", + "build": "gts clean && tsc", "lint": "gts lint", "fix": "gts fix" }, - "devDependencies": { - "gts": "^5.0.1", - "tsc-multi": "^1.1.0", - "typescript": "~5.1.6" - }, "dependencies": { "@types/throttle-debounce": "^5.0.2", "@welshman/dvm": "~0.0.11", diff --git a/packages/app/src/collection.ts b/packages/app/src/collection.ts index 94ab4aa..24027c5 100644 --- a/packages/app/src/collection.ts +++ b/packages/app/src/collection.ts @@ -1,7 +1,7 @@ -import {readable, derived, type Readable} from 'svelte/store' -import {indexBy, type Maybe, now} from '@welshman/lib' -import {withGetter} from '@welshman/store' -import {getFreshness, setFreshnessThrottled} from './freshness' +import {readable, derived, type Readable} from "svelte/store" +import {indexBy, type Maybe, now} from "@welshman/lib" +import {withGetter} from "@welshman/store" +import {getFreshness, setFreshnessThrottled} from "./freshness.js" export const collection = ({ name, diff --git a/packages/app/src/commands.ts b/packages/app/src/commands.ts index c358c12..af6c789 100644 --- a/packages/app/src/commands.ts +++ b/packages/app/src/commands.ts @@ -1,9 +1,9 @@ -import {get} from 'svelte/store' -import {ctx} from '@welshman/lib' -import {addToListPublicly, removeFromList, makeList, FOLLOWS, MUTES} from '@welshman/util' -import {userFollows, userMutes} from './user' -import {nip44EncryptToSelf} from './session' -import {publishThunk} from './thunk' +import {get} from "svelte/store" +import {ctx} from "@welshman/lib" +import {addToListPublicly, removeFromList, makeList, FOLLOWS, MUTES} from "@welshman/util" +import {userFollows, userMutes} from "./user.js" +import {nip44EncryptToSelf} from "./session.js" +import {publishThunk} from "./thunk.js" export const unfollow = async (value: string) => { const list = get(userFollows) || makeList({kind: FOLLOWS}) @@ -32,4 +32,3 @@ export const mute = async (tag: string[]) => { return publishThunk({event, relays: ctx.app.router.FromUser().getUrls()}) } - diff --git a/packages/app/src/context.ts b/packages/app/src/context.ts index 13ef73c..8e4c2b3 100644 --- a/packages/app/src/context.ts +++ b/packages/app/src/context.ts @@ -1,12 +1,15 @@ import {partition} from "@welshman/lib" -import {defaultOptimizeSubscriptions, getDefaultNetContext as originalGetDefaultNetContext} from "@welshman/net" +import { + defaultOptimizeSubscriptions, + getDefaultNetContext as originalGetDefaultNetContext, +} from "@welshman/net" import type {Subscription, RelaysAndFilters, NetContext} from "@welshman/net" -import {LOCAL_RELAY_URL, isEphemeralKind, isDVMKind, unionFilters} from "@welshman/util" +import {LOCAL_RELAY_URL, isEphemeralKind, isDVMKind, unionFilters} from "@welshman/util" import type {TrustedEvent, StampedEvent} from "@welshman/util" -import {tracker, repository} from './core' -import {makeRouter, getFilterSelections} from './router' -import {signer} from './session' -import type {Router} from './router' +import {tracker, repository} from "./core.js" +import {makeRouter, getFilterSelections} from "./router.js" +import {signer} from "./session.js" +import type {Router} from "./router.js" export type AppContext = { router: Router @@ -52,4 +55,3 @@ export const getDefaultAppContext = (overrides: Partial = {}) => ({ requestTimeout: 3000, ...overrides, }) - diff --git a/packages/app/src/core.ts b/packages/app/src/core.ts index a3eca00..66c439f 100644 --- a/packages/app/src/core.ts +++ b/packages/app/src/core.ts @@ -1,4 +1,4 @@ -import {throttle} from '@welshman/lib' +import {throttle} from "@welshman/lib" import {Repository, Relay} from "@welshman/util" import type {TrustedEvent} from "@welshman/util" import {Tracker} from "@welshman/net" @@ -13,33 +13,39 @@ export const tracker = new Tracker() // Adapt above objects to stores export const makeRepositoryStore = ({throttle: t = 300}: {throttle?: number} = {}) => - custom(setter => { - let onUpdate = () => setter(repository) + custom( + setter => { + let onUpdate = () => setter(repository) - if (t) { - onUpdate = throttle(t, onUpdate) - } + if (t) { + onUpdate = throttle(t, onUpdate) + } - onUpdate() - repository.on('update', onUpdate) + onUpdate() + repository.on("update", onUpdate) - return () => repository.off('update', onUpdate) - }, { - set: (other: Repository) => repository.load(other.dump()), - }) + return () => repository.off("update", onUpdate) + }, + { + set: (other: Repository) => repository.load(other.dump()), + }, + ) export const makeTrackerStore = ({throttle: t = 300}: {throttle?: number} = {}) => - custom(setter => { - let onUpdate = () => setter(tracker) + custom( + setter => { + let onUpdate = () => setter(tracker) - if (t) { - onUpdate = throttle(t, onUpdate) - } + if (t) { + onUpdate = throttle(t, onUpdate) + } - onUpdate() - tracker.on('update', onUpdate) + onUpdate() + tracker.on("update", onUpdate) - return () => tracker.off('update', onUpdate) - }, { - set: (other: Tracker) => tracker.load(other.relaysById), - }) + return () => tracker.off("update", onUpdate) + }, + { + set: (other: Tracker) => tracker.load(other.relaysById), + }, + ) diff --git a/packages/app/src/feeds.ts b/packages/app/src/feeds.ts index dfab53c..05d2866 100644 --- a/packages/app/src/feeds.ts +++ b/packages/app/src/feeds.ts @@ -1,23 +1,20 @@ -import {ctx, nthEq, now} from '@welshman/lib' -import {createEvent, getPubkeyTagValues} from '@welshman/util' -import {Scope, FeedController} from '@welshman/feeds' -import type {RequestOpts, FeedOptions, DVMOpts, Feed} from '@welshman/feeds' -import {makeDvmRequest, DVMEvent} from '@welshman/dvm' -import {makeSecret, Nip01Signer} from '@welshman/signer' -import {pubkey, signer} from './session' -import {getFilterSelections} from './router' -import {loadRelaySelections} from './relaySelections' -import {wotGraph, maxWot, getFollows, getNetwork, getFollowers} from './wot' -import {load} from './subscribe' +import {ctx, nthEq, now} from "@welshman/lib" +import {createEvent, getPubkeyTagValues} from "@welshman/util" +import {Scope, FeedController} from "@welshman/feeds" +import type {RequestOpts, FeedOptions, DVMOpts, Feed} from "@welshman/feeds" +import {makeDvmRequest, DVMEvent} from "@welshman/dvm" +import {makeSecret, Nip01Signer} from "@welshman/signer" +import {pubkey, signer} from "./session.js" +import {getFilterSelections} from "./router.js" +import {loadRelaySelections} from "./relaySelections.js" +import {wotGraph, maxWot, getFollows, getNetwork, getFollowers} from "./wot.js" +import {load} from "./subscribe.js" export const request = async ({filters = [{}], relays = [], onEvent}: RequestOpts) => { if (relays.length > 0) { await load({onEvent, filters, relays}) } else { - await Promise.all( - getFilterSelections(filters) - .map(opts => load({onEvent, ...opts})) - ) + await Promise.all(getFilterSelections(filters).map(opts => load({onEvent, ...opts}))) } } @@ -32,24 +29,23 @@ export const requestDVM = async ({kind, onEvent, ...request}: DVMOpts) => { const tags = request.tags || [] const $signer = signer.get() || new Nip01Signer(makeSecret()) const pubkey = await $signer.getPubkey() - const relays = - request.relays - ? ctx.app.router.FromRelays(request.relays).getUrls() - : ctx.app.router.FromPubkeys(getPubkeyTagValues(tags)).getUrls() + const relays = request.relays + ? ctx.app.router.FromRelays(request.relays).getUrls() + : ctx.app.router.FromPubkeys(getPubkeyTagValues(tags)).getUrls() - if (!tags.some(nthEq(0, 'expiration'))) { + if (!tags.some(nthEq(0, "expiration"))) { tags.push(["expiration", String(now() + 60)]) } - if (!tags.some(nthEq(0, 'relays'))) { + if (!tags.some(nthEq(0, "relays"))) { tags.push(["relays", ...relays]) } - if (!tags.some(nthEq(1, 'user'))) { + if (!tags.some(nthEq(1, "user"))) { tags.push(["param", "user", pubkey]) } - if (!tags.some(nthEq(1, 'max_results'))) { + if (!tags.some(nthEq(1, "max_results"))) { tags.push(["param", "max_results", "200"]) } @@ -72,11 +68,16 @@ export const getPubkeysForScope = (scope: string) => { } switch (scope) { - case Scope.Self: return [$pubkey] - case Scope.Follows: return getFollows($pubkey) - case Scope.Network: return getNetwork($pubkey) - case Scope.Followers: return getFollowers($pubkey) - default: return [] + case Scope.Self: + return [$pubkey] + case Scope.Follows: + return getFollows($pubkey) + case Scope.Network: + return getNetwork($pubkey) + case Scope.Followers: + return getFollowers($pubkey) + default: + return [] } } @@ -94,7 +95,7 @@ export const getPubkeysForWOTRange = (min: number, max: number) => { return pubkeys } -type _FeedOptions = Partial> & {feed: Feed} +type _FeedOptions = Partial> & {feed: Feed} export const createFeedController = (options: _FeedOptions) => new FeedController({ diff --git a/packages/app/src/follows.ts b/packages/app/src/follows.ts index 4301342..d0a9a61 100644 --- a/packages/app/src/follows.ts +++ b/packages/app/src/follows.ts @@ -1,17 +1,16 @@ -import {FOLLOWS, asDecryptedEvent, readList} from '@welshman/util' -import {type TrustedEvent, type PublishedList} from '@welshman/util' +import {FOLLOWS, asDecryptedEvent, readList} from "@welshman/util" +import {type TrustedEvent, type PublishedList} from "@welshman/util" import {type SubscribeRequestWithHandlers} from "@welshman/net" -import {deriveEventsMapped} from '@welshman/store' -import {repository} from './core' -import {load} from './subscribe' -import {collection} from './collection' -import {loadRelaySelections} from './relaySelections' +import {deriveEventsMapped} from "@welshman/store" +import {repository} from "./core.js" +import {load} from "./subscribe.js" +import {collection} from "./collection.js" +import {loadRelaySelections} from "./relaySelections.js" export const follows = deriveEventsMapped(repository, { filters: [{kinds: [FOLLOWS]}], itemToEvent: item => item.event, - eventToItem: (event: TrustedEvent) => - readList(asDecryptedEvent(event)), + eventToItem: (event: TrustedEvent) => readList(asDecryptedEvent(event)), }) export const { diff --git a/packages/app/src/freshness.ts b/packages/app/src/freshness.ts index 61f4c2c..367f33f 100644 --- a/packages/app/src/freshness.ts +++ b/packages/app/src/freshness.ts @@ -1,6 +1,6 @@ -import {writable} from 'svelte/store' -import {assoc, batch} from '@welshman/lib' -import {withGetter} from '@welshman/store' +import {writable} from "svelte/store" +import {assoc, batch} from "@welshman/lib" +import {withGetter} from "@welshman/store" export type FreshnessUpdate = { ns: string @@ -25,5 +25,5 @@ export const setFreshnessThrottled = batch(100, (updates: FreshnessUpdate[]) => } return $freshness - }) + }), ) diff --git a/packages/app/src/handles.ts b/packages/app/src/handles.ts index 2e476f9..732ef5b 100644 --- a/packages/app/src/handles.ts +++ b/packages/app/src/handles.ts @@ -1,8 +1,8 @@ -import {writable, derived} from 'svelte/store' +import {writable, derived} from "svelte/store" import {type SubscribeRequestWithHandlers} from "@welshman/net" -import {ctx, tryCatch, fetchJson, uniq, batcher, postJson, last} from '@welshman/lib' -import {collection} from './collection' -import {deriveProfile} from './profiles' +import {ctx, tryCatch, fetchJson, uniq, batcher, postJson, last} from "@welshman/lib" +import {collection} from "./collection.js" +import {deriveProfile} from "./profiles.js" export type Handle = { nip05: string @@ -18,10 +18,14 @@ export async function queryProfile(nip05: string) { if (!match) return undefined - const [_, name = '_', domain] = match + const [_, name = "_", domain] = match try { - const {names, relays = {}, nip46 = {}} = await fetchJson(`https://${domain}/.well-known/nostr.json?name=${name}`) + const { + names, + relays = {}, + nip46 = {}, + } = await fetchJson(`https://${domain}/.well-known/nostr.json?name=${name}`) const pubkey = names[name] @@ -48,14 +52,19 @@ export const fetchHandles = async (nip05s: string[]) => { // Use dufflepud if we it's set up to protect user privacy, otherwise fetch directly if (base) { - const res: any = await tryCatch(async () => await postJson(`${base}/handle/info`, {handles: nip05s})) + const res: any = await tryCatch( + async () => await postJson(`${base}/handle/info`, {handles: nip05s}), + ) for (const {handle: nip05, info} of res?.data || []) { handlesByNip05.set(nip05, info) } } else { const results = await Promise.all( - nip05s.map(async nip05 => ({nip05, info: await tryCatch(async () => await queryProfile(nip05))})) + nip05s.map(async nip05 => ({ + nip05, + info: await tryCatch(async () => await queryProfile(nip05)), + })), ) for (const {nip05, info} of results) { @@ -94,21 +103,21 @@ export const { }), }) -export const deriveHandleForPubkey = (pubkey: string, request: Partial = {}) => - derived( - [handlesByNip05, deriveProfile(pubkey, request)], - ([$handlesByNip05, $profile]) => { - if (!$profile?.nip05) { - return undefined - } - - loadHandle($profile.nip05) - - return $handlesByNip05.get($profile.nip05) +export const deriveHandleForPubkey = ( + pubkey: string, + request: Partial = {}, +) => + derived([handlesByNip05, deriveProfile(pubkey, request)], ([$handlesByNip05, $profile]) => { + if (!$profile?.nip05) { + return undefined } - ) + + loadHandle($profile.nip05) + + return $handlesByNip05.get($profile.nip05) + }) export const displayNip05 = (nip05: string) => - (nip05?.startsWith("_@") ? last(nip05.split("@")) : nip05) + nip05?.startsWith("_@") ? last(nip05.split("@")) : nip05 export const displayHandle = (handle: Handle) => displayNip05(handle.nip05) diff --git a/packages/app/src/index.ts b/packages/app/src/index.ts index 4cb6476..a12aea8 100644 --- a/packages/app/src/index.ts +++ b/packages/app/src/index.ts @@ -1,26 +1,36 @@ -export * from './context' -export * from './core' -export * from './collection' -export * from './commands' -export * from './feeds' -export * from './freshness' -export * from './follows' -export * from './handles' -export * from './mutes' -export * from './plaintext' -export * from './profiles' -export * from './relays' -export * from './relaySelections' -export * from './router' -export * from './search' -export * from './session' -export * from './storage' -export * from './subscribe' -export * from './sync' -export * from './tags' -export * from './thunk' -export * from './topics' -export * from './user' -export * from './util' -export * from './wot' -export * from './zappers' +export * from "./context.js" +export * from "./core.js" +export * from "./collection.js" +export * from "./commands.js" +export * from "./feeds.js" +export * from "./freshness.js" +export * from "./follows.js" +export * from "./handles.js" +export * from "./mutes.js" +export * from "./plaintext.js" +export * from "./profiles.js" +export * from "./relays.js" +export * from "./relaySelections.js" +export * from "./router.js" +export * from "./search.js" +export * from "./session.js" +export * from "./storage.js" +export * from "./subscribe.js" +export * from "./sync.js" +export * from "./tags.js" +export * from "./thunk.js" +export * from "./topics.js" +export * from "./user.js" +export * from "./util.js" +export * from "./wot.js" +export * from "./zappers.js" + +import type {NetContext} from "@welshman/net" +import type {AppContext} from "./context.js" + +declare module "@welshman/lib" { + interface Context { + net: NetContext + app: AppContext + } +} diff --git a/packages/app/src/mutes.ts b/packages/app/src/mutes.ts index 5e57101..d403cac 100644 --- a/packages/app/src/mutes.ts +++ b/packages/app/src/mutes.ts @@ -1,12 +1,12 @@ -import {MUTES, asDecryptedEvent, readList} from '@welshman/util' -import {type TrustedEvent, type PublishedList} from '@welshman/util' +import {MUTES, asDecryptedEvent, readList} from "@welshman/util" +import {type TrustedEvent, type PublishedList} from "@welshman/util" import {type SubscribeRequestWithHandlers} from "@welshman/net" -import {deriveEventsMapped} from '@welshman/store' -import {repository} from './core' -import {load} from './subscribe' -import {collection} from './collection' -import {ensurePlaintext} from './plaintext' -import {loadRelaySelections} from './relaySelections' +import {deriveEventsMapped} from "@welshman/store" +import {repository} from "./core.js" +import {load} from "./subscribe.js" +import {collection} from "./collection.js" +import {ensurePlaintext} from "./plaintext.js" +import {loadRelaySelections} from "./relaySelections.js" export const mutes = deriveEventsMapped(repository, { filters: [{kinds: [MUTES]}], @@ -32,4 +32,3 @@ export const { await load({...request, filters: [{kinds: [MUTES], authors: [pubkey]}]}) }, }) - diff --git a/packages/app/src/plaintext.ts b/packages/app/src/plaintext.ts index 4239b6a..e00f45c 100644 --- a/packages/app/src/plaintext.ts +++ b/packages/app/src/plaintext.ts @@ -1,9 +1,9 @@ -import {writable} from 'svelte/store' -import {assoc} from '@welshman/lib' -import type {TrustedEvent} from '@welshman/util' -import {withGetter} from '@welshman/store' +import {writable} from "svelte/store" +import {assoc} from "@welshman/lib" +import type {TrustedEvent} from "@welshman/util" +import {withGetter} from "@welshman/store" import {decrypt} from "@welshman/signer" -import {getSigner, getSession} from './session' +import {getSigner, getSession} from "./session.js" export const plaintext = withGetter(writable>({})) diff --git a/packages/app/src/profiles.ts b/packages/app/src/profiles.ts index 97095c4..a4f8e34 100644 --- a/packages/app/src/profiles.ts +++ b/packages/app/src/profiles.ts @@ -1,19 +1,19 @@ -import {derived, readable} from 'svelte/store' -import {readProfile, displayProfile, displayPubkey, PROFILE} from '@welshman/util' +import {derived, readable} from "svelte/store" +import {readProfile, displayProfile, displayPubkey, PROFILE} from "@welshman/util" import type {SubscribeRequestWithHandlers} from "@welshman/net" import type {PublishedProfile} from "@welshman/util" -import {deriveEventsMapped, withGetter} from '@welshman/store' -import {repository} from './core' -import {load} from './subscribe' -import {collection} from './collection' -import {loadRelaySelections} from './relaySelections' +import {deriveEventsMapped, withGetter} from "@welshman/store" +import {repository} from "./core.js" +import {load} from "./subscribe.js" +import {collection} from "./collection.js" +import {loadRelaySelections} from "./relaySelections.js" export const profiles = withGetter( deriveEventsMapped(repository, { filters: [{kinds: [PROFILE]}], eventToItem: readProfile, itemToEvent: item => item.event, - }) + }), ) export const { @@ -45,9 +45,7 @@ export const { }) export const displayProfileByPubkey = (pubkey: string | undefined) => - pubkey - ? displayProfile(profilesByPubkey.get().get(pubkey), displayPubkey(pubkey)) - : "" + pubkey ? displayProfile(profilesByPubkey.get().get(pubkey), displayPubkey(pubkey)) : "" export const deriveProfileDisplay = (pubkey: string | undefined) => pubkey diff --git a/packages/app/src/relaySelections.ts b/packages/app/src/relaySelections.ts index 532401a..eddb06c 100644 --- a/packages/app/src/relaySelections.ts +++ b/packages/app/src/relaySelections.ts @@ -1,11 +1,20 @@ -import {uniq} from '@welshman/lib' -import {INBOX_RELAYS, RELAYS, normalizeRelayUrl, asDecryptedEvent, readList, getListTags, getRelayTags, getRelayTagValues} from '@welshman/util' -import type {TrustedEvent, PublishedList, List} from '@welshman/util' +import {uniq} from "@welshman/lib" +import { + INBOX_RELAYS, + RELAYS, + normalizeRelayUrl, + asDecryptedEvent, + readList, + getListTags, + getRelayTags, + getRelayTagValues, +} from "@welshman/util" +import type {TrustedEvent, PublishedList, List} from "@welshman/util" import type {SubscribeRequestWithHandlers} from "@welshman/net" -import {deriveEventsMapped} from '@welshman/store' -import {repository} from './core' -import {load} from './subscribe' -import {collection} from './collection' +import {deriveEventsMapped} from "@welshman/store" +import {repository} from "./core.js" +import {load} from "./subscribe.js" +import {collection} from "./collection.js" export const getRelayUrls = (list?: List): string[] => uniq(getRelayTagValues(getListTags(list)).map(normalizeRelayUrl)) @@ -14,21 +23,20 @@ export const getReadRelayUrls = (list?: List): string[] => uniq( getRelayTags(getListTags(list)) .filter((t: string[]) => !t[2] || t[2] === "read") - .map((t: string[]) => normalizeRelayUrl(t[1])) + .map((t: string[]) => normalizeRelayUrl(t[1])), ) export const getWriteRelayUrls = (list?: List): string[] => uniq( getRelayTags(getListTags(list)) .filter((t: string[]) => !t[2] || t[2] === "write") - .map((t: string[]) => normalizeRelayUrl(t[1])) + .map((t: string[]) => normalizeRelayUrl(t[1])), ) export const relaySelections = deriveEventsMapped(repository, { filters: [{kinds: [RELAYS]}], itemToEvent: item => item.event, - eventToItem: (event: TrustedEvent) => - readList(asDecryptedEvent(event)), + eventToItem: (event: TrustedEvent) => readList(asDecryptedEvent(event)), }) export const { @@ -46,8 +54,7 @@ export const { export const inboxRelaySelections = deriveEventsMapped(repository, { filters: [{kinds: [INBOX_RELAYS]}], itemToEvent: item => item.event, - eventToItem: (event: TrustedEvent) => - readList(asDecryptedEvent(event)), + eventToItem: (event: TrustedEvent) => readList(asDecryptedEvent(event)), }) export const { diff --git a/packages/app/src/relays.ts b/packages/app/src/relays.ts index 304b465..46f7aa9 100644 --- a/packages/app/src/relays.ts +++ b/packages/app/src/relays.ts @@ -1,11 +1,11 @@ -import {writable, derived} from 'svelte/store' -import {withGetter} from '@welshman/store' -import {ctx, groupBy, indexBy, batch, now, ago, uniq, batcher, postJson} from '@welshman/lib' +import {writable, derived} from "svelte/store" +import {withGetter} from "@welshman/store" +import {ctx, groupBy, indexBy, batch, now, ago, uniq, batcher, postJson} from "@welshman/lib" import type {RelayProfile} from "@welshman/util" import {normalizeRelayUrl, displayRelayUrl, displayRelayProfile} from "@welshman/util" -import {ConnectionEvent} from '@welshman/net' -import type {Connection, Message} from '@welshman/net' -import {collection} from './collection' +import {ConnectionEvent} from "@welshman/net" +import type {Connection, Message} from "@welshman/net" +import {collection} from "./collection.js" export type RelayStats = { first_seen: number @@ -151,78 +151,108 @@ const updateRelayStats = batch(500, (updates: RelayStatsUpdate[]) => { }) const onConnectionOpen = ({url}: Connection) => - updateRelayStats([url, stats => { - stats.last_open = now() - stats.open_count++ - }]) + updateRelayStats([ + url, + stats => { + stats.last_open = now() + stats.open_count++ + }, + ]) const onConnectionClose = ({url}: Connection) => - updateRelayStats([url, stats => { - stats.last_close = now() - stats.close_count++ - }]) + updateRelayStats([ + url, + stats => { + stats.last_close = now() + stats.close_count++ + }, + ]) const onConnectionSend = ({url}: Connection, [verb]: Message) => { - if (verb === 'REQ') { - updateRelayStats([url, stats => { - stats.request_count++ - stats.last_request = now() - }]) - } else if (verb === 'EVENT') { - updateRelayStats([url, stats => { - stats.publish_count++ - stats.last_publish = now() - }]) + if (verb === "REQ") { + updateRelayStats([ + url, + stats => { + stats.request_count++ + stats.last_request = now() + }, + ]) + } else if (verb === "EVENT") { + updateRelayStats([ + url, + stats => { + stats.publish_count++ + stats.last_publish = now() + }, + ]) } } const onConnectionReceive = ({url, state}: Connection, [verb, ...extra]: Message) => { - if (verb === 'OK') { + if (verb === "OK") { const [eventId, ok] = extra const pub = state.pendingPublishes.get(eventId) - updateRelayStats([url, stats => { - if (pub) { - stats.publish_timer += ago(pub.sent) - } + updateRelayStats([ + url, + stats => { + if (pub) { + stats.publish_timer += ago(pub.sent) + } - if (ok) { - stats.publish_success_count++ - } else { - stats.publish_failure_count++ - } - }]) - } else if (verb === 'AUTH') { - updateRelayStats([url, stats => { - stats.last_auth = now() - }]) - } else if (verb === 'EVENT') { - updateRelayStats([url, stats => { - stats.event_count++ - stats.last_event = now() - }]) - } else if (verb === 'EOSE') { + if (ok) { + stats.publish_success_count++ + } else { + stats.publish_failure_count++ + } + }, + ]) + } else if (verb === "AUTH") { + updateRelayStats([ + url, + stats => { + stats.last_auth = now() + }, + ]) + } else if (verb === "EVENT") { + updateRelayStats([ + url, + stats => { + stats.event_count++ + stats.last_event = now() + }, + ]) + } else if (verb === "EOSE") { const request = state.pendingRequests.get(extra[0]) // Only count the first eose if (request && !request.eose) { - updateRelayStats([url, stats => { - stats.eose_count++ - stats.eose_timer += now() - request.sent - }]) + updateRelayStats([ + url, + stats => { + stats.eose_count++ + stats.eose_timer += now() - request.sent + }, + ]) } - } else if (verb === 'NOTICE') { - updateRelayStats([url, stats => { - stats.notice_count++ - }]) + } else if (verb === "NOTICE") { + updateRelayStats([ + url, + stats => { + stats.notice_count++ + }, + ]) } } const onConnectionError = ({url}: Connection) => - updateRelayStats([url, stats => { - stats.last_error = now() - stats.recent_errors = uniq(stats.recent_errors.concat(now())).slice(-10) - }]) + updateRelayStats([ + url, + stats => { + stats.last_error = now() + stats.recent_errors = uniq(stats.recent_errors.concat(now())).slice(-10) + }, + ]) export const trackRelayStats = (connection: Connection) => { connection.on(ConnectionEvent.Open, onConnectionOpen) diff --git a/packages/app/src/router.ts b/packages/app/src/router.ts index 379ad9c..dcf6f3e 100644 --- a/packages/app/src/router.ts +++ b/packages/app/src/router.ts @@ -32,15 +32,15 @@ import { } from "@welshman/util" import type {TrustedEvent, Filter} from "@welshman/util" import type {RelaysAndFilters} from "@welshman/net" -import {pubkey} from "./session" +import {pubkey} from "./session.js" import { relaySelectionsByPubkey, inboxRelaySelectionsByPubkey, getReadRelayUrls, getWriteRelayUrls, getRelayUrls, -} from "./relaySelections" -import {relays, relaysByUrl} from "./relays" +} from "./relaySelections.js" +import {relays, relaysByUrl} from "./relays.js" export const INDEXED_KINDS = [PROFILE, RELAYS, INBOX_RELAYS, FOLLOWS] @@ -99,12 +99,14 @@ export type RouterOptions = { } export type Selection = { - weight: number, - relays: string[], + weight: number + relays: string[] } -const makeSelection = (relays: string[], weight = 1): Selection => - ({relays: relays.map(normalizeRelayUrl), weight}) +const makeSelection = (relays: string[], weight = 1): Selection => ({ + relays: relays.map(normalizeRelayUrl), + weight, +}) // Fallback policies @@ -112,7 +114,7 @@ export type FallbackPolicy = (count: number, limit: number) => number export const addNoFallbacks = (count: number, limit: number) => 0 -export const addMinimalFallbacks = (count: number, limit: number) => count > 0 ? 0 : 1 +export const addMinimalFallbacks = (count: number, limit: number) => (count > 0 ? 0 : 1) export const addMaximalFallbacks = (count: number, limit: number) => limit - count @@ -142,35 +144,26 @@ export class Router { // Routing scenarios - FromRelays = (relays: string[]) => - this.scenario([makeSelection(relays)]) + FromRelays = (relays: string[]) => this.scenario([makeSelection(relays)]) - ForUser = () => - this.FromRelays(this.getRelaysForUser(RelayMode.Read)) + ForUser = () => this.FromRelays(this.getRelaysForUser(RelayMode.Read)) - FromUser = () => - this.FromRelays(this.getRelaysForUser(RelayMode.Write)) + FromUser = () => this.FromRelays(this.getRelaysForUser(RelayMode.Write)) - UserInbox = () => - this.FromRelays(this.getRelaysForUser(RelayMode.Inbox)).policy(addNoFallbacks) + UserInbox = () => this.FromRelays(this.getRelaysForUser(RelayMode.Inbox)).policy(addNoFallbacks) - ForPubkey = (pubkey: string) => - this.FromRelays(this.getRelaysForPubkey(pubkey, RelayMode.Read)) + ForPubkey = (pubkey: string) => this.FromRelays(this.getRelaysForPubkey(pubkey, RelayMode.Read)) - FromPubkey = (pubkey: string) => - this.FromRelays(this.getRelaysForPubkey(pubkey, RelayMode.Write)) + FromPubkey = (pubkey: string) => this.FromRelays(this.getRelaysForPubkey(pubkey, RelayMode.Write)) PubkeyInbox = (pubkey: string) => this.FromRelays(this.getRelaysForPubkey(pubkey, RelayMode.Inbox)).policy(addNoFallbacks) - ForPubkeys = (pubkeys: string[]) => - this.merge(pubkeys.map(pubkey => this.ForPubkey(pubkey))) + ForPubkeys = (pubkeys: string[]) => this.merge(pubkeys.map(pubkey => this.ForPubkey(pubkey))) - FromPubkeys = (pubkeys: string[]) => - this.merge(pubkeys.map(pubkey => this.FromPubkey(pubkey))) + FromPubkeys = (pubkeys: string[]) => this.merge(pubkeys.map(pubkey => this.FromPubkey(pubkey))) - PubkeyInboxes = (pubkeys: string[]) => - this.merge(pubkeys.map(pubkey => this.PubkeyInbox(pubkey))) + PubkeyInboxes = (pubkeys: string[]) => this.merge(pubkeys.map(pubkey => this.PubkeyInbox(pubkey))) Event = (event: TrustedEvent) => this.FromRelays(this.getRelaysForPubkey(event.pubkey, RelayMode.Write)) @@ -180,10 +173,7 @@ export class Router { Quote = (event: TrustedEvent, value: string, relays: string[] = []) => { const tag = event.tags.find(t => t[1] === value) - const scenarios = [ - this.ForPubkey(event.pubkey), - this.FromPubkey(event.pubkey), - ] + const scenarios = [this.ForPubkey(event.pubkey), this.FromPubkey(event.pubkey)] if (tag?.[2] && isShareableRelayUrl(tag[2])) { scenarios.push(this.FromRelays([tag[2]])) @@ -198,21 +188,19 @@ export class Router { EventAncestors = (event: TrustedEvent, type: "mentions" | "replies" | "roots") => { return this.scenario( - getAncestorTags(event.tags)[type].flatMap( - ([_, value, relay, pubkey]) => { - const selections = [makeSelection(this.ForUser().getUrls(), 0.5)] + getAncestorTags(event.tags)[type].flatMap(([_, value, relay, pubkey]) => { + const selections = [makeSelection(this.ForUser().getUrls(), 0.5)] - if (pubkey) { - selections.push(makeSelection(this.FromPubkey(pubkey).getUrls())) - } - - if (relay) { - selections.push(makeSelection([relay], 0.9)) - } - - return selections + if (pubkey) { + selections.push(makeSelection(this.FromPubkey(pubkey).getUrls())) } - ) + + if (relay) { + selections.push(makeSelection([relay], 0.9)) + } + + return selections + }), ) } @@ -240,16 +228,28 @@ export type RouterScenarioOptions = { } export class RouterScenario { - constructor(readonly router: Router, readonly selections: Selection[], readonly options: RouterScenarioOptions = {}) {} + constructor( + readonly router: Router, + readonly selections: Selection[], + readonly options: RouterScenarioOptions = {}, + ) {} clone = (options: RouterScenarioOptions) => new RouterScenario(this.router, this.selections, {...this.options, ...options}) filter = (f: (selection: Selection) => boolean) => - new RouterScenario(this.router, this.selections.filter(selection => f(selection)), this.options) + new RouterScenario( + this.router, + this.selections.filter(selection => f(selection)), + this.options, + ) update = (f: (selection: Selection) => Selection) => - new RouterScenario(this.router, this.selections.map(selection => f(selection)), this.options) + new RouterScenario( + this.router, + this.selections.map(selection => f(selection)), + this.options, + ) policy = (policy: FallbackPolicy) => this.clone({policy}) @@ -290,11 +290,7 @@ export class RouterScenario { const relays = take( limit, - sortBy( - scoreRelay, - Array.from(relayWeights.keys()) - .filter(scoreRelay) - ) + sortBy(scoreRelay, Array.from(relayWeights.keys()).filter(scoreRelay)), ) const fallbacksNeeded = fallbackPolicy(relays.length, limit) @@ -348,14 +344,14 @@ export const getIndexerRelays = () => ctx.app.indexerRelays || getFallbackRelays export const getFallbackRelays = throttleWithValue(300, () => sortBy(r => -getRelayQuality(r.url), relays.get()) .slice(0, 30) - .map(r => r.url) + .map(r => r.url), ) export const getSearchRelays = throttleWithValue(300, () => sortBy(r => -getRelayQuality(r.url), relays.get()) .filter(r => r.profile?.supported_nips?.includes(50)) .slice(0, 30) - .map(r => r.url) + .map(r => r.url), ) export const makeRouter = (options: Partial = {}) => @@ -372,7 +368,7 @@ export const makeRouter = (options: Partial = {}) => // Infer relay selections from filters -type FilterScenario = {filter: Filter, scenario: RouterScenario} +type FilterScenario = {filter: Filter; scenario: RouterScenario} type FilterSelectionRule = (filter: Filter) => FilterScenario[] @@ -387,10 +383,12 @@ export const getFilterSelectionsForSearch = (filter: Filter) => { export const getFilterSelectionsForWraps = (filter: Filter) => { if (!filter.kinds?.includes(WRAP) || filter.authors) return [] - return [{ - filter: {...filter, kinds: [WRAP]}, - scenario: ctx.app.router.UserInbox(), - }] + return [ + { + filter: {...filter, kinds: [WRAP]}, + scenario: ctx.app.router.UserInbox(), + }, + ] } export const getFilterSelectionsForIndexedKinds = (filter: Filter) => { @@ -400,10 +398,12 @@ export const getFilterSelectionsForIndexedKinds = (filter: Filter) => { const relays = ctx.app.router.options.getIndexerRelays?.() || [] - return [{ - filter: {...filter, kinds}, - scenario: ctx.app.router.FromRelays(relays), - }] + return [ + { + filter: {...filter, kinds}, + scenario: ctx.app.router.FromRelays(relays), + }, + ] } export const getFilterSelectionsForAuthors = (filter: Filter) => { @@ -411,15 +411,15 @@ export const getFilterSelectionsForAuthors = (filter: Filter) => { const chunkCount = clamp([1, 30], Math.round(filter.authors.length / 30)) - return chunks(chunkCount, filter.authors) - .map(authors => ({ - filter: {...filter, authors}, - scenario: ctx.app.router.FromPubkeys(authors), - })) + return chunks(chunkCount, filter.authors).map(authors => ({ + filter: {...filter, authors}, + scenario: ctx.app.router.FromPubkeys(authors), + })) } -export const getFilterSelectionsForUser = (filter: Filter) => - [{filter, scenario: ctx.app.router.ForUser().weight(0.2)}] +export const getFilterSelectionsForUser = (filter: Filter) => [ + {filter, scenario: ctx.app.router.ForUser().weight(0.2)}, +] export const defaultFilterSelectionRules = [ getFilterSelectionsForSearch, @@ -431,7 +431,7 @@ export const defaultFilterSelectionRules = [ export const getFilterSelections = ( filters: Filter[], - rules: FilterSelectionRule[] = defaultFilterSelectionRules + rules: FilterSelectionRule[] = defaultFilterSelectionRules, ): RelaysAndFilters[] => { const filtersById = new Map() const scenariosById = new Map() diff --git a/packages/app/src/search.ts b/packages/app/src/search.ts index 2fdce95..1e775ff 100644 --- a/packages/app/src/search.ts +++ b/packages/app/src/search.ts @@ -1,19 +1,19 @@ import Fuse from "fuse.js" import type {IFuseOptions, FuseResult} from "fuse.js" -import {debounce} from 'throttle-debounce' -import {derived} from 'svelte/store' -import {dec, sortBy} from '@welshman/lib' -import {PROFILE} from '@welshman/util' -import {throttled} from '@welshman/store' +import {debounce} from "throttle-debounce" +import {derived} from "svelte/store" +import {dec, sortBy} from "@welshman/lib" +import {PROFILE} from "@welshman/util" +import {throttled} from "@welshman/store" import type {PublishedProfile} from "@welshman/util" -import {load} from './subscribe' -import {wotGraph} from './wot' -import {profiles} from './profiles' -import {topics} from './topics' -import type {Topic} from './topics' -import {relays} from './relays' -import type {Relay} from './relays' -import {handlesByNip05} from './handles' +import {load} from "./subscribe.js" +import {wotGraph} from "./wot.js" +import {profiles} from "./profiles.js" +import {topics} from "./topics.js" +import type {Topic} from "./topics.js" +import {relays} from "./relays.js" +import type {Relay} from "./relays.js" +import {handlesByNip05} from "./handles.js" export type SearchOptions = { getValue: (item: T) => V @@ -65,12 +65,11 @@ export const profileSearch = derived( [throttled(800, profiles), throttled(800, handlesByNip05)], ([$profiles, $handlesByNip05]) => { // Remove invalid nip05's from profiles - const options = $profiles - .map(p => { - const isNip05Valid = !p.nip05 || $handlesByNip05.get(p.nip05)?.pubkey === p.event.pubkey + const options = $profiles.map(p => { + const isNip05Valid = !p.nip05 || $handlesByNip05.get(p.nip05)?.pubkey === p.event.pubkey - return isNip05Valid ? p : {...p, nip05: ""} - }) + return isNip05Valid ? p : {...p, nip05: ""} + }) return createSearch(options, { onSearch: searchProfiles, @@ -93,7 +92,7 @@ export const profileSearch = derived( shouldSort: false, }, }) - } + }, ) export const topicSearch = derived(topics, $topics => diff --git a/packages/app/src/session.ts b/packages/app/src/session.ts index 965a47c..9fecc02 100644 --- a/packages/app/src/session.ts +++ b/packages/app/src/session.ts @@ -4,18 +4,18 @@ import {withGetter, synced} from "@welshman/store" import {Nip46Broker, Nip46Signer, Nip07Signer, Nip01Signer, Nip55Signer} from "@welshman/signer" export type SessionNip01 = { - method: 'nip01' + method: "nip01" pubkey: string secret: string } export type SessionNip07 = { - method: 'nip07' + method: "nip07" pubkey: string } export type SessionNip46 = { - method: 'nip46' + method: "nip46" pubkey: string secret: string handler: { @@ -25,22 +25,22 @@ export type SessionNip46 = { } export type SessionNip55 = { - method: 'nip55' + method: "nip55" pubkey: string signer: string } export type SessionPubkey = { - method: 'pubkey' + method: "pubkey" pubkey: string } export type SessionAnyMethod = - SessionNip01 | - SessionNip07 | - SessionNip46 | - SessionNip55 | - SessionPubkey + | SessionNip01 + | SessionNip07 + | SessionNip46 + | SessionNip55 + | SessionPubkey export type Session = SessionAnyMethod & Record @@ -88,14 +88,14 @@ export const getSigner = cached({ clientSecret: session.secret!, relays: session.handler!.relays, signerPubkey: session.handler!.pubkey, - }) + }), ) case "nip55": return new Nip55Signer(session.signer!) default: return null } - } + }, }) export const signer = withGetter(derived(session, getSigner)) diff --git a/packages/app/src/storage.ts b/packages/app/src/storage.ts index bdb2dc8..8afdbde 100644 --- a/packages/app/src/storage.ts +++ b/packages/app/src/storage.ts @@ -49,35 +49,37 @@ export const initIndexedDbAdapter = async (name: string, adapter: IndexedDbAdapt adapter.store.set(prevRecords) - adapter.store.subscribe( - async (currentRecords: any[]) => { - if (dead.get()) { - return - } + adapter.store.subscribe(async (currentRecords: any[]) => { + if (dead.get()) { + return + } - const currentIds = new Set(currentRecords.map(item => item[adapter.keyPath])) - const removedRecords = prevRecords.filter(r => !currentIds.has(r[adapter.keyPath])) + const currentIds = new Set(currentRecords.map(item => item[adapter.keyPath])) + const removedRecords = prevRecords.filter(r => !currentIds.has(r[adapter.keyPath])) - const prevRecordsById = indexBy(item => item[adapter.keyPath], prevRecords) - const updatedRecords = currentRecords.filter(r => r !== prevRecordsById.get(r[adapter.keyPath])) + const prevRecordsById = indexBy(item => item[adapter.keyPath], prevRecords) + const updatedRecords = currentRecords.filter(r => r !== prevRecordsById.get(r[adapter.keyPath])) - prevRecords = currentRecords + prevRecords = currentRecords - if (updatedRecords.length > 0) { - await bulkPut(name, updatedRecords) - } + if (updatedRecords.length > 0) { + await bulkPut(name, updatedRecords) + } - if (removedRecords.length > 0) { - await bulkDelete( - name, - removedRecords.map(item => item[adapter.keyPath]), - ) - } - }, - ) + if (removedRecords.length > 0) { + await bulkDelete( + name, + removedRecords.map(item => item[adapter.keyPath]), + ) + } + }) } -export const initStorage = async (name: string, version: number, adapters: Record) => { +export const initStorage = async ( + name: string, + version: number, + adapters: Record, +) => { if (!window.indexedDB) return window.addEventListener("beforeunload", () => closeStorage()) @@ -131,14 +133,20 @@ const migrate = (data: any[], options: StorageAdapterOptions) => options.migrate ? options.migrate(data) : data export const storageAdapters = { - fromObjectStore: (store: Writable>, options: StorageAdapterOptions = {}) => ({ + fromObjectStore: ( + store: Writable>, + options: StorageAdapterOptions = {}, + ) => ({ options, keyPath: "key", store: adapter({ store: throttled(options.throttle || 0, store), forward: (data: Record) => - migrate(Object.entries(data).map(([key, value]) => ({key, value})), options), - backward: (data: {key: string, value: T}[]) => + migrate( + Object.entries(data).map(([key, value]) => ({key, value})), + options, + ), + backward: (data: {key: string; value: T}[]) => fromPairs(data.map(({key, value}) => [key, value])), }), }), @@ -148,100 +156,113 @@ export const storageAdapters = { store: adapter({ store: throttled(options.throttle || 0, store), forward: (data: Map) => - migrate(Array.from(data.entries()).map(([key, value]) => ({key, value})), options), - backward: (data: {key: string, value: T}[]) => + migrate( + Array.from(data.entries()).map(([key, value]) => ({key, value})), + options, + ), + backward: (data: {key: string; value: T}[]) => new Map(data.map(({key, value}) => [key, value])), }), }), fromTracker: (tracker: Tracker, options: StorageAdapterOptions = {}) => ({ options, - keyPath: 'key', - store: custom(setter => { - let onUpdate = () => - setter( - migrate( - Array.from(tracker.relaysById.entries()) - .map(([key, urls]) => ({key, value: Array.from(urls)})), - options + keyPath: "key", + store: custom( + setter => { + let onUpdate = () => + setter( + migrate( + Array.from(tracker.relaysById.entries()).map(([key, urls]) => ({ + key, + value: Array.from(urls), + })), + options, + ), ) - ) - if (options.throttle) { - onUpdate = throttle(options.throttle, onUpdate) - } + if (options.throttle) { + onUpdate = throttle(options.throttle, onUpdate) + } - onUpdate() - tracker.on('update', onUpdate) + onUpdate() + tracker.on("update", onUpdate) - return () => tracker.off('update', onUpdate) - }, { - set: (data: {key: string, value: string[]}[]) => - tracker.load(new Map(data.map(({key, value}) => [key, new Set(value)]))), - }), + return () => tracker.off("update", onUpdate) + }, + { + set: (data: {key: string; value: string[]}[]) => + tracker.load(new Map(data.map(({key, value}) => [key, new Set(value)]))), + }, + ), }), fromRepository: (repository: Repository, options: StorageAdapterOptions = {}) => ({ options, - keyPath: 'id', - store: custom(setter => { - let onUpdate = () => setter(migrate(repository.dump(), options)) + keyPath: "id", + store: custom( + setter => { + let onUpdate = () => setter(migrate(repository.dump(), options)) - if (options.throttle) { - onUpdate = throttle(options.throttle, onUpdate) - } + if (options.throttle) { + onUpdate = throttle(options.throttle, onUpdate) + } - onUpdate() - repository.on('update', onUpdate) + onUpdate() + repository.on("update", onUpdate) - return () => repository.off('update', onUpdate) - }, { - set: (events: TrustedEvent[]) => repository.load(events), - }), + return () => repository.off("update", onUpdate) + }, + { + set: (events: TrustedEvent[]) => repository.load(events), + }, + ), }), fromRepositoryAndTracker: ( repository: Repository, tracker: Tracker, - options: StorageAdapterOptions = {} + options: StorageAdapterOptions = {}, ) => ({ options, - keyPath: 'id', - store: custom(setter => { - let onUpdate = () => { - const events = migrate(repository.dump(), options) + keyPath: "id", + store: custom( + setter => { + let onUpdate = () => { + const events = migrate(repository.dump(), options) - setter( - events.map(event => { - const relays = Array.from(tracker.getRelays(event.id)) + setter( + events.map(event => { + const relays = Array.from(tracker.getRelays(event.id)) - return {id: event.id, event, relays} - }) - ) - } - - if (options.throttle) { - onUpdate = throttle(options.throttle, onUpdate) - } - - onUpdate() - tracker.on('update', onUpdate) - repository.on('update', onUpdate) - - return () => { - tracker.off('update', onUpdate) - } - }, { - set: (items: {event: TrustedEvent, relays: string[]}[]) => { - const events: TrustedEvent[] = [] - const relaysById = new Map>() - - for (const {event, relays} of items) { - events.push(event) - relaysById.set(event.id, new Set(relays)) + return {id: event.id, event, relays} + }), + ) } - repository.load(events) - tracker.load(relaysById) - }, - }), - }) -} + if (options.throttle) { + onUpdate = throttle(options.throttle, onUpdate) + } + onUpdate() + tracker.on("update", onUpdate) + repository.on("update", onUpdate) + + return () => { + tracker.off("update", onUpdate) + } + }, + { + set: (items: {event: TrustedEvent; relays: string[]}[]) => { + const events: TrustedEvent[] = [] + const relaysById = new Map>() + + for (const {event, relays} of items) { + events.push(event) + relaysById.set(event.id, new Set(relays)) + } + + repository.load(events) + tracker.load(relaysById) + }, + }, + ), + }), +} diff --git a/packages/app/src/subscribe.ts b/packages/app/src/subscribe.ts index 0e26fff..a66b8e9 100644 --- a/packages/app/src/subscribe.ts +++ b/packages/app/src/subscribe.ts @@ -3,7 +3,7 @@ import {LOCAL_RELAY_URL, getFilterResultCardinality} from "@welshman/util" import type {TrustedEvent, Filter} from "@welshman/util" import {subscribe as baseSubscribe, SubscriptionEvent} from "@welshman/net" import type {SubscribeRequestWithHandlers} from "@welshman/net" -import {repository} from './core' +import {repository} from "./core.js" export type PartialSubscribeRequest = Partial & {filters: Filter[]} diff --git a/packages/app/src/sync.ts b/packages/app/src/sync.ts index 7dbe754..8383cca 100644 --- a/packages/app/src/sync.ts +++ b/packages/app/src/sync.ts @@ -1,14 +1,21 @@ -import type {Filter} from '@welshman/util' -import {isSignedEvent} from '@welshman/util' -import {push as basePush, pull as basePull, sync as baseSync, pushWithoutNegentropy, pullWithoutNegentropy, syncWithoutNegentropy} from "@welshman/net" -import {repository} from './core' -import {relaysByUrl} from './relays' +import type {Filter} from "@welshman/util" +import {isSignedEvent} from "@welshman/util" +import { + push as basePush, + pull as basePull, + sync as baseSync, + pushWithoutNegentropy, + pullWithoutNegentropy, + syncWithoutNegentropy, +} from "@welshman/net" +import {repository} from "./core.js" +import {relaysByUrl} from "./relays.js" export const hasNegentropy = (url: string) => { const p = relaysByUrl.get().get(url)?.profile if (p?.supported_nips?.includes(77)) return true - if (p?.software?.includes('strfry') && !p?.version?.match(/^0\./)) return true + if (p?.software?.includes("strfry") && !p?.version?.match(/^0\./)) return true return false } @@ -23,12 +30,10 @@ export const pull = async ({relays, filters}: AppSyncOpts) => { await Promise.all( relays.map(async relay => { - await ( - hasNegentropy(relay) - ? basePull({filters, events, relays: [relay]}) - : pullWithoutNegentropy({filters, relays: [relay]}) - ) - }) + await (hasNegentropy(relay) + ? basePull({filters, events, relays: [relay]}) + : pullWithoutNegentropy({filters, relays: [relay]})) + }), ) } @@ -37,12 +42,10 @@ export const push = async ({relays, filters}: AppSyncOpts) => { await Promise.all( relays.map(async relay => { - await ( - hasNegentropy(relay) - ? basePush({filters, events, relays: [relay]}) - : pushWithoutNegentropy({events, relays: [relay]}) - ) - }) + await (hasNegentropy(relay) + ? basePush({filters, events, relays: [relay]}) + : pushWithoutNegentropy({events, relays: [relay]})) + }), ) } @@ -51,12 +54,9 @@ export const sync = async ({relays, filters}: AppSyncOpts) => { await Promise.all( relays.map(async relay => { - await ( - hasNegentropy(relay) - ? baseSync({filters, events, relays: [relay]}) - : syncWithoutNegentropy({filters, events, relays: [relay]}) - ) - }) + await (hasNegentropy(relay) + ? baseSync({filters, events, relays: [relay]}) + : syncWithoutNegentropy({filters, events, relays: [relay]})) + }), ) } - diff --git a/packages/app/src/tags.ts b/packages/app/src/tags.ts index f6cf102..20f77f2 100644 --- a/packages/app/src/tags.ts +++ b/packages/app/src/tags.ts @@ -1,8 +1,14 @@ -import {ctx} from '@welshman/lib' -import {getAddress, isReplaceable, getAncestorTags, getPubkeyTagValues, getIdAndAddress} from '@welshman/util' -import type {TrustedEvent} from '@welshman/util' -import {displayProfileByPubkey} from './profiles' -import {pubkey} from './session' +import {ctx} from "@welshman/lib" +import { + getAddress, + isReplaceable, + getAncestorTags, + getPubkeyTagValues, + getIdAndAddress, +} from "@welshman/util" +import type {TrustedEvent} from "@welshman/util" +import {displayProfileByPubkey} from "./profiles.js" +import {pubkey} from "./session.js" export const tagZapSplit = (pubkey: string, split = 1) => [ "zap", @@ -102,6 +108,3 @@ export const tagReactionTo = (event: TrustedEvent) => { return tags } - - - diff --git a/packages/app/src/thunk.ts b/packages/app/src/thunk.ts index b0a8c15..49c0180 100644 --- a/packages/app/src/thunk.ts +++ b/packages/app/src/thunk.ts @@ -1,13 +1,26 @@ -import {writable, derived, get} from 'svelte/store' -import type {Writable, Readable} from 'svelte/store' -import {Worker, identity, uniq, defer, sleep, assoc} from '@welshman/lib' -import type {Deferred} from '@welshman/lib' +import {writable, derived, get} from "svelte/store" +import type {Writable, Readable} from "svelte/store" +import {Worker, identity, uniq, defer, sleep, assoc} from "@welshman/lib" +import type {Deferred} from "@welshman/lib" import {stamp, own, hash} from "@welshman/signer" -import type {TrustedEvent, HashedEvent, EventTemplate, SignedEvent, StampedEvent, OwnedEvent} from '@welshman/util' -import {isStampedEvent, isOwnedEvent, isHashedEvent, isUnwrappedEvent, isSignedEvent} from '@welshman/util' +import type { + TrustedEvent, + HashedEvent, + EventTemplate, + SignedEvent, + StampedEvent, + OwnedEvent, +} from "@welshman/util" +import { + isStampedEvent, + isOwnedEvent, + isHashedEvent, + isUnwrappedEvent, + isSignedEvent, +} from "@welshman/util" import {publish, PublishStatus} from "@welshman/net" -import {repository, tracker} from './core' -import {pubkey, getSession, getSigner} from './session' +import {repository, tracker} from "./core.js" +import {pubkey, getSession, getSigner} from "./session.js" const {Pending, Success, Failure, Timeout, Aborted} = PublishStatus @@ -53,8 +66,8 @@ export const prepEvent = (event: ThunkEvent) => { export const makeThunk = (request: ThunkRequest) => { const event = prepEvent(request.event) const controller = new AbortController() - const result: Thunk['result'] = defer() - const status: Thunk['status'] = writable({}) + const result: Thunk["result"] = defer() + const status: Thunk["status"] = writable({}) return {event, request, controller, result, status} } @@ -72,7 +85,7 @@ export const isMergedThunk = (thunk: Thunk | MergedThunk): thunk is MergedThunk export const mergeThunks = (thunks: Thunk[]) => { const controller = new AbortController() - controller.signal.addEventListener('abort', () => { + controller.signal.addEventListener("abort", () => { for (const thunk of thunks) { thunk.controller.abort() } @@ -99,8 +112,8 @@ export const mergeThunks = (thunks: Thunk[]) => { } return mergedStatus - } - ) + }, + ), } } @@ -125,7 +138,7 @@ export const publishThunk = (request: ThunkRequest) => { thunks.update(assoc(thunk.event.id, thunk)) - thunk.controller.signal.addEventListener('abort', () => { + thunk.controller.signal.addEventListener("abort", () => { repository.removeEvent(thunk.event.id) }) @@ -143,7 +156,7 @@ export const publishThunks = (requests: ThunkRequest[]) => { thunks.update(assoc(thunk.event.id, mergedThunk)) - thunk.controller.signal.addEventListener('abort', () => { + thunk.controller.signal.addEventListener("abort", () => { repository.removeEvent(thunk.event.id) }) } @@ -221,4 +234,3 @@ thunkWorker.addGlobalHandler((thunk: Thunk) => { }) }) }) - diff --git a/packages/app/src/topics.ts b/packages/app/src/topics.ts index 3cf5120..8926c32 100644 --- a/packages/app/src/topics.ts +++ b/packages/app/src/topics.ts @@ -1,6 +1,6 @@ -import {inc, throttle} from '@welshman/lib' -import {custom} from '@welshman/store' -import {repository} from './core' +import {inc, throttle} from "@welshman/lib" +import {custom} from "@welshman/store" +import {repository} from "./core.js" export type Topic = { name: string diff --git a/packages/app/src/types.d.ts b/packages/app/src/types.d.ts deleted file mode 100644 index 5006653..0000000 --- a/packages/app/src/types.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type {NetContext} from '@welshman/net' -import type {AppContext} from './context' - -declare module "@welshman/lib" { - interface Context { - net: NetContext - app: AppContext - } -} diff --git a/packages/app/src/user.ts b/packages/app/src/user.ts index 1230a9b..0e2db2b 100644 --- a/packages/app/src/user.ts +++ b/packages/app/src/user.ts @@ -1,43 +1,39 @@ -import {derived} from 'svelte/store' -import {pubkey} from './session' -import {profilesByPubkey, loadProfile} from './profiles' -import {followsByPubkey, loadFollows} from './follows' -import {mutesByPubkey, loadMutes} from './mutes' -import {relaySelectionsByPubkey, inboxRelaySelectionsByPubkey, loadRelaySelections, loadInboxRelaySelections} from './relaySelections' -import {wotGraph} from './wot' +import {derived} from "svelte/store" +import {pubkey} from "./session.js" +import {profilesByPubkey, loadProfile} from "./profiles.js" +import {followsByPubkey, loadFollows} from "./follows.js" +import {mutesByPubkey, loadMutes} from "./mutes.js" +import { + relaySelectionsByPubkey, + inboxRelaySelectionsByPubkey, + loadRelaySelections, + loadInboxRelaySelections, +} from "./relaySelections.js" +import {wotGraph} from "./wot.js" -export const userProfile = derived( - [profilesByPubkey, pubkey], - ([$profilesByPubkey, $pubkey]) => { - if (!$pubkey) return undefined +export const userProfile = derived([profilesByPubkey, pubkey], ([$profilesByPubkey, $pubkey]) => { + if (!$pubkey) return undefined - loadProfile($pubkey) + loadProfile($pubkey) - return $profilesByPubkey.get($pubkey) - } -) + return $profilesByPubkey.get($pubkey) +}) -export const userFollows = derived( - [followsByPubkey, pubkey], - ([$followsByPubkey, $pubkey]) => { - if (!$pubkey) return undefined +export const userFollows = derived([followsByPubkey, pubkey], ([$followsByPubkey, $pubkey]) => { + if (!$pubkey) return undefined - loadFollows($pubkey) + loadFollows($pubkey) - return $followsByPubkey.get($pubkey) - } -) + return $followsByPubkey.get($pubkey) +}) -export const userMutes = derived( - [mutesByPubkey, pubkey], - ([$mutesByPubkey, $pubkey]) => { - if (!$pubkey) return undefined +export const userMutes = derived([mutesByPubkey, pubkey], ([$mutesByPubkey, $pubkey]) => { + if (!$pubkey) return undefined - loadMutes($pubkey) + loadMutes($pubkey) - return $mutesByPubkey.get($pubkey) - } -) + return $mutesByPubkey.get($pubkey) +}) export const userRelaySelections = derived( [relaySelectionsByPubkey, pubkey], @@ -47,7 +43,7 @@ export const userRelaySelections = derived( loadRelaySelections($pubkey) return $relaySelectionsByPubkey.get($pubkey) - } + }, ) export const userInboxRelaySelections = derived( @@ -58,7 +54,7 @@ export const userInboxRelaySelections = derived( loadInboxRelaySelections($pubkey) return $inboxRelaySelectionsByPubkey.get($pubkey) - } + }, ) export const getUserWotScore = (tpk: string) => wotGraph.get().get(tpk) || 0 diff --git a/packages/app/src/wot.ts b/packages/app/src/wot.ts index 56cc1a8..b502642 100644 --- a/packages/app/src/wot.ts +++ b/packages/app/src/wot.ts @@ -1,10 +1,10 @@ -import {derived, writable} from 'svelte/store' -import {max, throttle, addToMapKey, inc, dec} from '@welshman/lib' -import {getListTags, getPubkeyTagValues} from '@welshman/util' -import {throttled, withGetter} from '@welshman/store' -import {pubkey} from './session' -import {follows, followsByPubkey} from './follows' -import {mutes, mutesByPubkey} from './mutes' +import {derived, writable} from "svelte/store" +import {max, throttle, addToMapKey, inc, dec} from "@welshman/lib" +import {getListTags, getPubkeyTagValues} from "@welshman/util" +import {throttled, withGetter} from "@welshman/store" +import {pubkey} from "./session.js" +import {follows, followsByPubkey} from "./follows.js" +import {mutes, mutesByPubkey} from "./mutes.js" export const getFollows = (pubkey: string) => getPubkeyTagValues(getListTags(followsByPubkey.get().get(pubkey))) @@ -27,46 +27,38 @@ export const getNetwork = (pubkey: string) => { return Array.from(network) } - export const followersByPubkey = withGetter( - derived( - throttled(1000, follows), - lists => { - const $followersByPubkey = new Map>() + derived(throttled(1000, follows), lists => { + const $followersByPubkey = new Map>() - for (const list of lists) { - for (const pubkey of getPubkeyTagValues(getListTags(list))) { - addToMapKey($followersByPubkey, pubkey, list.event.pubkey) - } + for (const list of lists) { + for (const pubkey of getPubkeyTagValues(getListTags(list))) { + addToMapKey($followersByPubkey, pubkey, list.event.pubkey) } - - return $followersByPubkey } - ) + + return $followersByPubkey + }), ) export const mutersByPubkey = withGetter( - derived( - throttled(1000, mutes), - lists => { - const $mutersByPubkey = new Map>() + derived(throttled(1000, mutes), lists => { + const $mutersByPubkey = new Map>() - for (const list of lists) { - for (const pubkey of getPubkeyTagValues(getListTags(list))) { - addToMapKey($mutersByPubkey, pubkey, list.event.pubkey) - } + for (const list of lists) { + for (const pubkey of getPubkeyTagValues(getListTags(list))) { + addToMapKey($mutersByPubkey, pubkey, list.event.pubkey) } - - return $mutersByPubkey } - ) + + return $mutersByPubkey + }), ) export const getFollowers = (pubkey: string) => Array.from(followersByPubkey.get().get(pubkey) || []) -export const getMuters = (pubkey: string) => - Array.from(mutersByPubkey.get().get(pubkey) || []) +export const getMuters = (pubkey: string) => Array.from(mutersByPubkey.get().get(pubkey) || []) export const getFollowsWhoFollow = (pubkey: string, target: string) => getFollows(pubkey).filter(other => getFollows(other).includes(target)) diff --git a/packages/app/src/zappers.ts b/packages/app/src/zappers.ts index 7c30f4f..37267b3 100644 --- a/packages/app/src/zappers.ts +++ b/packages/app/src/zappers.ts @@ -1,9 +1,19 @@ -import {writable, derived} from 'svelte/store' -import {type Zapper} from '@welshman/util' +import {writable, derived} from "svelte/store" +import {type Zapper} from "@welshman/util" import {type SubscribeRequestWithHandlers} from "@welshman/net" -import {ctx, identity, fetchJson, uniq, bech32ToHex, hexToBech32, tryCatch, batcher, postJson} from '@welshman/lib' -import {collection} from './collection' -import {deriveProfile} from './profiles' +import { + ctx, + identity, + fetchJson, + uniq, + bech32ToHex, + hexToBech32, + tryCatch, + batcher, + postJson, +} from "@welshman/lib" +import {collection} from "./collection.js" +import {deriveProfile} from "./profiles.js" export const zappers = writable([]) @@ -16,7 +26,9 @@ export const fetchZappers = async (lnurls: string[]) => { const hexUrls = lnurls.map(lnurl => tryCatch(() => bech32ToHex(lnurl))).filter(identity) if (hexUrls.length > 0) { - const res: any = await tryCatch(async () => await postJson(`${base}/zapper/info`, {lnurls: hexUrls})) + const res: any = await tryCatch( + async () => await postJson(`${base}/zapper/info`, {lnurls: hexUrls}), + ) for (const {lnurl, info} of res?.data || []) { tryCatch(() => zappersByLnurl.set(hexToBech32("lnurl", lnurl), info)) @@ -29,7 +41,7 @@ export const fetchZappers = async (lnurls: string[]) => { const info = hexUrl ? await tryCatch(async () => await fetchJson(hexUrl)) : undefined return {lnurl, hexUrl, info} - }) + }), ) for (const {lnurl, info} of results) { @@ -68,17 +80,16 @@ export const { }), }) -export const deriveZapperForPubkey = (pubkey: string, request: Partial = {}) => - derived( - [zappersByLnurl, deriveProfile(pubkey, request)], - ([$zappersByLnurl, $profile]) => { - if (!$profile?.lnurl) { - return undefined - } - - loadZapper($profile.lnurl) - - return $zappersByLnurl.get($profile.lnurl) +export const deriveZapperForPubkey = ( + pubkey: string, + request: Partial = {}, +) => + derived([zappersByLnurl, deriveProfile(pubkey, request)], ([$zappersByLnurl, $profile]) => { + if (!$profile?.lnurl) { + return undefined } - ) + loadZapper($profile.lnurl) + + return $zappersByLnurl.get($profile.lnurl) + }) diff --git a/packages/app/tsc-multi.json b/packages/app/tsc-multi.json deleted file mode 100644 index 6c37019..0000000 --- a/packages/app/tsc-multi.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "targets": [ - {"extname": ".cjs", "module": "commonjs"}, - {"extname": ".mjs", "module": "esnext", "moduleResolution": "node"} - ], - "projects": ["tsconfig.json"] -} diff --git a/packages/app/tsconfig.json b/packages/app/tsconfig.json index 8defcdc..97e6372 100644 --- a/packages/app/tsconfig.json +++ b/packages/app/tsconfig.json @@ -3,9 +3,12 @@ "compilerOptions": { "rootDir": ".", "outDir": "build", - "esModuleInterop": true, - "skipLibCheck": true, - "lib": ["esnext", "dom", "dom.iterable"] + "module": "nodenext", + "moduleResolution": "nodenext", + "lib": ["esnext", "dom"] }, - "include": ["src/**/*.ts"] + "include": [ + "src/**/*.ts", + "test/**/*.ts" + ] } diff --git a/packages/content/package.json b/packages/content/package.json index 6adfe9f..c306dc2 100644 --- a/packages/content/package.json +++ b/packages/content/package.json @@ -11,25 +11,23 @@ "files": [ "build" ], + "engines": { + "node": ">=12.0.0" + }, "types": "./build/src/index.d.ts", "exports": { ".": { "types": "./build/src/index.d.ts", - "import": "./build/src/index.mjs", - "require": "./build/src/index.cjs" + "import": "./build/src/index.js", + "require": "./build/src/index.js" } }, "scripts": { "pub": "npm run lint && npm run build && npm publish", - "build": "gts clean && tsc-multi", + "build": "gts clean && tsc", "lint": "gts lint", "fix": "gts fix" }, - "devDependencies": { - "gts": "^5.0.1", - "tsc-multi": "^1.1.0", - "typescript": "~5.1.6" - }, "dependencies": { "@braintree/sanitize-url": "^7.0.2", "@welshman/lib": "~0.0.33", diff --git a/packages/content/src/index.ts b/packages/content/src/index.ts index 1edf01a..79e7317 100644 --- a/packages/content/src/index.ts +++ b/packages/content/src/index.ts @@ -1,5 +1,5 @@ -import {nip19} from "nostr-tools" -import {sanitizeUrl} from '@braintree/sanitize-url' +import {decode, neventEncode, nprofileEncode, naddrEncode} from "nostr-tools/nip19" +import {sanitizeUrl} from "@braintree/sanitize-url" const last = (xs: T[], ...args: unknown[]) => xs[xs.length - 1] @@ -33,7 +33,7 @@ type ProfilePointer = { export type ParseContext = { results: Parsed[] - content: string, + content: string tags: string[][] } @@ -124,31 +124,36 @@ export type ParsedAddress = { } export type Parsed = - ParsedAddress | - ParsedCashu | - ParsedCode | - ParsedEllipsis | - ParsedEvent | - ParsedInvoice | - ParsedLink | - ParsedNewline | - ParsedProfile | - ParsedText | - ParsedTopic + | ParsedAddress + | ParsedCashu + | ParsedCode + | ParsedEllipsis + | ParsedEvent + | ParsedInvoice + | ParsedLink + | ParsedNewline + | ParsedProfile + | ParsedText + | ParsedTopic // Matchers -export const isAddress = (parsed: Parsed): parsed is ParsedAddress => parsed.type === ParsedType.Address -export const isCashu = (parsed: Parsed): parsed is ParsedCashu => parsed.type === ParsedType.Cashu -export const isCode = (parsed: Parsed): parsed is ParsedCode => parsed.type === ParsedType.Code -export const isEllipsis = (parsed: Parsed): parsed is ParsedEllipsis => parsed.type === ParsedType.Ellipsis -export const isEvent = (parsed: Parsed): parsed is ParsedEvent => parsed.type === ParsedType.Event -export const isInvoice = (parsed: Parsed): parsed is ParsedInvoice => parsed.type === ParsedType.Invoice -export const isLink = (parsed: Parsed): parsed is ParsedLink => parsed.type === ParsedType.Link -export const isNewline = (parsed: Parsed): parsed is ParsedNewline => parsed.type === ParsedType.Newline -export const isProfile = (parsed: Parsed): parsed is ParsedProfile => parsed.type === ParsedType.Profile -export const isText = (parsed: Parsed): parsed is ParsedText => parsed.type === ParsedType.Text -export const isTopic = (parsed: Parsed): parsed is ParsedTopic => parsed.type === ParsedType.Topic +export const isAddress = (parsed: Parsed): parsed is ParsedAddress => + parsed.type === ParsedType.Address +export const isCashu = (parsed: Parsed): parsed is ParsedCashu => parsed.type === ParsedType.Cashu +export const isCode = (parsed: Parsed): parsed is ParsedCode => parsed.type === ParsedType.Code +export const isEllipsis = (parsed: Parsed): parsed is ParsedEllipsis => + parsed.type === ParsedType.Ellipsis +export const isEvent = (parsed: Parsed): parsed is ParsedEvent => parsed.type === ParsedType.Event +export const isInvoice = (parsed: Parsed): parsed is ParsedInvoice => + parsed.type === ParsedType.Invoice +export const isLink = (parsed: Parsed): parsed is ParsedLink => parsed.type === ParsedType.Link +export const isNewline = (parsed: Parsed): parsed is ParsedNewline => + parsed.type === ParsedType.Newline +export const isProfile = (parsed: Parsed): parsed is ParsedProfile => + parsed.type === ParsedType.Profile +export const isText = (parsed: Parsed): parsed is ParsedText => parsed.type === ParsedType.Text +export const isTopic = (parsed: Parsed): parsed is ParsedTopic => parsed.type === ParsedType.Topic // Parsers for known formats @@ -157,7 +162,7 @@ export const parseAddress = (text: string, context: ParseContext): ParsedAddress if (naddr) { try { - const {data} = nip19.decode(fromNostrURI(naddr)) + const {data} = decode(fromNostrURI(naddr)) return {type: ParsedType.Address, value: data as AddressPointer, raw: naddr} } catch (e) { @@ -195,10 +200,8 @@ export const parseEvent = (text: string, context: ParseContext): ParsedEvent | v if (entity) { try { - const {type, data} = nip19.decode(fromNostrURI(entity)) - const value = type === "note" - ? {id: data as string, relays: []} - : data as EventPointer + const {type, data} = decode(fromNostrURI(entity)) + const value = type === "note" ? {id: data as string, relays: []} : (data as EventPointer) return {type: ParsedType.Event, value, raw: entity} } catch (e) { @@ -217,15 +220,16 @@ export const parseInvoice = (text: string, context: ParseContext): ParsedInvoice export const parseLink = (text: string, context: ParseContext): ParsedLink | void => { const prev = last(context.results) - const [link] = text.match(/^([a-z\+:]{2,30}:\/\/)?[-\.~\w]+\.[\w]{2,6}([^\s]*[^<>"'\.!,:\s\)\(]+)?/gi) || [] + const [link] = + text.match(/^([a-z\+:]{2,30}:\/\/)?[-\.~\w]+\.[\w]{2,6}([^\s]*[^<>"'\.!,:\s\)\(]+)?/gi) || [] // Skip url if it's just the end of a filepath or an ellipse - if (!link || prev?.type === ParsedType.Text && prev.value.endsWith("/") || link.match(/\.\./)) { + if (!link || (prev?.type === ParsedType.Text && prev.value.endsWith("/")) || link.match(/\.\./)) { return } // Skip it if it looks like an IP address but doesn't have a protocol - if (link.match(/\d+\.\d+/) && !link.includes('://')) { + if (link.match(/\d+\.\d+/) && !link.includes("://")) { return } @@ -240,7 +244,7 @@ export const parseLink = (text: string, context: ParseContext): ParsedLink | voi const meta = Object.fromEntries(new URLSearchParams(url.hash.slice(1)).entries()) for (const tag of context.tags) { - if (tag[0] === 'imeta' && tag.find(t => t.includes(`url ${link}`))) { + if (tag[0] === "imeta" && tag.find(t => t.includes(`url ${link}`))) { Object.assign(meta, Object.fromEntries(tag.slice(1).map((m: string) => m.split(" ")))) } } @@ -261,10 +265,9 @@ export const parseProfile = (text: string, context: ParseContext): ParsedProfile if (entity) { try { - const {type, data} = nip19.decode(fromNostrURI(entity.replace('@', ''))) - const value = type === "npub" - ? {pubkey: data as string, relays: []} - : data as ProfilePointer + const {type, data} = decode(fromNostrURI(entity.replace("@", ""))) + const value = + type === "npub" ? {pubkey: data as string, relays: []} : (data as ProfilePointer) return {type: ParsedType.Profile, value, raw: entity} } catch (e) { @@ -282,10 +285,12 @@ export const parseTopic = (text: string, context: ParseContext): ParsedTopic | v } } - // Parse other formats to known types -export const parseLegacyMention = (text: string, context: ParseContext): ParsedProfile | ParsedEvent | void => { +export const parseLegacyMention = ( + text: string, + context: ParseContext, +): ParsedProfile | ParsedEvent | void => { const mentionMatch = text.match(/^#\[(\d+)\]/i) || [] if (mentionMatch) { @@ -313,7 +318,7 @@ export const parsers = [ parseEvent, parseCashu, parseInvoice, - parseLink + parseLink, ] export const parseNext = (raw: string, context: ParseContext): Parsed | void => { @@ -371,12 +376,7 @@ type TruncateOpts = { export const truncate = ( content: Parsed[], - { - minLength = 500, - maxLength = 700, - mediaLength = 200, - entityLength = 30, - }: TruncateOpts = {}, + {minLength = 500, maxLength = 700, mediaLength = 200, entityLength = 30}: TruncateOpts = {}, ) => { // Get a list of content sizes so we know where to truncate // Non-plaintext things might take up more or less room if rendered @@ -429,7 +429,7 @@ export class Renderer { toString = () => this.value addText = (value: string) => { - const element = document.createElement('div') + const element = document.createElement("div") element.innerText = value @@ -459,17 +459,17 @@ export type RenderOptions = { } export const textRenderOptions = { - newline: '\n', - entityBase: '', + newline: "\n", + entityBase: "", renderLink: (href: string, display: string) => href, - renderEntity: (entity: string) => entity.slice(0, 16) + '…', + renderEntity: (entity: string) => entity.slice(0, 16) + "…", } export const htmlRenderOptions = { - newline: '\n', - entityBase: 'https://njump.me/', + newline: "\n", + entityBase: "https://njump.me/", renderLink: (href: string, display: string) => { - const element = document.createElement('a') + const element = document.createElement("a") element.href = sanitizeUrl(href) element.target = "_blank" @@ -477,7 +477,7 @@ export const htmlRenderOptions = { return element.outerHTML }, - renderEntity: (entity: string) => entity.slice(0, 16) + '…', + renderEntity: (entity: string) => entity.slice(0, 16) + "…", } export const makeTextRenderer = (options: Partial = {}) => @@ -499,31 +499,56 @@ export const renderInvoice = (p: ParsedInvoice, r: Renderer) => r.addText(p.valu export const renderLink = (p: ParsedLink, r: Renderer) => r.addLink(p.value.url.toString(), p.value.url.host + p.value.url.pathname) -export const renderNewline = (p: ParsedNewline, r: Renderer) => r.addNewlines(Array.from(p.value).length) +export const renderNewline = (p: ParsedNewline, r: Renderer) => + r.addNewlines(Array.from(p.value).length) export const renderText = (p: ParsedText, r: Renderer) => r.addText(p.value) export const renderTopic = (p: ParsedTopic, r: Renderer) => r.addText(p.value) -export const renderEvent = (p: ParsedEvent, r: Renderer) => r.addEntityLink(nip19.neventEncode(p.value)) +export const renderEvent = (p: ParsedEvent, r: Renderer) => r.addEntityLink(neventEncode(p.value)) -export const renderProfile = (p: ParsedProfile, r: Renderer) => r.addEntityLink(nip19.nprofileEncode(p.value)) +export const renderProfile = (p: ParsedProfile, r: Renderer) => + r.addEntityLink(nprofileEncode(p.value)) -export const renderAddress = (p: ParsedAddress, r: Renderer) => r.addEntityLink(nip19.naddrEncode(p.value)) +export const renderAddress = (p: ParsedAddress, r: Renderer) => + r.addEntityLink(naddrEncode(p.value)) export const renderOne = (parsed: Parsed, renderer: Renderer) => { switch (parsed.type) { - case ParsedType.Address: renderAddress(parsed as ParsedAddress, renderer); break - case ParsedType.Cashu: renderCashu(parsed as ParsedCashu, renderer); break - case ParsedType.Code: renderCode(parsed as ParsedCode, renderer); break - case ParsedType.Ellipsis: renderEllipsis(parsed as ParsedEllipsis, renderer); break - case ParsedType.Event: renderEvent(parsed as ParsedEvent, renderer); break - case ParsedType.Invoice: renderInvoice(parsed as ParsedInvoice, renderer); break - case ParsedType.Link: renderLink(parsed as ParsedLink, renderer); break - case ParsedType.Newline: renderNewline(parsed as ParsedNewline, renderer); break - case ParsedType.Profile: renderProfile(parsed as ParsedProfile, renderer); break - case ParsedType.Text: renderText(parsed as ParsedText, renderer); break - case ParsedType.Topic: renderTopic(parsed as ParsedTopic, renderer); break + case ParsedType.Address: + renderAddress(parsed as ParsedAddress, renderer) + break + case ParsedType.Cashu: + renderCashu(parsed as ParsedCashu, renderer) + break + case ParsedType.Code: + renderCode(parsed as ParsedCode, renderer) + break + case ParsedType.Ellipsis: + renderEllipsis(parsed as ParsedEllipsis, renderer) + break + case ParsedType.Event: + renderEvent(parsed as ParsedEvent, renderer) + break + case ParsedType.Invoice: + renderInvoice(parsed as ParsedInvoice, renderer) + break + case ParsedType.Link: + renderLink(parsed as ParsedLink, renderer) + break + case ParsedType.Newline: + renderNewline(parsed as ParsedNewline, renderer) + break + case ParsedType.Profile: + renderProfile(parsed as ParsedProfile, renderer) + break + case ParsedType.Text: + renderText(parsed as ParsedText, renderer) + break + case ParsedType.Topic: + renderTopic(parsed as ParsedTopic, renderer) + break } return renderer diff --git a/packages/content/tsc-multi.json b/packages/content/tsc-multi.json deleted file mode 100644 index 6c37019..0000000 --- a/packages/content/tsc-multi.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "targets": [ - {"extname": ".cjs", "module": "commonjs"}, - {"extname": ".mjs", "module": "esnext", "moduleResolution": "node"} - ], - "projects": ["tsconfig.json"] -} diff --git a/packages/content/tsconfig.json b/packages/content/tsconfig.json index 8defcdc..97e6372 100644 --- a/packages/content/tsconfig.json +++ b/packages/content/tsconfig.json @@ -3,9 +3,12 @@ "compilerOptions": { "rootDir": ".", "outDir": "build", - "esModuleInterop": true, - "skipLibCheck": true, - "lib": ["esnext", "dom", "dom.iterable"] + "module": "nodenext", + "moduleResolution": "nodenext", + "lib": ["esnext", "dom"] }, - "include": ["src/**/*.ts"] + "include": [ + "src/**/*.ts", + "test/**/*.ts" + ] } diff --git a/packages/dvm/README.md b/packages/dvm/README.md index e876370..3311367 100644 --- a/packages/dvm/README.md +++ b/packages/dvm/README.md @@ -29,11 +29,11 @@ req.emitter.on(DVMEvent.Result, (url, event) => console.log(event)) # Handler example ```javascript -const {bytesToHex} = require('@noble/hashes/utils') -const {generateSecretKey} = require('nostr-tools') -const {createEvent} = require('@welshman/util') -const {subscribe} = require('@welshman/net') -const {DVM} = require('@welshman/dvm') +import {bytesToHex} from '@noble/hashes/utils' +import {generateSecretKey} from 'nostr-tools' +import {createEvent} from '@welshman/util' +import {subscribe} from '@welshman/net' +import {DVM} from '@welshman/dvm' // Your DVM's private key. Store this somewhere safe // const hexPrivateKey = bytesToHex(generateSecretKey()) diff --git a/packages/dvm/package.json b/packages/dvm/package.json index c7fb55e..c7f848a 100644 --- a/packages/dvm/package.json +++ b/packages/dvm/package.json @@ -15,22 +15,18 @@ "exports": { ".": { "types": "./build/src/index.d.ts", - "import": "./build/src/index.mjs", - "require": "./build/src/index.cjs" + "import": "./build/src/index.js", + "require": "./build/src/index.js" } }, "scripts": { "pub": "npm run lint && npm run build && npm publish", - "build": "gts clean && tsc-multi", + "build": "gts clean && tsc", "lint": "gts lint", "fix": "gts fix" }, - "devDependencies": { - "gts": "^5.0.1", - "tsc-multi": "^1.1.0", - "typescript": "~5.1.6" - }, "dependencies": { + "@noble/hashes": "^1.6.1", "@welshman/lib": "~0.0.33", "@welshman/net": "~0.0.41", "@welshman/util": "~0.0.50", diff --git a/packages/dvm/src/handler.ts b/packages/dvm/src/handler.ts index 67bf923..ed8706b 100644 --- a/packages/dvm/src/handler.ts +++ b/packages/dvm/src/handler.ts @@ -1,8 +1,8 @@ -import {hexToBytes} from '@noble/hashes/utils' -import {getPublicKey, finalizeEvent} from 'nostr-tools' -import {now} from '@welshman/lib' -import type {TrustedEvent, StampedEvent, Filter} from '@welshman/util' -import {subscribe, publish} from '@welshman/net' +import {hexToBytes} from "@noble/hashes/utils" +import {getPublicKey, finalizeEvent} from "nostr-tools/pure" +import {now} from "@welshman/lib" +import type {TrustedEvent, StampedEvent, Filter} from "@welshman/util" +import {subscribe, publish} from "@welshman/net" export type DVMHandler = { stop?: () => void @@ -43,14 +43,14 @@ export class DVM { const filter: Filter = {kinds, since} if (requireMention) { - filter['#p'] = [getPublicKey(hexToBytes(sk))] + filter["#p"] = [getPublicKey(hexToBytes(sk))] } const filters = [filter] const sub = subscribe({relays, filters}) - sub.emitter.on('event', (url: string, e: TrustedEvent) => this.onEvent(e)) - sub.emitter.on('complete', () => resolve()) + sub.emitter.on("event", (url: string, e: TrustedEvent) => this.onEvent(e)) + sub.emitter.on("complete", () => resolve()) }) } } @@ -79,29 +79,29 @@ export class DVM { this.seen.add(request.id) if (this.logEvents) { - console.info('Handling request', request) + console.info("Handling request", request) } for await (const event of handler.handleEvent(request)) { if (event.kind !== 7000) { - event.tags.push(['request', JSON.stringify(request)]) + event.tags.push(["request", JSON.stringify(request)]) - const inputTag = request.tags.find((t: string[]) => t[0] === 'i') + const inputTag = request.tags.find((t: string[]) => t[0] === "i") if (inputTag) { event.tags.push(inputTag) } } - event.tags.push(['p', request.pubkey]) - event.tags.push(['e', request.id]) + event.tags.push(["p", request.pubkey]) + event.tags.push(["e", request.id]) if (expireAfter) { - event.tags.push(['expiration', String(now() + expireAfter)]) + event.tags.push(["expiration", String(now() + expireAfter)]) } if (this.logEvents) { - console.info('Publishing event', event) + console.info("Publishing event", event) } this.publish(event) @@ -113,7 +113,7 @@ export class DVM { const event = finalizeEvent(template, hexToBytes(sk)) await new Promise(resolve => { - publish({event, relays}).emitter.on('success', () => resolve()) + publish({event, relays}).emitter.on("success", () => resolve()) }) } } diff --git a/packages/dvm/src/index.ts b/packages/dvm/src/index.ts index 8f18980..ea75dd2 100644 --- a/packages/dvm/src/index.ts +++ b/packages/dvm/src/index.ts @@ -1,2 +1,2 @@ -export * from './handler' -export * from './request' +export * from "./handler.js" +export * from "./request.js" diff --git a/packages/dvm/src/request.ts b/packages/dvm/src/request.ts index c98d174..7847467 100644 --- a/packages/dvm/src/request.ts +++ b/packages/dvm/src/request.ts @@ -1,7 +1,7 @@ -import {Emitter, now} from '@welshman/lib' -import type {TrustedEvent, SignedEvent, Filter} from '@welshman/util' -import {subscribe, publish, SubscriptionEvent} from '@welshman/net' -import type {Subscription, Publish} from '@welshman/net' +import {Emitter, now} from "@welshman/lib" +import type {TrustedEvent, SignedEvent, Filter} from "@welshman/util" +import {subscribe, publish, SubscriptionEvent} from "@welshman/net" +import type {Subscription, Publish} from "@welshman/net" export enum DVMEvent { Progress = "progress", @@ -18,7 +18,7 @@ export type DVMRequestOptions = { export type DVMRequest = { request: DVMRequestOptions - emitter: Emitter, + emitter: Emitter sub: Subscription pub: Publish } diff --git a/packages/dvm/tsc-multi.json b/packages/dvm/tsc-multi.json deleted file mode 100644 index 6c37019..0000000 --- a/packages/dvm/tsc-multi.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "targets": [ - {"extname": ".cjs", "module": "commonjs"}, - {"extname": ".mjs", "module": "esnext", "moduleResolution": "node"} - ], - "projects": ["tsconfig.json"] -} diff --git a/packages/dvm/tsconfig.json b/packages/dvm/tsconfig.json index 8defcdc..97e6372 100644 --- a/packages/dvm/tsconfig.json +++ b/packages/dvm/tsconfig.json @@ -3,9 +3,12 @@ "compilerOptions": { "rootDir": ".", "outDir": "build", - "esModuleInterop": true, - "skipLibCheck": true, - "lib": ["esnext", "dom", "dom.iterable"] + "module": "nodenext", + "moduleResolution": "nodenext", + "lib": ["esnext", "dom"] }, - "include": ["src/**/*.ts"] + "include": [ + "src/**/*.ts", + "test/**/*.ts" + ] } diff --git a/packages/feeds/package.json b/packages/feeds/package.json index 1c40076..784cffa 100644 --- a/packages/feeds/package.json +++ b/packages/feeds/package.json @@ -15,21 +15,16 @@ "exports": { ".": { "types": "./build/src/index.d.ts", - "import": "./build/src/index.mjs", - "require": "./build/src/index.cjs" + "import": "./build/src/index.js", + "require": "./build/src/index.js" } }, "scripts": { "pub": "npm run lint && npm run build && npm publish", - "build": "gts clean && tsc-multi", + "build": "gts clean && tsc", "lint": "gts lint", "fix": "gts fix" }, - "devDependencies": { - "gts": "^5.0.1", - "tsc-multi": "^1.1.0", - "typescript": "~5.1.6" - }, "dependencies": { "@welshman/lib": "~0.0.33", "@welshman/util": "~0.0.50" diff --git a/packages/feeds/src/compiler.ts b/packages/feeds/src/compiler.ts index bf6f840..9886482 100644 --- a/packages/feeds/src/compiler.ts +++ b/packages/feeds/src/compiler.ts @@ -1,15 +1,25 @@ -import {uniq, identity, flatten, pushToMapKey, intersection, tryCatch, now} from '@welshman/lib' -import type {TrustedEvent, Filter} from '@welshman/util' -import {intersectFilters, matchFilter, getAddress, getIdFilters, unionFilters} from '@welshman/util' -import type {CreatedAtItem, RequestItem, ListItem, LabelItem, WOTItem, DVMItem, Scope, Feed, FeedOptions} from './core' -import {getFeedArgs, feedsFromTags} from './utils' -import {FeedType} from './core' +import {uniq, identity, flatten, pushToMapKey, intersection, tryCatch, now} from "@welshman/lib" +import type {TrustedEvent, Filter} from "@welshman/util" +import {intersectFilters, matchFilter, getAddress, getIdFilters, unionFilters} from "@welshman/util" +import type { + CreatedAtItem, + RequestItem, + ListItem, + LabelItem, + WOTItem, + DVMItem, + Scope, + Feed, + FeedOptions, +} from "./core.js" +import {getFeedArgs, feedsFromTags} from "./utils.js" +import {FeedType} from "./core.js" export class FeedCompiler { constructor(readonly options: FeedOptions) {} canCompile(feed: Feed): boolean { - switch(feed[0]) { + switch (feed[0]) { case FeedType.Union: case FeedType.Intersection: return getFeedArgs(feed).every(f => this.canCompile(f)) @@ -34,22 +44,37 @@ export class FeedCompiler { } async compile(feed: Feed): Promise { - switch(feed[0]) { - case FeedType.ID: return this._compileFilter('ids', getFeedArgs(feed)) - case FeedType.Kind: return this._compileFilter('kinds', getFeedArgs(feed)) - case FeedType.Author: return this._compileFilter('authors', getFeedArgs(feed)) - case FeedType.DVM: return await this._compileDvms(getFeedArgs(feed)) - case FeedType.Intersection: return await this._compileIntersection(getFeedArgs(feed)) - case FeedType.List: return await this._compileLists(getFeedArgs(feed)) - case FeedType.Label: return await this._compileLabels(getFeedArgs(feed)) - case FeedType.Union: return await this._compileUnion(getFeedArgs(feed)) - case FeedType.Address: return this._compileAddresses(getFeedArgs(feed)) - case FeedType.CreatedAt: return this._compileCreatedAt(getFeedArgs(feed)) - case FeedType.Scope: return this._compileScopes(getFeedArgs(feed)) - case FeedType.Search: return this._compileSearches(getFeedArgs(feed)) - case FeedType.WOT: return this._compileWot(getFeedArgs(feed)) - case FeedType.Relay: return [{relays: getFeedArgs(feed)}] - case FeedType.Global: return [{filters: [{}]}] + switch (feed[0]) { + case FeedType.ID: + return this._compileFilter("ids", getFeedArgs(feed)) + case FeedType.Kind: + return this._compileFilter("kinds", getFeedArgs(feed)) + case FeedType.Author: + return this._compileFilter("authors", getFeedArgs(feed)) + case FeedType.DVM: + return await this._compileDvms(getFeedArgs(feed)) + case FeedType.Intersection: + return await this._compileIntersection(getFeedArgs(feed)) + case FeedType.List: + return await this._compileLists(getFeedArgs(feed)) + case FeedType.Label: + return await this._compileLabels(getFeedArgs(feed)) + case FeedType.Union: + return await this._compileUnion(getFeedArgs(feed)) + case FeedType.Address: + return this._compileAddresses(getFeedArgs(feed)) + case FeedType.CreatedAt: + return this._compileCreatedAt(getFeedArgs(feed)) + case FeedType.Scope: + return this._compileScopes(getFeedArgs(feed)) + case FeedType.Search: + return this._compileSearches(getFeedArgs(feed)) + case FeedType.WOT: + return this._compileWot(getFeedArgs(feed)) + case FeedType.Relay: + return [{relays: getFeedArgs(feed)}] + case FeedType.Global: + return [{filters: [{}]}] case FeedType.Tag: { const [key, ...value] = getFeedArgs(feed) @@ -99,7 +124,13 @@ export class FeedCompiler { } _compileWot(wotItems: WOTItem[]) { - return [{filters: wotItems.map(({min = 0, max = 1}) => ({authors: this.options.getPubkeysForWOTRange(min, max)}))}] + return [ + { + filters: wotItems.map(({min = 0, max = 1}) => ({ + authors: this.options.getPubkeysForWOTRange(min, max), + })), + }, + ] } async _compileDvms(items: DVMItem[]): Promise { @@ -110,14 +141,14 @@ export class FeedCompiler { this.options.requestDVM({ ...request, onEvent: async (e: TrustedEvent) => { - const tags = await tryCatch(() => JSON.parse(e.content)) || [] + const tags = (await tryCatch(() => JSON.parse(e.content))) || [] for (const feed of feedsFromTags(tags, mappings)) { feeds.push(feed) } }, - }) - ) + }), + ), ) return await this._compileUnion(feeds) @@ -129,16 +160,15 @@ export class FeedCompiler { const result = [] for (let {filters, relays} of head || []) { - const matchingGroups = tail.map( - items => items.filter( - it => ( - (!relays || !it.relays || intersection(relays, it.relays).length > 0) && - (!filters || !it.filters || intersectFilters([filters, it.filters]).length > 0) - ) + const matchingGroups = tail + .map(items => + items.filter( + it => + (!relays || !it.relays || intersection(relays, it.relays).length > 0) && + (!filters || !it.filters || intersectFilters([filters, it.filters]).length > 0), + ), ) - ).filter( - items => items.length > 0 - ) + .filter(items => items.length > 0) if (matchingGroups.length < tail.length) { continue @@ -190,7 +220,7 @@ export class FeedCompiler { } } } - }) + }), ) const items: RequestItem[] = [] @@ -238,8 +268,8 @@ export class FeedCompiler { } return feeds - }) - ) + }), + ), ) return this._compileUnion(feeds) @@ -254,8 +284,8 @@ export class FeedCompiler { relays, filters: [{kinds: [1985], ...filter}], onEvent: (e: TrustedEvent) => events.push(e), - }) - ) + }), + ), ) const feeds = flatten( @@ -272,8 +302,8 @@ export class FeedCompiler { } return feedsFromTags(tags, mappings) - }) - ) + }), + ), ) return this._compileUnion(feeds) diff --git a/packages/feeds/src/controller.ts b/packages/feeds/src/controller.ts index 7317471..f5de6b4 100644 --- a/packages/feeds/src/controller.ts +++ b/packages/feeds/src/controller.ts @@ -1,9 +1,9 @@ -import {inc, memoize, omitVals, max, min, now} from '@welshman/lib' -import type {TrustedEvent, Filter} from '@welshman/util' -import {EPOCH, trimFilters, guessFilterDelta} from '@welshman/util' -import type {Feed, RequestItem, FeedOptions} from './core' -import {FeedType} from './core' -import {FeedCompiler} from './compiler' +import {inc, memoize, omitVals, max, min, now} from "@welshman/lib" +import type {TrustedEvent, Filter} from "@welshman/util" +import {EPOCH, trimFilters, guessFilterDelta} from "@welshman/util" +import type {Feed, RequestItem, FeedOptions} from "./core.js" +import {FeedType} from "./core.js" +import {FeedCompiler} from "./compiler.js" export class FeedController { compiler: FeedCompiler @@ -26,7 +26,7 @@ export class FeedController { return this._getRequestsLoader(requestItems) } - switch(type) { + switch (type) { case FeedType.Difference: return this._getDifferenceLoader(feed as Feed[]) case FeedType.Intersection: @@ -45,8 +45,8 @@ export class FeedController { const seen = new Set() const exhausted = new Set() const loaders = await Promise.all( - requests.map( - request => this._getRequestLoader(request, { + requests.map(request => + this._getRequestLoader(request, { onExhausted: () => exhausted.add(request), onEvent: e => { if (!seen.has(e.id)) { @@ -54,8 +54,8 @@ export class FeedController { seen.add(e.id) } }, - }) - ) + }), + ), ) return async (limit: number) => { @@ -75,8 +75,8 @@ export class FeedController { filters = [{}] } - const untils = filters.flatMap((filter: Filter) => filter.until ? [filter.until] : []) - const sinces = filters.flatMap((filter: Filter) => filter.since ? [filter.since] : []) + const untils = filters.flatMap((filter: Filter) => (filter.until ? [filter.until] : [])) + const sinces = filters.flatMap((filter: Filter) => (filter.since ? [filter.since] : [])) const maxUntil = untils.length === filters.length ? max(untils) : now() const minSince = sinces.length === filters.length ? min(sinces) : EPOCH const initialDelta = guessFilterDelta(filters) @@ -110,15 +110,17 @@ export class FeedController { let count = 0 - await request(omitVals([undefined], { - relays, - filters: trimFilters(requestFilters), - onEvent: (event: TrustedEvent) => { - count += 1 - until = Math.min(until, event.created_at - 1) - onEvent?.(event) - }, - })) + await request( + omitVals([undefined], { + relays, + filters: trimFilters(requestFilters), + onEvent: (event: TrustedEvent) => { + count += 1 + until = Math.min(until, event.created_at - 1) + onEvent?.(event) + }, + }), + ) if (useWindowing) { if (since === minSince) { @@ -149,20 +151,21 @@ export class FeedController { const seen = new Set() const controllers = await Promise.all( - feeds.map((thisFeed: Feed, i: number) => - new FeedController({ - ...options, - feed: thisFeed, - onExhausted: () => exhausted.add(i), - onEvent: (event: TrustedEvent) => { - if (i === 0) { - events.push(event) - } else { - skip.add(event.id) - } - }, - }) - ) + feeds.map( + (thisFeed: Feed, i: number) => + new FeedController({ + ...options, + feed: thisFeed, + onExhausted: () => exhausted.add(i), + onEvent: (event: TrustedEvent) => { + if (i === 0) { + events.push(event) + } else { + skip.add(event.id) + } + }, + }), + ), ) return async (limit: number) => { @@ -173,7 +176,7 @@ export class FeedController { } await controller.load(limit) - }) + }), ) for (const event of events.splice(0)) { @@ -197,17 +200,18 @@ export class FeedController { const seen = new Set() const controllers = await Promise.all( - feeds.map((thisFeed: Feed, i: number) => - new FeedController({ - ...options, - feed: thisFeed, - onExhausted: () => exhausted.add(i), - onEvent: (event: TrustedEvent) => { - events.push(event) - counts.set(event.id, inc(counts.get(event.id))) - }, - }) - ) + feeds.map( + (thisFeed: Feed, i: number) => + new FeedController({ + ...options, + feed: thisFeed, + onExhausted: () => exhausted.add(i), + onEvent: (event: TrustedEvent) => { + events.push(event) + counts.set(event.id, inc(counts.get(event.id))) + }, + }), + ), ) return async (limit: number) => { @@ -218,7 +222,7 @@ export class FeedController { } await controller.load(limit) - }) + }), ) for (const event of events.splice(0)) { @@ -240,19 +244,20 @@ export class FeedController { const seen = new Set() const controllers = await Promise.all( - feeds.map((thisFeed: Feed, i: number) => - new FeedController({ - ...options, - feed: thisFeed, - onExhausted: () => exhausted.add(i), - onEvent: (event: TrustedEvent) => { - if (!seen.has(event.id)) { - onEvent?.(event) - seen.add(event.id) - } - }, - }) - ) + feeds.map( + (thisFeed: Feed, i: number) => + new FeedController({ + ...options, + feed: thisFeed, + onExhausted: () => exhausted.add(i), + onEvent: (event: TrustedEvent) => { + if (!seen.has(event.id)) { + onEvent?.(event) + seen.add(event.id) + } + }, + }), + ), ) return async (limit: number) => { @@ -263,7 +268,7 @@ export class FeedController { } await controller.load(limit) - }) + }), ) if (exhausted.size === controllers.length) { diff --git a/packages/feeds/src/core.ts b/packages/feeds/src/core.ts index 51d46fe..5c20042 100644 --- a/packages/feeds/src/core.ts +++ b/packages/feeds/src/core.ts @@ -1,4 +1,4 @@ -import type {TrustedEvent, Filter} from '@welshman/util' +import type {TrustedEvent, Filter} from "@welshman/util" export enum FeedType { Address = "address", @@ -28,43 +28,43 @@ export enum Scope { } export type FilterFeedType = - FeedType.ID | - FeedType.Address | - FeedType.Author | - FeedType.Kind | - FeedType.Relay | - FeedType.Tag + | FeedType.ID + | FeedType.Address + | FeedType.Author + | FeedType.Kind + | FeedType.Relay + | FeedType.Tag export type TagFeedMapping = [string, Feed] export type DVMItem = { - kind: number, - tags?: string[][], - relays?: string[], - mappings?: TagFeedMapping[], + kind: number + tags?: string[][] + relays?: string[] + mappings?: TagFeedMapping[] } export type ListItem = { - addresses: string[], - mappings?: TagFeedMapping[], + addresses: string[] + mappings?: TagFeedMapping[] } export type LabelItem = { - relays?: string[], + relays?: string[] authors?: string[] [key: `#${string}`]: string[] - mappings?: TagFeedMapping[], + mappings?: TagFeedMapping[] } export type WOTItem = { - min?: number, - max?: number, + min?: number + max?: number } export type CreatedAtItem = { - since?: number, - until?: number, - relative?: string[], + since?: number + until?: number + relative?: string[] } export type AddressFeed = [type: FeedType.Address, ...addresses: string[]] @@ -86,23 +86,23 @@ export type TagFeed = [type: FeedType.Tag, key: string, ...values: string[]] export type UnionFeed = [type: FeedType.Union, ...feeds: Feed[]] export type Feed = - AddressFeed | - AuthorFeed | - CreatedAtFeed | - DVMFeed | - DifferenceFeed | - IDFeed | - IntersectionFeed | - GlobalFeed | - KindFeed | - ListFeed | - LabelFeed | - WOTFeed | - RelayFeed | - ScopeFeed | - SearchFeed | - TagFeed | - UnionFeed + | AddressFeed + | AuthorFeed + | CreatedAtFeed + | DVMFeed + | DifferenceFeed + | IDFeed + | IntersectionFeed + | GlobalFeed + | KindFeed + | ListFeed + | LabelFeed + | WOTFeed + | RelayFeed + | ScopeFeed + | SearchFeed + | TagFeed + | UnionFeed export type RequestItem = { relays?: string[] @@ -114,9 +114,9 @@ export type RequestOpts = RequestItem & { } export type DVMRequest = { - kind: number, - tags?: string[][], - relays?: string[], + kind: number + tags?: string[][] + relays?: string[] } export type DVMOpts = DVMRequest & { diff --git a/packages/feeds/src/index.ts b/packages/feeds/src/index.ts index f25e136..a03773e 100644 --- a/packages/feeds/src/index.ts +++ b/packages/feeds/src/index.ts @@ -1,4 +1,4 @@ -export * from './core' -export * from './compiler' -export * from './controller' -export * from './utils' +export * from "./core.js" +export * from "./compiler.js" +export * from "./controller.js" +export * from "./utils.js" diff --git a/packages/feeds/src/utils.ts b/packages/feeds/src/utils.ts index 9af901e..f27d95e 100644 --- a/packages/feeds/src/utils.ts +++ b/packages/feeds/src/utils.ts @@ -1,6 +1,6 @@ -import {ensureNumber} from '@welshman/lib' -import type {Filter} from '@welshman/util' -import {getTagValues} from '@welshman/util' +import {ensureNumber} from "@welshman/lib" +import type {Filter} from "@welshman/util" +import {getTagValues} from "@welshman/util" import { FeedType, Feed, @@ -28,46 +28,66 @@ import { ListItem, LabelItem, CreatedAtItem, -} from './core' +} from "./core.js" -export const makeAddressFeed = (...addresses: string[]): AddressFeed => [FeedType.Address, ...addresses] -export const makeAuthorFeed = (...pubkeys: string[]): AuthorFeed => [FeedType.Author, ...pubkeys] -export const makeCreatedAtFeed = (...items: CreatedAtItem[]): CreatedAtFeed => [FeedType.CreatedAt, ...items] -export const makeDVMFeed = (...items: DVMItem[]): DVMFeed => [FeedType.DVM, ...items] -export const makeDifferenceFeed = (...feeds: Feed[]): DifferenceFeed => [FeedType.Difference, ...feeds] -export const makeIDFeed = (...ids: string[]): IDFeed => [FeedType.ID, ...ids] -export const makeIntersectionFeed = (...feeds: Feed[]): IntersectionFeed => [FeedType.Intersection, ...feeds] -export const makeGlobalFeed = (): GlobalFeed => [FeedType.Global] -export const makeKindFeed = (...kinds: number[]): KindFeed => [FeedType.Kind, ...kinds] -export const makeListFeed = (...items: ListItem[]): ListFeed => [FeedType.List, ...items] -export const makeLabelFeed = (...items: LabelItem[]): LabelFeed => [FeedType.Label, ...items] -export const makeWOTFeed = (...items: WOTItem[]): WOTFeed => [FeedType.WOT, ...items] -export const makeRelayFeed = (...urls: string[]): RelayFeed => [FeedType.Relay, ...urls] -export const makeScopeFeed = (...scopes: Scope[]): ScopeFeed => [FeedType.Scope, ...scopes] -export const makeSearchFeed = (...searches: string[]): SearchFeed => [FeedType.Search, ...searches] -export const makeTagFeed = (key: string, ...values: string[]): TagFeed => [FeedType.Tag, key, ...values] -export const makeUnionFeed = (...feeds: Feed[]): UnionFeed => [FeedType.Union, ...feeds] +export const makeAddressFeed = (...addresses: string[]): AddressFeed => [ + FeedType.Address, + ...addresses, +] +export const makeAuthorFeed = (...pubkeys: string[]): AuthorFeed => [FeedType.Author, ...pubkeys] +export const makeCreatedAtFeed = (...items: CreatedAtItem[]): CreatedAtFeed => [ + FeedType.CreatedAt, + ...items, +] +export const makeDVMFeed = (...items: DVMItem[]): DVMFeed => [FeedType.DVM, ...items] +export const makeDifferenceFeed = (...feeds: Feed[]): DifferenceFeed => [ + FeedType.Difference, + ...feeds, +] +export const makeIDFeed = (...ids: string[]): IDFeed => [FeedType.ID, ...ids] +export const makeIntersectionFeed = (...feeds: Feed[]): IntersectionFeed => [ + FeedType.Intersection, + ...feeds, +] +export const makeGlobalFeed = (): GlobalFeed => [FeedType.Global] +export const makeKindFeed = (...kinds: number[]): KindFeed => [FeedType.Kind, ...kinds] +export const makeListFeed = (...items: ListItem[]): ListFeed => [FeedType.List, ...items] +export const makeLabelFeed = (...items: LabelItem[]): LabelFeed => [FeedType.Label, ...items] +export const makeWOTFeed = (...items: WOTItem[]): WOTFeed => [FeedType.WOT, ...items] +export const makeRelayFeed = (...urls: string[]): RelayFeed => [FeedType.Relay, ...urls] +export const makeScopeFeed = (...scopes: Scope[]): ScopeFeed => [FeedType.Scope, ...scopes] +export const makeSearchFeed = (...searches: string[]): SearchFeed => [FeedType.Search, ...searches] +export const makeTagFeed = (key: string, ...values: string[]): TagFeed => [ + FeedType.Tag, + key, + ...values, +] +export const makeUnionFeed = (...feeds: Feed[]): UnionFeed => [FeedType.Union, ...feeds] -export const isAddressFeed = (feed: Feed): feed is AddressFeed => feed[0] === FeedType.Address -export const isAuthorFeed = (feed: Feed): feed is AuthorFeed => feed[0] === FeedType.Author -export const isCreatedAtFeed = (feed: Feed): feed is CreatedAtFeed => feed[0] === FeedType.CreatedAt -export const isDVMFeed = (feed: Feed): feed is DVMFeed => feed[0] === FeedType.DVM -export const isDifferenceFeed = (feed: Feed): feed is DifferenceFeed => feed[0] === FeedType.Difference -export const isIDFeed = (feed: Feed): feed is IDFeed => feed[0] === FeedType.ID -export const isIntersectionFeed = (feed: Feed): feed is IntersectionFeed => feed[0] === FeedType.Intersection -export const isGlobalFeed = (feed: Feed): feed is GlobalFeed => feed[0] === FeedType.Global -export const isKindFeed = (feed: Feed): feed is KindFeed => feed[0] === FeedType.Kind -export const isListFeed = (feed: Feed): feed is ListFeed => feed[0] === FeedType.List -export const isLabelFeed = (feed: Feed): feed is LabelFeed => feed[0] === FeedType.Label -export const isWOTFeed = (feed: Feed): feed is WOTFeed => feed[0] === FeedType.WOT -export const isRelayFeed = (feed: Feed): feed is RelayFeed => feed[0] === FeedType.Relay -export const isScopeFeed = (feed: Feed): feed is ScopeFeed => feed[0] === FeedType.Scope -export const isSearchFeed = (feed: Feed): feed is SearchFeed => feed[0] === FeedType.Search -export const isTagFeed = (feed: Feed): feed is TagFeed => feed[0] === FeedType.Tag -export const isUnionFeed = (feed: Feed): feed is UnionFeed => feed[0] === FeedType.Union +export const isAddressFeed = (feed: Feed): feed is AddressFeed => feed[0] === FeedType.Address +export const isAuthorFeed = (feed: Feed): feed is AuthorFeed => feed[0] === FeedType.Author +export const isCreatedAtFeed = (feed: Feed): feed is CreatedAtFeed => feed[0] === FeedType.CreatedAt +export const isDVMFeed = (feed: Feed): feed is DVMFeed => feed[0] === FeedType.DVM +export const isDifferenceFeed = (feed: Feed): feed is DifferenceFeed => + feed[0] === FeedType.Difference +export const isIDFeed = (feed: Feed): feed is IDFeed => feed[0] === FeedType.ID +export const isIntersectionFeed = (feed: Feed): feed is IntersectionFeed => + feed[0] === FeedType.Intersection +export const isGlobalFeed = (feed: Feed): feed is GlobalFeed => feed[0] === FeedType.Global +export const isKindFeed = (feed: Feed): feed is KindFeed => feed[0] === FeedType.Kind +export const isListFeed = (feed: Feed): feed is ListFeed => feed[0] === FeedType.List +export const isLabelFeed = (feed: Feed): feed is LabelFeed => feed[0] === FeedType.Label +export const isWOTFeed = (feed: Feed): feed is WOTFeed => feed[0] === FeedType.WOT +export const isRelayFeed = (feed: Feed): feed is RelayFeed => feed[0] === FeedType.Relay +export const isScopeFeed = (feed: Feed): feed is ScopeFeed => feed[0] === FeedType.Scope +export const isSearchFeed = (feed: Feed): feed is SearchFeed => feed[0] === FeedType.Search +export const isTagFeed = (feed: Feed): feed is TagFeed => feed[0] === FeedType.Tag +export const isUnionFeed = (feed: Feed): feed is UnionFeed => feed[0] === FeedType.Union export function getFeedArgs(feed: IntersectionFeed | UnionFeed | DifferenceFeed): Feed[] -export function getFeedArgs(feed: AddressFeed | AuthorFeed | IDFeed | RelayFeed | SearchFeed): string[] +export function getFeedArgs( + feed: AddressFeed | AuthorFeed | IDFeed | RelayFeed | SearchFeed, +): string[] export function getFeedArgs(feed: CreatedAtFeed): CreatedAtItem[] export function getFeedArgs(feed: ListFeed): ListItem[] export function getFeedArgs(feed: LabelFeed): LabelItem[] @@ -79,23 +99,42 @@ export function getFeedArgs(feed: TagFeed): [string, ...string[]] export function getFeedArgs(feed: GlobalFeed): [] export function getFeedArgs(feed: Feed) { switch (feed[0]) { - case FeedType.Intersection: return feed.slice(1) as Feed[] - case FeedType.Union: return feed.slice(1) as Feed[] - case FeedType.Difference: return feed.slice(1) as Feed[] - case FeedType.Address: return feed.slice(1) as string[] - case FeedType.Author: return feed.slice(1) as string[] - case FeedType.ID: return feed.slice(1) as string[] - case FeedType.Relay: return feed.slice(1) as string[] - case FeedType.Search: return feed.slice(1) as string[] - case FeedType.Tag: return feed.slice(1) as [string, ...string[]] - case FeedType.CreatedAt: return feed.slice(1) as CreatedAtItem[] - case FeedType.List: return feed.slice(1) as ListItem[] - case FeedType.Label: return feed.slice(1) as LabelItem[] - case FeedType.DVM: return feed.slice(1) as DVMItem[] - case FeedType.WOT: return feed.slice(1) as WOTItem[] - case FeedType.Scope: return feed.slice(1) as Scope[] - case FeedType.Kind: return feed.slice(1) as number[] - case FeedType.Global: return feed.slice(1) as never[] + case FeedType.Intersection: + return feed.slice(1) as Feed[] + case FeedType.Union: + return feed.slice(1) as Feed[] + case FeedType.Difference: + return feed.slice(1) as Feed[] + case FeedType.Address: + return feed.slice(1) as string[] + case FeedType.Author: + return feed.slice(1) as string[] + case FeedType.ID: + return feed.slice(1) as string[] + case FeedType.Relay: + return feed.slice(1) as string[] + case FeedType.Search: + return feed.slice(1) as string[] + case FeedType.Tag: + return feed.slice(1) as [string, ...string[]] + case FeedType.CreatedAt: + return feed.slice(1) as CreatedAtItem[] + case FeedType.List: + return feed.slice(1) as ListItem[] + case FeedType.Label: + return feed.slice(1) as LabelItem[] + case FeedType.DVM: + return feed.slice(1) as DVMItem[] + case FeedType.WOT: + return feed.slice(1) as WOTItem[] + case FeedType.Scope: + return feed.slice(1) as Scope[] + case FeedType.Kind: + return feed.slice(1) as number[] + case FeedType.Global: + return feed.slice(1) as never[] + default: + throw new Error(`Invalid feed type ${feed[0]}`) } } @@ -103,11 +142,11 @@ export const hasSubFeeds = (feed: Feed): feed is IntersectionFeed | UnionFeed | [FeedType.Union, FeedType.Intersection, FeedType.Difference].includes(feed[0]) export const defaultTagFeedMappings: TagFeedMapping[] = [ - ['a', [FeedType.Address]], - ['e', [FeedType.ID]], - ['p', [FeedType.Author]], - ['r', [FeedType.Relay]], - ['t', [FeedType.Tag, '#t']], + ["a", [FeedType.Address]], + ["e", [FeedType.ID]], + ["p", [FeedType.Author]], + ["r", [FeedType.Relay]], + ["t", [FeedType.Tag, "#t"]], ] export const feedsFromTags = (tags: string[][], mappings?: TagFeedMapping[]) => { @@ -143,18 +182,17 @@ export const feedsFromFilter = ({since, until, ...filter}: Filter) => { } for (const [k, v] of Object.entries(filter)) { - if (k === 'ids') feeds.push(makeIDFeed(...v as string[])) - else if (k === 'kinds') feeds.push(makeKindFeed(...v as number[])) - else if (k === 'authors') feeds.push(makeAuthorFeed(...v as string[])) - else if (k.startsWith('#')) feeds.push(makeTagFeed(k as string, ...v as string[])) + if (k === "ids") feeds.push(makeIDFeed(...(v as string[]))) + else if (k === "kinds") feeds.push(makeKindFeed(...(v as number[]))) + else if (k === "authors") feeds.push(makeAuthorFeed(...(v as string[]))) + else if (k.startsWith("#")) feeds.push(makeTagFeed(k as string, ...(v as string[]))) else throw new Error(`Unable to create feed from filter ${k}: ${v}`) } return feeds } -export const feedFromFilter = (filter: Filter) => - makeIntersectionFeed(...feedsFromFilter(filter)) +export const feedFromFilter = (filter: Filter) => makeIntersectionFeed(...feedsFromFilter(filter)) export const feedsFromFilters = (filters: Filter[]) => makeUnionFeed(...filters.map(filter => feedFromFilter(filter))) diff --git a/packages/feeds/tsc-multi.json b/packages/feeds/tsc-multi.json deleted file mode 100644 index 6c37019..0000000 --- a/packages/feeds/tsc-multi.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "targets": [ - {"extname": ".cjs", "module": "commonjs"}, - {"extname": ".mjs", "module": "esnext", "moduleResolution": "node"} - ], - "projects": ["tsconfig.json"] -} diff --git a/packages/feeds/tsconfig.json b/packages/feeds/tsconfig.json index 8defcdc..97e6372 100644 --- a/packages/feeds/tsconfig.json +++ b/packages/feeds/tsconfig.json @@ -3,9 +3,12 @@ "compilerOptions": { "rootDir": ".", "outDir": "build", - "esModuleInterop": true, - "skipLibCheck": true, - "lib": ["esnext", "dom", "dom.iterable"] + "module": "nodenext", + "moduleResolution": "nodenext", + "lib": ["esnext", "dom"] }, - "include": ["src/**/*.ts"] + "include": [ + "src/**/*.ts", + "test/**/*.ts" + ] } diff --git a/packages/lib/package.json b/packages/lib/package.json index 4ff59ea..fadd599 100644 --- a/packages/lib/package.json +++ b/packages/lib/package.json @@ -15,24 +15,19 @@ "exports": { ".": { "types": "./build/src/index.d.ts", - "import": "./build/src/index.mjs", - "require": "./build/src/index.cjs" + "import": "./build/src/index.js", + "require": "./build/src/index.js" } }, "scripts": { "pub": "npm run lint && npm run build && npm publish", - "build": "gts clean && tsc-multi", + "build": "gts clean && tsc", "lint": "gts lint", "fix": "gts fix" }, - "devDependencies": { - "gts": "^5.0.1", - "tsc-multi": "^1.1.0", - "typescript": "~5.1.6" - }, "dependencies": { - "@types/events": "^3.0.3", "@scure/base": "^1.1.6", + "@types/events": "^3.0.3", "events": "^3.3.0" } } diff --git a/packages/lib/src/Context.ts b/packages/lib/src/Context.ts index 604da2f..7abfffd 100644 --- a/packages/lib/src/Context.ts +++ b/packages/lib/src/Context.ts @@ -1,4 +1,4 @@ -import type {Context} from '@welshman/lib' +import type {Context} from "@welshman/lib" /** * A global context variable for configuring libraries and applications. diff --git a/packages/lib/src/Deferred.ts b/packages/lib/src/Deferred.ts index d21867d..0bd99d1 100644 --- a/packages/lib/src/Deferred.ts +++ b/packages/lib/src/Deferred.ts @@ -9,10 +9,7 @@ export type CustomPromise = Promise & { * @returns Promise with typed error */ export function makePromise( - executor: ( - resolve: (value: T | PromiseLike) => void, - reject: (reason: E) => void - ) => void + executor: (resolve: (value: T | PromiseLike) => void, reject: (reason: E) => void) => void, ): CustomPromise { return new Promise(executor) as CustomPromise } @@ -34,5 +31,5 @@ export const defer = (): Deferred => { reject = reject_ }) - return (Object.assign(p, {resolve, reject}) as unknown) as Deferred + return Object.assign(p, {resolve, reject}) as unknown as Deferred } diff --git a/packages/lib/src/Emitter.ts b/packages/lib/src/Emitter.ts index fc0a46f..318c4a3 100644 --- a/packages/lib/src/Emitter.ts +++ b/packages/lib/src/Emitter.ts @@ -1,4 +1,4 @@ -import {EventEmitter} from 'events' +import {EventEmitter} from "events" /** * Extended EventEmitter that also emits all events to '*' listeners @@ -12,7 +12,7 @@ export class Emitter extends EventEmitter { */ emit(type: string, ...args: any[]) { const a = super.emit(type, ...args) - const b = super.emit('*', type, ...args) + const b = super.emit("*", type, ...args) return a && b } diff --git a/packages/lib/src/LRUCache.ts b/packages/lib/src/LRUCache.ts index 4f76fd6..631a384 100644 --- a/packages/lib/src/LRUCache.ts +++ b/packages/lib/src/LRUCache.ts @@ -77,5 +77,5 @@ export function cached({ * @template Args - Function argument types */ export function simpleCache(getValue: (args: Args) => V) { - return cached({maxSize: 10**5, getKey: xs => xs.join(':'), getValue}) + return cached({maxSize: 10 ** 5, getKey: xs => xs.join(":"), getValue}) } diff --git a/packages/lib/src/Tools.ts b/packages/lib/src/Tools.ts index e4da6ca..e1b52e6 100644 --- a/packages/lib/src/Tools.ts +++ b/packages/lib/src/Tools.ts @@ -15,7 +15,8 @@ export type Maybe = T | undefined * @param f - Function to execute if x is defined * @returns Result of f(x) if x is defined, undefined otherwise */ -export const ifLet = (x: T | undefined, f: (x: T) => void) => x === undefined ? undefined : f(x) +export const ifLet = (x: T | undefined, f: (x: T) => void) => + x === undefined ? undefined : f(x) /** Function that does nothing and returns undefined */ export const noop = (...args: unknown[]) => undefined @@ -53,7 +54,10 @@ export const identity = (x: T, ...args: unknown[]) => x * @param x - Value to return * @returns Function that returns x */ -export const always = (x: T, ...args: unknown[]) => () => x +export const always = + (x: T, ...args: unknown[]) => + () => + x /** * Returns the logical NOT of a value @@ -63,7 +67,10 @@ export const always = (x: T, ...args: unknown[]) => () => x export const not = (x: any, ...args: unknown[]) => !x /** Returns a function that returns the boolean negation of the given function */ -export const complement = (f: (...args: T) => any) => (...args: T) => !f(...args) +export const complement = + (f: (...args: T) => any) => + (...args: T) => + !f(...args) /** Converts a Maybe to a number, defaulting to 0 */ export const num = (x: Maybe) => x || 0 @@ -227,17 +234,23 @@ export const mapVals = (f: (v: V) => U, x: Record) => { * Merges two objects, with left object taking precedence * @param a - Left object * @param b - Right object - * @returns Merged object with a's properties overriding b's + * @returns Merged object with a"s properties overriding b"s */ -export const mergeLeft = >(a: T, b: T) => ({...b, ...a}) +export const mergeLeft = >(a: T, b: T) => ({ + ...b, + ...a, +}) /** * Merges two objects, with right object taking precedence * @param a - Left object * @param b - Right object - * @returns Merged object with b's properties overriding a's + * @returns Merged object with b"s properties overriding a"s */ -export const mergeRight = >(a: T, b: T) => ({...a, ...b}) +export const mergeRight = >(a: T, b: T) => ({ + ...a, + ...b, +}) /** * Checks if a number is between two values (exclusive) @@ -281,7 +294,10 @@ export const stripProtocol = (url: string) => url.replace(/.*:\/\//, "") * @param url - URL to format * @returns Formatted URL */ -export const displayUrl = (url: string) => stripProtocol(url).replace(/^(www\.)?/i, "").replace(/\/$/, "") +export const displayUrl = (url: string) => + stripProtocol(url) + .replace(/^(www\.)?/i, "") + .replace(/\/$/, "") /** * Extracts and formats domain from URL @@ -302,7 +318,7 @@ export const sleep = (t: number) => new Promise(resolve => setTimeout(resolve, t * @param xs - Arrays to concatenate * @returns Combined array */ -export const concat = (...xs: T[][]) => xs.flatMap(x => isNil(x) ? [] : x) +export const concat = (...xs: T[][]) => xs.flatMap(x => (isNil(x) ? [] : x)) /** * Appends element to array @@ -366,7 +382,7 @@ export const without = (a: T[], b: T[]) => b.filter(x => !a.includes(x)) * @param xs - Source array * @returns New array with element added or removed */ -export const toggle = (x: T, xs: T[]) => xs.includes(x) ? remove(x, xs) : append(x, xs) +export const toggle = (x: T, xs: T[]) => (xs.includes(x) ? remove(x, xs) : append(x, xs)) /** * Constrains number between min and max values @@ -434,13 +450,13 @@ export const tryCatch = (f: () => T, onError?: (e: Error) => void): T | undef * @param suffix - String to append if truncated * @returns Truncated string */ -export const ellipsize = (s: string, l: number, suffix = '...') => { +export const ellipsize = (s: string, l: number, suffix = "...") => { if (s.length < l * 1.1) { return s } - while (s.length > l && s.includes(' ')) { - s = s.split(' ').slice(0, -1).join(' ') + while (s.length > l && s.includes(" ")) { + s = s.split(" ").slice(0, -1).join(" ") } return s + suffix @@ -522,13 +538,22 @@ export const equals = (a: any, b: any) => { // Curried utils /** Returns a function that gets the nth element of an array */ -export const nth = (i: number) => (xs: T[], ...args: unknown[]) => xs[i] +export const nth = + (i: number) => + (xs: T[], ...args: unknown[]) => + xs[i] /** Returns a function that checks if nth element equals value */ -export const nthEq = (i: number, v: any) => (xs: any[], ...args: unknown[]) => xs[i] === v +export const nthEq = + (i: number, v: any) => + (xs: any[], ...args: unknown[]) => + xs[i] === v /** Returns a function that checks if nth element does not equal value */ -export const nthNe = (i: number, v: any) => (xs: any[], ...args: unknown[]) => xs[i] !== v +export const nthNe = + (i: number, v: any) => + (xs: any[], ...args: unknown[]) => + xs[i] !== v /** Returns a function that checks if key/value pairs of x match all pairs in spec */ export const spec = (values: Record) => (x: Record) => { @@ -540,16 +565,28 @@ export const spec = (values: Record) => (x: Record) => } /** Returns a function that checks equality with value */ -export const eq = (v: T) => (x: T) => x === v +export const eq = + (v: T) => + (x: T) => + x === v /** Returns a function that checks inequality with value */ -export const ne = (v: T) => (x: T) => x !== v +export const ne = + (v: T) => + (x: T) => + x !== v /** Returns a function that gets property value from object */ -export const prop = (k: string) => (x: Record) => x[k] as T +export const prop = + (k: string) => + (x: Record) => + x[k] as T /** Returns a function that adds/updates property on object */ -export const assoc = (k: K, v: T) => (o: U) => ({...o, [k as K]: v}) as U & Record +export const assoc = + (k: K, v: T) => + (o: U) => + ({...o, [k as K]: v}) as U & Record /** Generates a hash string from input string */ export const hash = (s: string) => @@ -567,7 +604,8 @@ export const insert = (n: number, x: T, xs: T[]) => [...xs.slice(0, n), x, .. export const choice = (xs: T[]): T => xs[Math.floor(xs.length * Math.random())] /** Returns shuffled copy of iterable */ -export const shuffle = (xs: Iterable): T[] => Array.from(xs).sort(() => Math.random() > 0.5 ? 1 : -1) +export const shuffle = (xs: Iterable): T[] => + Array.from(xs).sort(() => (Math.random() > 0.5 ? 1 : -1)) /** Returns n random elements from array */ export const sample = (n: number, xs: T[]) => shuffle(xs).slice(0, n) @@ -576,7 +614,7 @@ export const sample = (n: number, xs: T[]) => shuffle(xs).slice(0, n) export const isIterable = (x: any) => Symbol.iterator in Object(x) /** Ensures value is iterable by wrapping in array if needed */ -export const toIterable = (x: any) => isIterable(x) ? x : [x] +export const toIterable = (x: any) => (isIterable(x) ? x : [x]) /** Ensures value is array by wrapping if needed */ export const ensurePlural = (x: T | T[]) => (x instanceof Array ? x : [x]) @@ -893,7 +931,7 @@ export const batch = (t: number, f: (xs: T[]) => void) => { * @returns Function that returns promise of result */ export const batcher = (t: number, execute: (request: T[]) => U[] | Promise) => { - const queue: {request: T, resolve: (x: U) => void}[] = [] + const queue: {request: T; resolve: (x: U) => void}[] = [] const _execute = async () => { const items = queue.splice(0) diff --git a/packages/lib/src/index.ts b/packages/lib/src/index.ts index df8dee4..b79a71c 100644 --- a/packages/lib/src/index.ts +++ b/packages/lib/src/index.ts @@ -1,7 +1,11 @@ -export * from './Context' -export * from './Deferred' -export * from './Emitter' -export * from './LRUCache' -export * from './Tools' -export * from './Worker' -export {default as normalizeUrl} from './normalize-url' +export * from "./Context.js" +export * from "./Deferred.js" +export * from "./Emitter.js" +export * from "./LRUCache.js" +export * from "./Tools.js" +export * from "./Worker.js" +export {default as normalizeUrl} from "./normalize-url/index.js" + +declare module "@welshman/lib" { + export interface Context {} +} diff --git a/packages/lib/src/normalize-url/index.ts b/packages/lib/src/normalize-url/index.ts index 5276674..e17c6ec 100644 --- a/packages/lib/src/normalize-url/index.ts +++ b/packages/lib/src/normalize-url/index.ts @@ -514,7 +514,7 @@ export default function normalizeUrl(urlString: string, opts?: Options): string // Remove query unwanted parameters if (Array.isArray(options.removeQueryParameters)) { - // eslint-disable-next-line unicorn/no-useless-spread -- We are intentionally spreading to get a copy. + // @ts-ignore for (const key of [...urlObject.searchParams.keys()]) { if (testParameter(key, options.removeQueryParameters)) { urlObject.searchParams.delete(key) @@ -528,7 +528,7 @@ export default function normalizeUrl(urlString: string, opts?: Options): string // Keep wanted query parameters if (Array.isArray(options.keepQueryParameters) && options.keepQueryParameters.length > 0) { - // eslint-disable-next-line unicorn/no-useless-spread -- We are intentionally spreading to get a copy. + // @ts-ignore for (const key of [...urlObject.searchParams.keys()]) { if (!testParameter(key, options.keepQueryParameters)) { urlObject.searchParams.delete(key) diff --git a/packages/lib/src/types.d.ts b/packages/lib/src/types.d.ts deleted file mode 100644 index 0f66428..0000000 --- a/packages/lib/src/types.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare module "@welshman/lib" { - export interface Context {} -} diff --git a/packages/lib/tsc-multi.json b/packages/lib/tsc-multi.json deleted file mode 100644 index 6c37019..0000000 --- a/packages/lib/tsc-multi.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "targets": [ - {"extname": ".cjs", "module": "commonjs"}, - {"extname": ".mjs", "module": "esnext", "moduleResolution": "node"} - ], - "projects": ["tsconfig.json"] -} diff --git a/packages/lib/tsconfig.json b/packages/lib/tsconfig.json index 8defcdc..97e6372 100644 --- a/packages/lib/tsconfig.json +++ b/packages/lib/tsconfig.json @@ -3,9 +3,12 @@ "compilerOptions": { "rootDir": ".", "outDir": "build", - "esModuleInterop": true, - "skipLibCheck": true, - "lib": ["esnext", "dom", "dom.iterable"] + "module": "nodenext", + "moduleResolution": "nodenext", + "lib": ["esnext", "dom"] }, - "include": ["src/**/*.ts"] + "include": [ + "src/**/*.ts", + "test/**/*.ts" + ] } diff --git a/packages/net/package.json b/packages/net/package.json index 52d3537..0c8f909 100644 --- a/packages/net/package.json +++ b/packages/net/package.json @@ -15,22 +15,19 @@ "exports": { ".": { "types": "./build/src/index.d.ts", - "import": "./build/src/index.mjs", - "require": "./build/src/index.cjs" + "import": "./build/src/index.js", + "require": "./build/src/index.js" } }, "scripts": { "pub": "npm run lint && npm run build && npm publish", - "build": "gts clean && tsc-multi", + "build": "gts clean && tsc", "lint": "gts lint", "fix": "gts fix", "test": "mocha" }, "devDependencies": { - "gts": "^5.0.1", - "mocha": "^10.7.3", - "tsc-multi": "^1.1.0", - "typescript": "~5.1.6" + "mocha": "^10.7.3" }, "dependencies": { "@welshman/lib": "~0.0.33", diff --git a/packages/net/src/Connection.ts b/packages/net/src/Connection.ts index 044e0c5..fe8df3b 100644 --- a/packages/net/src/Connection.ts +++ b/packages/net/src/Connection.ts @@ -1,12 +1,12 @@ -import {Emitter} from '@welshman/lib' -import {normalizeRelayUrl} from '@welshman/util' -import {Socket} from './Socket' -import type {Message} from './Socket' -import {ConnectionEvent} from './ConnectionEvent' -import {ConnectionState} from './ConnectionState' -import {ConnectionStats} from './ConnectionStats' -import {ConnectionAuth} from './ConnectionAuth' -import {ConnectionSender} from './ConnectionSender' +import {Emitter} from "@welshman/lib" +import {normalizeRelayUrl} from "@welshman/util" +import {Socket} from "./Socket.js" +import type {Message} from "./Socket.js" +import {ConnectionEvent} from "./ConnectionEvent.js" +import {ConnectionState} from "./ConnectionState.js" +import {ConnectionStats} from "./ConnectionStats.js" +import {ConnectionAuth} from "./ConnectionAuth.js" +import {ConnectionSender} from "./ConnectionSender.js" export enum ConnectionStatus { Open = "open", diff --git a/packages/net/src/ConnectionAuth.ts b/packages/net/src/ConnectionAuth.ts index 5532936..cba4bcd 100644 --- a/packages/net/src/ConnectionAuth.ts +++ b/packages/net/src/ConnectionAuth.ts @@ -1,33 +1,26 @@ -import {ctx, sleep} from '@welshman/lib' -import {CLIENT_AUTH, createEvent} from '@welshman/util' -import {ConnectionEvent} from './ConnectionEvent' -import type {Connection} from './Connection' -import type {Message} from './Socket' +import {ctx, sleep} from "@welshman/lib" +import {CLIENT_AUTH, createEvent} from "@welshman/util" +import {ConnectionEvent} from "./ConnectionEvent.js" +import type {Connection} from "./Connection.js" +import type {Message} from "./Socket.js" export enum AuthMode { - Implicit = 'implicit', - Explicit = 'explicit', + Implicit = "implicit", + Explicit = "explicit", } export enum AuthStatus { - None = 'none', - Requested = 'requested', - PendingSignature = 'pending_signature', - DeniedSignature = 'denied_signature', - PendingResponse = 'pending_response', - Forbidden = 'forbidden', - Ok = 'ok', + None = "none", + Requested = "requested", + PendingSignature = "pending_signature", + DeniedSignature = "denied_signature", + PendingResponse = "pending_response", + Forbidden = "forbidden", + Ok = "ok", } -const { - None, - Requested, - PendingSignature, - DeniedSignature, - PendingResponse, - Forbidden, - Ok, -} = AuthStatus +const {None, Requested, PendingSignature, DeniedSignature, PendingResponse, Forbidden, Ok} = + AuthStatus export class ConnectionAuth { challenge: string | undefined @@ -41,7 +34,7 @@ export class ConnectionAuth { } #onReceive = (cxn: Connection, [verb, ...extra]: Message) => { - if (verb === 'OK') { + if (verb === "OK") { const [id, ok, message] = extra if (id === this.request) { @@ -50,7 +43,7 @@ export class ConnectionAuth { } } - if (verb === 'AUTH' && extra[0] !== this.challenge) { + if (verb === "AUTH" && extra[0] !== this.challenge) { this.challenge = extra[0] this.request = undefined this.message = undefined @@ -81,8 +74,7 @@ export class ConnectionAuth { } } - waitForChallenge = async (timeout = 300) => - this.waitFor(() => Boolean(this.challenge), timeout) + waitForChallenge = async (timeout = 300) => this.waitFor(() => Boolean(this.challenge), timeout) waitForResolution = async (timeout = 300) => this.waitFor(() => [None, DeniedSignature, Forbidden, Ok].includes(this.status), timeout) @@ -105,14 +97,11 @@ export class ConnectionAuth { ], }) - const [event] = await Promise.all([ - ctx.net.signEvent(template), - this.cxn.socket.open(), - ]) + const [event] = await Promise.all([ctx.net.signEvent(template), this.cxn.socket.open()]) if (event) { this.request = event.id - this.cxn.send(['AUTH', event]) + this.cxn.send(["AUTH", event]) this.status = PendingResponse } else { this.status = DeniedSignature diff --git a/packages/net/src/ConnectionEvent.ts b/packages/net/src/ConnectionEvent.ts index 7df6e42..dfbcd55 100644 --- a/packages/net/src/ConnectionEvent.ts +++ b/packages/net/src/ConnectionEvent.ts @@ -1,11 +1,11 @@ export enum ConnectionEvent { - InvalidUrl = 'invalid:url', - InvalidMessage = 'invalid:message:receive', - Open = 'socket:open', - Reset = 'socket:reset', - Close = 'socket:close', - Error = 'socket:error', - Receive = 'receive:message', - Notice = 'receive:notice', - Send = 'send:message', + InvalidUrl = "invalid:url", + InvalidMessage = "invalid:message:receive", + Open = "socket:open", + Reset = "socket:reset", + Close = "socket:close", + Error = "socket:error", + Receive = "receive:message", + Notice = "receive:notice", + Send = "send:message", } diff --git a/packages/net/src/ConnectionSender.ts b/packages/net/src/ConnectionSender.ts index 2bf7f56..285c366 100644 --- a/packages/net/src/ConnectionSender.ts +++ b/packages/net/src/ConnectionSender.ts @@ -1,9 +1,9 @@ -import {Worker} from '@welshman/lib' -import {AUTH_JOIN} from '@welshman/util' -import {SocketStatus} from './Socket' -import type {Message} from './Socket' -import type {Connection} from './Connection' -import {AuthStatus} from './ConnectionAuth' +import {Worker} from "@welshman/lib" +import {AUTH_JOIN} from "@welshman/util" +import {SocketStatus} from "./Socket.js" +import type {Message} from "./Socket.js" +import type {Connection} from "./Connection.js" +import {AuthStatus} from "./ConnectionAuth.js" export class ConnectionSender { worker: Worker @@ -12,22 +12,22 @@ export class ConnectionSender { this.worker = new Worker({ shouldDefer: ([verb, ...extra]: Message) => { // Always send CLOSE to clean up pending requests, even if the connection is closed - if (verb === 'CLOSE') return false + if (verb === "CLOSE") return false // If we're not connected, nothing we can do if (cxn.socket.status !== SocketStatus.Open) return true // Always allow sending AUTH - if (verb === 'AUTH') return false + if (verb === "AUTH") return false // Always allow sending join requests - if (verb === 'EVENT' && extra[0].kind === AUTH_JOIN) return false + if (verb === "EVENT" && extra[0].kind === AUTH_JOIN) return false // Wait for auth if (![AuthStatus.None, AuthStatus.Ok].includes(cxn.auth.status)) return true // Limit concurrent requests - if (verb === 'REQ') return cxn.state.pendingRequests.size >= 8 + if (verb === "REQ") return cxn.state.pendingRequests.size >= 8 return false }, @@ -35,8 +35,8 @@ export class ConnectionSender { this.worker.addGlobalHandler(([verb, ...extra]: Message) => { // If we ended up handling a CLOSE before we handled the REQ, don't send the REQ - if (verb === 'CLOSE') { - this.worker.buffer = this.worker.buffer.filter(m => !(m[0] === 'REQ' && m[1] === extra[0])) + if (verb === "CLOSE") { + this.worker.buffer = this.worker.buffer.filter(m => !(m[0] === "REQ" && m[1] === extra[0])) } // Re-check socket status since we let CLOSE through diff --git a/packages/net/src/ConnectionState.ts b/packages/net/src/ConnectionState.ts index 458bc70..1c87d6b 100644 --- a/packages/net/src/ConnectionState.ts +++ b/packages/net/src/ConnectionState.ts @@ -1,9 +1,9 @@ -import {sleep} from '@welshman/lib' -import {AUTH_JOIN} from '@welshman/util' -import type {SignedEvent, Filter} from '@welshman/util' -import type {Message} from './Socket' -import type {Connection} from './Connection' -import {ConnectionEvent} from './ConnectionEvent' +import {sleep} from "@welshman/lib" +import {AUTH_JOIN} from "@welshman/util" +import type {SignedEvent, Filter} from "@welshman/util" +import type {Message} from "./Socket.js" +import type {Connection} from "./Connection.js" +import {ConnectionEvent} from "./ConnectionEvent.js" export type PublishState = { sent: number @@ -22,19 +22,19 @@ export class ConnectionState { constructor(readonly cxn: Connection) { cxn.sender.worker.addGlobalHandler(([verb, ...extra]: Message) => { - if (verb === 'REQ') { + if (verb === "REQ") { const [reqId, ...filters] = extra this.pendingRequests.set(reqId, {filters, sent: Date.now()}) } - if (verb === 'CLOSE') { + if (verb === "CLOSE") { const [reqId] = extra this.pendingRequests.delete(reqId) } - if (verb === 'EVENT') { + if (verb === "EVENT") { const [event] = extra this.pendingPublishes.set(event.id, {sent: Date.now(), event}) @@ -42,21 +42,21 @@ export class ConnectionState { }) cxn.socket.worker.addGlobalHandler(([verb, ...extra]: Message) => { - if (verb === 'OK') { + if (verb === "OK") { const [eventId, _ok, notice] = extra const pub = this.pendingPublishes.get(eventId) if (!pub) return // Re-enqueue pending events when auth challenge is received - if (notice?.startsWith('auth-required:') && pub.event.kind !== AUTH_JOIN) { - this.cxn.send(['EVENT', pub.event]) + if (notice?.startsWith("auth-required:") && pub.event.kind !== AUTH_JOIN) { + this.cxn.send(["EVENT", pub.event]) } else { this.pendingPublishes.delete(eventId) } } - if (verb === 'EOSE') { + if (verb === "EOSE") { const [reqId] = extra const req = this.pendingRequests.get(reqId) @@ -65,15 +65,15 @@ export class ConnectionState { } } - if (verb === 'CLOSED') { + if (verb === "CLOSED") { const [reqId] = extra // Re-enqueue pending reqs when auth challenge is received - if (extra[1]?.startsWith('auth-required:')) { + if (extra[1]?.startsWith("auth-required:")) { const req = this.pendingRequests.get(reqId) if (req) { - this.cxn.send(['REQ', reqId, ...req.filters]) + this.cxn.send(["REQ", reqId, ...req.filters]) } if (extra[1]) { @@ -84,7 +84,7 @@ export class ConnectionState { this.pendingRequests.delete(reqId) } - if (verb === 'NOTICE') { + if (verb === "NOTICE") { const [notice] = extra this.cxn.emit(ConnectionEvent.Notice, notice) @@ -101,11 +101,11 @@ export class ConnectionState { } for (const [reqId, req] of this.pendingRequests.entries()) { - this.cxn.send(['REQ', reqId, ...req.filters]) + this.cxn.send(["REQ", reqId, ...req.filters]) } for (const [_, pub] of this.pendingPublishes.entries()) { - this.cxn.send(['EVENT', pub.event]) + this.cxn.send(["EVENT", pub.event]) } }) } diff --git a/packages/net/src/ConnectionStats.ts b/packages/net/src/ConnectionStats.ts index 3ac0fdc..332b154 100644 --- a/packages/net/src/ConnectionStats.ts +++ b/packages/net/src/ConnectionStats.ts @@ -1,6 +1,6 @@ -import type {Message} from './Socket' -import type {Connection} from './Connection' -import {ConnectionEvent} from './ConnectionEvent' +import type {Message} from "./Socket.js" +import type {Connection} from "./Connection.js" +import {ConnectionEvent} from "./ConnectionEvent.js" export class ConnectionStats { openCount = 0 @@ -40,19 +40,19 @@ export class ConnectionStats { }) cxn.on(ConnectionEvent.Send, (cxn: Connection, [verb]: Message) => { - if (verb === 'REQ') { + if (verb === "REQ") { this.requestCount++ this.lastRequest = Date.now() } - if (verb === 'EVENT') { + if (verb === "EVENT") { this.publishCount++ this.lastPublish = Date.now() } }) cxn.on(ConnectionEvent.Receive, (cxn: Connection, [verb, ...extra]: Message) => { - if (verb === 'OK') { + if (verb === "OK") { const pub = this.cxn.state.pendingPublishes.get(extra[0]) if (pub) { @@ -66,16 +66,16 @@ export class ConnectionStats { } } - if (verb === 'AUTH') { + if (verb === "AUTH") { this.lastAuth = Date.now() } - if (verb === 'EVENT') { + if (verb === "EVENT") { this.eventCount++ this.lastEvent = Date.now() } - if (verb === 'EOSE') { + if (verb === "EOSE") { const request = this.cxn.state.pendingRequests.get(extra[0]) // Only count the first eose @@ -85,13 +85,14 @@ export class ConnectionStats { } } - if (verb === 'NOTICE') { + if (verb === "NOTICE") { this.noticeCount++ } }) } - getRequestSpeed = () => this.eoseCount ? this.eoseTimer / this.eoseCount : 0 + getRequestSpeed = () => (this.eoseCount ? this.eoseTimer / this.eoseCount : 0) - getPublishSpeed = () => this.publishSuccessCount ? this.publishTimer / this.publishSuccessCount : 0 + getPublishSpeed = () => + this.publishSuccessCount ? this.publishTimer / this.publishSuccessCount : 0 } diff --git a/packages/net/src/Context.ts b/packages/net/src/Context.ts index 276e06b..3f8eaf0 100644 --- a/packages/net/src/Context.ts +++ b/packages/net/src/Context.ts @@ -1,15 +1,21 @@ -import {ctx, randomInt, uniq, noop, always} from '@welshman/lib' -import {LOCAL_RELAY_URL, matchFilters, unionFilters, isSignedEvent, hasValidSignature} from '@welshman/util' -import type {StampedEvent, SignedEvent, Filter, TrustedEvent} from '@welshman/util' -import {Pool} from "./Pool" -import {Executor} from "./Executor" -import {AuthMode} from "./ConnectionAuth" -import {Relays} from "./target/Relays" -import type {Subscription, RelaysAndFilters} from "./Subscribe" +import {ctx, randomInt, uniq, noop, always} from "@welshman/lib" +import { + LOCAL_RELAY_URL, + matchFilters, + unionFilters, + isSignedEvent, + hasValidSignature, +} from "@welshman/util" +import type {StampedEvent, SignedEvent, Filter, TrustedEvent} from "@welshman/util" +import {Pool} from "./Pool.js" +import {Executor} from "./Executor.js" +import {AuthMode} from "./ConnectionAuth.js" +import {Relays} from "./target/Relays.js" +import type {Subscription, RelaysAndFilters} from "./Subscribe.js" export type NetContext = { pool: Pool - authMode: AuthMode, + authMode: AuthMode onEvent: (url: string, event: TrustedEvent) => void signEvent: (event: StampedEvent) => Promise getExecutor: (relays: string[]) => Executor @@ -20,13 +26,12 @@ export type NetContext = { } export const defaultOptimizeSubscriptions = (subs: Subscription[]) => - uniq(subs.flatMap(sub => sub.request.relays || [])) - .map(relay => { - const relaySubs = subs.filter(sub => sub.request.relays.includes(relay)) - const filters = unionFilters(relaySubs.flatMap(sub => sub.request.filters)) + uniq(subs.flatMap(sub => sub.request.relays || [])).map(relay => { + const relaySubs = subs.filter(sub => sub.request.relays.includes(relay)) + const filters = unionFilters(relaySubs.flatMap(sub => sub.request.filters)) - return {relays: [relay], filters} - }) + return {relays: [relay], filters} + }) export const eventValidationScores = new Map() @@ -56,8 +61,10 @@ export const getDefaultNetContext = (overrides: Partial = {}) => ({ signEvent: noop, isDeleted: always(false), isValid: isEventValid, - getExecutor: (relays: string[]) => new Executor(new Relays(relays.map((relay: string) => ctx.net.pool.get(relay)))), - matchFilters: (url: string, filters: Filter[], event: TrustedEvent) => matchFilters(filters, event), + getExecutor: (relays: string[]) => + new Executor(new Relays(relays.map((relay: string) => ctx.net.pool.get(relay)))), + matchFilters: (url: string, filters: Filter[], event: TrustedEvent) => + matchFilters(filters, event), optimizeSubscriptions: defaultOptimizeSubscriptions, ...overrides, }) diff --git a/packages/net/src/Executor.ts b/packages/net/src/Executor.ts index 0aa121e..f93b27b 100644 --- a/packages/net/src/Executor.ts +++ b/packages/net/src/Executor.ts @@ -1,9 +1,9 @@ -import {ctx, noop} from '@welshman/lib' -import type {Emitter} from '@welshman/lib' -import type {SignedEvent, TrustedEvent, Filter} from '@welshman/util' -import type {Message} from './Socket' -import type {Connection} from './Connection' -import {Negentropy, NegentropyStorageVector} from './Negentropy' +import {ctx, noop} from "@welshman/lib" +import type {Emitter} from "@welshman/lib" +import type {SignedEvent, TrustedEvent, Filter} from "@welshman/util" +import type {Message} from "./Socket.js" +import type {Connection} from "./Connection.js" +import {Negentropy, NegentropyStorageVector} from "./Negentropy.js" export type Target = Emitter & { connections: Connection[] @@ -21,21 +21,21 @@ type EoseCallback = (url: string) => void type CloseCallback = () => void type OkCallback = (url: string, id: string, ...extra: any[]) => void type ErrorCallback = (url: string, id: string, ...extra: any[]) => void -type DiffMessageCallback = (url: string, {have, need}: {have: string[], need: string[]}) => void -type SubscribeOpts = {onEvent?: EventCallback, onEose?: EoseCallback} -type PublishOpts = {verb?: string, onOk?: OkCallback, onError?: ErrorCallback} -type DiffOpts = {onError?: ErrorCallback, onMessage?: DiffMessageCallback, onClose?: CloseCallback} +type DiffMessage = {have: string[]; need: string[]} +type DiffMessageCallback = (url: string, {have, need}: DiffMessage) => void +type SubscribeOpts = {onEvent?: EventCallback; onEose?: EoseCallback} +type PublishOpts = {verb?: string; onOk?: OkCallback; onError?: ErrorCallback} +type DiffOpts = {onError?: ErrorCallback; onMessage?: DiffMessageCallback; onClose?: CloseCallback} -const createSubId = (prefix: string) => [prefix, Math.random().toString().slice(2, 10)].join('-') +const createSubId = (prefix: string) => [prefix, Math.random().toString().slice(2, 10)].join("-") export class Executor { - constructor(readonly target: Target) {} subscribe(filters: Filter[], {onEvent, onEose}: SubscribeOpts = {}) { let closed = false - const id = createSubId('REQ') + const id = createSubId("REQ") const eventListener = (url: string, subid: string, e: TrustedEvent) => { if (subid === id) { @@ -50,8 +50,8 @@ export class Executor { } } - this.target.on('EVENT', eventListener) - this.target.on('EOSE', eoseListener) + this.target.on("EVENT", eventListener) + this.target.on("EOSE", eoseListener) this.target.send("REQ", id, ...filters) return { @@ -59,15 +59,15 @@ export class Executor { if (closed) return this.target.send("CLOSE", id).catch(noop) - this.target.off('EVENT', eventListener) - this.target.off('EOSE', eoseListener) + this.target.off("EVENT", eventListener) + this.target.off("EOSE", eoseListener) closed = true }, } } - publish(event: SignedEvent, {verb = 'EVENT', onOk, onError}: PublishOpts = {}) { + publish(event: SignedEvent, {verb = "EVENT", onOk, onError}: PublishOpts = {}) { const okListener = (url: string, id: string, ok: boolean, message: string) => { if (id === event.id) { if (ok) { @@ -84,22 +84,22 @@ export class Executor { } } - this.target.on('OK', okListener) - this.target.on('ERROR', errorListener) + this.target.on("OK", okListener) + this.target.on("ERROR", errorListener) this.target.send(verb, event) return { unsubscribe: () => { - this.target.off('OK', okListener) - this.target.off('ERROR', errorListener) - } + this.target.off("OK", okListener) + this.target.off("ERROR", errorListener) + }, } } diff(filter: Filter, events: TrustedEvent[], {onMessage, onError, onClose}: DiffOpts = {}) { let closed = false - const id = createSubId('NEG') + const id = createSubId("NEG") const storage = new NegentropyStorageVector() const neg = new Negentropy(storage, 50_000) @@ -116,7 +116,7 @@ export class Executor { onMessage?.(url, {have, need}) if (newMsg) { - this.target.send('NEG-MSG', id, newMsg) + this.target.send("NEG-MSG", id, newMsg) } else { close() } @@ -132,16 +132,16 @@ export class Executor { const close = () => { if (closed) return - this.target.send('NEG-CLOSE', id).catch(noop) - this.target.off('NEG-MSG', msgListener) - this.target.off('NEG-ERR', errListener) + this.target.send("NEG-CLOSE", id).catch(noop) + this.target.off("NEG-MSG", msgListener) + this.target.off("NEG-ERR", errListener) closed = true onClose?.() } - this.target.on('NEG-MSG', msgListener) - this.target.on('NEG-ERR', errListener) + this.target.on("NEG-MSG", msgListener) + this.target.on("NEG-ERR", errListener) neg.initiate().then((msg: string) => { this.target.send("NEG-OPEN", id, filter, msg) diff --git a/packages/net/src/Pool.ts b/packages/net/src/Pool.ts index ffa84ef..ff2b412 100644 --- a/packages/net/src/Pool.ts +++ b/packages/net/src/Pool.ts @@ -1,5 +1,5 @@ -import {Emitter} from '@welshman/lib' -import {Connection} from "./Connection" +import {Emitter} from "@welshman/lib" +import {Connection} from "./Connection.js" export class Pool extends Emitter { data: Map @@ -24,7 +24,7 @@ export class Pool extends Emitter { const newConnection = new Connection(url) this.data.set(url, newConnection) - this.emit('init', newConnection) + this.emit("init", newConnection) return newConnection } diff --git a/packages/net/src/Publish.ts b/packages/net/src/Publish.ts index c5a1c65..6f02d79 100644 --- a/packages/net/src/Publish.ts +++ b/packages/net/src/Publish.ts @@ -1,7 +1,7 @@ -import {ctx, Emitter, now, randomId, defer} from '@welshman/lib' -import type {Deferred} from '@welshman/lib' -import {asSignedEvent} from '@welshman/util' -import type {SignedEvent} from '@welshman/util' +import {ctx, Emitter, now, randomId, defer} from "@welshman/lib" +import type {Deferred} from "@welshman/lib" +import {asSignedEvent} from "@welshman/util" +import type {SignedEvent} from "@welshman/util" export enum PublishStatus { Pending = "pending", @@ -34,8 +34,8 @@ export const makePublish = (request: PublishRequest) => { const id = randomId() const created_at = now() const emitter = new Emitter() - const result: Publish['result'] = defer() - const status: Publish['status'] = new Map() + const result: Publish["result"] = defer() + const status: Publish["status"] = new Map() return {id, created_at, request, emitter, result, status} } @@ -77,7 +77,7 @@ export const publish = (request: PublishRequest) => { const timeout = setTimeout(() => abort(PublishStatus.Timeout), request.timeout || 10_000) // If we have a signal, use it - request.signal?.addEventListener('abort', () => abort(PublishStatus.Aborted)) + request.signal?.addEventListener("abort", () => abort(PublishStatus.Aborted)) // Delegate to our executor const executorSub = executor.publish(event, { @@ -96,4 +96,3 @@ export const publish = (request: PublishRequest) => { return pub } - diff --git a/packages/net/src/Socket.ts b/packages/net/src/Socket.ts index ce22503..5f69fe7 100644 --- a/packages/net/src/Socket.ts +++ b/packages/net/src/Socket.ts @@ -1,30 +1,20 @@ import WebSocket from "isomorphic-ws" -import {Worker, sleep} from '@welshman/lib' -import {ConnectionEvent} from './ConnectionEvent' -import type {Connection} from './Connection' +import {Worker, sleep} from "@welshman/lib" +import {ConnectionEvent} from "./ConnectionEvent.js" +import type {Connection} from "./Connection.js" export type Message = [string, ...any[]] export enum SocketStatus { - New = 'new', - Open = 'open', - Opening = 'opening', - Closing = 'closing', - Closed = 'closed', - Error = 'error', - Invalid = 'invalid', + New = "new", + Open = "open", + Opening = "opening", + Closing = "closing", + Closed = "closed", + Error = "error", + Invalid = "invalid", } -const { - New, - Open, - Opening, - Closing, - Closed, - Error, - Invalid, -} = SocketStatus - export class Socket { lastError = 0 status = SocketStatus.New @@ -39,7 +29,7 @@ export class Socket { } wait = async () => { - while ([Opening, Closing].includes(this.status)) { + while ([SocketStatus.Opening, SocketStatus.Closing].includes(this.status)) { await sleep(100) } } @@ -49,19 +39,19 @@ export class Socket { await this.wait() // If the socket is closed, reset - if (this.status === Closed) { - this.status = New + if (this.status === SocketStatus.Closed) { + this.status = SocketStatus.New this.cxn.emit(ConnectionEvent.Reset) } // If we're closed due to an error retry after a delay - if (this.status === Error && Date.now() - this.lastError > 15_000) { - this.status = New + if (this.status === SocketStatus.Error && Date.now() - this.lastError > 15_000) { + this.status = SocketStatus.New this.cxn.emit(ConnectionEvent.Reset) } // If the socket is new, connect - if (this.status === New) { + if (this.status === SocketStatus.New) { this.#init() } @@ -84,6 +74,10 @@ export class Socket { send = async (message: Message) => { await this.open() + if (!this.ws) { + throw new Error(`No websocket available when sending to ${this.cxn.url}`) + } + this.cxn.emit(ConnectionEvent.Send, message) this.ws.send(JSON.stringify(message)) } @@ -91,42 +85,44 @@ export class Socket { #init = () => { try { this.ws = new WebSocket(this.cxn.url) - this.status = Opening + this.status = SocketStatus.Opening this.ws.onopen = () => { - this.status = Open + this.status = SocketStatus.Open this.cxn.emit(ConnectionEvent.Open) } this.ws.onerror = () => { - this.status = Error + this.status = SocketStatus.Error this.lastError = Date.now() this.cxn.emit(ConnectionEvent.Error) } this.ws.onclose = () => { - if (this.status !== Error) { - this.status = Closed + if (this.status !== SocketStatus.Error) { + this.status = SocketStatus.Closed } this.cxn.emit(ConnectionEvent.Close) } - this.ws.onmessage = (event: {data: string}) => { + this.ws.onmessage = (event: any) => { + const data = event.data as string + try { - const message = JSON.parse(event.data as string) + const message = JSON.parse(data) if (Array.isArray(message)) { this.worker.push(message as Message) } else { - this.cxn.emit(ConnectionEvent.InvalidMessage, event.data) + this.cxn.emit(ConnectionEvent.InvalidMessage, data) } } catch (e) { - this.cxn.emit(ConnectionEvent.InvalidMessage, event.data) + this.cxn.emit(ConnectionEvent.InvalidMessage, data) } } } catch (e) { - this.status = Invalid + this.status = SocketStatus.Invalid this.cxn.emit(ConnectionEvent.InvalidUrl) } } diff --git a/packages/net/src/Subscribe.ts b/packages/net/src/Subscribe.ts index e0e3704..11ff60a 100644 --- a/packages/net/src/Subscribe.ts +++ b/packages/net/src/Subscribe.ts @@ -1,9 +1,15 @@ -import {ctx, Emitter, max, chunk, randomId, once, groupBy, uniq} from '@welshman/lib' -import {LOCAL_RELAY_URL, matchFilters, normalizeRelayUrl, unionFilters, TrustedEvent} from '@welshman/util' -import type {Filter} from '@welshman/util' -import {Tracker} from "./Tracker" -import {Connection} from './Connection' -import {ConnectionEvent} from './ConnectionEvent' +import {ctx, Emitter, max, chunk, randomId, once, groupBy, uniq} from "@welshman/lib" +import { + LOCAL_RELAY_URL, + matchFilters, + normalizeRelayUrl, + unionFilters, + TrustedEvent, +} from "@welshman/util" +import type {Filter} from "@welshman/util" +import {Tracker} from "./Tracker.js" +import {Connection} from "./Connection.js" +import {ConnectionEvent} from "./ConnectionEvent.js" // `subscribe` is a super function that handles batching subscriptions by merging // them based on parameters (filters and subscribe opts), then splits them by relay. @@ -71,9 +77,9 @@ export const calculateSubscriptionGroup = (sub: Subscription) => { if (sub.request.timeout) parts.push(`timeout:${sub.request.timeout}`) if (sub.request.authTimeout) parts.push(`authTimeout:${sub.request.authTimeout}`) - if (sub.request.closeOnEose) parts.push('closeOnEose') + if (sub.request.closeOnEose) parts.push("closeOnEose") - return parts.join('|') + return parts.join("|") } export const mergeSubscriptions = (subs: Subscription[]) => { @@ -85,7 +91,7 @@ export const mergeSubscriptions = (subs: Subscription[]) => { closeOnEose: subs.every(sub => sub.request.closeOnEose), }) - mergedSub.controller.signal.addEventListener('abort', () => { + mergedSub.controller.signal.addEventListener("abort", () => { for (const sub of subs) { sub.close() } @@ -130,93 +136,92 @@ export const mergeSubscriptions = (subs: Subscription[]) => { } export const optimizeSubscriptions = (subs: Subscription[]) => { - return Array.from(groupBy(calculateSubscriptionGroup, subs).values()) - .flatMap(group => { - const timeout = max(group.map(sub => sub.request.timeout || 0)) - const authTimeout = max(group.map(sub => sub.request.authTimeout || 0)) - const closeOnEose = group.every(sub => sub.request.closeOnEose) - const completedSubs = new Set() - const abortedSubs = new Set() - const closedSubs = new Set() - const eosedSubs = new Set() - const sentSubs = new Set() - const mergedSubs: Subscription[] = [] + return Array.from(groupBy(calculateSubscriptionGroup, subs).values()).flatMap(group => { + const timeout = max(group.map(sub => sub.request.timeout || 0)) + const authTimeout = max(group.map(sub => sub.request.authTimeout || 0)) + const closeOnEose = group.every(sub => sub.request.closeOnEose) + const completedSubs = new Set() + const abortedSubs = new Set() + const closedSubs = new Set() + const eosedSubs = new Set() + const sentSubs = new Set() + const mergedSubs: Subscription[] = [] - for (const {relays, filters} of ctx.net.optimizeSubscriptions(group)) { - for (const filter of filters) { - const mergedSub = makeSubscription({ - filters: [filter], - relays, - timeout, - authTimeout, - closeOnEose - }) + for (const {relays, filters} of ctx.net.optimizeSubscriptions(group)) { + for (const filter of filters) { + const mergedSub = makeSubscription({ + filters: [filter], + relays, + timeout, + authTimeout, + closeOnEose, + }) - for (const {id, controller, request} of group) { - const onAbort = () => { - abortedSubs.add(id) + for (const {id, controller, request} of group) { + const onAbort = () => { + abortedSubs.add(id) - if (abortedSubs.size === group.length) { - mergedSub.close() - } + if (abortedSubs.size === group.length) { + mergedSub.close() } - - request.signal?.addEventListener('abort', onAbort) - controller.signal.addEventListener('abort', onAbort) } - mergedSub.emitter.on(SubscriptionEvent.Event, (url: string, event: TrustedEvent) => { + request.signal?.addEventListener("abort", onAbort) + controller.signal.addEventListener("abort", onAbort) + } + + mergedSub.emitter.on(SubscriptionEvent.Event, (url: string, event: TrustedEvent) => { + for (const sub of group) { + if (matchFilters(sub.request.filters, event) && !sub.tracker.track(event.id, url)) { + sub.emitter.emit(SubscriptionEvent.Event, url, event) + } + } + }) + + // Pass events back to caller + const propagateEvent = (type: SubscriptionEvent) => + mergedSub.emitter.on(type, (url: string, event: TrustedEvent) => { for (const sub of group) { - if (matchFilters(sub.request.filters, event) && !sub.tracker.track(event.id, url)) { - sub.emitter.emit(SubscriptionEvent.Event, url, event) + if (matchFilters(sub.request.filters, event)) { + sub.emitter.emit(type, url, event) } } }) - // Pass events back to caller - const propagateEvent = (type: SubscriptionEvent) => - mergedSub.emitter.on(type, (url: string, event: TrustedEvent) => { + propagateEvent(SubscriptionEvent.Duplicate) + propagateEvent(SubscriptionEvent.DeletedEvent) + propagateEvent(SubscriptionEvent.Invalid) + + const propagateFinality = (type: SubscriptionEvent, subIds: Set) => + mergedSub.emitter.on(type, (...args: any[]) => { + subIds.add(mergedSub.id) + + // Wait for all subscriptions to complete before reporting finality to the caller. + // This is sub-optimal, but because we're outsourcing filter/relay optimization + // we can't make any assumptions about which caller subscriptions have completed + // at any given time. + if (subIds.size === mergedSubs.length) { for (const sub of group) { - if (matchFilters(sub.request.filters, event)) { - sub.emitter.emit(type, url, event) - } + sub.emitter.emit(type, ...args) } - }) + } - propagateEvent(SubscriptionEvent.Duplicate) - propagateEvent(SubscriptionEvent.DeletedEvent) - propagateEvent(SubscriptionEvent.Invalid) + if (type === SubscriptionEvent.Complete) { + mergedSub.emitter.removeAllListeners() + } + }) - const propagateFinality = (type: SubscriptionEvent, subIds: Set) => - mergedSub.emitter.on(type, (...args: any[]) => { - subIds.add(mergedSub.id) + propagateFinality(SubscriptionEvent.Send, sentSubs) + propagateFinality(SubscriptionEvent.Eose, eosedSubs) + propagateFinality(SubscriptionEvent.Close, closedSubs) + propagateFinality(SubscriptionEvent.Complete, completedSubs) - // Wait for all subscriptions to complete before reporting finality to the caller. - // This is sub-optimal, but because we're outsourcing filter/relay optimization - // we can't make any assumptions about which caller subscriptions have completed - // at any given time. - if (subIds.size === mergedSubs.length) { - for (const sub of group) { - sub.emitter.emit(type, ...args) - } - } - - if (type === SubscriptionEvent.Complete) { - mergedSub.emitter.removeAllListeners() - } - }) - - propagateFinality(SubscriptionEvent.Send, sentSubs) - propagateFinality(SubscriptionEvent.Eose, eosedSubs) - propagateFinality(SubscriptionEvent.Close, closedSubs) - propagateFinality(SubscriptionEvent.Complete, completedSubs) - - mergedSubs.push(mergedSub) - } + mergedSubs.push(mergedSub) } + } - return mergedSubs - }) + return mergedSubs + }) } const _executeSubscription = (sub: Subscription) => { @@ -267,19 +272,17 @@ const _executeSubscription = (sub: Subscription) => { } } - const onEose = (url: string) => - emitter.emit(SubscriptionEvent.Eose, url) + const onEose = (url: string) => emitter.emit(SubscriptionEvent.Eose, url) - const onClose = (connection: Connection) => - emitter.emit(SubscriptionEvent.Close, connection.url) + const onClose = (connection: Connection) => emitter.emit(SubscriptionEvent.Close, connection.url) const onComplete = once(() => emitter.emit(SubscriptionEvent.Complete)) // Listen for abort via caller signal - signal?.addEventListener('abort', onComplete) + signal?.addEventListener("abort", onComplete) // Listen for abort via our own internal signal - controller.signal.addEventListener('abort', onComplete) + controller.signal.addEventListener("abort", onComplete) // If we have a timeout, complete the subscription automatically if (timeout) setTimeout(onComplete, timeout + authTimeout) @@ -297,7 +300,7 @@ const _executeSubscription = (sub: Subscription) => { if (authTimeout) { await connection.auth.attempt(authTimeout) } - }) + }), ).then(() => { // If we send too many filters in a request relays will refuse to respond. REQs are rate // limited client-side by Connection, so this will throttle concurrent requests. @@ -333,9 +336,7 @@ export const executeSubscriptionBatched = (() => { return (sub: Subscription) => { subs.push(sub) - timeouts.push( - setTimeout(executeAll, Math.max(16, sub.request.delay!)) as unknown as number - ) + timeouts.push(setTimeout(executeAll, Math.max(16, sub.request.delay!)) as unknown as number) } })() @@ -346,7 +347,13 @@ export type SubscribeRequestWithHandlers = SubscribeRequest & { onComplete?: () => void } -export const subscribe = ({onEvent, onEose, onClose, onComplete, ...request}: SubscribeRequestWithHandlers) => { +export const subscribe = ({ + onEvent, + onEose, + onClose, + onComplete, + ...request +}: SubscribeRequestWithHandlers) => { const sub: Subscription = makeSubscription({delay: 50, ...request}) for (const relay of request.relays) { @@ -362,7 +369,8 @@ export const subscribe = ({onEvent, onEose, onClose, onComplete, ...request}: Su } // Signature for onEvent is different from emitter signature for historical reasons and convenience - if (onEvent) sub.emitter.on(SubscriptionEvent.Event, (url: string, event: TrustedEvent) => onEvent(event)) + if (onEvent) + sub.emitter.on(SubscriptionEvent.Event, (url: string, event: TrustedEvent) => onEvent(event)) if (onEose) sub.emitter.on(SubscriptionEvent.Eose, onEose) if (onClose) sub.emitter.on(SubscriptionEvent.Close, onClose) if (onComplete) sub.emitter.on(SubscriptionEvent.Complete, onComplete) diff --git a/packages/net/src/Sync.ts b/packages/net/src/Sync.ts index bb9ecde..61810af 100644 --- a/packages/net/src/Sync.ts +++ b/packages/net/src/Sync.ts @@ -1,7 +1,7 @@ -import {ctx, assoc, lt, groupBy, now, pushToMapKey, inc, flatten, chunk} from '@welshman/lib' -import type {SignedEvent, TrustedEvent, Filter} from '@welshman/util' -import {subscribe} from './Subscribe' -import {publish} from './Publish' +import {ctx, assoc, lt, groupBy, now, pushToMapKey, inc, flatten, chunk} from "@welshman/lib" +import type {SignedEvent, TrustedEvent, Filter} from "@welshman/util" +import {subscribe} from "./Subscribe.js" +import {publish} from "./Publish.js" export type DiffOpts = { relays: string[] @@ -19,11 +19,11 @@ export const diff = async ({relays, filters, events}: DiffOpts) => { const have = new Set() const need = new Set() - await new Promise((resolve, reject) => { + await new Promise((resolve, reject) => { executor.diff(filter, events, { onClose: resolve, - onError: (_, message) => reject(message), - onMessage: (_, message) => { + onError: (url, message) => reject(message), + onMessage: (url, message) => { for (const id of message.have) { have.add(id) } @@ -36,29 +36,28 @@ export const diff = async ({relays, filters, events}: DiffOpts) => { }) return {relay, have, need} - }) + }), ) - }) - ) + }), + ), ) - return Array.from(groupBy(diff => diff.relay, diffs).entries()) - .map(([relay, diffs]) => { - const have = new Set() - const need = new Set() + return Array.from(groupBy(diff => diff.relay, diffs).entries()).map(([relay, diffs]) => { + const have = new Set() + const need = new Set() - for (const diff of diffs) { - for (const id of diff.have) { - have.add(id) - } - - for (const id of diff.need) { - need.add(id) - } + for (const diff of diffs) { + for (const id of diff.have) { + have.add(id) } - return {relay, have: Array.from(have), need: Array.from(need)} - }) + for (const id of diff.need) { + need.add(id) + } + } + + return {relay, have: Array.from(have), need: Array.from(need)} + }) } export type PullOpts = { @@ -103,9 +102,9 @@ export const pull = async ({relays, filters, events, onEvent}: PullOpts) => { }, }) }) - }) + }), ) - }) + }), ) return result @@ -133,7 +132,7 @@ export const push = async ({relays, filters, events}: PushOpts) => { if (relays) { await publish({event, relays}).result } - }) + }), ) } @@ -156,7 +155,11 @@ export type PullWithoutNegentropyOpts = { onEvent?: (event: TrustedEvent) => void } -export const pullWithoutNegentropy = async ({relays, filters, onEvent}: PullWithoutNegentropyOpts) => { +export const pullWithoutNegentropy = async ({ + relays, + filters, + onEvent, +}: PullWithoutNegentropyOpts) => { let done = false let until = now() + 30 @@ -168,7 +171,7 @@ export const pullWithoutNegentropy = async ({relays, filters, onEvent}: PullWith await new Promise(resolve => { subscribe({ relays, - filters: filters.filter(f => lt(f.since, until)).map(assoc('until', until)), + filters: filters.filter(f => lt(f.since, until)).map(assoc("until", until)), closeOnEose: true, onComplete: () => { done = !anyResults @@ -196,7 +199,7 @@ export const pushWithoutNegentropy = ({relays, events}: PushWithoutNegentropyOpt Promise.all( events.map(async event => { await publish({event, relays}).result - }) + }), ) export const syncWithoutNegentropy = async (opts: SyncOpts) => { diff --git a/packages/net/src/Tracker.ts b/packages/net/src/Tracker.ts index 64fa3ac..3654214 100644 --- a/packages/net/src/Tracker.ts +++ b/packages/net/src/Tracker.ts @@ -1,4 +1,4 @@ -import {Emitter, addToMapKey} from '@welshman/lib' +import {Emitter, addToMapKey} from "@welshman/lib" export class Tracker extends Emitter { relaysById = new Map>() @@ -36,7 +36,7 @@ export class Tracker extends Emitter { this.relaysById.set(eventId, relays) this.idsByRelay.set(eventId, relays) - this.emit('update') + this.emit("update") } removeRelay = (eventId: string, relay: string) => { @@ -45,7 +45,7 @@ export class Tracker extends Emitter { if (!didDeleteRelay && !didDeleteId) return - this.emit('update') + this.emit("update") } track = (eventId: string, relay: string) => { @@ -62,7 +62,7 @@ export class Tracker extends Emitter { } } - load = (relaysById: Tracker['relaysById']) => { + load = (relaysById: Tracker["relaysById"]) => { this.relaysById.clear() this.idsByRelay.clear() @@ -73,13 +73,13 @@ export class Tracker extends Emitter { } } - this.emit('update') + this.emit("update") } clear = () => { this.relaysById.clear() this.idsByRelay.clear() - this.emit('update') + this.emit("update") } } diff --git a/packages/net/src/index.ts b/packages/net/src/index.ts index 62ed71b..d4414ef 100644 --- a/packages/net/src/index.ts +++ b/packages/net/src/index.ts @@ -1,19 +1,27 @@ -export * from "./Connection" -export * from "./ConnectionAuth" -export * from "./ConnectionEvent" -export * from "./ConnectionSender" -export * from "./ConnectionState" -export * from "./ConnectionStats" -export * from "./Context" -export * from "./Executor" -export * from "./Pool" -export * from "./Publish" -export * from "./Socket" -export * from "./Subscribe" -export * from "./Sync" -export * from "./Tracker" -export * from "./target/Echo" -export * from "./target/Multi" -export * from "./target/Relay" -export * from "./target/Relays" -export * from "./target/Local" +export * from "./Connection.js" +export * from "./ConnectionAuth.js" +export * from "./ConnectionEvent.js" +export * from "./ConnectionSender.js" +export * from "./ConnectionState.js" +export * from "./ConnectionStats.js" +export * from "./Context.js" +export * from "./Executor.js" +export * from "./Pool.js" +export * from "./Publish.js" +export * from "./Socket.js" +export * from "./Subscribe.js" +export * from "./Sync.js" +export * from "./Tracker.js" +export * from "./target/Echo.js" +export * from "./target/Multi.js" +export * from "./target/Relay.js" +export * from "./target/Relays.js" +export * from "./target/Local.js" + +import type {NetContext} from "./Context.js" + +declare module "@welshman/lib" { + interface Context { + net: NetContext + } +} diff --git a/packages/net/src/target/Echo.ts b/packages/net/src/target/Echo.ts index 04fcd49..f0c05be 100644 --- a/packages/net/src/target/Echo.ts +++ b/packages/net/src/target/Echo.ts @@ -1,5 +1,5 @@ -import {Emitter} from '@welshman/lib' -import type {Message} from '../Socket' +import {Emitter} from "@welshman/lib" +import type {Message} from "../Socket.js" export class Echo extends Emitter { get connections() { diff --git a/packages/net/src/target/Local.ts b/packages/net/src/target/Local.ts index b975941..e7f442b 100644 --- a/packages/net/src/target/Local.ts +++ b/packages/net/src/target/Local.ts @@ -1,12 +1,12 @@ -import {Emitter} from '@welshman/lib' -import {Relay, LOCAL_RELAY_URL} from '@welshman/util' -import type {Message} from '../Socket' +import {Emitter} from "@welshman/lib" +import {Relay, LOCAL_RELAY_URL} from "@welshman/util" +import type {Message} from "../Socket.js" export class Local extends Emitter { constructor(readonly relay: Relay) { super() - relay.on('*', this.onMessage) + relay.on("*", this.onMessage) } get connections() { @@ -25,6 +25,6 @@ export class Local extends Emitter { cleanup = () => { this.removeAllListeners() - this.relay.off('*', this.onMessage) + this.relay.off("*", this.onMessage) } } diff --git a/packages/net/src/target/Multi.ts b/packages/net/src/target/Multi.ts index 83fbd51..5a3b84f 100644 --- a/packages/net/src/target/Multi.ts +++ b/packages/net/src/target/Multi.ts @@ -1,13 +1,13 @@ -import {Emitter} from '@welshman/lib' -import type {Message} from '../Socket' -import type {Target} from '../Executor' +import {Emitter} from "@welshman/lib" +import type {Message} from "../Socket.js" +import type {Target} from "../Executor.js" export class Multi extends Emitter { constructor(readonly targets: Target[]) { super() targets.forEach(t => { - t.on('*', (verb, ...args) => this.emit(verb, ...args)) + t.on("*", (verb, ...args) => this.emit(verb, ...args)) }) } diff --git a/packages/net/src/target/Relay.ts b/packages/net/src/target/Relay.ts index 51b12d7..ff9f256 100644 --- a/packages/net/src/target/Relay.ts +++ b/packages/net/src/target/Relay.ts @@ -1,7 +1,7 @@ -import {Emitter} from '@welshman/lib' -import {ConnectionEvent} from '../ConnectionEvent' -import type {Message} from '../Socket' -import type {Connection} from '../Connection' +import {Emitter} from "@welshman/lib" +import {ConnectionEvent} from "../ConnectionEvent.js" +import type {Message} from "../Socket.js" +import type {Connection} from "../Connection.js" export class Relay extends Emitter { constructor(readonly connection: Connection) { diff --git a/packages/net/src/target/Relays.ts b/packages/net/src/target/Relays.ts index d942d50..a2ce763 100644 --- a/packages/net/src/target/Relays.ts +++ b/packages/net/src/target/Relays.ts @@ -1,7 +1,7 @@ -import {Emitter} from '@welshman/lib' -import type {Message} from '../Socket' -import type {Connection} from '../Connection' -import {ConnectionEvent} from '../ConnectionEvent' +import {Emitter} from "@welshman/lib" +import type {Message} from "../Socket.js" +import type {Connection} from "../Connection.js" +import {ConnectionEvent} from "../ConnectionEvent.js" export class Relays extends Emitter { constructor(readonly connections: Connection[]) { @@ -23,7 +23,7 @@ export class Relays extends Emitter { cleanup = () => { this.removeAllListeners() this.connections.forEach(connection => { - connection.off('receive', this.onMessage) + connection.off("receive", this.onMessage) }) } } diff --git a/packages/net/src/types.d.ts b/packages/net/src/types.d.ts deleted file mode 100644 index dc933cc..0000000 --- a/packages/net/src/types.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type {NetContext} from './Context' - - -declare module "@welshman/lib" { - interface Context { - net: NetContext - } -} diff --git a/packages/net/tsc-multi.json b/packages/net/tsc-multi.json deleted file mode 100644 index 6c37019..0000000 --- a/packages/net/tsc-multi.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "targets": [ - {"extname": ".cjs", "module": "commonjs"}, - {"extname": ".mjs", "module": "esnext", "moduleResolution": "node"} - ], - "projects": ["tsconfig.json"] -} diff --git a/packages/net/tsconfig.json b/packages/net/tsconfig.json index 669c5f4..97e6372 100644 --- a/packages/net/tsconfig.json +++ b/packages/net/tsconfig.json @@ -3,9 +3,12 @@ "compilerOptions": { "rootDir": ".", "outDir": "build", - "esModuleInterop": true, - "skipLibCheck": true, + "module": "nodenext", + "moduleResolution": "nodenext", "lib": ["esnext", "dom"] }, - "include": ["src/**/*.ts"] + "include": [ + "src/**/*.ts", + "test/**/*.ts" + ] } diff --git a/packages/signer/package.json b/packages/signer/package.json index 8b26db1..9415f03 100644 --- a/packages/signer/package.json +++ b/packages/signer/package.json @@ -11,26 +11,26 @@ "files": [ "build" ], + "engines": { + "node": ">=10.x" + }, "types": "./build/src/index.d.ts", "exports": { ".": { "types": "./build/src/index.d.ts", - "import": "./build/src/index.mjs", - "require": "./build/src/index.cjs" + "import": "./build/src/index.js", + "require": "./build/src/index.js" } }, "scripts": { "pub": "npm run lint && npm run build && npm publish", - "build": "gts clean && tsc-multi", + "build": "gts clean && tsc", "lint": "gts lint", "fix": "gts fix" }, - "devDependencies": { - "gts": "^5.0.1", - "tsc-multi": "^1.1.0", - "typescript": "~5.1.6" - }, "dependencies": { + "@noble/curves": "^1.7.0", + "@noble/hashes": "^1.6.1", "@welshman/lib": "~0.0.33", "@welshman/net": "~0.0.41", "@welshman/util": "~0.0.50", diff --git a/packages/signer/src/index.ts b/packages/signer/src/index.ts index f4d3a61..8a22420 100644 --- a/packages/signer/src/index.ts +++ b/packages/signer/src/index.ts @@ -1,6 +1,6 @@ -export * from './util' -export * from './nip59' -export * from './signers/nip01' -export * from './signers/nip07' -export * from './signers/nip46' -export * from './signers/nip55' +export * from "./util.js" +export * from "./nip59.js" +export * from "./signers/nip01.js" +export * from "./signers/nip07.js" +export * from "./signers/nip46.js" +export * from "./signers/nip55.js" diff --git a/packages/signer/src/nip59.ts b/packages/signer/src/nip59.ts index b81aafb..6b63131 100644 --- a/packages/signer/src/nip59.ts +++ b/packages/signer/src/nip59.ts @@ -1,6 +1,6 @@ -import {UnwrappedEvent, SignedEvent, HashedEvent, StampedEvent, WRAP, SEAL} from '@welshman/util' -import {own, hash, decrypt, ISigner} from './util' -import {Nip01Signer} from './signers/nip01' +import {UnwrappedEvent, SignedEvent, HashedEvent, StampedEvent, WRAP, SEAL} from "@welshman/util" +import {own, hash, decrypt, ISigner} from "./util.js" +import {Nip01Signer} from "./signers/nip01.js" export const seen = new Map() @@ -11,24 +11,39 @@ export const getRumor = async (signer: ISigner, template: StampedEvent) => hash(own(template, await signer.getPubkey())) export const getSeal = async (signer: ISigner, pubkey: string, rumor: HashedEvent) => - signer.sign(hash({ - kind: SEAL, - pubkey: await signer.getPubkey(), - content: await signer.nip44.encrypt(pubkey, JSON.stringify(rumor)), - created_at: now(5), - tags: [], - })) + signer.sign( + hash({ + kind: SEAL, + pubkey: await signer.getPubkey(), + content: await signer.nip44.encrypt(pubkey, JSON.stringify(rumor)), + created_at: now(5), + tags: [], + }), + ) -export const getWrap = async (wrapper: ISigner, pubkey: string, seal: SignedEvent, tags: string[][]) => - wrapper.sign(hash({ - kind: WRAP, - pubkey: await wrapper.getPubkey(), - content: await wrapper.nip44.encrypt(pubkey, JSON.stringify(seal)), - created_at: now(5), - tags: [...tags, ["p", pubkey]], - })) +export const getWrap = async ( + wrapper: ISigner, + pubkey: string, + seal: SignedEvent, + tags: string[][], +) => + wrapper.sign( + hash({ + kind: WRAP, + pubkey: await wrapper.getPubkey(), + content: await wrapper.nip44.encrypt(pubkey, JSON.stringify(seal)), + created_at: now(5), + tags: [...tags, ["p", pubkey]], + }), + ) -export const wrap = async (signer: ISigner, wrapper: ISigner, pubkey: string, template: StampedEvent, tags: string[][] = []) => { +export const wrap = async ( + signer: ISigner, + wrapper: ISigner, + pubkey: string, + template: StampedEvent, + tags: string[][] = [], +) => { const rumor = await getRumor(signer, template) const seal = await getSeal(signer, pubkey, rumor) const wrap = await getWrap(wrapper, pubkey, seal, tags) @@ -69,7 +84,10 @@ export const unwrap = async (signer: ISigner, wrap: SignedEvent) => { // wrapping a single user signer and omit the wrapper signer argument to wrap, while still // making it possible to pass a wrapper signer if desired. export class Nip59 { - constructor(private signer: ISigner, private wrapper?: ISigner) {} + constructor( + private signer: ISigner, + private wrapper?: ISigner, + ) {} static fromSigner = (signer: ISigner) => new Nip59(signer) @@ -80,6 +98,5 @@ export class Nip59 { wrap = (pubkey: string, template: StampedEvent, tags: string[][] = []) => wrap(this.signer, this.wrapper || Nip01Signer.ephemeral(), pubkey, template, tags) - unwrap = (event: SignedEvent) => - unwrap(this.signer, event) + unwrap = (event: SignedEvent) => unwrap(this.signer, event) } diff --git a/packages/signer/src/signers/nip01.ts b/packages/signer/src/signers/nip01.ts index 39eb605..c89c11f 100644 --- a/packages/signer/src/signers/nip01.ts +++ b/packages/signer/src/signers/nip01.ts @@ -1,5 +1,5 @@ -import {StampedEvent} from '@welshman/util' -import {nip04, nip44, own, hash, sign, getPubkey, ISigner, makeSecret} from "../util" +import {StampedEvent} from "@welshman/util" +import {nip04, nip44, own, hash, sign, getPubkey, ISigner, makeSecret} from "../util.js" export class Nip01Signer implements ISigner { #pubkey: string @@ -17,16 +17,12 @@ export class Nip01Signer implements ISigner { sign = async (event: StampedEvent) => sign(hash(own(event, this.#pubkey)), this.secret) nip04 = { - encrypt: async (pubkey: string, message: string) => - nip04.encrypt(pubkey, this.secret, message), - decrypt: async (pubkey: string, message: string) => - nip04.decrypt(pubkey, this.secret, message), + encrypt: async (pubkey: string, message: string) => nip04.encrypt(pubkey, this.secret, message), + decrypt: async (pubkey: string, message: string) => nip04.decrypt(pubkey, this.secret, message), } nip44 = { - encrypt: async (pubkey: string, message: string) => - nip44.encrypt(pubkey, this.secret, message), - decrypt: async (pubkey: string, message: string) => - nip44.decrypt(pubkey, this.secret, message), + encrypt: async (pubkey: string, message: string) => nip44.encrypt(pubkey, this.secret, message), + decrypt: async (pubkey: string, message: string) => nip44.decrypt(pubkey, this.secret, message), } } diff --git a/packages/signer/src/signers/nip07.ts b/packages/signer/src/signers/nip07.ts index 9010995..9f6b371 100644 --- a/packages/signer/src/signers/nip07.ts +++ b/packages/signer/src/signers/nip07.ts @@ -1,5 +1,5 @@ -import {StampedEvent} from '@welshman/util' -import {hash, own, Sign, ISigner, EncryptionImplementation} from '../util' +import {StampedEvent} from "@welshman/util" +import {hash, own, Sign, ISigner, EncryptionImplementation} from "../util.js" export type Nip07 = { signEvent: Sign @@ -23,7 +23,10 @@ export class Nip07Signer implements ISigner { }) // Recover from errors - this.#lock = promise.then(() => undefined, () => undefined) + this.#lock = promise.then( + () => undefined, + () => undefined, + ) return promise } @@ -50,4 +53,3 @@ export class Nip07Signer implements ISigner { this.#then(ext => ext.nip44.decrypt(pubkey, message)), } } - diff --git a/packages/signer/src/signers/nip46.ts b/packages/signer/src/signers/nip46.ts index d2fe4fe..c51b0d1 100644 --- a/packages/signer/src/signers/nip46.ts +++ b/packages/signer/src/signers/nip46.ts @@ -1,14 +1,29 @@ -import {Emitter, throttle, makePromise, defer, sleep, tryCatch, randomId, equals} from "@welshman/lib" -import {createEvent, normalizeRelayUrl, TrustedEvent, StampedEvent, NOSTR_CONNECT} from "@welshman/util" +import { + Emitter, + throttle, + makePromise, + defer, + sleep, + tryCatch, + randomId, + equals, +} from "@welshman/lib" +import { + createEvent, + normalizeRelayUrl, + TrustedEvent, + StampedEvent, + NOSTR_CONNECT, +} from "@welshman/util" import {subscribe, publish, Subscription, SubscriptionEvent} from "@welshman/net" -import {ISigner, decrypt, hash, own} from '../util' -import {Nip01Signer} from './nip01' +import {ISigner, EncryptionImplementation, decrypt, hash, own} from "../util.js" +import {Nip01Signer} from "./nip01.js" export type Nip46Algorithm = "nip04" | "nip44" export enum Nip46Event { - Send = 'send', - Receive = 'receive', + Send = "send", + Receive = "receive", } export type Nip46BrokerParams = { @@ -82,7 +97,10 @@ const popupManager = (() => { export class Nip46Receiver extends Emitter { public sub?: Subscription - constructor(public signer: ISigner, public params: Nip46BrokerParams) { + constructor( + public signer: ISigner, + public params: Nip46BrokerParams, + ) { super() } @@ -125,7 +143,10 @@ export class Nip46Sender extends Emitter { public processing = false public queue: Nip46Request[] = [] - constructor(public signer: ISigner, public params: Nip46BrokerParams) { + constructor( + public signer: ISigner, + public params: Nip46BrokerParams, + ) { super() } @@ -160,7 +181,7 @@ export class Nip46Sender extends Emitter { try { await this.send(request) } catch (error: any) { - console.error(`nip46 error:`, error, request) + console.error("nip46 error:", error, request) } } } finally { @@ -182,7 +203,10 @@ export class Nip46Request { id = randomId() promise = defer() - constructor(readonly method: string, readonly params: string[]) {} + constructor( + readonly method: string, + readonly params: string[], + ) {} listen = async (receiver: Nip46Receiver) => { await receiver.start() @@ -251,7 +275,7 @@ export class Nip46Broker extends Emitter { const sender = new Nip46Sender(this.signer, this.params) sender.on(Nip46Event.Send, (data: any) => { - console.log('nip46 send:', data) + console.log("nip46 send:", data) }) return sender @@ -261,7 +285,7 @@ export class Nip46Broker extends Emitter { const receiver = new Nip46Receiver(this.signer, this.params) receiver.on(Nip46Event.Receive, (data: any) => { - console.log('nip46 receive:', data) + console.log("nip46 receive:", data) }) return receiver @@ -314,7 +338,7 @@ export class Nip46Broker extends Emitter { const _url = new URL(url) relays = _url.searchParams.getAll("relay") || [] - signerPubkey = _url.hostname || _url.pathname.replace(/\//g, '') + signerPubkey = _url.hostname || _url.pathname.replace(/\//g, "") connectSecret = _url.searchParams.get("secret") || "" } catch { // pass @@ -329,22 +353,24 @@ export class Nip46Broker extends Emitter { const params = new URLSearchParams({...meta, secret}) for (const relay of this.params.relays) { - params.append('relay', relay) + params.append("relay", relay) } return `nostrconnect://${clientPubkey}?${params.toString()}` } waitForNostrconnect = (url: string, abort?: AbortController) => { - const secret = new URL(url).searchParams.get('secret') + const secret = new URL(url).searchParams.get("secret") return makePromise((resolve, reject) => { const onReceive = (response: Nip46Response) => { if (["ack", secret].includes(response.result!)) { this.setParams({signerPubkey: response.event.pubkey}) - if (response.result === 'ack') { - console.warn("Bunker responded to nostrconnect with 'ack', which can lead to session hijacking") + if (response.result === "ack") { + console.warn( + "Bunker responded to nostrconnect with 'ack', which can lead to session hijacking", + ) } resolve(response as Nip46ResponseWithResult) @@ -362,7 +388,7 @@ export class Nip46Broker extends Emitter { this.receiver.on(Nip46Event.Receive, onReceive) this.receiver.start() - abort?.signal.addEventListener('abort', () => { + abort?.signal.addEventListener("abort", () => { reject(undefined) cleanup() }) @@ -394,9 +420,21 @@ export class Nip46Broker extends Emitter { } export class Nip46Signer implements ISigner { - public pubkey?: string + pubkey?: string + nip04: EncryptionImplementation + nip44: EncryptionImplementation - constructor(public broker: Nip46Broker) {} + constructor(public broker: Nip46Broker) { + this.nip04 = { + encrypt: this.broker.nip04Encrypt, + decrypt: this.broker.nip04Decrypt, + } + + this.nip44 = { + encrypt: this.broker.nip44Encrypt, + decrypt: this.broker.nip44Decrypt, + } + } getPubkey = async () => { if (!this.pubkey) { @@ -408,14 +446,4 @@ export class Nip46Signer implements ISigner { sign = async (template: StampedEvent) => this.broker.signEvent(hash(own(template, await this.getPubkey()))) - - nip04 = { - encrypt: this.broker.nip04Encrypt, - decrypt: this.broker.nip04Decrypt, - } - - nip44 = { - encrypt: this.broker.nip44Encrypt, - decrypt: this.broker.nip44Decrypt, - } } diff --git a/packages/signer/src/signers/nip55.ts b/packages/signer/src/signers/nip55.ts index e1cf1f4..201ce89 100644 --- a/packages/signer/src/signers/nip55.ts +++ b/packages/signer/src/signers/nip55.ts @@ -1,7 +1,7 @@ -import {SignedEvent, StampedEvent} from "@welshman/util" -import {hash, own, ISigner} from "../util" import {NostrSignerPlugin, AppInfo} from "nostr-signer-capacitor-plugin" -import {nip19} from "nostr-tools" +import {decode} from "nostr-tools/nip19" +import {SignedEvent, StampedEvent} from "@welshman/util" +import {hash, own, ISigner} from "../util.js" export const getNip55 = async (): Promise => { const {apps} = await NostrSignerPlugin.getInstalledSignerApps() @@ -21,10 +21,11 @@ export class Nip55Signer implements ISigner { this.#initialize() } - async #initialize() { + #initialize() { if (!this.#packageNameSet) { - await this.#plugin.setPackageName({packageName: this.#packageName}) - this.#packageNameSet = true + void this.#plugin.setPackageName({packageName: this.#packageName}).then(() => { + this.#packageNameSet = true + }) } } @@ -53,10 +54,10 @@ export class Nip55Signer implements ISigner { try { const {npub} = await signer.getPublicKey() this.#npub = npub - const {data} = nip19.decode(npub) + const {data} = decode(npub) this.#publicKey = data as string } catch (error) { - throw new Error(`Failed to get public key`) + throw new Error("Failed to get public key") } } return this.#publicKey diff --git a/packages/signer/src/util.ts b/packages/signer/src/util.ts index 2dc9d42..508c6d8 100644 --- a/packages/signer/src/util.ts +++ b/packages/signer/src/util.ts @@ -1,8 +1,10 @@ -import {schnorr} from '@noble/curves/secp256k1' -import {bytesToHex, hexToBytes} from '@noble/hashes/utils' -import {nip04 as nt04, nip44 as nt44, generateSecretKey, getPublicKey, getEventHash} from "nostr-tools" -import {cached, now} from '@welshman/lib' -import {SignedEvent, HashedEvent, EventTemplate, StampedEvent, OwnedEvent} from '@welshman/util' +import {schnorr} from "@noble/curves/secp256k1" +import {bytesToHex, hexToBytes} from "@noble/hashes/utils" +import * as nt04 from "nostr-tools/nip04" +import * as nt44 from "nostr-tools/nip44" +import {generateSecretKey, getPublicKey, getEventHash} from "nostr-tools/pure" +import {cached, now} from "@welshman/lib" +import {SignedEvent, HashedEvent, EventTemplate, StampedEvent, OwnedEvent} from "@welshman/util" export const makeSecret = () => bytesToHex(generateSecretKey()) @@ -24,17 +26,20 @@ export const sign = (event: HashedEvent, secret: string) => ({...event, sig: get export const nip04 = { detect: (m: string) => m.includes("?iv="), encrypt: (pubkey: string, secret: string, m: string) => nt04.encrypt(secret, pubkey, m), - decrypt: (pubkey: string, secret: string, m: string) => nt04.decrypt(secret, pubkey, m), + decrypt: (pubkey: string, secret: string, m: string) => nt04.decrypt(secret, pubkey, m), } export const nip44 = { getSharedSecret: cached({ maxSize: 10000, getKey: ([secret, pubkey]) => [secret, pubkey].join(":"), - getValue: ([secret, pubkey]: string[]) => nt44.v2.utils.getConversationKey(hexToBytes(secret), pubkey), + getValue: ([secret, pubkey]: string[]) => + nt44.v2.utils.getConversationKey(hexToBytes(secret), pubkey), }), - encrypt: (pubkey: string, secret: string, m: string) => nt44.v2.encrypt(m, nip44.getSharedSecret(secret, pubkey)!), - decrypt: (pubkey: string, secret: string, m: string) => nt44.v2.decrypt(m, nip44.getSharedSecret(secret, pubkey)!), + encrypt: (pubkey: string, secret: string, m: string) => + nt44.v2.encrypt(m, nip44.getSharedSecret(secret, pubkey)!), + decrypt: (pubkey: string, secret: string, m: string) => + nt44.v2.decrypt(m, nip44.getSharedSecret(secret, pubkey)!), } export type Sign = (event: StampedEvent) => Promise diff --git a/packages/signer/tsc-multi.json b/packages/signer/tsc-multi.json deleted file mode 100644 index 6c37019..0000000 --- a/packages/signer/tsc-multi.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "targets": [ - {"extname": ".cjs", "module": "commonjs"}, - {"extname": ".mjs", "module": "esnext", "moduleResolution": "node"} - ], - "projects": ["tsconfig.json"] -} diff --git a/packages/signer/tsconfig.json b/packages/signer/tsconfig.json index 8defcdc..97e6372 100644 --- a/packages/signer/tsconfig.json +++ b/packages/signer/tsconfig.json @@ -3,9 +3,12 @@ "compilerOptions": { "rootDir": ".", "outDir": "build", - "esModuleInterop": true, - "skipLibCheck": true, - "lib": ["esnext", "dom", "dom.iterable"] + "module": "nodenext", + "moduleResolution": "nodenext", + "lib": ["esnext", "dom"] }, - "include": ["src/**/*.ts"] + "include": [ + "src/**/*.ts", + "test/**/*.ts" + ] } diff --git a/packages/store/package.json b/packages/store/package.json index b48f1af..621b060 100644 --- a/packages/store/package.json +++ b/packages/store/package.json @@ -15,21 +15,16 @@ "exports": { ".": { "types": "./build/src/index.d.ts", - "import": "./build/src/index.mjs", - "require": "./build/src/index.cjs" + "import": "./build/src/index.js", + "require": "./build/src/index.js" } }, "scripts": { "pub": "npm run lint && npm run build && npm publish", - "build": "gts clean && tsc-multi", + "build": "gts clean && tsc", "lint": "gts lint", "fix": "gts fix" }, - "devDependencies": { - "gts": "^5.0.1", - "tsc-multi": "^1.1.0", - "typescript": "~5.1.6" - }, "dependencies": { "@welshman/lib": "~0.0.33", "@welshman/util": "~0.0.50", diff --git a/packages/store/src/index.ts b/packages/store/src/index.ts index 85c0317..52e53ab 100644 --- a/packages/store/src/index.ts +++ b/packages/store/src/index.ts @@ -1,6 +1,15 @@ import {derived, writable} from "svelte/store" import type {Readable, Writable, Subscriber, Unsubscriber} from "svelte/store" -import {identity, throttle, ensurePlural, getJson, setJson, batch, partition, first} from "@welshman/lib" +import { + identity, + throttle, + ensurePlural, + getJson, + setJson, + batch, + partition, + first, +} from "@welshman/lib" import type {Maybe} from "@welshman/lib" import type {Repository} from "@welshman/util" import {matchFilters, getIdAndAddress, getIdFilters} from "@welshman/util" @@ -59,7 +68,10 @@ export type CustomStoreOpts = { set?: (x: T) => void } -export const custom = (start: Start, opts: CustomStoreOpts = {}): WritableWithGetter => { +export const custom = ( + start: Start, + opts: CustomStoreOpts = {}, +): WritableWithGetter => { const subs: Subscriber[] = [] let value: T @@ -118,9 +130,9 @@ export const adapter = ({ forward, backward, }: { - store: Writable, - forward: (x: Source) => Target, - backward: (x: Target) => Source, + store: Writable + forward: (x: Source) => Target + backward: (x: Target) => Source }) => ({ ...derived(store, forward), set: (x: Target) => store.set(backward(x)), @@ -137,109 +149,118 @@ export type DeriveEventsMappedOptions = { includeDeleted?: boolean } -export const deriveEventsMapped = (repository: Repository, { - filters, - eventToItem, - itemToEvent, - throttle = 0, - includeDeleted = false, -}: DeriveEventsMappedOptions) => - custom(setter => { - let data: T[] = [] - const deferred = new Set() +export const deriveEventsMapped = ( + repository: Repository, + { + filters, + eventToItem, + itemToEvent, + throttle = 0, + includeDeleted = false, + }: DeriveEventsMappedOptions, +) => + custom( + setter => { + let data: T[] = [] + const deferred = new Set() - const defer = (event: TrustedEvent, promise: Promise) => { - deferred.add(event.id) + const defer = (event: TrustedEvent, promise: Promise) => { + deferred.add(event.id) - promise.then(items => { - if (deferred.has(event.id)) { - deferred.delete(event.id) + void promise.then(items => { + if (deferred.has(event.id)) { + deferred.delete(event.id) + for (const item of ensurePlural(items)) { + data.push(item) + } + + setter(data) + } + }) + } + + for (const event of repository.query(filters, {includeDeleted})) { + const items = eventToItem(event) + + if (!items) { + continue + } + + if (items instanceof Promise) { + defer(event, items) + } else { for (const item of ensurePlural(items)) { data.push(item) } - - setter(data) - } - }) - } - - for (const event of repository.query(filters, {includeDeleted})) { - const items = eventToItem(event) - - if (!items) { - continue - } - - if (items instanceof Promise) { - defer(event, items) - } else { - for (const item of ensurePlural(items)) { - data.push(item) - } - } - } - - setter(data) - - const onUpdate = batch(300, (updates: {added: TrustedEvent[]; removed: Set}[]) => { - const removed = new Set() - const added = new Map() - - // Apply updates in order - for (const update of updates) { - for (const event of update.added.values()) { - added.set(event.id, event) - removed.delete(event.id) - } - - for (const id of update.removed) { - removed.add(id) - added.delete(id) - deferred.delete(id) } } - let dirty = false - for (const event of added.values()) { - if (matchFilters(filters, event)) { - const items = eventToItem(event) + setter(data) - if (items instanceof Promise) { - defer(event, items) - } else if (items) { - dirty = true + const onUpdate = batch(300, (updates: {added: TrustedEvent[]; removed: Set}[]) => { + const removed = new Set() + const added = new Map() - for (const item of ensurePlural(items)) { - data.push(item as T) + // Apply updates in order + for (const update of updates) { + for (const event of update.added.values()) { + added.set(event.id, event) + removed.delete(event.id) + } + + for (const id of update.removed) { + removed.add(id) + added.delete(id) + deferred.delete(id) + } + } + + let dirty = false + for (const event of added.values()) { + if (matchFilters(filters, event)) { + const items = eventToItem(event) + + if (items instanceof Promise) { + defer(event, items) + } else if (items) { + dirty = true + + for (const item of ensurePlural(items)) { + data.push(item as T) + } } } } - } - if (!includeDeleted && removed.size > 0) { - const [deleted, ok] = partition( - (item: T) => getIdAndAddress(itemToEvent(item)).some((id: string) => removed.has(id)), - data, - ) + if (!includeDeleted && removed.size > 0) { + const [deleted, ok] = partition( + (item: T) => getIdAndAddress(itemToEvent(item)).some((id: string) => removed.has(id)), + data, + ) - if (deleted.length > 0) { - dirty = true - data = ok + if (deleted.length > 0) { + dirty = true + data = ok + } } - } - if (dirty) { - setter(data) - } - }) + if (dirty) { + setter(data) + } + }) - repository.on("update", onUpdate) + repository.on("update", onUpdate) - return () => repository.off("update", onUpdate) - }, {throttle}) + return () => repository.off("update", onUpdate) + }, + {throttle}, + ) -export type DeriveEventsOptions = Omit, "itemToEvent" | "eventToItem"> +export type DeriveEventsOptions = Omit< + DeriveEventsMappedOptions, + "itemToEvent" | "eventToItem" +> export const deriveEvents = (repository: Repository, opts: DeriveEventsOptions) => deriveEventsMapped(repository, { @@ -254,7 +275,7 @@ export const deriveEvent = (repository: Repository, idOrAddress: string) => filters: getIdFilters([idOrAddress]), includeDeleted: true, }), - first + first, ) export const deriveIsDeleted = (repository: Repository, event: TrustedEvent) => diff --git a/packages/store/tsc-multi.json b/packages/store/tsc-multi.json deleted file mode 100644 index 6c37019..0000000 --- a/packages/store/tsc-multi.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "targets": [ - {"extname": ".cjs", "module": "commonjs"}, - {"extname": ".mjs", "module": "esnext", "moduleResolution": "node"} - ], - "projects": ["tsconfig.json"] -} diff --git a/packages/store/tsconfig.json b/packages/store/tsconfig.json index 8defcdc..97e6372 100644 --- a/packages/store/tsconfig.json +++ b/packages/store/tsconfig.json @@ -3,9 +3,12 @@ "compilerOptions": { "rootDir": ".", "outDir": "build", - "esModuleInterop": true, - "skipLibCheck": true, - "lib": ["esnext", "dom", "dom.iterable"] + "module": "nodenext", + "moduleResolution": "nodenext", + "lib": ["esnext", "dom"] }, - "include": ["src/**/*.ts"] + "include": [ + "src/**/*.ts", + "test/**/*.ts" + ] } diff --git a/packages/util/package.json b/packages/util/package.json index 4177169..3cafa6d 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -11,26 +11,25 @@ "files": [ "build" ], + "engines": { + "node": ">=10.4.0" + }, "types": "./build/src/index.d.ts", "exports": { ".": { "types": "./build/src/index.d.ts", - "import": "./build/src/index.mjs", - "require": "./build/src/index.cjs" + "import": "./build/src/index.js", + "require": "./build/src/index.js" } }, "scripts": { "pub": "npm run lint && npm run build && npm publish", - "build": "gts clean && tsc-multi", + "build": "gts clean && tsc", "lint": "gts lint", "fix": "gts fix" }, - "devDependencies": { - "gts": "^5.0.1", - "tsc-multi": "^1.1.0", - "typescript": "~5.1.6" - }, "dependencies": { + "@types/ws": "^8.5.13", "@welshman/lib": "~0.0.33", "nostr-tools": "^2.7.2" } diff --git a/packages/util/src/Address.ts b/packages/util/src/Address.ts index 1684901..b96c218 100644 --- a/packages/util/src/Address.ts +++ b/packages/util/src/Address.ts @@ -1,4 +1,4 @@ -import {nip19} from "nostr-tools" +import {decode, naddrEncode} from "nostr-tools/nip19" // Define this locally to avoid circular dependencies type AddressableEvent = { @@ -12,7 +12,7 @@ export class Address { readonly kind: number, readonly pubkey: string, readonly identifier: string, - readonly relays: string[] = [] + readonly relays: string[] = [], ) {} static isAddress(address: string) { @@ -26,22 +26,21 @@ export class Address { } static fromNaddr(naddr: string) { - let type - let data = {} as any + let decoded: any + try { - ({type, data} = nip19.decode(naddr) as { - type: "naddr" - data: any - }) + decoded = decode(naddr) } catch (e) { // pass } - if (type !== "naddr") { + if (decoded?.type !== "naddr") { throw new Error(`Invalid naddr ${naddr}`) } - return new Address(data.kind, data.pubkey, data.identifier, data.relays) + const {kind, pubkey, identifier, relays} = decoded.data + + return new Address(kind, pubkey, identifier, relays) } static fromEvent(event: AddressableEvent, relays: string[] = []) { @@ -52,7 +51,7 @@ export class Address { toString = () => [this.kind, this.pubkey, this.identifier].join(":") - toNaddr = () => nip19.naddrEncode(this) + toNaddr = () => naddrEncode(this) } // Utils diff --git a/packages/util/src/Encryptable.ts b/packages/util/src/Encryptable.ts index a87709c..884f290 100644 --- a/packages/util/src/Encryptable.ts +++ b/packages/util/src/Encryptable.ts @@ -1,4 +1,4 @@ -import type {EventContent, TrustedEvent, EventTemplate} from './Events' +import type {EventContent, TrustedEvent, EventTemplate} from "./Events.js" export type Encrypt = (x: string) => Promise @@ -27,7 +27,10 @@ export class Encryptable { * const eventTemplate = await encryptable.reconcile(myEncryptFunction) * ``` */ - constructor(readonly event: Partial, readonly updates: EncryptableUpdates) {} + constructor( + readonly event: Partial, + readonly updates: EncryptableUpdates, + ) {} /** * Encrypts plaintext updates and merges them into the event template. @@ -49,7 +52,7 @@ export class Encryptable { tag[1] = await encrypt(tag[1]) return tag - }) + }), ) } diff --git a/packages/util/src/Events.ts b/packages/util/src/Events.ts index bc73d0b..83ef72d 100644 --- a/packages/util/src/Events.ts +++ b/packages/util/src/Events.ts @@ -1,8 +1,13 @@ -import {verifiedSymbol, getEventHash, verifyEvent} from 'nostr-tools' -import {cached, pick, now} from '@welshman/lib' -import {getAncestorTagValues} from './Tags' -import {getAddress} from './Address' -import {isEphemeralKind, isReplaceableKind, isPlainReplaceableKind, isParameterizedReplaceableKind} from './Kinds' +import {verifiedSymbol, getEventHash, verifyEvent} from "nostr-tools/pure" +import {cached, pick, now} from "@welshman/lib" +import {getAncestorTagValues} from "./Tags.js" +import {getAddress} from "./Address.js" +import { + isEphemeralKind, + isReplaceableKind, + isPlainReplaceableKind, + isParameterizedReplaceableKind, +} from "./Kinds.js" export type EventContent = { tags: string[][] @@ -46,8 +51,10 @@ export type CreateEventOpts = { created_at?: number } -export const createEvent = (kind: number, {content = "", tags = [], created_at = now()}: CreateEventOpts) => - ({kind, content, tags, created_at}) +export const createEvent = ( + kind: number, + {content = "", tags = [], created_at = now()}: CreateEventOpts, +) => ({kind, content, tags, created_at}) export const isEventTemplate = (e: EventTemplate): e is EventTemplate => Boolean(typeof e.kind === "number" && Array.isArray(e.tags) && typeof e.content === "string") @@ -58,8 +65,7 @@ export const isStampedEvent = (e: StampedEvent): e is StampedEvent => export const isOwnedEvent = (e: OwnedEvent): e is OwnedEvent => Boolean(isStampedEvent(e) && e.pubkey) -export const isHashedEvent = (e: HashedEvent): e is HashedEvent => - Boolean(isOwnedEvent(e) && e.id) +export const isHashedEvent = (e: HashedEvent): e is HashedEvent => Boolean(isOwnedEvent(e) && e.id) export const isSignedEvent = (e: TrustedEvent): e is SignedEvent => Boolean(isHashedEvent(e) && e.sig) @@ -71,25 +77,25 @@ export const isTrustedEvent = (e: TrustedEvent): e is TrustedEvent => isSignedEvent(e) || isUnwrappedEvent(e) export const asEventTemplate = (e: EventTemplate): EventTemplate => - pick(['kind', 'tags', 'content'], e) + pick(["kind", "tags", "content"], e) export const asStampedEvent = (e: StampedEvent): StampedEvent => - pick(['kind', 'tags', 'content', 'created_at'], e) + pick(["kind", "tags", "content", "created_at"], e) export const asOwnedEvent = (e: OwnedEvent): OwnedEvent => - pick(['kind', 'tags', 'content', 'created_at', 'pubkey'], e) + pick(["kind", "tags", "content", "created_at", "pubkey"], e) export const asHashedEvent = (e: HashedEvent): HashedEvent => - pick(['kind', 'tags', 'content', 'created_at', 'pubkey', 'id'], e) + pick(["kind", "tags", "content", "created_at", "pubkey", "id"], e) export const asSignedEvent = (e: SignedEvent): SignedEvent => - pick(['kind', 'tags', 'content', 'created_at', 'pubkey', 'id', 'sig'], e) + pick(["kind", "tags", "content", "created_at", "pubkey", "id", "sig"], e) export const asUnwrappedEvent = (e: UnwrappedEvent): UnwrappedEvent => - pick(['kind', 'tags', 'content', 'created_at', 'pubkey', 'id', 'wrap'], e) + pick(["kind", "tags", "content", "created_at", "pubkey", "id", "wrap"], e) export const asTrustedEvent = (e: TrustedEvent): TrustedEvent => - pick(['kind', 'tags', 'content', 'created_at', 'pubkey', 'id', 'sig', 'wrap'], e) + pick(["kind", "tags", "content", "created_at", "pubkey", "id", "sig", "wrap"], e) const _hasValidSignature = cached({ maxSize: 10000, @@ -97,7 +103,7 @@ const _hasValidSignature = cached({ try { return [getEventHash(e), e.sig].join(":") } catch (err) { - return 'invalid' + return "invalid" } }, getValue: ([e]: [SignedEvent]) => { @@ -113,11 +119,12 @@ const _hasValidSignature = cached({ export const hasValidSignature = (e: SignedEvent) => e[verifiedSymbol] || _hasValidSignature(e) -export const getIdentifier = (e: EventTemplate) => e.tags.find(t => t[0] === 'd')?.[1] +export const getIdentifier = (e: EventTemplate) => e.tags.find(t => t[0] === "d")?.[1] -export const getIdOrAddress = (e: HashedEvent) => isReplaceable(e) ? getAddress(e) : e.id +export const getIdOrAddress = (e: HashedEvent) => (isReplaceable(e) ? getAddress(e) : e.id) -export const getIdAndAddress = (e: HashedEvent) => isReplaceable(e) ? [e.id, getAddress(e)] : [e.id] +export const getIdAndAddress = (e: HashedEvent) => + isReplaceable(e) ? [e.id, getAddress(e)] : [e.id] export const isEphemeral = (e: EventTemplate) => isEphemeralKind(e.kind) @@ -125,7 +132,8 @@ export const isReplaceable = (e: EventTemplate) => isReplaceableKind(e.kind) export const isPlainReplaceable = (e: EventTemplate) => isPlainReplaceableKind(e.kind) -export const isParameterizedReplaceable = (e: EventTemplate) => isParameterizedReplaceableKind(e.kind) +export const isParameterizedReplaceable = (e: EventTemplate) => + isParameterizedReplaceableKind(e.kind) export const isChildOf = (child: EventContent, parent: HashedEvent) => { const {roots, replies} = getAncestorTagValues(child.tags) diff --git a/packages/util/src/Filters.ts b/packages/util/src/Filters.ts index 3b29476..803a81f 100644 --- a/packages/util/src/Filters.ts +++ b/packages/util/src/Filters.ts @@ -1,8 +1,19 @@ -import {matchFilter as nostrToolsMatchFilter} from 'nostr-tools' -import {without, uniqBy, prop, mapVals, shuffle, avg, hash, groupBy, randomId, uniq} from '@welshman/lib' -import type {HashedEvent, TrustedEvent, SignedEvent} from './Events' -import {isReplaceableKind} from './Kinds' -import {Address, getAddress} from './Address' +import {matchFilter as nostrToolsMatchFilter} from "nostr-tools/filter" +import { + without, + uniqBy, + prop, + mapVals, + shuffle, + avg, + hash, + groupBy, + randomId, + uniq, +} from "@welshman/lib" +import type {HashedEvent, TrustedEvent, SignedEvent} from "./Events.js" +import {isReplaceableKind} from "./Kinds.js" +import {Address, getAddress} from "./Address.js" export const EPOCH = 1609459200 @@ -58,12 +69,12 @@ export const getFilterId = (filter: Filter) => { const parts = [] for (const k of keys) { const v = filter[k as keyof Filter] - const s = Array.isArray(v) ? v.join(',') : v + const s = Array.isArray(v) ? v.join(",") : v - parts.push([k, s].join(':')) + parts.push([k, s].join(":")) } - return hash(parts.join('|')) + return hash(parts.join("|")) } export const calculateFilterGroup = ({since, until, limit, search, ...filter}: Filter) => { @@ -107,13 +118,13 @@ export const intersectFilters = (groups: Filter[][]) => { const f3: Filter = {} for (const k of uniq([...Object.keys(f1), ...Object.keys(f2)]) as (keyof Filter)[]) { - if (k === 'since' || k === 'limit') { + if (k === "since" || k === "limit") { f3[k] = Math.max(f1[k] || 0, f2[k] || 0) - } else if (k === 'until') { + } else if (k === "until") { f3[k] = Math.min(f1[k] || f2[k] || 0, f2[k] || f1[k] || 0) - } else if (k === 'search') { + } else if (k === "search") { if (f1[k] && f2[k] && f1[k] !== f2[k]) { - f3[k] = [f1[k], f2[k]].join(' ') + f3[k] = [f1[k], f2[k]].join(" ") } else { f3[k] = f1[k] || f2[k] } @@ -186,7 +197,6 @@ export const getReplyFilters = (events: TrustedEvent[], filter: Filter = {}) => return filters } - export const addRepostFilters = (filters: Filter[]) => filters.flatMap(original => { const filterChunk = [original] @@ -239,6 +249,9 @@ export const getFilterResultCardinality = (filter: Filter) => { } export const trimFilter = (filter: Filter): Filter => - mapVals(v => Array.isArray(v) && v.length > 1000 ? shuffle(v as string[]).slice(0, 1000) : v, filter) as Filter + mapVals( + v => (Array.isArray(v) && v.length > 1000 ? shuffle(v as string[]).slice(0, 1000) : v), + filter, + ) as Filter export const trimFilters = (filters: Filter[]) => filters.map(trimFilter) diff --git a/packages/util/src/Handler.ts b/packages/util/src/Handler.ts index 2f80cb1..a3ab0ab 100644 --- a/packages/util/src/Handler.ts +++ b/packages/util/src/Handler.ts @@ -1,7 +1,7 @@ import {fromPairs, last, first, parseJson} from "@welshman/lib" -import {getAddress} from "./Address" -import {getAddressTags, getKindTagValues} from "./Tags" -import type {TrustedEvent} from "./Events" +import {getAddress} from "./Address.js" +import {getAddressTags, getKindTagValues} from "./Tags.js" +import type {TrustedEvent} from "./Events.js" export type Handler = { kind: number @@ -32,8 +32,12 @@ export const readHandlers = (event: TrustedEvent) => { return [] } - return getKindTagValues(event.tags) - .map(kind => ({...normalizedMeta, kind, identifier, event})) as Handler[] + return getKindTagValues(event.tags).map(kind => ({ + ...normalizedMeta, + kind, + identifier, + event, + })) as Handler[] } export const getHandlerKey = (handler: Handler) => `${handler.kind}:${getAddress(handler.event)}` diff --git a/packages/util/src/Kinds.ts b/packages/util/src/Kinds.ts index e227948..b8b99a1 100644 --- a/packages/util/src/Kinds.ts +++ b/packages/util/src/Kinds.ts @@ -1,12 +1,16 @@ -import {kinds} from 'nostr-tools' -import {between} from '@welshman/lib' +import { + isRegularKind, + isEphemeralKind, + isReplaceableKind as isPlainReplaceableKind, + isParameterizedReplaceableKind, +} from "nostr-tools/kinds" +import {between} from "@welshman/lib" + +export {isRegularKind, isEphemeralKind, isPlainReplaceableKind, isParameterizedReplaceableKind} -export const isRegularKind = kinds.isRegularKind -export const isEphemeralKind = kinds.isEphemeralKind -export const isPlainReplaceableKind = kinds.isReplaceableKind -export const isParameterizedReplaceableKind = kinds.isParameterizedReplaceableKind export const isReplaceableKind = (kind: number) => isPlainReplaceableKind(kind) || isParameterizedReplaceableKind(kind) + export const isDVMKind = (kind: number) => between([4999, 7001], kind) export const PROFILE = 0 @@ -162,4 +166,3 @@ export const GROUP_ADMINS = 39001 export const DEPRECATED_RELAY_RECOMMENDATION = 2 export const DEPRECATED_DIRECT_MESSAGE = 4 export const DEPRECATED_NAMED_GENERIC = 30001 - diff --git a/packages/util/src/Links.ts b/packages/util/src/Links.ts index e8bd226..9c9e702 100644 --- a/packages/util/src/Links.ts +++ b/packages/util/src/Links.ts @@ -1,3 +1,3 @@ export const fromNostrURI = (s: string) => s.replace(/^nostr:\/?\/?/, "") -export const toNostrURI = (s: string) => s.startsWith('nostr:') ? s : `nostr:${s}` +export const toNostrURI = (s: string) => (s.startsWith("nostr:") ? s : `nostr:${s}`) diff --git a/packages/util/src/List.ts b/packages/util/src/List.ts index d44e238..26554e6 100644 --- a/packages/util/src/List.ts +++ b/packages/util/src/List.ts @@ -1,9 +1,9 @@ import {parseJson, nthEq} from "@welshman/lib" -import {Address} from "./Address" -import {uniqTags} from "./Tags" -import {isShareableRelayUrl} from "./Relay" -import {Encryptable, DecryptedEvent} from "./Encryptable" -import type {EncryptableUpdates} from "./Encryptable" +import {Address} from "./Address.js" +import {uniqTags} from "./Tags.js" +import {isShareableRelayUrl} from "./Relay.js" +import {Encryptable, DecryptedEvent} from "./Encryptable.js" +import type {EncryptableUpdates} from "./Encryptable.js" export type ListParams = { kind: number @@ -19,8 +19,11 @@ export type PublishedList = Omit & { event: DecryptedEvent } -export const makeList = (list: ListParams & Partial): List => - ({publicTags: [], privateTags: [], ...list}) +export const makeList = (list: ListParams & Partial): List => ({ + publicTags: [], + privateTags: [], + ...list, +}) const isValidTag = (tag: string[]) => { if (tag[0] === "p") return tag[1]?.length === 64 @@ -41,8 +44,10 @@ export const readList = (event: DecryptedEvent): PublishedList => { return {event, kind: event.kind, publicTags, privateTags} } -export const getListTags = (list: List | undefined) => - [...list?.publicTags || [], ...list?.privateTags || []] +export const getListTags = (list: List | undefined) => [ + ...(list?.publicTags || []), + ...(list?.privateTags || []), +] export const removeFromListByPredicate = (list: List, pred: (t: string[]) => boolean) => { const plaintext: EncryptableUpdates = {} diff --git a/packages/util/src/Profile.ts b/packages/util/src/Profile.ts index e803292..89e3cdb 100644 --- a/packages/util/src/Profile.ts +++ b/packages/util/src/Profile.ts @@ -1,8 +1,8 @@ -import {nip19} from "nostr-tools" +import {npubEncode} from "nostr-tools/nip19" import {ellipsize, parseJson} from "@welshman/lib" -import {TrustedEvent} from "./Events" -import {getLnUrl} from './Zaps' -import {PROFILE} from "./Kinds" +import {TrustedEvent} from "./Events.js" +import {getLnUrl} from "./Zaps.js" +import {PROFILE} from "./Kinds.js" export type Profile = { name?: string @@ -27,13 +27,15 @@ export const isPublishedProfile = (profile: Profile): profile is PublishedProfil export const makeProfile = (profile: Partial = {}): Profile => { const address = profile.lud06 || profile.lud16 - const lnurl = typeof address === 'string' ? getLnUrl(address) : null + const lnurl = typeof address === "string" ? getLnUrl(address) : null return lnurl ? {lnurl, ...profile} : profile } -export const readProfile = (event: TrustedEvent): PublishedProfile => - ({...makeProfile(parseJson(event.content) || {}), event}) +export const readProfile = (event: TrustedEvent): PublishedProfile => ({ + ...makeProfile(parseJson(event.content) || {}), + event, +}) export const createProfile = ({event, ...profile}: Profile) => ({ kind: PROFILE, @@ -47,7 +49,7 @@ export const editProfile = ({event, ...profile}: PublishedProfile) => ({ }) export const displayPubkey = (pubkey: string) => { - const d = nip19.npubEncode(pubkey) + const d = npubEncode(pubkey) return d.slice(0, 8) + "…" + d.slice(-5) } diff --git a/packages/util/src/Relay.ts b/packages/util/src/Relay.ts index 3e89110..8d047d5 100644 --- a/packages/util/src/Relay.ts +++ b/packages/util/src/Relay.ts @@ -1,8 +1,8 @@ -import {last, Emitter, normalizeUrl, sleep, stripProtocol} from '@welshman/lib' -import {matchFilters} from './Filters' -import type {Repository} from './Repository' -import type {Filter} from './Filters' -import type {HashedEvent, TrustedEvent} from './Events' +import {last, Emitter, normalizeUrl, sleep, stripProtocol} from "@welshman/lib" +import {matchFilters} from "./Filters.js" +import type {Repository} from "./Repository.js" +import type {Filter} from "./Filters.js" +import type {HashedEvent, TrustedEvent} from "./Events.js" // Constants and types @@ -31,8 +31,8 @@ export type RelayProfile = { // Utils related to bare urls export const isRelayUrl = (url: string) => { - if (!url.includes('://')) { - url = 'wss://' + url + if (!url.includes("://")) { + url = "wss://" + url } try { @@ -47,16 +47,16 @@ export const isRelayUrl = (url: string) => { export const isShareableRelayUrl = (url: string) => Boolean( isRelayUrl(url) && - // Is it actually a websocket url and has a dot - url.match(/^wss:\/\/.+\..+/) && - // Don't match stuff with a port number - !url.slice(6).match(/:\d+/) && - // Don't match stuff with a numeric tld - !url.slice(6).match(/\.\d+\b/) && - // Don't match raw ip addresses - !url.slice(6).match(/\d+\.\d+\.\d+\.\d+/) && - // Skip nostr.wine's virtual relays - !url.slice(6).match(/\/npub/) + // Is it actually a websocket url and has a dot + url.match(/^wss:\/\/.+\..+/) && + // Don't match stuff with a port number + !url.slice(6).match(/:\d+/) && + // Don't match stuff with a numeric tld + !url.slice(6).match(/\.\d+\b/) && + // Don't match raw ip addresses + !url.slice(6).match(/\d+\.\d+\.\d+\.\d+/) && + // Skip nostr.wine's virtual relays + !url.slice(6).match(/\/npub/), ) export const normalizeRelayUrl = (url: string) => { @@ -78,7 +78,8 @@ export const normalizeRelayUrl = (url: string) => { export const displayRelayUrl = (url: string) => last(url.split("://")).replace(/\/$/, "") -export const displayRelayProfile = (profile?: RelayProfile, fallback = "") => profile?.name || fallback +export const displayRelayProfile = (profile?: RelayProfile, fallback = "") => + profile?.name || fallback // In-memory relay implementation backed by Repository @@ -90,10 +91,13 @@ export class Relay extends Emitter { } send(type: string, ...message: any[]) { - switch(type) { - case 'EVENT': return this.handleEVENT(message as [E]) - case 'CLOSE': return this.handleCLOSE(message as [string]) - case 'REQ': return this.handleREQ(message as [string, ...Filter[]]) + switch (type) { + case "EVENT": + return this.handleEVENT(message as [E]) + case "CLOSE": + return this.handleCLOSE(message as [string]) + case "REQ": + return this.handleREQ(message as [string, ...Filter[]]) } } @@ -101,13 +105,13 @@ export class Relay extends Emitter { this.repository.publish(event) // Callers generally expect async relays - sleep(1).then(() => { - this.emit('OK', event.id, true, "") + void sleep(1).then(() => { + this.emit("OK", event.id, true, "") if (!this.repository.isDeleted(event)) { for (const [subId, filters] of this.subs.entries()) { if (matchFilters(filters, event)) { - this.emit('EVENT', subId, event) + this.emit("EVENT", subId, event) } } } @@ -122,12 +126,12 @@ export class Relay extends Emitter { this.subs.set(subId, filters) // Callers generally expect async relays - sleep(1).then(() => { + void sleep(1).then(() => { for (const event of this.repository.query(filters)) { - this.emit('EVENT', subId, event) + this.emit("EVENT", subId, event) } - this.emit('EOSE', subId) + this.emit("EOSE", subId) }) } } diff --git a/packages/util/src/Repository.ts b/packages/util/src/Repository.ts index 98e3cf6..8c70807 100644 --- a/packages/util/src/Repository.ts +++ b/packages/util/src/Repository.ts @@ -1,10 +1,10 @@ -import {flatten, Emitter, sortBy, inc, chunk, uniq, omit, now, range, identity} from '@welshman/lib' -import {DELETE} from './Kinds' -import {EPOCH, matchFilter} from './Filters' -import {isReplaceable, isUnwrappedEvent} from './Events' -import {getAddress} from './Address' -import type {Filter} from './Filters' -import type {TrustedEvent, HashedEvent} from './Events' +import {flatten, Emitter, sortBy, inc, chunk, uniq, omit, now, range, identity} from "@welshman/lib" +import {DELETE} from "./Kinds.js" +import {EPOCH, matchFilter} from "./Filters.js" +import {isReplaceable, isUnwrappedEvent} from "./Events.js" +import {getAddress} from "./Address.js" +import type {Filter} from "./Filters.js" +import type {TrustedEvent, HashedEvent} from "./Events.js" export const DAY = 86400 @@ -69,22 +69,19 @@ export class Repository extends Emitter { removed.add(id) } - this.emit('update', {added, removed}) + this.emit("update", {added, removed}) } // API getEvent = (idOrAddress: string) => { - return idOrAddress.includes(':') + return idOrAddress.includes(":") ? this.eventsByAddress.get(idOrAddress) : this.eventsById.get(idOrAddress) } hasEvent = (event: E) => { - const duplicate = ( - this.eventsById.get(event.id) || - this.eventsByAddress.get(getAddress(event)) - ) + const duplicate = this.eventsById.get(event.id) || this.eventsByAddress.get(getAddress(event)) return duplicate && duplicate.created_at >= event.created_at } @@ -110,7 +107,7 @@ export class Repository extends Emitter { this._updateIndex(this.eventsByDay, getDay(event.created_at), undefined, event) this._updateIndex(this.eventsByAuthor, event.pubkey, undefined, event) - this.emit('update', {added: [], removed: [event.id]}) + this.emit("update", {added: [], removed: [event.id]}) } } @@ -121,27 +118,28 @@ export class Repository extends Emitter { if (filter.ids) { events = filter.ids!.map(id => this.eventsById.get(id)).filter(identity) as E[] - filter = omit(['ids'], filter) + filter = omit(["ids"], filter) } else if (filter.authors) { events = uniq(filter.authors!.flatMap(pubkey => this.eventsByAuthor.get(pubkey) || [])) - filter = omit(['authors'], filter) + filter = omit(["authors"], filter) } else if (filter.since || filter.until) { const sinceDay = getDay(filter.since || EPOCH) const untilDay = getDay(filter.until || now()) events = uniq( - Array.from(range(sinceDay, inc(untilDay))) - .flatMap((day: number) => this.eventsByDay.get(day) || []) + Array.from(range(sinceDay, inc(untilDay))).flatMap( + (day: number) => this.eventsByDay.get(day) || [], + ), ) } else { for (const [k, values] of Object.entries(filter)) { - if (!k.startsWith('#') || k.length !== 2) { + if (!k.startsWith("#") || k.length !== 2) { continue } filter = omit([k], filter) events = uniq( - (values as string[]).flatMap(v => this.eventsByTag.get(`${k[1]}:${v}`) || []) + (values as string[]).flatMap(v => this.eventsByTag.get(`${k[1]}:${v}`) || []), ) break @@ -218,7 +216,7 @@ export class Repository extends Emitter { // Update our tag indexes for (const tag of event.tags) { if (tag[0]?.length === 1) { - this._updateIndex(this.eventsByTag, tag.slice(0, 2).join(':'), event, duplicate) + this._updateIndex(this.eventsByTag, tag.slice(0, 2).join(":"), event, duplicate) // If this is a delete event, the tag value is an id or address. Track when it was // deleted so that replaceables can be restored. @@ -235,7 +233,7 @@ export class Repository extends Emitter { } if (shouldNotify) { - this.emit('update', {added: [event], removed}) + this.emit("update", {added: [event], removed}) } return true diff --git a/packages/util/src/Tags.ts b/packages/util/src/Tags.ts index d9af2db..4d0be08 100644 --- a/packages/util/src/Tags.ts +++ b/packages/util/src/Tags.ts @@ -1,6 +1,6 @@ import {uniq, uniqBy, mapVals, nth, nthEq, ensurePlural} from "@welshman/lib" -import {isRelayUrl, isShareableRelayUrl} from "./Relay" -import {Address} from "./Address" +import {isRelayUrl, isShareableRelayUrl} from "./Relay.js" +import {Address} from "./Address.js" export const getTags = (types: string | string[], tags: string[][]) => { types = ensurePlural(types) @@ -17,8 +17,7 @@ export const getTag = (types: string | string[], tags: string[][]) => { export const getTagValues = (types: string | string[], tags: string[][]) => getTags(types, tags).map(nth(1)) -export const getTagValue = (types: string | string[], tags: string[][]) => - getTag(types, tags)?.[1] +export const getTagValue = (types: string | string[], tags: string[][]) => getTag(types, tags)?.[1] export const getEventTags = (tags: string[][]) => tags.filter(t => ["e"].includes(t[0]) && t[1].length === 64) @@ -37,7 +36,8 @@ export const getPubkeyTagValues = (tags: string[][]) => getPubkeyTags(tags).map( export const getTopicTags = (tags: string[][]) => tags.filter(nthEq(0, "t")) -export const getTopicTagValues = (tags: string[][]) => getTopicTags(tags).map(t => t[1].replace(/^#/, '')) +export const getTopicTagValues = (tags: string[][]) => + getTopicTags(tags).map(t => t[1].replace(/^#/, "")) export const getRelayTags = (tags: string[][]) => tags.filter(t => ["r", "relay"].includes(t[0]) && isRelayUrl(t[1] || "")) diff --git a/packages/util/src/Zaps.ts b/packages/util/src/Zaps.ts index d866641..b9dec1f 100644 --- a/packages/util/src/Zaps.ts +++ b/packages/util/src/Zaps.ts @@ -1,5 +1,5 @@ -import {hexToBech32, fromPairs} from '@welshman/lib' -import type {TrustedEvent} from './Events' +import {hexToBech32, fromPairs} from "@welshman/lib" +import type {TrustedEvent} from "./Events.js" const DIVISORS = { m: BigInt(1e3), @@ -71,7 +71,7 @@ export const getLnUrl = (address: string) => { export type Zapper = { lnurl: string - pubkey?: string, + pubkey?: string callback?: string minSendable?: number maxSendable?: number @@ -81,7 +81,7 @@ export type Zapper = { export type Zap = { request: TrustedEvent - response: TrustedEvent, + response: TrustedEvent invoiceAmount: number } diff --git a/packages/util/src/index.ts b/packages/util/src/index.ts index 2a209ab..cb2a4c6 100644 --- a/packages/util/src/index.ts +++ b/packages/util/src/index.ts @@ -1,13 +1,13 @@ -export * from './Address' -export * from './Encryptable' -export * from './Events' -export * from './Filters' -export * from './Handler' -export * from './Kinds' -export * from './Links' -export * from './List' -export * from './Profile' -export * from './Relay' -export * from './Repository' -export * from './Tags' -export * from './Zaps' +export * from "./Address.js" +export * from "./Encryptable.js" +export * from "./Events.js" +export * from "./Filters.js" +export * from "./Handler.js" +export * from "./Kinds.js" +export * from "./Links.js" +export * from "./List.js" +export * from "./Profile.js" +export * from "./Relay.js" +export * from "./Repository.js" +export * from "./Tags.js" +export * from "./Zaps.js" diff --git a/packages/util/tsc-multi.json b/packages/util/tsc-multi.json deleted file mode 100644 index 6c37019..0000000 --- a/packages/util/tsc-multi.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "targets": [ - {"extname": ".cjs", "module": "commonjs"}, - {"extname": ".mjs", "module": "esnext", "moduleResolution": "node"} - ], - "projects": ["tsconfig.json"] -} diff --git a/packages/util/tsconfig.json b/packages/util/tsconfig.json index 669c5f4..3415cbd 100644 --- a/packages/util/tsconfig.json +++ b/packages/util/tsconfig.json @@ -3,9 +3,13 @@ "compilerOptions": { "rootDir": ".", "outDir": "build", - "esModuleInterop": true, + "module": "nodenext", + "moduleResolution": "nodenext", "skipLibCheck": true, "lib": ["esnext", "dom"] }, - "include": ["src/**/*.ts"] + "include": [ + "src/**/*.ts", + "test/**/*.ts" + ] } diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..909bf1e --- /dev/null +++ b/src/index.ts @@ -0,0 +1,26 @@ +console.log("Try npm run lint/fix!"); + +const longString = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer ut aliquet diam.'; + +const trailing = 'Semicolon' + + const why={am:'I tabbed?'}; + +const iWish = "I didn't have a trailing space..."; + +const sicilian = true;; + +const vizzini = (!!sicilian) ? !!!sicilian : sicilian; + +const re = /foo bar/; + +export function doSomeStuff(withThis: string, andThat: string, andThose: string[]) { + //function on one line + if(!Boolean(andThose.length)) {return false;} + console.log(withThis); + console.log(andThat); + console.dir(andThose); + console.log(longString, trailing, why, iWish, vizzini, re); + return; +} +// TODO: more examples