7 Commits
Author SHA1 Message Date
david 0d8c500430 adds missing backtick in README.md 2023-11-25 16:04:26 -05:00
david 7da707522c flesh out README.md and add MIT license 2023-11-25 16:03:21 -05:00
david c64f6ca781 replace imgs with nostr.build urls 2023-11-25 15:46:08 -05:00
david 8b11c41267 include node_modules so release .zip is deployable 2023-11-24 17:44:25 -05:00
david 6c86cfe5d2 get nsec from ssm 2023-11-24 17:42:21 -05:00
david a74d7b91b8 remove node_modules and .gitignore them 2023-11-24 14:40:32 -05:00
david 5f6e638903 strip html from description 2023-11-24 14:39:28 -05:00
577 changed files with 17804 additions and 23262 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 David Lick
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+19 -3
View File
@@ -1,6 +1,7 @@
setup:
export EAGER_SERVICE_LOADING=1 \
SERVICES=lambda,ssm \
SERVICES=lambda,ssm,kms \
LAMBDA_EXECUTOR=docker \
AWS_ACCESS_KEY_ID=test \
AWS_SECRET_ACCESS_KEY=test
dockerd &
@@ -10,13 +11,20 @@ setup:
--function-name rss-nostr-lambda \
--runtime nodejs20.x \
--timeout 900 \
--memory-size 1024 \
--zip-file fileb://handler.zip \
--handler index.handler \
--role arn:aws:iam::000000000000:role/lambda-role \
--region us-east-1
awslocal ssm put-parameter \
--name /rss-nostr-lambda/last-run-time \
--value "2023-11-24T00:00:00Z" \
--value "2023-11-23T14:46:56Z" \
--type "String" \
--overwrite
awslocal ssm put-parameter \
--name /rss-nostr-lambda/nsec \
--value "nsec1azwx0ajvjquay870m69vxxmkf7prxekdj58fq92yulvt2l02zwnsmzt5vf" \
--type "SecureString" \
--overwrite
rm handler.zip
@@ -25,7 +33,15 @@ invoke:
--region us-east-1 \
--function-name rss-nostr-lambda \
--cli-binary-format raw-in-base64-out \
--payload '{"dryRun":true,"feedUrl":"https://nitter.1d4.us/culturaltutor/rss","nostrNsec":"nsec1wp68lflspznnmelmjh5ktflyv6c4ljf8xrlkey0w8yqr4305awcs8wyakj","lastRunTimeParam":"/rss-nostr-lambda/last-run-time"}' \
--payload '{"feedUrl":"https://nitter.1d4.us/culturaltutor/rss","nostrNsecParam":"/rss-nostr-lambda/nsec","lastRunTimeParam":"/rss-nostr-lambda/last-run-time"}' \
response.json
invoke-dryrun:
awslocal lambda invoke \
--region us-east-1 \
--function-name rss-nostr-lambda \
--cli-binary-format raw-in-base64-out \
--payload '{"dryRun":true,"feedUrl":"https://nitter.1d4.us/culturaltutor/rss","nostrNsecParam":"/rss-nostr-lambda/nsec","lastRunTimeParam":"/rss-nostr-lambda/last-run-time"}' \
response.json
recreate:
+42
View File
@@ -1,3 +1,45 @@
# rss-nostr-lambda
An AWS Lambda function that posts content found in an RSS feed to Nostr.
## Running Locally
### Local environment
A [nix-shell development environment](https://nixos.wiki/wiki/Development_environment_with_nix-shell) is configured in this repo. You can use it by running `nix-shell` from within the project directory. This is the easiest way to get started working with this application.
If you are not running [nix](https://nixos.wiki/wiki/Nix_package_manager) you will need to configure your environment manually. You will need the following dependencies:
- [awscliv2](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html) is used for creating and updating resources in AWS.
- [LocalStack](https://docs.localstack.cloud/getting-started/) is a service that mocks AWS services locally on your machine.
- [Docker](https://docs.docker.com/engine/install/) is used by LocalStack under the hood. You'll need the Docker daemon up and running to use LocalStack.
- [awscli-local](https://github.com/localstack/awscli-local) is a thin wrapper around the awscli which provides the `awslocal` command and allows you to call awscli commands without needing to provide the `--endpoint=http://localhost:xxxx` for LocalStack on each call.
### Makefile
A [Makefile](https://www.gnu.org/software/make/manual/make.html) has been set up to bundle some of the commands used to set up and use this Lambda.
## Running in AWS
This function runs in AWS Lambda and uses SSM to store state like the `last-run-time`. The function will read and write parameters in Parameter Store under the paths provided in the event spec below; be sure to [grant the proper permissions](https://docs.aws.amazon.com/systems-manager/latest/userguide/ps-integration-lambda-extensions.html) or this function will fail.
The function requires the `AWS_REGION` environment variable to be set. This should already be set for you by AWS Lambda.
### Event Specification
This Lambda function expects the following event to be passed in:
```
{
"feedUrl": String - The url of the RSS feed,
"nostrNsecParam": String - The path of the SSM parameter holding the nsec,
"lastRunTimeParam": String - The path of the SSM parameter holding the last run time
}
```
## Things to be awre of
The following are not ideal and may be changed in the future:
- This function uses a checkpointing algorithm based on the last run time. If the last run time is set to too long of a timeframe this function will timeout and misbehave. It's recommended to set the last run time to something recent -- like the last day or two.
- This function does not generalize a way to provide the path to the needed data in the RSS feed.
- This function hard-codes in the https://nostr.build URL. This is tightly coupled to using that service and provides no alternatives.
- Further, if the function fails to upload the image to https://nostr.build it will simply remove the image entirely. There's room to improve this.
+139 -44
View File
@@ -4,6 +4,7 @@ import { extract } from '@extractus/feed-extractor'
import NDK, { NDKEvent, NDKPrivateKeySigner } from '@nostr-dev-kit/ndk'
import bech32 from 'bech32-buffer'
import moment from 'moment'
import FormData from 'form-data'
const toHexString = nsec => {
let buffer = bech32.decode(nsec).data
@@ -13,12 +14,79 @@ const toHexString = nsec => {
return s + hex;
}, '');
}
const imgUrls = html => {
const re = /<img[^>]+src="?([^"\s]+)"?[^>]*\/>/g
const urls = []
let m
while (m = re.exec(html)) {
urls.push(m[1])
}
return urls
}
const putImgPlaceholders = html => {
const re = /<img[^>]+src="?([^"\s]+)"?[^>]*\/>/g
const placeholders = []
let replaced = html
const matches = html.matchAll(re)
let img = 0
for(let match of matches) {
replaced = replaced.replace(match[0], `%%${img}%%`)
placeholders.push(`%%${img}%%`)
img++
}
return {
replaced,
placeholders
}
}
const stripHtml = html => {
return html.replaceAll(/<[^>]*>/g, "")
}
const replaceImgsWithNostrBuildUrls = async (storeUrl, html, npub) => {
const urls = imgUrls(html)
let { replaced, placeholders } = putImgPlaceholders(html)
let count = 0
for (let placeholder of placeholders) {
const nbUrl = await uploadImgStore(storeUrl, urls[count], npub)
if (nbUrl) {
replaced = replaced.replace(placeholder, nbUrl)
} else {
replaced = replaced.replace(placeholder, '')
}
count++
}
return replaced
}
const uploadImgStore = async (hostUrl, imgUrl, npub) => {
const response = await fetch(hostUrl, {
method: 'POST',
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
"url": imgUrl,
"npub": npub
}) })
const json = await response.json()
if (json.status !== "success") {
return false
}
return json.data[0].url
}
export const handler = async (event) => {
const region = process.env.AWS_REGION || "us-east-1"
const feed = event.feedUrl
const privkey = toHexString(event.nostrNsec)
const nostrNsecParam = event.nostrNsecParam
const lastRunTimeParam = event.lastRunTimeParam
const dryrun = event.dryRun
@@ -33,93 +101,120 @@ export const handler = async (event) => {
}
const ssmClient = new SSMClient({ region })
const params = {
Name: lastRunTimeParam
}
const ssmGetCommand = new GetParameterCommand(params);
const ssmGetLastRunCommand = new GetParameterCommand({
Name: lastRunTimeParam
});
let since = new Date()
ssmClient.send(ssmGetCommand)
await ssmClient.send(ssmGetLastRunCommand)
.then(data => {
console.log("Using since date:", data.Parameter.Value)
since = data.Parameter.Value
}).catch(err => {
console.error(err)
console.dir(err)
response.errors.push(err)
return response
})
const ssmGetNsecCommand = new GetParameterCommand({
Name: nostrNsecParam,
WithDecryption: true
})
let privkey = ''
await ssmClient.send(ssmGetNsecCommand)
.then(data => {
privkey = toHexString(data.Parameter.Value)
}).catch(err => {
console.dir(err)
response.errors.push(err)
return response
})
if (feed === "") {
console.error("feedUrl was not set.")
console.dir("feedUrl was not set.")
return {
statusCode: 400,
body: "feedUrl must be provided in payload."
}
}
const ndk = new NDK({
signer: new NDKPrivateKeySigner(privkey),
explicitRelayUrls: [
"wss://nostr.mom",
"wss://nostr.snort.social",
"wss://nostr.lol"
"wss://nostr.lol",
"wss://nostr.oxtr.dev",
"wss://relay.nostr.bg",
"wss://nostr-pub.wellorder.net",
"wss://relay.damus.io",
"wss://nostr.milou.lol",
"wss://nostr.bitcoiner.social"
]
})
const user = await ndk.signer.user()
for (let url of user.relayUrls) {
ndk.pool.relays.push(url)
}
await ndk.connect(6000).catch(err => {
console.error(err)
response.errors.push ("failed to connect to relay", err)
console.dir(err)
response.errors.push("failed to connect to relay", err)
})
let rss = await extract(feed)
const signer = new NDKPrivateKeySigner(privkey)
ndk.signer = signer
let rss = await extract(feed, { descriptionMaxLen: 0, normalization: false })
.catch(err => {
console.error(err.error);
console.dir(err);
return response
})
let filtered = rss.entries.filter(item => moment(item.published).isAfter(since))
let filtered = rss.item.filter(item => moment(item.pubDate).isAfter(since))
response.totalItems = filtered.length
for (let item of filtered) {
const user = await ndk.signer.user()
let content = ''
let replaced = await replaceImgsWithNostrBuildUrls(
'https://nostr.build/api/v2/upload/url',
item.description,
user.npub)
if (replaced) {
content = stripHtml(replaced)
}
let ndkEvent = new NDKEvent(ndk, {
kind: 1,
pubkey: user.hexpubkey,
content: `${item.description}\n${item.link}`,
content,
created_at: Math.floor(Date.now() / 1000),
})
if (dryrun) {
console.dir(content)
response.successes++
continue
}
await ndkEvent.publish()
.then(() => {
try {
await ndkEvent.sign(ndk.signer)
const published = await ndkEvent.publish()
if (published) {
response.successes++
}).catch(err => {
console.error(err)
response.errors.push(err)
})
}
params.Name = lastRunTimeParam
params.Value = new Date().toISOString()
params.Type = "String"
params.Overwrite = true
const ssmPutCommand = new PutParameterCommand(params)
ssmClient.send(ssmPutCommand)
.catch(err => {
console.error(err)
response.errors.push(err)
const ssmPutCommand = new PutParameterCommand({
Name: lastRunTimeParam,
Value: new Date().toISOString(),
Type: "String",
Overwrite: true
})
await ssmClient.send(ssmPutCommand)
continue
}
console.dir(`failed to publish note`)
response.errors.push(`failed to publish note`)
} catch(e) {
console.dir(e)
response.errors.push(e)
return response
})
}
}
return response
}
+116 -71
View File
@@ -624,6 +624,28 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@noble/curves": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz",
"integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
"dependencies": {
"@noble/hashes": "1.3.1"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@noble/curves/node_modules/@noble/hashes": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@noble/hashes": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
@@ -647,40 +669,23 @@
]
},
"node_modules/@nostr-dev-kit/ndk": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk/-/ndk-2.0.5.tgz",
"integrity": "sha512-EvVr43y+4N9lMSZIIe5V8jFWYlKTVkMyyJfbgRaDGCGiBZAm9iy0wcO2Yi+W57awOAlvr3mjT5fAwG9RDb6HSA==",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk/-/ndk-2.1.1.tgz",
"integrity": "sha512-sMD4re3QVpSVMzoyvJAFiftPxJBXkhjRInrK5DcjhSkkPTlJhI+oiVXCpcvCJ3PHT5PilhDgKkg3LyOvv135UQ==",
"dependencies": {
"@noble/hashes": "^1.3.1",
"@noble/secp256k1": "^2.0.0",
"@scure/base": "^1.1.1",
"debug": "^4.3.4",
"eventemitter3": "^5.0.1",
"light-bolt11-decoder": "^3.0.0",
"node-fetch": "^3.3.1",
"nostr-tools": "^1.15.0",
"tseep": "^1.1.1",
"typescript-lru-cache": "^2.0.0",
"utf8-buffer": "^1.0.0",
"websocket-polyfill": "^0.0.3"
}
},
"node_modules/@nostr-dev-kit/ndk/node_modules/node-fetch": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
"dependencies": {
"data-uri-to-buffer": "^4.0.0",
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-fetch"
}
},
"node_modules/@scure/base": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.3.tgz",
@@ -702,28 +707,6 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@scure/bip32/node_modules/@noble/curves": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz",
"integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
"dependencies": {
"@noble/hashes": "1.3.1"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@scure/bip32/node_modules/@noble/hashes": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@scure/bip39": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz",
@@ -1250,6 +1233,11 @@
"node": ">=14.0.0"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/bech32-buffer": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/bech32-buffer/-/bech32-buffer-0.2.1.tgz",
@@ -1283,6 +1271,17 @@
"node": ">=6.14.2"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/cross-fetch": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
@@ -1291,6 +1290,25 @@
"node-fetch": "^2.6.12"
}
},
"node_modules/cross-fetch/node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/d": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
@@ -1324,6 +1342,14 @@
}
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/es5-ext": {
"version": "0.10.62",
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
@@ -1357,11 +1383,6 @@
"ext": "^1.1.2"
}
},
"node_modules/eventemitter3": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
},
"node_modules/ext": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz",
@@ -1418,6 +1439,19 @@
"node": "^12.20 || >= 14.13"
}
},
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
@@ -1468,6 +1502,25 @@
}
]
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/moment": {
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
@@ -1505,22 +1558,20 @@
}
},
"node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
"dependencies": {
"whatwg-url": "^5.0.0"
"data-uri-to-buffer": "^4.0.0",
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
},
"engines": {
"node": "4.x || >=6.0.0"
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-fetch"
}
},
"node_modules/node-gyp-build": {
@@ -1554,17 +1605,6 @@
}
}
},
"node_modules/nostr-tools/node_modules/@noble/curves": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz",
"integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
"dependencies": {
"@noble/hashes": "1.3.1"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/nostr-tools/node_modules/@noble/hashes": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
@@ -1597,6 +1637,11 @@
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/tseep": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/tseep/-/tseep-1.1.3.tgz",
"integrity": "sha512-deBIcIlXUMlr3xaN0UEochqjU/zXGaZGPqHPd1rxo4w6DklBdRM6WQQtsk7bekIF+qY6QTeen3nE6OA7BxL9rg=="
},
"node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",

Some files were not shown because too many files have changed in this diff Show More