Browse Source

Refactor Nostr Client and Update Dependencies

- Replaced NDKPrivateKeySigner with PrivateKeySigner from applesauce-signers for improved signing functionality.
- Updated the Nostr client implementation to utilize nostr-tools for event management and connection pooling.
- Enhanced event fetching logic to support multiple versions of replaceable events based on limit parameters.
- Updated package dependencies in package.json and bun.lock, including the addition of applesauce-core and applesauce-signers.
- Refined event kind definitions and improved documentation for clarity and consistency with NIP specifications.
- Adjusted CSS styles in bundle.css for better visual consistency across components.
main
mleku 3 months ago
parent
commit
c5ff2c648c
No known key found for this signature in database
  1. 66
      app/handle-req.go
  2. 140
      app/web/bun.lock
  3. 2
      app/web/dist/bundle.css
  4. 37
      app/web/dist/bundle.js
  5. 2
      app/web/dist/bundle.js.map
  6. 900
      app/web/package-lock.json
  7. 4
      app/web/package.json
  8. 170
      app/web/src/App.svelte
  9. 239
      app/web/src/LoginModal.svelte
  10. 340
      app/web/src/nostr.js
  11. 80
      pkg/database/query-events.go
  12. 211
      pkg/encoders/kind/kind.go
  13. 146
      pkg/protocol/relayinfo/types.go

66
app/handle-req.go

@ -201,33 +201,13 @@ func (l *Listener) HandleReq(msg []byte) (err error) { @@ -201,33 +201,13 @@ func (l *Listener) HandleReq(msg []byte) (err error) {
// Process this chunk
var chunkEvents event.S
showAllVersions := false
if chunkFilter.Tags != nil {
if showAllTag := chunkFilter.Tags.GetFirst([]byte("show_all_versions")); showAllTag != nil {
if string(showAllTag.Value()) == "true" {
showAllVersions = true
}
}
}
if showAllVersions {
if chunkEvents, err = l.QueryAllVersions(queryCtx, chunkFilter); chk.E(err) {
if errors.Is(err, badger.ErrDBClosed) {
return
}
log.E.F("QueryAllVersions failed for chunk filter: %v", err)
err = nil
continue
}
} else {
if chunkEvents, err = l.QueryEvents(queryCtx, chunkFilter); chk.E(err) {
if errors.Is(err, badger.ErrDBClosed) {
return
}
log.E.F("QueryEvents failed for chunk filter: %v", err)
err = nil
continue
if chunkEvents, err = l.QueryEvents(queryCtx, chunkFilter); chk.E(err) {
if errors.Is(err, badger.ErrDBClosed) {
return
}
log.E.F("QueryEvents failed for chunk filter: %v", err)
err = nil
continue
}
// Add chunk results to overall results
@ -250,35 +230,13 @@ func (l *Listener) HandleReq(msg []byte) (err error) { @@ -250,35 +230,13 @@ func (l *Listener) HandleReq(msg []byte) (err error) {
}
}
var filterEvents event.S
// Check if the filter has the special "show_all_versions" tag
showAllVersions := false
if f.Tags != nil {
if showAllTag := f.Tags.GetFirst([]byte("show_all_versions")); showAllTag != nil {
if string(showAllTag.Value()) == "true" {
showAllVersions = true
log.T.F("REQ %s: detected show_all_versions tag, using QueryAllVersions", env.Subscription)
}
}
}
if showAllVersions {
if filterEvents, err = l.QueryAllVersions(queryCtx, f); chk.E(err) {
if errors.Is(err, badger.ErrDBClosed) {
return
}
log.E.F("QueryAllVersions failed for filter: %v", err)
err = nil
continue
}
} else {
if filterEvents, err = l.QueryEvents(queryCtx, f); chk.E(err) {
if errors.Is(err, badger.ErrDBClosed) {
return
}
log.E.F("QueryEvents failed for filter: %v", err)
err = nil
continue
if filterEvents, err = l.QueryEvents(queryCtx, f); chk.E(err) {
if errors.Is(err, badger.ErrDBClosed) {
return
}
log.E.F("QueryEvents failed for filter: %v", err)
err = nil
continue
}
// Append events from this filter to the overall collection
allEvents = append(allEvents, filterEvents...)

140
app/web/bun.lock

@ -4,7 +4,9 @@ @@ -4,7 +4,9 @@
"": {
"name": "svelte-app",
"dependencies": {
"@nostr-dev-kit/ndk": "^2.17.3",
"applesauce-core": "^4.1.0",
"applesauce-signers": "^4.1.0",
"nostr-tools": "^2.17.0",
"sirv-cli": "^2.0.0",
},
"devDependencies": {
@ -21,10 +23,6 @@ @@ -21,10 +23,6 @@
},
},
"packages": {
"@codesandbox/nodebox": ["@codesandbox/nodebox@0.1.8", "", { "dependencies": { "outvariant": "^1.4.0", "strict-event-emitter": "^0.4.3" } }, "sha512-2VRS6JDSk+M+pg56GA6CryyUSGPjBEe8Pnae0QL3jJF1mJZJVMDKr93gJRtBbLkfZN6LD/DwMtf+2L0bpWrjqg=="],
"@codesandbox/sandpack-client": ["@codesandbox/sandpack-client@2.19.8", "", { "dependencies": { "@codesandbox/nodebox": "0.1.8", "buffer": "^6.0.3", "dequal": "^2.0.2", "mime-db": "^1.52.0", "outvariant": "1.4.0", "static-browser-server": "1.0.3" } }, "sha512-CMV4nr1zgKzVpx4I3FYvGRM5YT0VaQhALMW9vy4wZRhEyWAtJITQIqZzrTGWqB1JvV7V72dVEUCUPLfYz5hgJQ=="],
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
@ -37,11 +35,11 @@ @@ -37,11 +35,11 @@
"@noble/ciphers": ["@noble/ciphers@0.5.3", "", {}, "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w=="],
"@noble/curves": ["@noble/curves@1.9.7", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw=="],
"@noble/curves": ["@noble/curves@1.2.0", "", { "dependencies": { "@noble/hashes": "1.3.2" } }, "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw=="],
"@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="],
"@noble/secp256k1": ["@noble/secp256k1@2.3.0", "", {}, "sha512-0TQed2gcBbIrh7Ccyw+y/uZQvbJwm7Ao4scBUxqpBCcsOlZG0O4KGfjtNAy/li4W8n1xt3dxrwJ0beZ2h2G6Kw=="],
"@noble/secp256k1": ["@noble/secp256k1@1.7.2", "", {}, "sha512-/qzwYl5eFLH8OWIecQWM31qld2g1NfjgylK+TNhqtaUKP37Nm+Y+z30Fjhw0Ct8p9yCQEm2N3W/AckdIb3SMcQ=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
@ -49,10 +47,6 @@ @@ -49,10 +47,6 @@
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
"@nostr-dev-kit/ndk": ["@nostr-dev-kit/ndk@2.17.3", "", { "dependencies": { "@codesandbox/sandpack-client": "^2.19.8", "@noble/curves": "^1.6.0", "@noble/hashes": "^1.5.0", "@noble/secp256k1": "^2.1.0", "@scure/base": "^1.1.9", "debug": "^4.3.6", "light-bolt11-decoder": "^3.2.0", "shiki": "^3.13.0", "tseep": "^1.3.1", "typescript-lru-cache": "^2" }, "peerDependencies": { "nostr-tools": "^2" } }, "sha512-CwOTRPxyOcxg5X4VEBzI7leA/bE7t4Yv9tZ6KpG4H4fDhuI6YXRbb9oKLG9KJqVOIbRrYT27sBF82Z6dE3B1qw=="],
"@open-draft/deferred-promise": ["@open-draft/deferred-promise@2.2.0", "", {}, "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA=="],
"@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="],
"@rollup/plugin-commonjs": ["@rollup/plugin-commonjs@24.1.0", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "commondir": "^1.0.1", "estree-walker": "^2.0.2", "glob": "^8.0.3", "is-reference": "1.2.1", "magic-string": "^0.27.0" }, "peerDependencies": { "rollup": "^2.68.0||^3.0.0" }, "optionalPeers": ["rollup"] }, "sha512-eSL45hjhCWI0jCCXcNtLVqM5N1JlBGvlFfY0m6oOYnLCJ6N0qEXoZql4sY2MOUArzhH4SA/qBpTxvvZp2Sc+DQ=="],
@ -69,72 +63,42 @@ @@ -69,72 +63,42 @@
"@scure/bip39": ["@scure/bip39@1.2.1", "", { "dependencies": { "@noble/hashes": "~1.3.0", "@scure/base": "~1.1.0" } }, "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg=="],
"@shikijs/core": ["@shikijs/core@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA=="],
"@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg=="],
"@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg=="],
"@shikijs/langs": ["@shikijs/langs@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ=="],
"@shikijs/themes": ["@shikijs/themes@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg=="],
"@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="],
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
"@types/fs-extra": ["@types/fs-extra@8.1.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-0dzKcwO+S8s2kuF5Z9oUWatQJj5Uq/iqphEtE3GQJVRRYm/tD1LglU2UnXi2A8jLq5umkGouOXOR9y0n613ZwQ=="],
"@types/glob": ["@types/glob@7.2.0", "", { "dependencies": { "@types/minimatch": "*", "@types/node": "*" } }, "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA=="],
"@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="],
"@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
"@types/minimatch": ["@types/minimatch@6.0.0", "", { "dependencies": { "minimatch": "*" } }, "sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA=="],
"@types/node": ["@types/node@24.7.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q=="],
"@types/resolve": ["@types/resolve@1.20.2", "", {}, "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="],
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
"anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
"applesauce-core": ["applesauce-core@4.1.0", "", { "dependencies": { "@noble/hashes": "^1.7.1", "@scure/base": "^1.2.4", "debug": "^4.4.0", "fast-deep-equal": "^3.1.3", "hash-sum": "^2.0.0", "light-bolt11-decoder": "^3.2.0", "nanoid": "^5.0.9", "nostr-tools": "~2.17", "rxjs": "^7.8.1" } }, "sha512-vFOHfqWW4DJfvPkMYLYNiy2ozO2IF+ZNwetGqaLuPjgE1Iwu4trZmG3GJUH+lO1Oq1N4e/OQ/EcotJoEBEiW7Q=="],
"applesauce-signers": ["applesauce-signers@4.1.0", "", { "dependencies": { "@noble/hashes": "^1.7.1", "@noble/secp256k1": "^1.7.1", "@scure/base": "^1.2.4", "applesauce-core": "^4.1.0", "debug": "^4.4.0", "nanoid": "^5.0.9", "nostr-tools": "~2.17", "rxjs": "^7.8.2" } }, "sha512-S+nTkAt1CAGhalwI7warLTINsxxjBpS3NqbViz6LVy1ZrzEqaNirlalX+rbCjxjRrvIGhYV+rszkxDFhCYbPkg=="],
"array-union": ["array-union@2.1.0", "", {}, "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="],
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
"binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
"brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
"buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
"ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
"character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="],
"character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="],
"chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
"colorette": ["colorette@1.4.0", "", {}, "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g=="],
"comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
"commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
"commondir": ["commondir@1.0.1", "", {}, "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="],
@ -147,16 +111,12 @@ @@ -147,16 +111,12 @@
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
"devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
"dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="],
"dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
"estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
"fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
"fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
@ -181,15 +141,9 @@ @@ -181,15 +141,9 @@
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
"hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="],
"hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="],
"hash-sum": ["hash-sum@2.0.0", "", {}, "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg=="],
"html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="],
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
@ -227,24 +181,10 @@ @@ -227,24 +181,10 @@
"magic-string": ["magic-string@0.27.0", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.13" } }, "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA=="],
"mdast-util-to-hast": ["mdast-util-to-hast@13.2.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA=="],
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
"micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="],
"micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="],
"micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="],
"micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="],
"micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="],
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
"mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
"minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
"mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="],
@ -253,6 +193,8 @@ @@ -253,6 +193,8 @@
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="],
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
"nostr-tools": ["nostr-tools@2.17.0", "", { "dependencies": { "@noble/ciphers": "^0.5.1", "@noble/curves": "1.2.0", "@noble/hashes": "1.3.1", "@scure/base": "1.1.1", "@scure/bip32": "1.3.1", "@scure/bip39": "1.2.1", "nostr-wasm": "0.1.0" }, "peerDependencies": { "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-lrvHM7cSaGhz7F0YuBvgHMoU2s8/KuThihDoOYk8w5gpVHTy0DeUCAgCN8uLGeuSl5MAWekJr9Dkfo5HClqO9w=="],
@ -261,14 +203,8 @@ @@ -261,14 +203,8 @@
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
"oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="],
"oniguruma-to-es": ["oniguruma-to-es@4.3.3", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg=="],
"opts": ["opts@2.0.2", "", {}, "sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg=="],
"outvariant": ["outvariant@1.4.0", "", {}, "sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw=="],
"path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="],
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
@ -277,20 +213,12 @@ @@ -277,20 +213,12 @@
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
"property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="],
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
"randombytes": ["randombytes@2.1.0", "", { "dependencies": { "safe-buffer": "^5.1.0" } }, "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ=="],
"readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
"regex": ["regex@6.0.1", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA=="],
"regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="],
"regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="],
"resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="],
"resolve.exports": ["resolve.exports@2.0.3", "", {}, "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A=="],
@ -309,6 +237,8 @@ @@ -309,6 +237,8 @@
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
"rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="],
"sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
@ -317,8 +247,6 @@ @@ -317,8 +247,6 @@
"serialize-javascript": ["serialize-javascript@6.0.2", "", { "dependencies": { "randombytes": "^2.1.0" } }, "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g=="],
"shiki": ["shiki@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/engine-javascript": "3.13.0", "@shikijs/engine-oniguruma": "3.13.0", "@shikijs/langs": "3.13.0", "@shikijs/themes": "3.13.0", "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g=="],
"sirv": ["sirv@2.0.4", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ=="],
"sirv-cli": ["sirv-cli@2.0.2", "", { "dependencies": { "console-clear": "^1.1.0", "get-port": "^3.2.0", "kleur": "^4.1.4", "local-access": "^1.0.1", "sade": "^1.6.0", "semiver": "^1.0.0", "sirv": "^2.0.0", "tinydate": "^1.0.0" }, "bin": { "sirv": "bin.js" } }, "sha512-OtSJDwxsF1NWHc7ps3Sa0s+dPtP15iQNJzfKVz+MxkEo3z72mCD+yu30ct79rPr0CaV1HXSOBp+MIY5uIhHZ1A=="],
@ -331,14 +259,6 @@ @@ -331,14 +259,6 @@
"source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="],
"space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
"static-browser-server": ["static-browser-server@1.0.3", "", { "dependencies": { "@open-draft/deferred-promise": "^2.1.0", "dotenv": "^16.0.3", "mime-db": "^1.52.0", "outvariant": "^1.3.0" } }, "sha512-ZUyfgGDdFRbZGGJQ1YhiM930Yczz5VlbJObrQLlk24+qNHVQx4OlLcYswEUo3bIyNAbQUIUR9Yr5/Hqjzqb4zA=="],
"strict-event-emitter": ["strict-event-emitter@0.4.6", "", {}, "sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg=="],
"stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
"svelte": ["svelte@3.59.2", "", {}, "sha512-vzSyuGr3eEoAtT/A6bmajosJZIUWySzY2CzB3w2pgPvnkUjGqlDnsNnA0PMO+mMAhuyMul6C2uuZzY6ELSkzyA=="],
@ -351,35 +271,17 @@ @@ -351,35 +271,17 @@
"totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="],
"trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
"tseep": ["tseep@1.3.1", "", {}, "sha512-ZPtfk1tQnZVyr7BPtbJ93qaAh2lZuIOpTMjhrYa4XctT8xe7t4SAW9LIxrySDuYMsfNNayE51E/WNGrNVgVicQ=="],
"typescript-lru-cache": ["typescript-lru-cache@2.0.0", "", {}, "sha512-Jp57Qyy8wXeMkdNuZiglE6v2Cypg13eDA1chHwDG6kq51X7gk4K7P7HaDdzZKCxkegXkVHNcPD0n5aW6OZH3aA=="],
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="],
"unist-util-is": ["unist-util-is@6.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw=="],
"unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="],
"unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="],
"unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
"unist-util-visit-parents": ["unist-util-visit-parents@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw=="],
"universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="],
"vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
"vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="],
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
"ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="],
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
"@noble/curves/@noble/hashes": ["@noble/hashes@1.3.2", "", {}, "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ=="],
"@scure/bip32/@noble/curves": ["@noble/curves@1.1.0", "", { "dependencies": { "@noble/hashes": "1.3.1" } }, "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA=="],
@ -399,8 +301,6 @@ @@ -399,8 +301,6 @@
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"nostr-tools/@noble/curves": ["@noble/curves@1.2.0", "", { "dependencies": { "@noble/hashes": "1.3.2" } }, "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw=="],
"nostr-tools/@noble/hashes": ["@noble/hashes@1.3.1", "", {}, "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA=="],
"nostr-tools/@scure/base": ["@scure/base@1.1.1", "", {}, "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA=="],
@ -413,8 +313,6 @@ @@ -413,8 +313,6 @@
"globby/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
"nostr-tools/@noble/curves/@noble/hashes": ["@noble/hashes@1.3.2", "", {}, "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ=="],
"rollup-plugin-svelte/@rollup/pluginutils/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"globby/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],

2
app/web/dist/bundle.css vendored

File diff suppressed because one or more lines are too long

37
app/web/dist/bundle.js vendored

File diff suppressed because one or more lines are too long

2
app/web/dist/bundle.js.map vendored

File diff suppressed because one or more lines are too long

900
app/web/package-lock.json generated

File diff suppressed because it is too large Load Diff

4
app/web/package.json

@ -20,7 +20,9 @@ @@ -20,7 +20,9 @@
"svelte": "^3.55.0"
},
"dependencies": {
"@nostr-dev-kit/ndk": "^2.17.3",
"applesauce-core": "^4.1.0",
"applesauce-signers": "^4.1.0",
"nostr-tools": "^2.17.0",
"sirv-cli": "^2.0.0"
}
}

170
app/web/src/App.svelte

@ -11,8 +11,9 @@ @@ -11,8 +11,9 @@
fetchDeleteEventsByTarget,
nostrClient,
NostrClient,
Nip07Signer,
PrivateKeySigner,
} from "./nostr.js";
import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
import { publishEventWithAuth } from "./websocket-auth.js";
let isDarkTheme = false;
@ -128,88 +129,101 @@ @@ -128,88 +129,101 @@
{ value: 34550, label: "Community Definition (34550)" },
];
// Kind name mapping based on repository kind definitions
// Kind name mapping based on NIP specification
// Matches official Nostr event kinds from https://github.com/nostr-protocol/nips
const kindNames = {
0: "ProfileMetadata",
1: "TextNote",
2: "RecommendRelay",
3: "FollowList",
4: "EncryptedDirectMessage",
5: "EventDeletion",
0: "User Metadata",
1: "Short Text Note",
2: "Recommend Relay",
3: "Follows",
4: "Encrypted Direct Messages",
5: "Event Deletion Request",
6: "Repost",
7: "Reaction",
8: "BadgeAward",
8: "Badge Award",
9: "Chat Message",
10: "Group Chat Threaded Reply",
11: "Thread",
12: "Group Thread Reply",
13: "Seal",
14: "PrivateDirectMessage",
15: "ReadReceipt",
16: "GenericRepost",
40: "ChannelCreation",
41: "ChannelMetadata",
42: "ChannelMessage",
43: "ChannelHideMessage",
44: "ChannelMuteUser",
14: "Direct Message",
15: "File Message",
16: "Generic Repost",
17: "Reaction to a website",
20: "Picture",
40: "Channel Creation",
41: "Channel Metadata",
42: "Channel Message",
43: "Channel Hide Message",
44: "Channel Mute User",
1021: "Bid",
1022: "BidConfirmation",
1022: "Bid Confirmation",
1040: "OpenTimestamps",
1059: "GiftWrap",
1060: "GiftWrapWithKind4",
1063: "FileMetadata",
1311: "LiveChatMessage",
1517: "BitcoinBlock",
1808: "LiveStream",
1971: "ProblemTracker",
1063: "File Metadata",
1311: "Live Chat Message",
1971: "Problem Tracker",
1984: "Reporting",
1985: "Label",
4550: "CommunityPostApproval",
5000: "JobRequestStart",
5999: "JobRequestEnd",
6000: "JobResultStart",
6999: "JobResultEnd",
7000: "JobFeedback",
9041: "ZapGoal",
9734: "ZapRequest",
4550: "Community Post Approval",
5000: "Job Request",
5999: "Job Request",
6000: "Job Result",
6999: "Job Result",
7000: "Job Feedback",
9041: "Zap Goal",
9734: "Zap Request",
9735: "Zap",
9882: "Highlights",
10000: "BlockList",
10001: "PinList",
10002: "RelayListMetadata",
10003: "BookmarkList",
10004: "CommunitiesList",
10005: "PublicChatsList",
10006: "BlockedRelaysList",
10007: "SearchRelaysList",
10015: "InterestsList",
10030: "UserEmojiList",
10050: "DMRelaysList",
10096: "FileStorageServerList",
13004: "JWTBinding",
13194: "NWCWalletServiceInfo",
19999: "ReplaceableEnd",
20000: "EphemeralStart",
21000: "LightningPubRPC",
22242: "ClientAuthentication",
23194: "WalletRequest",
23195: "WalletResponse",
23196: "WalletNotificationNip4",
23197: "WalletNotification",
24133: "NostrConnect",
27235: "HTTPAuth",
29999: "EphemeralEnd",
30000: "FollowSets",
30001: "GenericLists",
30002: "RelaySets",
30003: "BookmarkSets",
30004: "CurationSets",
30008: "ProfileBadges",
30009: "BadgeDefinition",
30015: "InterestSets",
30017: "StallDefinition",
30018: "ProductDefinition",
30019: "MarketplaceUIUX",
30020: "ProductSoldAsAuction",
30023: "LongFormContent",
30024: "DraftLongFormContent",
30030: "EmojiSets",
10000: "Mute list",
10001: "Pin list",
10002: "Relay List Metadata",
10003: "Bookmarks list",
10004: "Communities list",
10005: "Public Chats list",
10006: "Blocked Relays list",
10007: "Search Relays list",
10015: "Interests",
10030: "User Emoji list",
10050: "DM relays",
10096: "File Storage Server List",
13194: "Wallet Service Info",
21000: "Lightning pub RPC",
22242: "Client Authentication",
23194: "Wallet Request",
23195: "Wallet Response",
23196: "Wallet Notification",
23197: "Wallet Notification",
24133: "Nostr Connect",
27235: "HTTP Auth",
30000: "Follow sets",
30001: "Generic lists",
30002: "Relay sets",
30003: "Bookmark sets",
30004: "Curation sets",
30008: "Profile Badges",
30009: "Badge Definition",
30015: "Interest sets",
30017: "Stall Definition",
30018: "Product Definition",
30019: "Marketplace UI/UX",
30020: "Product sold as an auction",
30023: "Long-form Content",
30024: "Draft Long-form Content",
30030: "Emoji sets",
30078: "Application-specific Data",
30311: "Live Event",
30315: "User Statuses",
30402: "Classified Listing",
30403: "Draft Classified Listing",
31922: "Date-Based Calendar Event",
31923: "Time-Based Calendar Event",
31924: "Calendar",
31925: "Calendar Event RSVP",
31989: "Handler recommendation",
31990: "Handler information",
34235: "Video Event Horizontal",
34236: "Video Event Vertical",
34550: "Community Definition",
};
function getKindName(kind) {
@ -663,12 +677,12 @@ @@ -663,12 +677,12 @@
);
isLoadingRecovery = true;
try {
// Always fetch all versions for replaceable kinds, then filter in the UI
// Fetch multiple versions using limit parameter
// For replaceable events, limit > 1 returns multiple versions
const filters = {
kinds: [kindToUse],
authors: [userPubkey],
limit: 100,
tags: [["show_all_versions", "true"]], // Always get all versions
limit: 100, // Get up to 100 versions
};
if (recoveryOldestTimestamp) {
@ -1358,8 +1372,8 @@ @@ -1358,8 +1372,8 @@
nostrClient.setSigner(signer);
} else if (method === "nsec" && privateKey) {
// Private key signer for nsec
const ndkSigner = new NDKPrivateKeySigner(privateKey);
nostrClient.setSigner(ndkSigner);
const keySigner = new PrivateKeySigner(privateKey);
nostrClient.setSigner(keySigner);
}
userProfile = await fetchUserProfile(pubkey);

239
app/web/src/LoginModal.svelte

@ -1,58 +1,60 @@ @@ -1,58 +1,60 @@
<script>
import { createEventDispatcher } from 'svelte';
import { NDKPrivateKeySigner } from '@nostr-dev-kit/ndk';
import { createEventDispatcher } from "svelte";
import { PrivateKeySigner } from "./nostr.js";
const dispatch = createEventDispatcher();
export let showModal = false;
export let isDarkTheme = false;
let activeTab = 'extension';
let nsecInput = '';
let activeTab = "extension";
let nsecInput = "";
let isLoading = false;
let errorMessage = '';
let successMessage = '';
let errorMessage = "";
let successMessage = "";
function closeModal() {
showModal = false;
nsecInput = '';
errorMessage = '';
successMessage = '';
dispatch('close');
nsecInput = "";
errorMessage = "";
successMessage = "";
dispatch("close");
}
function switchTab(tab) {
activeTab = tab;
errorMessage = '';
successMessage = '';
errorMessage = "";
successMessage = "";
}
async function loginWithExtension() {
isLoading = true;
errorMessage = '';
successMessage = '';
errorMessage = "";
successMessage = "";
try {
// Check if window.nostr is available
if (!window.nostr) {
throw new Error('No Nostr extension found. Please install a NIP-07 compatible extension like nos2x or Alby.');
throw new Error(
"No Nostr extension found. Please install a NIP-07 compatible extension like nos2x or Alby.",
);
}
// Get public key from extension
const pubkey = await window.nostr.getPublicKey();
if (pubkey) {
// Store authentication info
localStorage.setItem('nostr_auth_method', 'extension');
localStorage.setItem('nostr_pubkey', pubkey);
successMessage = 'Successfully logged in with extension!';
dispatch('login', {
method: 'extension',
localStorage.setItem("nostr_auth_method", "extension");
localStorage.setItem("nostr_pubkey", pubkey);
successMessage = "Successfully logged in with extension!";
dispatch("login", {
method: "extension",
pubkey: pubkey,
signer: window.nostr
signer: window.nostr,
});
setTimeout(() => {
closeModal();
}, 1500);
@ -63,10 +65,10 @@ @@ -63,10 +65,10 @@
isLoading = false;
}
}
function validateNsec(nsec) {
// Basic validation for nsec format
if (!nsec.startsWith('nsec1')) {
if (!nsec.startsWith("nsec1")) {
return false;
}
// Should be around 63 characters long
@ -75,7 +77,7 @@ @@ -75,7 +77,7 @@
}
return true;
}
function nsecToHex(nsec) {
// This is a simplified conversion - in a real app you'd use a proper library
// For demo purposes, we'll simulate the conversion
@ -84,45 +86,45 @@ @@ -84,45 +86,45 @@
const withoutPrefix = nsec.slice(5);
// In reality, you'd use bech32 decoding here
// For now, we'll generate a mock hex key
return 'mock_' + withoutPrefix.slice(0, 32);
return "mock_" + withoutPrefix.slice(0, 32);
} catch (error) {
throw new Error('Invalid nsec format');
throw new Error("Invalid nsec format");
}
}
async function loginWithNsec() {
isLoading = true;
errorMessage = '';
successMessage = '';
errorMessage = "";
successMessage = "";
try {
if (!nsecInput.trim()) {
throw new Error('Please enter your nsec');
throw new Error("Please enter your nsec");
}
if (!validateNsec(nsecInput.trim())) {
throw new Error('Invalid nsec format. Must start with "nsec1"');
}
// Create NDK signer from nsec
const signer = new NDKPrivateKeySigner(nsecInput.trim());
// Create PrivateKeySigner from nsec
const signer = PrivateKeySigner.fromKey(nsecInput.trim());
// Get the public key from the signer
const publicKey = await signer.user().then(user => user.pubkey);
const publicKey = await signer.getPublicKey();
// Store securely (in production, consider more secure storage)
localStorage.setItem('nostr_auth_method', 'nsec');
localStorage.setItem('nostr_pubkey', publicKey);
localStorage.setItem('nostr_privkey', nsecInput.trim());
successMessage = 'Successfully logged in with nsec!';
dispatch('login', {
method: 'nsec',
localStorage.setItem("nostr_auth_method", "nsec");
localStorage.setItem("nostr_pubkey", publicKey);
localStorage.setItem("nostr_privkey", nsecInput.trim());
successMessage = "Successfully logged in with nsec!";
dispatch("login", {
method: "nsec",
pubkey: publicKey,
privateKey: nsecInput.trim(),
signer: signer
signer: signer,
});
setTimeout(() => {
closeModal();
}, 1500);
@ -132,12 +134,12 @@ @@ -132,12 +134,12 @@
isLoading = false;
}
}
function handleKeydown(event) {
if (event.key === 'Escape') {
if (event.key === "Escape") {
closeModal();
}
if (event.key === 'Enter' && activeTab === 'nsec') {
if (event.key === "Enter" && activeTab === "nsec") {
loginWithNsec();
}
}
@ -146,69 +148,92 @@ @@ -146,69 +148,92 @@
<svelte:window on:keydown={handleKeydown} />
{#if showModal}
<div class="modal-overlay" on:click={closeModal} on:keydown={(e) => e.key === 'Escape' && closeModal()} role="button" tabindex="0">
<div class="modal" class:dark-theme={isDarkTheme} on:click|stopPropagation on:keydown|stopPropagation>
<div
class="modal-overlay"
on:click={closeModal}
on:keydown={(e) => e.key === "Escape" && closeModal()}
role="button"
tabindex="0"
>
<div
class="modal"
class:dark-theme={isDarkTheme}
on:click|stopPropagation
on:keydown|stopPropagation
>
<div class="modal-header">
<h2>Login to Nostr</h2>
<button class="close-btn" on:click={closeModal}>&times;</button>
</div>
<div class="tab-container">
<div class="tabs">
<button
<button
class="tab-btn"
class:active={activeTab === 'extension'}
on:click={() => switchTab('extension')}
class:active={activeTab === "extension"}
on:click={() => switchTab("extension")}
>
Extension
</button>
<button
<button
class="tab-btn"
class:active={activeTab === 'nsec'}
on:click={() => switchTab('nsec')}
class:active={activeTab === "nsec"}
on:click={() => switchTab("nsec")}
>
Nsec
</button>
</div>
<div class="tab-content">
{#if activeTab === 'extension'}
{#if activeTab === "extension"}
<div class="extension-login">
<p>Login using a NIP-07 compatible browser extension like nos2x or Alby.</p>
<button
<p>
Login using a NIP-07 compatible browser
extension like nos2x or Alby.
</p>
<button
class="login-extension-btn"
on:click={loginWithExtension}
disabled={isLoading}
>
{isLoading ? 'Connecting...' : 'Log in using extension'}
{isLoading
? "Connecting..."
: "Log in using extension"}
</button>
</div>
{:else}
<div class="nsec-login">
<p>Enter your nsec (private key) to login. This will be stored securely in your browser.</p>
<input
<p>
Enter your nsec (private key) to login. This
will be stored securely in your browser.
</p>
<input
type="password"
placeholder="nsec1..."
bind:value={nsecInput}
disabled={isLoading}
class="nsec-input"
/>
<button
<button
class="login-nsec-btn"
on:click={loginWithNsec}
disabled={isLoading || !nsecInput.trim()}
>
{isLoading ? 'Logging in...' : 'Log in with nsec'}
{isLoading
? "Logging in..."
: "Log in with nsec"}
</button>
</div>
{/if}
{#if errorMessage}
<div class="message error-message">{errorMessage}</div>
{/if}
{#if successMessage}
<div class="message success-message">{successMessage}</div>
<div class="message success-message">
{successMessage}
</div>
{/if}
</div>
</div>
@ -229,7 +254,7 @@ @@ -229,7 +254,7 @@
align-items: center;
z-index: 1000;
}
.modal {
background: var(--bg-color);
border-radius: 8px;
@ -240,7 +265,7 @@ @@ -240,7 +265,7 @@
overflow-y: auto;
border: 1px solid var(--border-color);
}
.modal-header {
display: flex;
justify-content: space-between;
@ -248,13 +273,13 @@ @@ -248,13 +273,13 @@
padding: 20px;
border-bottom: 1px solid var(--border-color);
}
.modal-header h2 {
margin: 0;
color: var(--text-color);
font-size: 1.5rem;
}
.close-btn {
background: none;
border: none;
@ -270,21 +295,21 @@ @@ -270,21 +295,21 @@
border-radius: 50%;
transition: background-color 0.2s;
}
.close-btn:hover {
background-color: var(--tab-hover-bg);
}
.tab-container {
padding: 20px;
}
.tabs {
display: flex;
border-bottom: 1px solid var(--border-color);
margin-bottom: 20px;
}
.tab-btn {
flex: 1;
padding: 12px 16px;
@ -296,34 +321,34 @@ @@ -296,34 +321,34 @@
transition: all 0.2s;
border-bottom: 2px solid transparent;
}
.tab-btn:hover {
background-color: var(--tab-hover-bg);
}
.tab-btn.active {
border-bottom-color: var(--primary);
color: var(--primary);
}
.tab-content {
min-height: 200px;
}
.extension-login,
.nsec-login {
display: flex;
flex-direction: column;
gap: 16px;
}
.extension-login p,
.nsec-login p {
margin: 0;
color: var(--text-color);
line-height: 1.5;
}
.login-extension-btn,
.login-nsec-btn {
padding: 12px 24px;
@ -335,18 +360,18 @@ @@ -335,18 +360,18 @@
font-size: 1rem;
transition: background-color 0.2s;
}
.login-extension-btn:hover:not(:disabled),
.login-nsec-btn:hover:not(:disabled) {
background: #00ACC1;
background: #00acc1;
}
.login-extension-btn:disabled,
.login-nsec-btn:disabled {
background: #ccc;
cursor: not-allowed;
}
.nsec-input {
padding: 12px;
border: 1px solid var(--input-border);
@ -355,40 +380,40 @@ @@ -355,40 +380,40 @@
background: var(--bg-color);
color: var(--text-color);
}
.nsec-input:focus {
outline: none;
border-color: var(--primary);
}
.message {
padding: 10px;
border-radius: 4px;
margin-top: 16px;
text-align: center;
}
.error-message {
background: #ffebee;
color: #c62828;
border: 1px solid #ffcdd2;
}
.success-message {
background: #e8f5e8;
color: #2e7d32;
border: 1px solid #c8e6c9;
}
.modal.dark-theme .error-message {
background: #4a2c2a;
color: #ffcdd2;
border: 1px solid #6d4c41;
}
.modal.dark-theme .success-message {
background: #2e4a2e;
color: #a5d6a7;
border: 1px solid #4caf50;
}
</style>
</style>

340
app/web/src/nostr.js

@ -1,42 +1,41 @@ @@ -1,42 +1,41 @@
import NDK, { NDKPrivateKeySigner, NDKEvent } from '@nostr-dev-kit/ndk';
import { SimplePool } from 'nostr-tools/pool';
import { EventStore } from 'applesauce-core';
import { PrivateKeySigner } from 'applesauce-signers';
import { DEFAULT_RELAYS } from "./constants.js";
// NDK-based Nostr client wrapper
// Nostr client wrapper using nostr-tools
class NostrClient {
constructor() {
this.ndk = new NDK({
explicitRelayUrls: DEFAULT_RELAYS
});
this.pool = new SimplePool();
this.eventStore = new EventStore();
this.isConnected = false;
this.signer = null;
this.relays = [...DEFAULT_RELAYS];
}
async connect() {
console.log("Starting NDK connection to", DEFAULT_RELAYS.length, "relays...");
console.log("Starting connection to", this.relays.length, "relays...");
try {
await this.ndk.connect();
// SimplePool doesn't require explicit connect
this.isConnected = true;
console.log("✓ NDK successfully connected to relays");
console.log("✓ Successfully initialized relay pool");
// Wait a bit for connections to stabilize
await new Promise((resolve) => setTimeout(resolve, 1000));
} catch (error) {
console.error("✗ NDK connection failed:", error);
console.error("✗ Connection failed:", error);
throw error;
}
}
async connectToRelay(relayUrl) {
console.log(`Adding relay to NDK: ${relayUrl}`);
console.log(`Adding relay: ${relayUrl}`);
try {
// For now, just update the DEFAULT_RELAYS array and reconnect
// This is a simpler approach that avoids replacing the NDK instance
DEFAULT_RELAYS.push(relayUrl);
// Reconnect with the updated relay list
await this.connect();
if (!this.relays.includes(relayUrl)) {
this.relays.push(relayUrl);
}
console.log(`✓ Successfully added relay ${relayUrl}`);
return true;
} catch (error) {
@ -46,69 +45,76 @@ class NostrClient { @@ -46,69 +45,76 @@ class NostrClient {
}
subscribe(filters, callback) {
console.log("Creating NDK subscription with filters:", filters);
console.log("Creating subscription with filters:", filters);
const subscription = this.ndk.subscribe(filters, {
closeOnEose: true
});
subscription.on('event', (event) => {
console.log("Event received via NDK:", event);
callback(event.rawEvent());
});
subscription.on('eose', () => {
console.log("EOSE received via NDK");
window.dispatchEvent(new CustomEvent('nostr-eose', {
detail: { subscriptionId: subscription.id }
}));
});
const sub = this.pool.subscribeMany(
this.relays,
filters,
{
onevent(event) {
console.log("Event received:", event);
callback(event);
},
oneose() {
console.log("EOSE received");
window.dispatchEvent(new CustomEvent('nostr-eose', {
detail: { subscriptionId: sub.id }
}));
}
}
);
return subscription.id;
return sub;
}
unsubscribe(subscriptionId) {
console.log(`Closing NDK subscription: ${subscriptionId}`);
// NDK handles subscription cleanup automatically
unsubscribe(subscription) {
console.log(`Closing subscription`);
if (subscription && subscription.close) {
subscription.close();
}
}
disconnect() {
console.log("Disconnecting NDK");
// Note: NDK doesn't have a destroy method, just disconnect
if (this.ndk && typeof this.ndk.disconnect === 'function') {
this.ndk.disconnect();
console.log("Disconnecting relay pool");
if (this.pool) {
this.pool.close(this.relays);
}
this.isConnected = false;
}
// Publish an event using NDK
// Publish an event
async publish(event) {
console.log("Publishing event via NDK:", event);
console.log("Publishing event:", event);
try {
const ndkEvent = new NDKEvent(this.ndk, event);
await ndkEvent.publish();
console.log("✓ Event published successfully via NDK");
const promises = this.pool.publish(this.relays, event);
await Promise.allSettled(promises);
console.log("✓ Event published successfully");
return { success: true, okCount: 1, errorCount: 0 };
} catch (error) {
console.error("✗ Failed to publish event via NDK:", error);
console.error("✗ Failed to publish event:", error);
throw error;
}
}
// Get NDK instance for advanced usage
getNDK() {
return this.ndk;
// Get pool for advanced usage
getPool() {
return this.pool;
}
// Get signer from NDK
// Get event store
getEventStore() {
return this.eventStore;
}
// Get signer
getSigner() {
return this.ndk.signer;
return this.signer;
}
// Set signer for NDK
// Set signer
setSigner(signer) {
this.ndk.signer = signer;
this.signer = signer;
}
}
@ -118,6 +124,54 @@ export const nostrClient = new NostrClient(); @@ -118,6 +124,54 @@ export const nostrClient = new NostrClient();
// Export the class for creating new instances
export { NostrClient };
// Export signer classes
export { PrivateKeySigner };
// Export NIP-07 helper
export class Nip07Signer {
async getPublicKey() {
if (window.nostr) {
return await window.nostr.getPublicKey();
}
throw new Error('NIP-07 extension not found');
}
async signEvent(event) {
if (window.nostr) {
return await window.nostr.signEvent(event);
}
throw new Error('NIP-07 extension not found');
}
async nip04Encrypt(pubkey, plaintext) {
if (window.nostr && window.nostr.nip04) {
return await window.nostr.nip04.encrypt(pubkey, plaintext);
}
throw new Error('NIP-07 extension does not support NIP-04');
}
async nip04Decrypt(pubkey, ciphertext) {
if (window.nostr && window.nostr.nip04) {
return await window.nostr.nip04.decrypt(pubkey, ciphertext);
}
throw new Error('NIP-07 extension does not support NIP-04');
}
async nip44Encrypt(pubkey, plaintext) {
if (window.nostr && window.nostr.nip44) {
return await window.nostr.nip44.encrypt(pubkey, plaintext);
}
throw new Error('NIP-07 extension does not support NIP-44');
}
async nip44Decrypt(pubkey, ciphertext) {
if (window.nostr && window.nostr.nip44) {
return await window.nostr.nip44.decrypt(pubkey, ciphertext);
}
throw new Error('NIP-07 extension does not support NIP-44');
}
}
// IndexedDB helpers for caching events (kind 0 profiles)
const DB_NAME = "nostrCache";
const DB_VERSION = 1;
@ -209,7 +263,7 @@ function parseProfileFromEvent(event) { @@ -209,7 +263,7 @@ function parseProfileFromEvent(event) {
}
}
// Fetch user profile metadata (kind 0) using NDK
// Fetch user profile metadata (kind 0)
export async function fetchUserProfile(pubkey) {
console.log(`Starting profile fetch for pubkey: ${pubkey}`);
@ -225,29 +279,32 @@ export async function fetchUserProfile(pubkey) { @@ -225,29 +279,32 @@ export async function fetchUserProfile(pubkey) {
console.warn("Failed to load cached profile", e);
}
// 2) Fetch profile using NDK
// 2) Fetch profile from relays
try {
const ndk = nostrClient.getNDK();
const user = ndk.getUser({ hexpubkey: pubkey });
const filters = [{
kinds: [0],
authors: [pubkey],
limit: 1
}];
// Fetch the latest profile event
const profileEvent = await user.fetchProfile();
const events = await fetchEvents(filters, { timeout: 10000 });
if (profileEvent) {
console.log("Profile fetched via NDK:", profileEvent);
if (events.length > 0) {
const profileEvent = events[0];
console.log("Profile fetched:", profileEvent);
// Cache the event
await putEvent(profileEvent.rawEvent());
await putEvent(profileEvent);
// Parse profile data
const profile = parseProfileFromEvent(profileEvent.rawEvent());
const profile = parseProfileFromEvent(profileEvent);
// Notify listeners that an updated profile is available
try {
if (typeof window !== "undefined" && window.dispatchEvent) {
window.dispatchEvent(
new CustomEvent("profile-updated", {
detail: { pubkey, profile, event: profileEvent.rawEvent() },
detail: { pubkey, profile, event: profileEvent },
}),
);
}
@ -260,49 +317,53 @@ export async function fetchUserProfile(pubkey) { @@ -260,49 +317,53 @@ export async function fetchUserProfile(pubkey) {
throw new Error("No profile found");
}
} catch (error) {
console.error("Failed to fetch profile via NDK:", error);
console.error("Failed to fetch profile:", error);
throw error;
}
}
// Fetch events using NDK
// Fetch events
export async function fetchEvents(filters, options = {}) {
console.log(`Starting event fetch with filters:`, filters);
const {
timeout = 30000,
limit = null
} = options;
try {
const ndk = nostrClient.getNDK();
// Add limit to filters if specified
const requestFilters = { ...filters };
if (limit) {
requestFilters.limit = limit;
}
console.log('Fetching events via NDK with filters:', requestFilters);
// Use NDK's fetchEvents method
const events = await ndk.fetchEvents(requestFilters, {
timeout
});
return new Promise((resolve, reject) => {
const events = [];
const timeoutId = setTimeout(() => {
console.log(`Timeout reached after ${timeout}ms, returning ${events.length} events`);
sub.close();
resolve(events);
}, timeout);
console.log(`Fetched ${events.size} events via NDK`);
// Convert NDK events to raw events
const rawEvents = Array.from(events).map(event => event.rawEvent());
return rawEvents;
} catch (error) {
console.error("Failed to fetch events via NDK:", error);
throw error;
}
try {
const sub = nostrClient.pool.subscribeMany(
nostrClient.relays,
filters,
{
onevent(event) {
console.log("Event received:", event);
events.push(event);
},
oneose() {
console.log(`EOSE received, got ${events.length} events`);
clearTimeout(timeoutId);
sub.close();
resolve(events);
}
}
);
} catch (error) {
clearTimeout(timeoutId);
console.error("Failed to fetch events:", error);
reject(error);
}
});
}
// Fetch all events with timestamp-based pagination using NDK (including delete events)
// Fetch all events with timestamp-based pagination (including delete events)
export async function fetchAllEvents(options = {}) {
const {
limit = 100,
@ -310,28 +371,25 @@ export async function fetchAllEvents(options = {}) { @@ -310,28 +371,25 @@ export async function fetchAllEvents(options = {}) {
until = null,
authors = null,
kinds = null,
tags = null
...rest
} = options;
const filters = {};
if (since) filters.since = since;
if (until) filters.until = until;
if (authors) filters.authors = authors;
if (kinds) filters.kinds = kinds;
if (tags) filters.tags = tags;
const filters = [{ ...rest }];
// Don't specify kinds filter - this will include all events including delete events (kind 5)
if (since) filters[0].since = since;
if (until) filters[0].until = until;
if (authors) filters[0].authors = authors;
if (kinds) filters[0].kinds = kinds;
if (limit) filters[0].limit = limit;
const events = await fetchEvents(filters, {
limit: limit,
timeout: 30000
});
return events;
}
// Fetch user's events with timestamp-based pagination using NDK
// Fetch user's events with timestamp-based pagination
export async function fetchUserEvents(pubkey, options = {}) {
const {
limit = 100,
@ -339,22 +397,22 @@ export async function fetchUserEvents(pubkey, options = {}) { @@ -339,22 +397,22 @@ export async function fetchUserEvents(pubkey, options = {}) {
until = null
} = options;
const filters = {
const filters = [{
authors: [pubkey]
};
}];
if (since) filters.since = since;
if (until) filters.until = until;
if (since) filters[0].since = since;
if (until) filters[0].until = until;
if (limit) filters[0].limit = limit;
const events = await fetchEvents(filters, {
limit: limit,
timeout: 30000
});
return events;
}
// NIP-50 search function using NDK
// NIP-50 search function
export async function searchEvents(searchQuery, options = {}) {
const {
limit = 100,
@ -363,16 +421,16 @@ export async function searchEvents(searchQuery, options = {}) { @@ -363,16 +421,16 @@ export async function searchEvents(searchQuery, options = {}) {
kinds = null
} = options;
const filters = {
const filters = [{
search: searchQuery
};
}];
if (since) filters.since = since;
if (until) filters.until = until;
if (kinds) filters.kinds = kinds;
if (since) filters[0].since = since;
if (until) filters[0].until = until;
if (kinds) filters[0].kinds = kinds;
if (limit) filters[0].limit = limit;
const events = await fetchEvents(filters, {
limit: limit,
timeout: 30000
});
@ -383,39 +441,30 @@ export async function searchEvents(searchQuery, options = {}) { @@ -383,39 +441,30 @@ export async function searchEvents(searchQuery, options = {}) {
export async function fetchEventById(eventId, options = {}) {
const {
timeout = 10000,
relays = null
} = options;
console.log(`Fetching event by ID: ${eventId}`);
try {
const ndk = nostrClient.getNDK();
const filters = {
const filters = [{
ids: [eventId]
};
}];
console.log('Fetching event via NDK with filters:', filters);
console.log('Fetching event with filters:', filters);
// Use NDK's fetchEvents method
const events = await ndk.fetchEvents(filters, {
timeout
});
const events = await fetchEvents(filters, { timeout });
console.log(`Fetched ${events.size} events via NDK`);
// Convert NDK events to raw events
const rawEvents = Array.from(events).map(event => event.rawEvent());
console.log(`Fetched ${events.length} events`);
// Return the first event if found, null otherwise
return rawEvents.length > 0 ? rawEvents[0] : null;
return events.length > 0 ? events[0] : null;
} catch (error) {
console.error("Failed to fetch event by ID via NDK:", error);
console.error("Failed to fetch event by ID:", error);
throw error;
}
}
// Fetch delete events that target a specific event ID using Nostr
// Fetch delete events that target a specific event ID
export async function fetchDeleteEventsByTarget(eventId, options = {}) {
const {
timeout = 10000
@ -424,33 +473,24 @@ export async function fetchDeleteEventsByTarget(eventId, options = {}) { @@ -424,33 +473,24 @@ export async function fetchDeleteEventsByTarget(eventId, options = {}) {
console.log(`Fetching delete events for target: ${eventId}`);
try {
const ndk = nostrClient.getNDK();
const filters = {
const filters = [{
kinds: [5], // Kind 5 is deletion
'#e': [eventId] // e-tag referencing the target event
};
}];
console.log('Fetching delete events via NDK with filters:', filters);
console.log('Fetching delete events with filters:', filters);
// Use NDK's fetchEvents method
const events = await ndk.fetchEvents(filters, {
timeout
});
const events = await fetchEvents(filters, { timeout });
console.log(`Fetched ${events.size} delete events via NDK`);
// Convert NDK events to raw events
const rawEvents = Array.from(events).map(event => event.rawEvent());
console.log(`Fetched ${events.length} delete events`);
return rawEvents;
return events;
} catch (error) {
console.error("Failed to fetch delete events via NDK:", error);
console.error("Failed to fetch delete events:", error);
throw error;
}
}
// Initialize client connection
export async function initializeNostrClient() {
await nostrClient.connect();

80
pkg/database/query-events.go

@ -51,6 +51,10 @@ func (d *D) QueryAllVersions(c context.Context, f *filter.F) ( @@ -51,6 +51,10 @@ func (d *D) QueryAllVersions(c context.Context, f *filter.F) (
func (d *D) QueryEventsWithOptions(c context.Context, f *filter.F, includeDeleteEvents bool, showAllVersions bool) (
evs event.S, err error,
) {
// Determine if we should return multiple versions of replaceable events
// based on the limit parameter
wantMultipleVersions := showAllVersions || (f.Limit != nil && *f.Limit > 1)
// if there is Ids in the query, this overrides anything else
var expDeletes types.Uint40s
var expEvs event.S
@ -135,11 +139,15 @@ func (d *D) QueryEventsWithOptions(c context.Context, f *filter.F, includeDelete @@ -135,11 +139,15 @@ func (d *D) QueryEventsWithOptions(c context.Context, f *filter.F, includeDelete
return
}
// log.T.F("QueryEvents: QueryForIds returned %d candidates", len(idPkTs))
// Create a map to store the latest version of replaceable events
// Create a map to store versions of replaceable events
// If wantMultipleVersions is true, we keep multiple versions (sorted by timestamp)
// Otherwise, we keep only the latest
replaceableEvents := make(map[string]*event.E)
replaceableEventVersions := make(map[string]event.S) // For multiple versions
// Create a map to store the latest version of parameterized replaceable
// events
paramReplaceableEvents := make(map[string]map[string]*event.E)
paramReplaceableEventVersions := make(map[string]map[string]event.S) // For multiple versions
// Regular events that are not replaceable
var regularEvents event.S
// Map to track deletion events by kind and pubkey (for replaceable
@ -435,11 +443,11 @@ func (d *D) QueryEventsWithOptions(c context.Context, f *filter.F, includeDelete @@ -435,11 +443,11 @@ func (d *D) QueryEventsWithOptions(c context.Context, f *filter.F, includeDelete
if deletionsByKindPubkey[key] && !isIdInFilter {
// This replaceable event has been deleted, skip it
continue
} else if showAllVersions {
// If showAllVersions is true, treat replaceable events as regular events
regularEvents = append(regularEvents, ev)
} else if wantMultipleVersions {
// If wantMultipleVersions is true, collect all versions
replaceableEventVersions[key] = append(replaceableEventVersions[key], ev)
} else {
// Normal replaceable event handling
// Normal replaceable event handling - keep only the newest
existing, exists := replaceableEvents[key]
if !exists || ev.CreatedAt > existing.CreatedAt {
replaceableEvents[key] = ev
@ -469,9 +477,12 @@ func (d *D) QueryEventsWithOptions(c context.Context, f *filter.F, includeDelete @@ -469,9 +477,12 @@ func (d *D) QueryEventsWithOptions(c context.Context, f *filter.F, includeDelete
}
}
if showAllVersions {
// If showAllVersions is true, treat parameterized replaceable events as regular events
regularEvents = append(regularEvents, ev)
if wantMultipleVersions {
// If wantMultipleVersions is true, collect all versions
if _, exists := paramReplaceableEventVersions[key]; !exists {
paramReplaceableEventVersions[key] = make(map[string]event.S)
}
paramReplaceableEventVersions[key][dValue] = append(paramReplaceableEventVersions[key][dValue], ev)
} else {
// Initialize the inner map if it doesn't exist
if _, exists := paramReplaceableEvents[key]; !exists {
@ -496,14 +507,57 @@ func (d *D) QueryEventsWithOptions(c context.Context, f *filter.F, includeDelete @@ -496,14 +507,57 @@ func (d *D) QueryEventsWithOptions(c context.Context, f *filter.F, includeDelete
}
}
// Add all the latest replaceable events to the result
for _, ev := range replaceableEvents {
evs = append(evs, ev)
if wantMultipleVersions {
// Add all versions (sorted by timestamp, newest first)
for key, versions := range replaceableEventVersions {
// Sort versions by timestamp (newest first)
sort.Slice(versions, func(i, j int) bool {
return versions[i].CreatedAt > versions[j].CreatedAt
})
// Add versions up to the limit
limit := len(versions)
if f.Limit != nil && int(*f.Limit) < limit {
limit = int(*f.Limit)
}
for i := 0; i < limit && i < len(versions); i++ {
evs = append(evs, versions[i])
}
_ = key // Use key to avoid unused variable warning
}
} else {
// Add only the newest version of each replaceable event
for _, ev := range replaceableEvents {
evs = append(evs, ev)
}
}
// Add all the latest parameterized replaceable events to the result
for _, innerMap := range paramReplaceableEvents {
for _, ev := range innerMap {
evs = append(evs, ev)
if wantMultipleVersions {
// Add all versions (sorted by timestamp, newest first)
for key, dTagMap := range paramReplaceableEventVersions {
for dTag, versions := range dTagMap {
// Sort versions by timestamp (newest first)
sort.Slice(versions, func(i, j int) bool {
return versions[i].CreatedAt > versions[j].CreatedAt
})
// Add versions up to the limit
limit := len(versions)
if f.Limit != nil && int(*f.Limit) < limit {
limit = int(*f.Limit)
}
for i := 0; i < limit && i < len(versions); i++ {
evs = append(evs, versions[i])
}
_ = key // Use key to avoid unused variable warning
_ = dTag // Use dTag to avoid unused variable warning
}
}
} else {
// Add only the newest version of each parameterized replaceable event
for _, innerMap := range paramReplaceableEvents {
for _, ev := range innerMap {
evs = append(evs, ev)
}
}
}
// Add all regular events to the result

211
pkg/encoders/kind/kind.go

@ -73,7 +73,8 @@ var Privileged = []*K{ @@ -73,7 +73,8 @@ var Privileged = []*K{
JWTBinding,
ApplicationSpecificData,
Seal,
PrivateDirectMessage,
DirectMessage,
FileMessage,
}
// IsPrivileged returns true if the type is the kind of message nobody else than
@ -165,37 +166,50 @@ var ( @@ -165,37 +166,50 @@ var (
// TextNote is a standard short text note of plain text a la twitter
TextNote = &K{1}
// RecommendServer is an event type that...
RecommendServer = &K{2}
RecommendRelay = &K{2}
RecommendRelay = &K{2}
// FollowList an event containing a list of pubkeys of users that should be
// shown as follows in a timeline.
FollowList = &K{3}
Follows = &K{3}
// EncryptedDirectMessage is an event type that...
EncryptedDirectMessage = &K{4}
// Deletion is an event type that...
Deletion = &K{5}
// EventDeletion is an event type that...
EventDeletion = &K{5}
Deletion = &K{5}
// Repost is an event type that...
Repost = &K{6}
// Reaction is an event type that...
Reaction = &K{7}
// BadgeAward is an event type
BadgeAward = &K{8}
// ChatMessage is an event type for NIP-C7 chat messages
ChatMessage = &K{9}
// GroupChatThreadedReply is deprecated
GroupChatThreadedReply = &K{10}
// Thread is an event type for NIP-7D threads
Thread = &K{11}
// GroupThreadReply is deprecated
GroupThreadReply = &K{12}
// Seal is an event that wraps a PrivateDirectMessage and is placed inside a
// GiftWrap or GiftWrapWithKind4
Seal = &K{13}
// PrivateDirectMessage is a nip-17 direct message with a different
// DirectMessage is a nip-17 direct message with a different
// construction. It doesn't actually appear as an event a relay might receive
// but only as the stringified content of a GiftWrap or GiftWrapWithKind4 inside
// a
PrivateDirectMessage = &K{14}
// ReadReceipt is a type of event that marks a list of tagged events (e
// tags) as being seen by the client, its distinctive feature is the
// "expiration" tag which indicates a time after which the marking expires
ReadReceipt = &K{15}
DirectMessage = &K{14}
// FileMessage is a NIP-17 file message
FileMessage = &K{15}
// GenericRepost is an event type that...
GenericRepost = &K{16}
// ReactionToWebsite is a reaction to a website
ReactionToWebsite = &K{17}
// Picture is an event type for NIP-68 picture-first feeds
Picture = &K{20}
// VideoEventHorizontal is for horizontal video events
VideoEventHorizontal = &K{34235}
// VideoEventVertical is for vertical video events
VideoEventVertical = &K{34236}
// ChannelCreation is an event type that...
ChannelCreation = &K{40}
// ChannelMetadata is an event type that...
@ -338,87 +352,96 @@ var ( @@ -338,87 +352,96 @@ var (
var MapMx sync.RWMutex
var Map = map[uint16]string{
ProfileMetadata.K: "ProfileMetadata",
TextNote.K: "TextNote",
RecommendRelay.K: "RecommendRelay",
FollowList.K: "FollowList",
EncryptedDirectMessage.K: "EncryptedDirectMessage",
EventDeletion.K: "EventDeletion",
Repost.K: "Repost",
Reaction.K: "Reaction",
BadgeAward.K: "BadgeAward",
ReadReceipt.K: "ReadReceipt",
GenericRepost.K: "GenericRepost",
ChannelCreation.K: "ChannelCreation",
ChannelMetadata.K: "ChannelMetadata",
ChannelMessage.K: "ChannelMessage",
ChannelHideMessage.K: "ChannelHideMessage",
ChannelMuteUser.K: "ChannelMuteUser",
Bid.K: "Bid",
BidConfirmation.K: "BidConfirmation",
OpenTimestamps.K: "OpenTimestamps",
FileMetadata.K: "FileMetadata",
LiveChatMessage.K: "LiveChatMessage",
ProblemTracker.K: "ProblemTracker",
Reporting.K: "Reporting",
Label.K: "Label",
CommunityPostApproval.K: "CommunityPostApproval",
JobRequestStart.K: "JobRequestStart",
JobRequestEnd.K: "JobRequestEnd",
JobResultStart.K: "JobResultStart",
JobResultEnd.K: "JobResultEnd",
JobFeedback.K: "JobFeedback",
ZapGoal.K: "ZapGoal",
ZapRequest.K: "ZapRequest",
Zap.K: "Zap",
Highlights.K: "Highlights",
BlockList.K: "BlockList",
PinList.K: "PinList",
RelayListMetadata.K: "RelayListMetadata",
BookmarkList.K: "BookmarkList",
CommunitiesList.K: "CommunitiesList",
PublicChatsList.K: "PublicChatsList",
BlockedRelaysList.K: "BlockedRelaysList",
SearchRelaysList.K: "SearchRelaysList",
InterestsList.K: "InterestsList",
UserEmojiList.K: "UserEmojiList",
DMRelaysList.K: "DMRelaysList",
FileStorageServerList.K: "FileStorageServerList",
NWCWalletServiceInfo.K: "NWCWalletServiceInfo",
LightningPubRPC.K: "LightningPubRPC",
ClientAuthentication.K: "ClientAuthentication",
WalletRequest.K: "WalletRequest",
WalletResponse.K: "WalletResponse",
WalletNotificationNip4.K: "WalletNotificationNip4",
WalletNotification.K: "WalletNotification",
NostrConnect.K: "NostrConnect",
HTTPAuth.K: "HTTPAuth",
FollowSets.K: "FollowSets",
GenericLists.K: "GenericLists",
RelaySets.K: "RelaySets",
BookmarkSets.K: "BookmarkSets",
CurationSets.K: "CurationSets",
ProfileBadges.K: "ProfileBadges",
BadgeDefinition.K: "BadgeDefinition",
InterestSets.K: "InterestSets",
StallDefinition.K: "StallDefinition",
ProductDefinition.K: "ProductDefinition",
MarketplaceUIUX.K: "MarketplaceUIUX",
ProductSoldAsAuction.K: "ProductSoldAsAuction",
LongFormContent.K: "LongFormContent",
DraftLongFormContent.K: "DraftLongFormContent",
EmojiSets.K: "EmojiSets",
ApplicationSpecificData.K: "ApplicationSpecificData",
ParameterizedReplaceableEnd.K: "ParameterizedReplaceableEnd",
LiveEvent.K: "LiveEvent",
UserStatuses.K: "UserStatuses",
ClassifiedListing.K: "ClassifiedListing",
DraftClassifiedListing.K: "DraftClassifiedListing",
DateBasedCalendarEvent.K: "DateBasedCalendarEvent",
TimeBasedCalendarEvent.K: "TimeBasedCalendarEvent",
Calendar.K: "Calendar",
CalendarEventRSVP.K: "CalendarEventRSVP",
HandlerRecommendation.K: "HandlerRecommendation",
HandlerInformation.K: "HandlerInformation",
CommunityDefinition.K: "CommunityDefinition",
ProfileMetadata.K: "User Metadata",
TextNote.K: "Short Text Note",
RecommendRelay.K: "Recommend Relay",
FollowList.K: "Follows",
EncryptedDirectMessage.K: "Encrypted Direct Messages",
EventDeletion.K: "Event Deletion Request",
Repost.K: "Repost",
Reaction.K: "Reaction",
BadgeAward.K: "Badge Award",
ChatMessage.K: "Chat Message",
GroupChatThreadedReply.K: "Group Chat Threaded Reply",
Thread.K: "Thread",
GroupThreadReply.K: "Group Thread Reply",
Seal.K: "Seal",
DirectMessage.K: "Direct Message",
FileMessage.K: "File Message",
GenericRepost.K: "Generic Repost",
ReactionToWebsite.K: "Reaction to a website",
Picture.K: "Picture",
ChannelCreation.K: "Channel Creation",
ChannelMetadata.K: "Channel Metadata",
ChannelMessage.K: "Channel Message",
ChannelHideMessage.K: "Channel Hide Message",
ChannelMuteUser.K: "Channel Mute User",
Bid.K: "Bid",
BidConfirmation.K: "Bid Confirmation",
OpenTimestamps.K: "OpenTimestamps",
FileMetadata.K: "File Metadata",
LiveChatMessage.K: "Live Chat Message",
ProblemTracker.K: "Problem Tracker",
Reporting.K: "Reporting",
Label.K: "Label",
CommunityPostApproval.K: "Community Post Approval",
JobRequestStart.K: "Job Request",
JobRequestEnd.K: "Job Request",
JobResultStart.K: "Job Result",
JobResultEnd.K: "Job Result",
JobFeedback.K: "Job Feedback",
ZapGoal.K: "Zap Goal",
ZapRequest.K: "Zap Request",
Zap.K: "Zap",
Highlights.K: "Highlights",
BlockList.K: "Mute list",
PinList.K: "Pin list",
RelayListMetadata.K: "Relay List Metadata",
BookmarkList.K: "Bookmarks list",
CommunitiesList.K: "Communities list",
PublicChatsList.K: "Public Chats list",
BlockedRelaysList.K: "Blocked Relays list",
SearchRelaysList.K: "Search Relays list",
InterestsList.K: "Interests",
UserEmojiList.K: "User Emoji list",
DMRelaysList.K: "DM relays",
FileStorageServerList.K: "File Storage Server List",
NWCWalletServiceInfo.K: "Wallet Service Info",
LightningPubRPC.K: "Lightning pub RPC",
ClientAuthentication.K: "Client Authentication",
WalletRequest.K: "Wallet Request",
WalletResponse.K: "Wallet Response",
WalletNotificationNip4.K: "Wallet Notification",
WalletNotification.K: "Wallet Notification",
NostrConnect.K: "Nostr Connect",
HTTPAuth.K: "HTTP Auth",
FollowSets.K: "Follow sets",
GenericLists.K: "Generic lists",
RelaySets.K: "Relay sets",
BookmarkSets.K: "Bookmark sets",
CurationSets.K: "Curation sets",
ProfileBadges.K: "Profile Badges",
BadgeDefinition.K: "Badge Definition",
InterestSets.K: "Interest sets",
StallDefinition.K: "Stall Definition",
ProductDefinition.K: "Product Definition",
MarketplaceUIUX.K: "Marketplace UI/UX",
ProductSoldAsAuction.K: "Product sold as an auction",
LongFormContent.K: "Long-form Content",
DraftLongFormContent.K: "Draft Long-form Content",
EmojiSets.K: "Emoji sets",
ApplicationSpecificData.K: "Application-specific Data",
LiveEvent.K: "Live Event",
UserStatuses.K: "User Statuses",
ClassifiedListing.K: "Classified Listing",
DraftClassifiedListing.K: "Draft Classified Listing",
DateBasedCalendarEvent.K: "Date-Based Calendar Event",
TimeBasedCalendarEvent.K: "Time-Based Calendar Event",
Calendar.K: "Calendar",
CalendarEventRSVP.K: "Calendar Event RSVP",
HandlerRecommendation.K: "Handler recommendation",
HandlerInformation.K: "Handler information",
CommunityDefinition.K: "Community Definition",
VideoEventHorizontal.K: "Video Event Horizontal",
VideoEventVertical.K: "Video Event Vertical",
}

146
pkg/protocol/relayinfo/types.go

@ -45,31 +45,37 @@ var ( @@ -45,31 +45,37 @@ var (
}
NIP3 = OpenTimestampsAttestations
EncryptedDirectMessage = NIP{
"Direct Message deprecated in favor of NIP-44", 4,
"Encrypted Direct Message -- unrecommended: deprecated in favor of NIP-17", 4,
}
NIP4 = EncryptedDirectMessage
MappingNostrKeysToDNS = NIP{
"Mapping Nostr keys to DNS-based identifiers", 5,
}
NIP5 = MappingNostrKeysToDNS
HandlingMentions = NIP{
"Handling Mentions deprecated in favor of NIP-27", 8,
}
NIP8 = HandlingMentions
EventDeletion = NIP{"Event Deletion", 9}
NIP9 = EventDeletion
RelayInformationDocument = NIP{"Client Information Document", 11}
NIP11 = RelayInformationDocument
GenericTagQueries = NIP{"Generic Tag Queries", 12}
NIP12 = GenericTagQueries
SubjectTag = NIP{"Subject tag in text events", 14}
NIP14 = SubjectTag
NostrMarketplace = NIP{
"Nostr Marketplace (for resilient marketplaces)", 15,
"Mapping Nostr keys to DNS-based internet identifiers", 5,
}
NIP5 = MappingNostrKeysToDNS
BasicKeyDerivation = NIP{"Basic key derivation from mnemonic seed phrase", 6}
NIP6 = BasicKeyDerivation
WindowNostrCapability = NIP{"window.nostr capability for web browsers", 7}
NIP7 = WindowNostrCapability
HandlingMentions = NIP{"Handling Mentions -- unrecommended: deprecated in favor of NIP-27", 8}
NIP8 = HandlingMentions
EventDeletion = NIP{"Event Deletion Request", 9}
NIP9 = EventDeletion
TextNotesAndThreads = NIP{"Text Notes and Threads", 10}
NIP10 = TextNotesAndThreads
RelayInformationDocument = NIP{"Relay Information Document", 11}
NIP11 = RelayInformationDocument
GenericTagQueries = NIP{"Generic Tag Queries", 12}
NIP12 = GenericTagQueries
ProofOfWork = NIP{"Proof of Work", 13}
NIP13 = ProofOfWork
SubjectTag = NIP{"Subject tag in text events", 14}
NIP14 = SubjectTag
NostrMarketplace = NIP{"Nostr Marketplace (for resilient marketplaces)", 15}
NIP15 = NostrMarketplace
EventTreatment = NIP{"EVent Treatment", 16}
EventTreatment = NIP{"Event Treatment", 16}
NIP16 = EventTreatment
PrivateDirectMessages = NIP{"Private Direct Messages", 17}
NIP17 = PrivateDirectMessages
Reposts = NIP{"Reposts", 18}
NIP18 = Reposts
Bech32EncodedEntities = NIP{"bech32-encoded entities", 19}
@ -86,40 +92,50 @@ var ( @@ -86,40 +92,50 @@ var (
NIP24 = ExtraMetadata
Reactions = NIP{"Reactions", 25}
NIP25 = Reactions
DelegatedEventSigning = NIP{"Delegated Event Signing", 26}
DelegatedEventSigning = NIP{"Delegated Event Signing -- unrecommended: adds unnecessary burden for little gain", 26}
NIP26 = DelegatedEventSigning
TextNoteReferences = NIP{"Text Note References", 27}
NIP27 = TextNoteReferences
PublicChat = NIP{"Public Chat", 28}
NIP28 = PublicChat
RelayBasedGroups = NIP{"Relay-based Groups", 29}
NIP29 = RelayBasedGroups
CustomEmoji = NIP{"Custom Emoji", 30}
NIP30 = CustomEmoji
DealingWithUnknownEvents = NIP{"Dealing with Unknown Events", 31}
NIP31 = DealingWithUnknownEvents
Labeling = NIP{"Labeling", 32}
NIP32 = Labeling
ParameterizedReplaceableEvents = NIP{"Parameterized Replaceable Events", 33}
NIP33 = ParameterizedReplaceableEvents
GitStuff = NIP{"git stuff", 34}
NIP34 = GitStuff
Torrents = NIP{"Torrents", 35}
NIP35 = Torrents
SensitiveContent = NIP{"Sensitive Content", 36}
NIP36 = SensitiveContent
DraftEvents = NIP{"Draft Events", 37}
NIP37 = DraftEvents
UserStatuses = NIP{"User Statuses", 38}
NIP38 = UserStatuses
ExternalIdentitiesInProfiles = NIP{"External Identities in Profiles", 39}
NIP39 = ExternalIdentitiesInProfiles
ExpirationTimestamp = NIP{"Expiration Timestamp", 40}
NIP40 = ExpirationTimestamp
Authentication = NIP{
"Authentication of clients to relays", 42,
}
Authentication = NIP{"Authentication of clients to relays", 42}
NIP42 = Authentication
VersionedEncryption = NIP{"Versioned Encryption", 44}
VersionedEncryption = NIP{"Encrypted Payloads (Versioned)", 44}
NIP44 = VersionedEncryption
CountingResults = NIP{"Counting results", 45}
NIP45 = CountingResults
NostrConnect = NIP{"Nostr Connect", 46}
NIP46 = NostrConnect
WalletConnect = NIP{"Wallet Connect", 47}
NostrRemoteSigning = NIP{"Nostr Remote Signing", 46}
NIP46 = NostrRemoteSigning
WalletConnect = NIP{"Nostr Wallet Connect", 47}
NIP47 = WalletConnect
ProxyTags = NIP{"Proxy Tags", 48}
NIP48 = ProxyTags
PrivateKeyEncryption = NIP{"Private Key Encryption", 49}
NIP49 = PrivateKeyEncryption
SearchCapability = NIP{"Search Capability", 50}
NIP50 = SearchCapability
Lists = NIP{"Lists", 51}
@ -128,51 +144,97 @@ var ( @@ -128,51 +144,97 @@ var (
NIP52 = CalendarEvents
LiveActivities = NIP{"Live Activities", 53}
NIP53 = LiveActivities
Wiki = NIP{"Wiki", 54}
NIP54 = Wiki
AndroidSignerApplication = NIP{"Android Signer Application", 55}
NIP55 = AndroidSignerApplication
Reporting = NIP{"Reporting", 56}
NIP56 = Reporting
LightningZaps = NIP{"Lightning Zaps", 57}
NIP57 = LightningZaps
Badges = NIP{"Badges", 58}
NIP58 = Badges
RelayListMetadata = NIP{"Client List Metadata", 65}
GiftWrap = NIP{"Gift Wrap", 59}
NIP59 = GiftWrap
CashuWallet = NIP{"Cashu Wallet", 60}
NIP60 = CashuWallet
Nutzaps = NIP{"Nutzaps", 61}
NIP61 = Nutzaps
RequestToVanish = NIP{"Request to Vanish", 62}
NIP62 = RequestToVanish
ChessPGN = NIP{"Chess (PGN)", 64}
NIP64 = ChessPGN
RelayListMetadata = NIP{"Relay List Metadata", 65}
NIP65 = RelayListMetadata
RelayDiscoveryAndLiveness = NIP{"Relay Discovery and Liveness Monitoring", 66}
NIP66 = RelayDiscoveryAndLiveness
PictureFirstFeeds = NIP{"Picture-first feeds", 68}
NIP68 = PictureFirstFeeds
PeerToPeerOrderEvents = NIP{"Peer-to-peer Order events", 69}
NIP69 = PeerToPeerOrderEvents
ProtectedEvents = NIP{"Protected Events", 70}
NIP70 = ProtectedEvents
VideoEvents = NIP{"Video Events", 71}
NIP71 = VideoEvents
ModeratedCommunities = NIP{"Moderated Communities", 72}
NIP72 = ModeratedCommunities
ExternalContentIDs = NIP{"External Content IDs", 73}
NIP73 = ExternalContentIDs
ZapGoals = NIP{"Zap Goals", 75}
NIP75 = ZapGoals
NegentropySync = NIP{"Negentropy Syncing", 77}
NIP77 = NegentropySync
ApplicationSpecificData = NIP{"Application-specific data", 78}
NIP78 = ApplicationSpecificData
Threads = NIP{"Threads", 0x7D}
NIP7D = Threads
Highlights = NIP{"Highlights", 84}
NIP84 = Highlights
RelayManagementAPI = NIP{"Relay Management API", 86}
NIP86 = RelayManagementAPI
EcashMintDiscoverability = NIP{"Ecash Mint Discoverability", 87}
NIP87 = EcashMintDiscoverability
Polls = NIP{"Polls", 88}
NIP88 = Polls
RecommendedApplicationHandlers = NIP{"Recommended Application Handlers", 89}
NIP89 = RecommendedApplicationHandlers
DataVendingMachines = NIP{"Data Vending Machines", 90}
NIP90 = DataVendingMachines
MediaAttachments = NIP{"Media Attachments", 92}
NIP92 = MediaAttachments
FileMetadata = NIP{"File Metadata", 94}
NIP94 = FileMetadata
HTTPFileStorageIntegration = NIP{"HTTP File Storage Integration", 96}
HTTPFileStorageIntegration = NIP{"HTTP File Storage Integration -- unrecommended: replaced by blossom APIs", 96}
NIP96 = HTTPFileStorageIntegration
HTTPAuth = NIP{"HTTP IsAuthed", 98}
HTTPAuth = NIP{"HTTP Auth", 98}
NIP98 = HTTPAuth
ClassifiedListings = NIP{"Classified Listings", 99}
NIP99 = ClassifiedListings
VoiceMessages = NIP{"Voice Messages", 0xA0}
NIPA0 = VoiceMessages
WebBookmarks = NIP{"Web Bookmarks", 0xB0}
NIPB0 = WebBookmarks
Blossom = NIP{"Blossom", 0xB7}
NIPB7 = Blossom
CodeSnippets = NIP{"Code Snippets", 0xC0}
NIPC0 = CodeSnippets
Chats = NIP{"Chats", 0xC7}
NIPC7 = Chats
E2EEMessagingUsingMLSProtocol = NIP{"E2EE Messaging using MLS Protocol", 0xEE}
NIPEE = E2EEMessagingUsingMLSProtocol
)
var NIPMap = map[int]NIP{
1: NIP1, 2: NIP2, 3: NIP3, 4: NIP4, 5: NIP5, 8: NIP8, 9: NIP9,
11: NIP11, 12: NIP12, 14: NIP14, 15: NIP15, 16: NIP16, 18: NIP18, 19: NIP19,
20: NIP20,
21: NIP21, 22: NIP22, 23: NIP23, 24: NIP24, 25: NIP25, 26: NIP26, 27: NIP27,
28: NIP28,
30: NIP30, 32: NIP32, 33: NIP33, 36: NIP36, 38: NIP38, 39: NIP39, 40: NIP40,
42: NIP42,
44: NIP44, 45: NIP45, 46: NIP46, 47: NIP47, 48: NIP48, 50: NIP50, 51: NIP51,
52: NIP52,
53: NIP53, 56: NIP56, 57: NIP57, 58: NIP58, 65: NIP65, 72: NIP72, 75: NIP75,
78: NIP78,
84: NIP84, 89: NIP89, 90: NIP90, 94: NIP94, 96: NIP96, 98: NIP98, 99: NIP99,
1: NIP1, 2: NIP2, 3: NIP3, 4: NIP4, 5: NIP5, 6: NIP6, 7: NIP7, 8: NIP8, 9: NIP9, 10: NIP10,
11: NIP11, 12: NIP12, 13: NIP13, 14: NIP14, 15: NIP15, 16: NIP16, 17: NIP17, 18: NIP18, 19: NIP19, 20: NIP20,
21: NIP21, 22: NIP22, 23: NIP23, 24: NIP24, 25: NIP25, 26: NIP26, 27: NIP27, 28: NIP28, 29: NIP29, 30: NIP30,
31: NIP31, 32: NIP32, 33: NIP33, 34: NIP34, 35: NIP35, 36: NIP36, 37: NIP37, 38: NIP38, 39: NIP39, 40: NIP40,
42: NIP42, 44: NIP44, 45: NIP45, 46: NIP46, 47: NIP47, 48: NIP48, 49: NIP49, 50: NIP50,
51: NIP51, 52: NIP52, 53: NIP53, 54: NIP54, 55: NIP55, 56: NIP56, 57: NIP57, 58: NIP58, 59: NIP59, 60: NIP60,
61: NIP61, 62: NIP62, 64: NIP64, 65: NIP65, 66: NIP66, 68: NIP68, 69: NIP69, 70: NIP70,
71: NIP71, 72: NIP72, 73: NIP73, 75: NIP75, 77: NIP77, 78: NIP78, 0x7D: NIP7D, 84: NIP84,
86: NIP86, 87: NIP87, 88: NIP88, 89: NIP89, 90: NIP90, 92: NIP92, 94: NIP94, 96: NIP96, 98: NIP98, 99: NIP99,
0xA0: NIPA0, 0xB0: NIPB0, 0xB7: NIPB7, 0xC0: NIPC0, 0xC7: NIPC7, 0xEE: NIPEE,
}
// Limits are rules about what is acceptable for events and filters on a relay.

Loading…
Cancel
Save