mirror of
https://github.com/nbd-wtf/nostr-tools.git
synced 2025-12-09 00:28:51 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55ff796b9f | ||
|
|
3ef2ad5bc4 | ||
|
|
45c07a5f45 | ||
|
|
6a037d1658 | ||
|
|
dcf101c6c2 | ||
|
|
eb97dbd9ef | ||
|
|
92988051c6 | ||
|
|
bf7e00d32a | ||
|
|
9241089997 | ||
|
|
32c47e9bd8 | ||
|
|
6e58fe371c | ||
|
|
26e35d50e0 | ||
|
|
ef3184a6e0 | ||
|
|
56fe3dd5dd | ||
|
|
f1bb5030c8 |
24
LICENSE
Normal file
24
LICENSE
Normal file
@@ -0,0 +1,24 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
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 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.
|
||||
|
||||
For more information, please refer to <https://unlicense.org>
|
||||
@@ -325,4 +325,4 @@ Please consult the tests or [the source code](https://github.com/fiatjaf/nostr-t
|
||||
|
||||
## License
|
||||
|
||||
Public domain.
|
||||
This is free and unencumbered software released into the public domain. By submitting patches to this project, you agree to dedicate any and all copyright interest in this software to the public domain.
|
||||
|
||||
11
event.ts
11
event.ts
@@ -1,6 +1,5 @@
|
||||
import {schnorr} from '@noble/curves/secp256k1'
|
||||
import * as secp256k1 from '@noble/secp256k1'
|
||||
import {sha256} from '@noble/hashes/sha256'
|
||||
import {bytesToHex} from '@noble/hashes/utils'
|
||||
|
||||
import {utf8Encoder} from './utils'
|
||||
import {getPublicKey} from './keys'
|
||||
@@ -79,7 +78,7 @@ export function serializeEvent(evt: UnsignedEvent): string {
|
||||
|
||||
export function getEventHash(event: UnsignedEvent): string {
|
||||
let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))
|
||||
return bytesToHex(eventHash)
|
||||
return secp256k1.utils.bytesToHex(eventHash)
|
||||
}
|
||||
|
||||
const isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object
|
||||
@@ -105,7 +104,7 @@ export function validateEvent<T>(event: T): event is T & UnsignedEvent {
|
||||
}
|
||||
|
||||
export function verifySignature(event: Event): boolean {
|
||||
return schnorr.verify(
|
||||
return secp256k1.schnorr.verifySync(
|
||||
event.sig,
|
||||
getEventHash(event),
|
||||
event.pubkey
|
||||
@@ -113,7 +112,7 @@ export function verifySignature(event: Event): boolean {
|
||||
}
|
||||
|
||||
export function signEvent(event: UnsignedEvent, key: string): string {
|
||||
return bytesToHex(
|
||||
schnorr.sign(getEventHash(event), key)
|
||||
return secp256k1.utils.bytesToHex(
|
||||
secp256k1.schnorr.signSync(getEventHash(event), key)
|
||||
)
|
||||
}
|
||||
|
||||
12
index.ts
12
index.ts
@@ -11,9 +11,21 @@ export * as nip06 from './nip06'
|
||||
export * as nip10 from './nip10'
|
||||
export * as nip13 from './nip13'
|
||||
export * as nip19 from './nip19'
|
||||
export * as nip21 from './nip21'
|
||||
export * as nip26 from './nip26'
|
||||
export * as nip27 from './nip27'
|
||||
export * as nip39 from './nip39'
|
||||
export * as nip42 from './nip42'
|
||||
export * as nip57 from './nip57'
|
||||
|
||||
export * as fj from './fakejson'
|
||||
export * as utils from './utils'
|
||||
|
||||
// monkey patch secp256k1
|
||||
import * as secp256k1 from '@noble/secp256k1'
|
||||
import {hmac} from '@noble/hashes/hmac'
|
||||
import {sha256} from '@noble/hashes/sha256'
|
||||
secp256k1.utils.hmacSha256Sync = (key, ...msgs) =>
|
||||
hmac(sha256, key, secp256k1.utils.concatBytes(...msgs))
|
||||
secp256k1.utils.sha256Sync = (...msgs) =>
|
||||
sha256(secp256k1.utils.concatBytes(...msgs))
|
||||
|
||||
7
keys.ts
7
keys.ts
@@ -1,10 +1,9 @@
|
||||
import {schnorr} from '@noble/curves/secp256k1'
|
||||
import {bytesToHex} from '@noble/hashes/utils'
|
||||
import * as secp256k1 from '@noble/secp256k1'
|
||||
|
||||
export function generatePrivateKey(): string {
|
||||
return bytesToHex(schnorr.utils.randomPrivateKey())
|
||||
return secp256k1.utils.bytesToHex(secp256k1.utils.randomPrivateKey())
|
||||
}
|
||||
|
||||
export function getPublicKey(privateKey: string): string {
|
||||
return bytesToHex(schnorr.getPublicKey(privateKey))
|
||||
return secp256k1.utils.bytesToHex(secp256k1.schnorr.getPublicKey(privateKey))
|
||||
}
|
||||
|
||||
2
nip04.ts
2
nip04.ts
@@ -1,5 +1,5 @@
|
||||
import {randomBytes} from '@noble/hashes/utils'
|
||||
import {secp256k1} from '@noble/curves/secp256k1'
|
||||
import * as secp256k1 from '@noble/secp256k1'
|
||||
import {base64} from '@scure/base'
|
||||
|
||||
import {utf8Decoder, utf8Encoder} from './utils'
|
||||
|
||||
4
nip06.ts
4
nip06.ts
@@ -1,4 +1,4 @@
|
||||
import {bytesToHex} from '@noble/hashes/utils'
|
||||
import * as secp256k1 from '@noble/secp256k1'
|
||||
import {wordlist} from '@scure/bip39/wordlists/english.js'
|
||||
import {
|
||||
generateMnemonic,
|
||||
@@ -14,7 +14,7 @@ export function privateKeyFromSeedWords(
|
||||
let root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase))
|
||||
let privateKey = root.derive(`m/44'/1237'/0'/0/0`).privateKey
|
||||
if (!privateKey) throw new Error('could not derive private key')
|
||||
return bytesToHex(privateKey)
|
||||
return secp256k1.utils.bytesToHex(privateKey)
|
||||
}
|
||||
|
||||
export function generateSeedWords(): string {
|
||||
|
||||
4
nip13.ts
4
nip13.ts
@@ -1,8 +1,8 @@
|
||||
import {hexToBytes} from '@noble/hashes/utils'
|
||||
import * as secp256k1 from '@noble/secp256k1'
|
||||
|
||||
/** Get POW difficulty from a Nostr hex ID. */
|
||||
export function getPow(id: string): number {
|
||||
return getLeadingZeroBits(hexToBytes(id))
|
||||
return getLeadingZeroBits(secp256k1.utils.hexToBytes(id))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
40
nip19.ts
40
nip19.ts
@@ -1,4 +1,4 @@
|
||||
import {bytesToHex, concatBytes, hexToBytes} from '@noble/hashes/utils'
|
||||
import * as secp256k1 from '@noble/secp256k1'
|
||||
import {bech32} from '@scure/base'
|
||||
|
||||
import {utf8Decoder, utf8Encoder} from './utils'
|
||||
@@ -23,10 +23,16 @@ export type AddressPointer = {
|
||||
relays?: string[]
|
||||
}
|
||||
|
||||
export function decode(nip19: string): {
|
||||
type: string
|
||||
data: ProfilePointer | EventPointer | AddressPointer | string
|
||||
} {
|
||||
export type DecodeResult =
|
||||
| {type: 'nprofile'; data: ProfilePointer}
|
||||
| {type: 'nrelay'; data: string}
|
||||
| {type: 'nevent'; data: EventPointer}
|
||||
| {type: 'naddr'; data: AddressPointer}
|
||||
| {type: 'nsec'; data: string}
|
||||
| {type: 'npub'; data: string}
|
||||
| {type: 'note'; data: string}
|
||||
|
||||
export function decode(nip19: string): DecodeResult {
|
||||
let {prefix, words} = bech32.decode(nip19, Bech32MaxSize)
|
||||
let data = new Uint8Array(bech32.fromWords(words))
|
||||
|
||||
@@ -39,7 +45,7 @@ export function decode(nip19: string): {
|
||||
return {
|
||||
type: 'nprofile',
|
||||
data: {
|
||||
pubkey: bytesToHex(tlv[0][0]),
|
||||
pubkey: secp256k1.utils.bytesToHex(tlv[0][0]),
|
||||
relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : []
|
||||
}
|
||||
}
|
||||
@@ -54,10 +60,10 @@ export function decode(nip19: string): {
|
||||
return {
|
||||
type: 'nevent',
|
||||
data: {
|
||||
id: bytesToHex(tlv[0][0]),
|
||||
id: secp256k1.utils.bytesToHex(tlv[0][0]),
|
||||
relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],
|
||||
author: tlv[2]?.[0]
|
||||
? bytesToHex(tlv[2][0])
|
||||
? secp256k1.utils.bytesToHex(tlv[2][0])
|
||||
: undefined
|
||||
}
|
||||
}
|
||||
@@ -75,8 +81,8 @@ export function decode(nip19: string): {
|
||||
type: 'naddr',
|
||||
data: {
|
||||
identifier: utf8Decoder.decode(tlv[0][0]),
|
||||
pubkey: bytesToHex(tlv[2][0]),
|
||||
kind: parseInt(bytesToHex(tlv[3][0]), 16),
|
||||
pubkey: secp256k1.utils.bytesToHex(tlv[2][0]),
|
||||
kind: parseInt(secp256k1.utils.bytesToHex(tlv[3][0]), 16),
|
||||
relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : []
|
||||
}
|
||||
}
|
||||
@@ -95,7 +101,7 @@ export function decode(nip19: string): {
|
||||
case 'nsec':
|
||||
case 'npub':
|
||||
case 'note':
|
||||
return {type: prefix, data: bytesToHex(data)}
|
||||
return {type: prefix, data: secp256k1.utils.bytesToHex(data)}
|
||||
|
||||
default:
|
||||
throw new Error(`unknown prefix ${prefix}`)
|
||||
@@ -132,14 +138,14 @@ export function noteEncode(hex: string): string {
|
||||
}
|
||||
|
||||
function encodeBytes(prefix: string, hex: string): string {
|
||||
let data = hexToBytes(hex)
|
||||
let data = secp256k1.utils.hexToBytes(hex)
|
||||
let words = bech32.toWords(data)
|
||||
return bech32.encode(prefix, words, Bech32MaxSize)
|
||||
}
|
||||
|
||||
export function nprofileEncode(profile: ProfilePointer): string {
|
||||
let data = encodeTLV({
|
||||
0: [hexToBytes(profile.pubkey)],
|
||||
0: [secp256k1.utils.hexToBytes(profile.pubkey)],
|
||||
1: (profile.relays || []).map(url => utf8Encoder.encode(url))
|
||||
})
|
||||
let words = bech32.toWords(data)
|
||||
@@ -148,9 +154,9 @@ export function nprofileEncode(profile: ProfilePointer): string {
|
||||
|
||||
export function neventEncode(event: EventPointer): string {
|
||||
let data = encodeTLV({
|
||||
0: [hexToBytes(event.id)],
|
||||
0: [secp256k1.utils.hexToBytes(event.id)],
|
||||
1: (event.relays || []).map(url => utf8Encoder.encode(url)),
|
||||
2: event.author ? [hexToBytes(event.author)] : []
|
||||
2: event.author ? [secp256k1.utils.hexToBytes(event.author)] : []
|
||||
})
|
||||
let words = bech32.toWords(data)
|
||||
return bech32.encode('nevent', words, Bech32MaxSize)
|
||||
@@ -163,7 +169,7 @@ export function naddrEncode(addr: AddressPointer): string {
|
||||
let data = encodeTLV({
|
||||
0: [utf8Encoder.encode(addr.identifier)],
|
||||
1: (addr.relays || []).map(url => utf8Encoder.encode(url)),
|
||||
2: [hexToBytes(addr.pubkey)],
|
||||
2: [secp256k1.utils.hexToBytes(addr.pubkey)],
|
||||
3: [new Uint8Array(kind)]
|
||||
})
|
||||
let words = bech32.toWords(data)
|
||||
@@ -191,5 +197,5 @@ function encodeTLV(tlv: TLV): Uint8Array {
|
||||
})
|
||||
})
|
||||
|
||||
return concatBytes(...entries)
|
||||
return secp256k1.utils.concatBytes(...entries)
|
||||
}
|
||||
|
||||
42
nip21.test.js
Normal file
42
nip21.test.js
Normal file
@@ -0,0 +1,42 @@
|
||||
/* eslint-env jest */
|
||||
const {nip21} = require('./lib/nostr.cjs')
|
||||
|
||||
test('test', () => {
|
||||
expect(
|
||||
nip21.test(
|
||||
'nostr:npub108pv4cg5ag52nq082kd5leu9ffrn2gdg6g4xdwatn73y36uzplmq9uyev6'
|
||||
)
|
||||
).toBe(true)
|
||||
expect(
|
||||
nip21.test(
|
||||
'nostr:note1gmtnz6q2m55epmlpe3semjdcq987av3jvx4emmjsa8g3s9x7tg4sclreky'
|
||||
)
|
||||
).toBe(true)
|
||||
expect(
|
||||
nip21.test(
|
||||
' nostr:npub108pv4cg5ag52nq082kd5leu9ffrn2gdg6g4xdwatn73y36uzplmq9uyev6'
|
||||
)
|
||||
).toBe(false)
|
||||
expect(nip21.test('nostr:')).toBe(false)
|
||||
expect(
|
||||
nip21.test(
|
||||
'nostr:npub108pv4cg5ag52nQq082kd5leu9ffrn2gdg6g4xdwatn73y36uzplmq9uyev6'
|
||||
)
|
||||
).toBe(false)
|
||||
expect(nip21.test('gggggg')).toBe(false)
|
||||
})
|
||||
|
||||
test('parse', () => {
|
||||
const result = nip21.parse(
|
||||
'nostr:note1gmtnz6q2m55epmlpe3semjdcq987av3jvx4emmjsa8g3s9x7tg4sclreky'
|
||||
)
|
||||
|
||||
expect(result).toEqual({
|
||||
uri: 'nostr:note1gmtnz6q2m55epmlpe3semjdcq987av3jvx4emmjsa8g3s9x7tg4sclreky',
|
||||
value: 'note1gmtnz6q2m55epmlpe3semjdcq987av3jvx4emmjsa8g3s9x7tg4sclreky',
|
||||
decoded: {
|
||||
type: 'note',
|
||||
data: '46d731680add2990efe1cc619dc9b8014feeb23261ab9dee50e9d11814de5a2b'
|
||||
}
|
||||
})
|
||||
})
|
||||
41
nip21.ts
Normal file
41
nip21.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import * as nip19 from './nip19'
|
||||
import * as nip21 from './nip21'
|
||||
|
||||
/**
|
||||
* Bech32 regex.
|
||||
* @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32
|
||||
*/
|
||||
export const BECH32_REGEX =
|
||||
/[\x21-\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/
|
||||
|
||||
/** Nostr URI regex, eg `nostr:npub1...` */
|
||||
export const NOSTR_URI_REGEX = new RegExp(`nostr:(${BECH32_REGEX.source})`)
|
||||
|
||||
/** Test whether the value is a Nostr URI. */
|
||||
export function test(value: unknown): value is `nostr:${string}` {
|
||||
return (
|
||||
typeof value === 'string' &&
|
||||
new RegExp(`^${NOSTR_URI_REGEX.source}$`).test(value)
|
||||
)
|
||||
}
|
||||
|
||||
/** Parsed Nostr URI data. */
|
||||
export interface NostrURI {
|
||||
/** Full URI including the `nostr:` protocol. */
|
||||
uri: `nostr:${string}`
|
||||
/** The bech32-encoded data (eg `npub1...`). */
|
||||
value: string
|
||||
/** Decoded bech32 string, according to NIP-19. */
|
||||
decoded: nip19.DecodeResult
|
||||
}
|
||||
|
||||
/** Parse and decode a Nostr URI. */
|
||||
export function parse(uri: string): NostrURI {
|
||||
const match = uri.match(new RegExp(`^${nip21.NOSTR_URI_REGEX.source}$`))
|
||||
if (!match) throw new Error(`Invalid Nostr URI: ${uri}`)
|
||||
return {
|
||||
uri: match[0] as `nostr:${string}`,
|
||||
value: match[1],
|
||||
decoded: nip19.decode(match[1])
|
||||
}
|
||||
}
|
||||
9
nip26.ts
9
nip26.ts
@@ -1,5 +1,4 @@
|
||||
import {schnorr} from '@noble/curves/secp256k1'
|
||||
import {bytesToHex} from '@noble/hashes/utils'
|
||||
import * as secp256k1 from '@noble/secp256k1'
|
||||
import {sha256} from '@noble/hashes/sha256'
|
||||
|
||||
import {Event} from './event'
|
||||
@@ -37,8 +36,8 @@ export function createDelegation(
|
||||
utf8Encoder.encode(`nostr:delegation:${parameters.pubkey}:${cond}`)
|
||||
)
|
||||
|
||||
let sig = bytesToHex(
|
||||
schnorr.sign(sighash, privateKey)
|
||||
let sig = secp256k1.utils.bytesToHex(
|
||||
secp256k1.schnorr.signSync(sighash, privateKey)
|
||||
)
|
||||
|
||||
return {
|
||||
@@ -85,7 +84,7 @@ export function getDelegator(event: Event): string | null {
|
||||
let sighash = sha256(
|
||||
utf8Encoder.encode(`nostr:delegation:${event.pubkey}:${cond}`)
|
||||
)
|
||||
if (!schnorr.verify(sig, sighash, pubkey)) return null
|
||||
if (!secp256k1.schnorr.verifySync(sig, sighash, pubkey)) return null
|
||||
|
||||
return pubkey
|
||||
}
|
||||
|
||||
49
nip27.test.js
Normal file
49
nip27.test.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/* eslint-env jest */
|
||||
const {nip27} = require('./lib/nostr.cjs')
|
||||
|
||||
test('matchAll', () => {
|
||||
const result = nip27.matchAll(
|
||||
'Hello nostr:npub108pv4cg5ag52nq082kd5leu9ffrn2gdg6g4xdwatn73y36uzplmq9uyev6!\n\nnostr:note1gmtnz6q2m55epmlpe3semjdcq987av3jvx4emmjsa8g3s9x7tg4sclreky'
|
||||
)
|
||||
|
||||
expect([...result]).toEqual([
|
||||
{
|
||||
uri: 'nostr:npub108pv4cg5ag52nq082kd5leu9ffrn2gdg6g4xdwatn73y36uzplmq9uyev6',
|
||||
value: 'npub108pv4cg5ag52nq082kd5leu9ffrn2gdg6g4xdwatn73y36uzplmq9uyev6',
|
||||
decoded: {
|
||||
type: 'npub',
|
||||
data: '79c2cae114ea28a981e7559b4fe7854a473521a8d22a66bbab9fa248eb820ff6'
|
||||
},
|
||||
start: 6,
|
||||
end: 75
|
||||
},
|
||||
{
|
||||
uri: 'nostr:note1gmtnz6q2m55epmlpe3semjdcq987av3jvx4emmjsa8g3s9x7tg4sclreky',
|
||||
value: 'note1gmtnz6q2m55epmlpe3semjdcq987av3jvx4emmjsa8g3s9x7tg4sclreky',
|
||||
decoded: {
|
||||
type: 'note',
|
||||
data: '46d731680add2990efe1cc619dc9b8014feeb23261ab9dee50e9d11814de5a2b'
|
||||
},
|
||||
start: 78,
|
||||
end: 147
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
test('replaceAll', () => {
|
||||
const content =
|
||||
'Hello nostr:npub108pv4cg5ag52nq082kd5leu9ffrn2gdg6g4xdwatn73y36uzplmq9uyev6!\n\nnostr:note1gmtnz6q2m55epmlpe3semjdcq987av3jvx4emmjsa8g3s9x7tg4sclreky'
|
||||
|
||||
const result = nip27.replaceAll(content, ({decoded, value}) => {
|
||||
switch (decoded.type) {
|
||||
case 'npub':
|
||||
return '@alex'
|
||||
case 'note':
|
||||
return '!1234'
|
||||
default:
|
||||
return value
|
||||
}
|
||||
})
|
||||
|
||||
expect(result).toEqual('Hello @alex!\n\n!1234')
|
||||
})
|
||||
63
nip27.ts
Normal file
63
nip27.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import * as nip19 from './nip19'
|
||||
import * as nip21 from './nip21'
|
||||
|
||||
/** Regex to find NIP-21 URIs inside event content. */
|
||||
export const regex = () =>
|
||||
new RegExp(`\\b${nip21.NOSTR_URI_REGEX.source}\\b`, 'g')
|
||||
|
||||
/** Match result for a Nostr URI in event content. */
|
||||
export interface NostrURIMatch extends nip21.NostrURI {
|
||||
/** Index where the URI begins in the event content. */
|
||||
start: number
|
||||
/** Index where the URI ends in the event content. */
|
||||
end: number
|
||||
}
|
||||
|
||||
/** Find and decode all NIP-21 URIs. */
|
||||
export function * matchAll(content: string): Iterable<NostrURIMatch> {
|
||||
const matches = content.matchAll(regex())
|
||||
|
||||
for (const match of matches) {
|
||||
const [uri, value] = match
|
||||
|
||||
yield {
|
||||
uri: uri as `nostr:${string}`,
|
||||
value,
|
||||
decoded: nip19.decode(value),
|
||||
start: match.index!,
|
||||
end: match.index! + uri.length
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace all occurrences of Nostr URIs in the text.
|
||||
*
|
||||
* WARNING: using this on an HTML string is potentially unsafe!
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* nip27.replaceAll(event.content, ({ decoded, value }) => {
|
||||
* switch(decoded.type) {
|
||||
* case 'npub':
|
||||
* return renderMention(decoded)
|
||||
* case 'note':
|
||||
* return renderNote(decoded)
|
||||
* default:
|
||||
* return value
|
||||
* }
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
export function replaceAll(
|
||||
content: string,
|
||||
replacer: (match: nip21.NostrURI) => string
|
||||
): string {
|
||||
return content.replaceAll(regex(), (uri, value) => {
|
||||
return replacer({
|
||||
uri: uri as `nostr:${string}`,
|
||||
value,
|
||||
decoded: nip19.decode(value)
|
||||
})
|
||||
})
|
||||
}
|
||||
27
nip42.test.js
Normal file
27
nip42.test.js
Normal file
@@ -0,0 +1,27 @@
|
||||
/* eslint-env jest */
|
||||
|
||||
require('websocket-polyfill')
|
||||
const {
|
||||
relayInit,
|
||||
generatePrivateKey,
|
||||
finishEvent,
|
||||
nip42
|
||||
} = require('./lib/nostr.cjs')
|
||||
|
||||
test('auth flow', done => {
|
||||
const relay = relayInit('wss://nostr.kollider.xyz')
|
||||
relay.connect()
|
||||
const sk = generatePrivateKey()
|
||||
|
||||
relay.on('auth', async challenge => {
|
||||
await expect(
|
||||
nip42.authenticate({
|
||||
challenge,
|
||||
relay,
|
||||
sign: e => finishEvent(e, sk)
|
||||
})
|
||||
).rejects.toBeTruthy()
|
||||
relay.close()
|
||||
done()
|
||||
})
|
||||
})
|
||||
42
nip42.ts
Normal file
42
nip42.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import {EventTemplate, Event, Kind} from './event'
|
||||
import {Relay} from './relay'
|
||||
|
||||
/**
|
||||
* Authenticate via NIP-42 flow.
|
||||
*
|
||||
* @example
|
||||
* const sign = window.nostr.signEvent
|
||||
* relay.on('auth', challenge =>
|
||||
* authenticate({ relay, sign, challenge })
|
||||
* )
|
||||
*/
|
||||
export const authenticate = async ({
|
||||
challenge,
|
||||
relay,
|
||||
sign
|
||||
}: {
|
||||
challenge: string
|
||||
relay: Relay
|
||||
sign: (e: EventTemplate) => Promise<Event>
|
||||
}): Promise<void> => {
|
||||
const e: EventTemplate = {
|
||||
kind: Kind.ClientAuth,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
tags: [
|
||||
['relay', relay.url],
|
||||
['challenge', challenge]
|
||||
],
|
||||
content: ''
|
||||
}
|
||||
const pub = relay.auth(await sign(e))
|
||||
return new Promise((resolve, reject) => {
|
||||
pub.on('ok', function ok() {
|
||||
pub.off('ok', ok)
|
||||
resolve()
|
||||
})
|
||||
pub.on('failed', function fail(reason: string) {
|
||||
pub.off('failed', fail)
|
||||
reject(reason)
|
||||
})
|
||||
})
|
||||
}
|
||||
12
package.json
12
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "nostr-tools",
|
||||
"version": "1.9.0",
|
||||
"version": "1.10.1",
|
||||
"description": "Tools for making a Nostr client.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -16,13 +16,13 @@
|
||||
"import": "./lib/esm/nostr.mjs",
|
||||
"require": "./lib/nostr.cjs.js"
|
||||
},
|
||||
"license": "Public domain",
|
||||
"license": "Unlicense",
|
||||
"dependencies": {
|
||||
"@noble/curves": "1.0.0",
|
||||
"@noble/hashes": "1.3.0",
|
||||
"@noble/hashes": "1.2.0",
|
||||
"@noble/secp256k1": "1.7.1",
|
||||
"@scure/base": "1.1.1",
|
||||
"@scure/bip32": "1.3.0",
|
||||
"@scure/bip39": "1.2.0"
|
||||
"@scure/bip32": "1.1.4",
|
||||
"@scure/bip39": "1.1.1"
|
||||
},
|
||||
"keywords": [
|
||||
"decentralization",
|
||||
|
||||
4
pool.ts
4
pool.ts
@@ -2,7 +2,7 @@ import {Relay, relayInit} from './relay'
|
||||
import {normalizeURL} from './utils'
|
||||
import {Filter} from './filter'
|
||||
import {Event} from './event'
|
||||
import {SubscriptionOptions, Sub, Pub} from './relay'
|
||||
import {SubscriptionOptions, Sub, Pub, CountPayload} from './relay'
|
||||
|
||||
export class SimplePool {
|
||||
private _conn: {[url: string]: Relay}
|
||||
@@ -53,7 +53,7 @@ export class SimplePool {
|
||||
}
|
||||
|
||||
let subs: Sub[] = []
|
||||
let eventListeners: Set<(event: Event) => void> = new Set()
|
||||
let eventListeners: Set<any> = new Set()
|
||||
let eoseListeners: Set<() => void> = new Set()
|
||||
let eosesMissing = relays.length
|
||||
|
||||
|
||||
112
relay.ts
112
relay.ts
@@ -9,9 +9,14 @@ type RelayEvent = {
|
||||
disconnect: () => void | Promise<void>
|
||||
error: () => void | Promise<void>
|
||||
notice: (msg: string) => void | Promise<void>
|
||||
auth: (challenge: string) => void | Promise<void>
|
||||
}
|
||||
export type CountPayload = {
|
||||
count: number
|
||||
}
|
||||
type SubEvent = {
|
||||
event: (event: Event) => void | Promise<void>
|
||||
count: (payload: CountPayload) => void | Promise<void>
|
||||
eose: () => void | Promise<void>
|
||||
}
|
||||
export type Relay = {
|
||||
@@ -22,7 +27,12 @@ export type Relay = {
|
||||
sub: (filters: Filter[], opts?: SubscriptionOptions) => Sub
|
||||
list: (filters: Filter[], opts?: SubscriptionOptions) => Promise<Event[]>
|
||||
get: (filter: Filter, opts?: SubscriptionOptions) => Promise<Event | null>
|
||||
count: (
|
||||
filters: Filter[],
|
||||
opts?: SubscriptionOptions
|
||||
) => Promise<CountPayload | null>
|
||||
publish: (event: Event) => Pub
|
||||
auth: (event: Event) => Pub
|
||||
off: <T extends keyof RelayEvent, U extends RelayEvent[T]>(
|
||||
event: T,
|
||||
listener: U
|
||||
@@ -51,27 +61,32 @@ export type Sub = {
|
||||
|
||||
export type SubscriptionOptions = {
|
||||
id?: string
|
||||
verb?: 'REQ' | 'COUNT'
|
||||
skipVerification?: boolean
|
||||
alreadyHaveEvent?: null | ((id: string, relay: string) => boolean)
|
||||
}
|
||||
|
||||
const newListeners = (): {[TK in keyof RelayEvent]: RelayEvent[TK][]} => ({
|
||||
connect: [],
|
||||
disconnect: [],
|
||||
error: [],
|
||||
notice: [],
|
||||
auth: []
|
||||
})
|
||||
|
||||
export function relayInit(
|
||||
url: string,
|
||||
options: {
|
||||
getTimeout?: number
|
||||
listTimeout?: number
|
||||
countTimeout?: number
|
||||
} = {}
|
||||
): Relay {
|
||||
let {listTimeout = 3000, getTimeout = 3000} = options
|
||||
let {listTimeout = 3000, getTimeout = 3000, countTimeout = 3000} = options
|
||||
|
||||
var ws: WebSocket
|
||||
var openSubs: {[id: string]: {filters: Filter[]} & SubscriptionOptions} = {}
|
||||
var listeners: {[TK in keyof RelayEvent]: RelayEvent[TK][]} = {
|
||||
connect: [],
|
||||
disconnect: [],
|
||||
error: [],
|
||||
notice: []
|
||||
}
|
||||
var listeners = newListeners()
|
||||
var subListeners: {
|
||||
[subid: string]: {[TK in keyof SubEvent]: SubEvent[TK][]}
|
||||
} = {}
|
||||
@@ -146,7 +161,7 @@ export function relayInit(
|
||||
// will naturally be caught by the encompassing try..catch block
|
||||
|
||||
switch (data[0]) {
|
||||
case 'EVENT':
|
||||
case 'EVENT': {
|
||||
let id = data[1]
|
||||
let event = data[2]
|
||||
if (
|
||||
@@ -159,6 +174,14 @@ export function relayInit(
|
||||
;(subListeners[id]?.event || []).forEach(cb => cb(event))
|
||||
}
|
||||
return
|
||||
}
|
||||
case 'COUNT':
|
||||
let id = data[1]
|
||||
let payload = data[2]
|
||||
if (openSubs[id]) {
|
||||
;(subListeners[id]?.count || []).forEach(cb => cb(payload))
|
||||
}
|
||||
return
|
||||
case 'EOSE': {
|
||||
let id = data[1]
|
||||
if (id in subListeners) {
|
||||
@@ -183,6 +206,11 @@ export function relayInit(
|
||||
let notice = data[1]
|
||||
listeners.notice.forEach(cb => cb(notice))
|
||||
return
|
||||
case 'AUTH': {
|
||||
let challenge = data[1]
|
||||
listeners.auth?.forEach(cb => cb(challenge))
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
return
|
||||
@@ -220,6 +248,7 @@ export function relayInit(
|
||||
const sub = (
|
||||
filters: Filter[],
|
||||
{
|
||||
verb = 'REQ',
|
||||
skipVerification = false,
|
||||
alreadyHaveEvent = null,
|
||||
id = Math.random().toString().slice(2)
|
||||
@@ -233,7 +262,7 @@ export function relayInit(
|
||||
skipVerification,
|
||||
alreadyHaveEvent
|
||||
}
|
||||
trySend(['REQ', subid, ...filters])
|
||||
trySend([verb, subid, ...filters])
|
||||
|
||||
return {
|
||||
sub: (newFilters, newOpts = {}) =>
|
||||
@@ -253,6 +282,7 @@ export function relayInit(
|
||||
): void => {
|
||||
subListeners[subid] = subListeners[subid] || {
|
||||
event: [],
|
||||
count: [],
|
||||
eose: []
|
||||
}
|
||||
subListeners[subid][type].push(cb)
|
||||
@@ -268,6 +298,29 @@ export function relayInit(
|
||||
}
|
||||
}
|
||||
|
||||
function _publishEvent(event: Event, type: string) {
|
||||
if (!event.id) throw new Error(`event ${event} has no id`)
|
||||
let id = event.id
|
||||
|
||||
trySend([type, event])
|
||||
|
||||
return {
|
||||
on: (type: 'ok' | 'failed', cb: any) => {
|
||||
pubListeners[id] = pubListeners[id] || {
|
||||
ok: [],
|
||||
failed: []
|
||||
}
|
||||
pubListeners[id][type].push(cb)
|
||||
},
|
||||
off: (type: 'ok' | 'failed', cb: any) => {
|
||||
let listeners = pubListeners[id]
|
||||
if (!listeners) return
|
||||
let idx = listeners[type].indexOf(cb)
|
||||
if (idx >= 0) listeners[type].splice(idx, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
url,
|
||||
sub,
|
||||
@@ -318,31 +371,28 @@ export function relayInit(
|
||||
resolve(event)
|
||||
})
|
||||
}),
|
||||
publish(event: Event): Pub {
|
||||
if (!event.id) throw new Error(`event ${event} has no id`)
|
||||
let id = event.id
|
||||
|
||||
trySend(['EVENT', event])
|
||||
|
||||
return {
|
||||
on: (type: 'ok' | 'failed', cb: any) => {
|
||||
pubListeners[id] = pubListeners[id] || {
|
||||
ok: [],
|
||||
failed: []
|
||||
}
|
||||
pubListeners[id][type].push(cb)
|
||||
},
|
||||
off: (type: 'ok' | 'failed', cb: any) => {
|
||||
let listeners = pubListeners[id]
|
||||
if (!listeners) return
|
||||
let idx = listeners[type].indexOf(cb)
|
||||
if (idx >= 0) listeners[type].splice(idx, 1)
|
||||
}
|
||||
}
|
||||
count: (filters: Filter[]): Promise<CountPayload | null> =>
|
||||
new Promise(resolve => {
|
||||
let s = sub(filters, {...sub, verb: 'COUNT'})
|
||||
let timeout = setTimeout(() => {
|
||||
s.unsub()
|
||||
resolve(null)
|
||||
}, countTimeout)
|
||||
s.on('count', (event: CountPayload) => {
|
||||
s.unsub()
|
||||
clearTimeout(timeout)
|
||||
resolve(event)
|
||||
})
|
||||
}),
|
||||
publish(event): Pub {
|
||||
return _publishEvent(event, 'EVENT')
|
||||
},
|
||||
auth(event): Pub {
|
||||
return _publishEvent(event, 'AUTH')
|
||||
},
|
||||
connect,
|
||||
close(): void {
|
||||
listeners = {connect: [], disconnect: [], error: [], notice: []}
|
||||
listeners = newListeners()
|
||||
subListeners = {}
|
||||
pubListeners = {}
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
|
||||
166
yarn.lock
166
yarn.lock
@@ -705,17 +705,15 @@
|
||||
"@jridgewell/resolve-uri" "3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "1.4.14"
|
||||
|
||||
"@noble/curves@1.0.0", "@noble/curves@~1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.0.0.tgz#e40be8c7daf088aaf291887cbc73f43464a92932"
|
||||
integrity sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==
|
||||
dependencies:
|
||||
"@noble/hashes" "1.3.0"
|
||||
"@noble/hashes@1.2.0", "@noble/hashes@~1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12"
|
||||
integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==
|
||||
|
||||
"@noble/hashes@1.3.0", "@noble/hashes@~1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1"
|
||||
integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==
|
||||
"@noble/secp256k1@1.7.1", "@noble/secp256k1@~1.7.0":
|
||||
version "1.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c"
|
||||
integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
@@ -743,21 +741,21 @@
|
||||
resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938"
|
||||
integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==
|
||||
|
||||
"@scure/bip32@1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.0.tgz#6c8d980ef3f290987736acd0ee2e0f0d50068d87"
|
||||
integrity sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q==
|
||||
"@scure/bip32@1.1.4":
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.4.tgz#2c91a7be0156b15f26dd0c843a06a1917f129efd"
|
||||
integrity sha512-m925ACYK0wPELsF7Z/VdLGmKj1StIeHraPMYB9xiAFiq/PnvqWd/99I0TQ2OZhjjlMDsDJeZlyXMWi0beaA7NA==
|
||||
dependencies:
|
||||
"@noble/curves" "~1.0.0"
|
||||
"@noble/hashes" "~1.3.0"
|
||||
"@noble/hashes" "~1.2.0"
|
||||
"@noble/secp256k1" "~1.7.0"
|
||||
"@scure/base" "~1.1.0"
|
||||
|
||||
"@scure/bip39@1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.0.tgz#a207e2ef96de354de7d0002292ba1503538fc77b"
|
||||
integrity sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg==
|
||||
"@scure/bip39@1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.1.tgz#b54557b2e86214319405db819c4b6a370cf340c5"
|
||||
integrity sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==
|
||||
dependencies:
|
||||
"@noble/hashes" "~1.3.0"
|
||||
"@noble/hashes" "~1.2.0"
|
||||
"@scure/base" "~1.1.0"
|
||||
|
||||
"@sinclair/typebox@^0.25.16":
|
||||
@@ -904,14 +902,14 @@
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^5.51.0":
|
||||
version "5.58.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.58.0.tgz#b1d4b0ad20243269d020ef9bbb036a40b0849829"
|
||||
integrity sha512-vxHvLhH0qgBd3/tW6/VccptSfc8FxPQIkmNTVLWcCOVqSBvqpnKkBTYrhcGlXfSnd78azwe+PsjYFj0X34/njA==
|
||||
version "5.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.0.tgz#c0e10eeb936debe5d1c3433cf36206a95befefd0"
|
||||
integrity sha512-p0QgrEyrxAWBecR56gyn3wkG15TJdI//eetInP3zYRewDh0XS+DhB3VUAd3QqvziFsfaQIoIuZMxZRB7vXYaYw==
|
||||
dependencies:
|
||||
"@eslint-community/regexpp" "^4.4.0"
|
||||
"@typescript-eslint/scope-manager" "5.58.0"
|
||||
"@typescript-eslint/type-utils" "5.58.0"
|
||||
"@typescript-eslint/utils" "5.58.0"
|
||||
"@typescript-eslint/scope-manager" "5.59.0"
|
||||
"@typescript-eslint/type-utils" "5.59.0"
|
||||
"@typescript-eslint/utils" "5.59.0"
|
||||
debug "^4.3.4"
|
||||
grapheme-splitter "^1.0.4"
|
||||
ignore "^5.2.0"
|
||||
@@ -920,71 +918,71 @@
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/parser@^5.51.0":
|
||||
version "5.58.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.58.0.tgz#2ac4464cf48bef2e3234cb178ede5af352dddbc6"
|
||||
integrity sha512-ixaM3gRtlfrKzP8N6lRhBbjTow1t6ztfBvQNGuRM8qH1bjFFXIJ35XY+FC0RRBKn3C6cT+7VW1y8tNm7DwPHDQ==
|
||||
version "5.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.0.tgz#0ad7cd019346cc5d150363f64869eca10ca9977c"
|
||||
integrity sha512-qK9TZ70eJtjojSUMrrEwA9ZDQ4N0e/AuoOIgXuNBorXYcBDk397D2r5MIe1B3cok/oCtdNC5j+lUUpVB+Dpb+w==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "5.58.0"
|
||||
"@typescript-eslint/types" "5.58.0"
|
||||
"@typescript-eslint/typescript-estree" "5.58.0"
|
||||
"@typescript-eslint/scope-manager" "5.59.0"
|
||||
"@typescript-eslint/types" "5.59.0"
|
||||
"@typescript-eslint/typescript-estree" "5.59.0"
|
||||
debug "^4.3.4"
|
||||
|
||||
"@typescript-eslint/scope-manager@5.58.0":
|
||||
version "5.58.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.58.0.tgz#5e023a48352afc6a87be6ce3c8e763bc9e2f0bc8"
|
||||
integrity sha512-b+w8ypN5CFvrXWQb9Ow9T4/6LC2MikNf1viLkYTiTbkQl46CnR69w7lajz1icW0TBsYmlpg+mRzFJ4LEJ8X9NA==
|
||||
"@typescript-eslint/scope-manager@5.59.0":
|
||||
version "5.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.0.tgz#86501d7a17885710b6716a23be2e93fc54a4fe8c"
|
||||
integrity sha512-tsoldKaMh7izN6BvkK6zRMINj4Z2d6gGhO2UsI8zGZY3XhLq1DndP3Ycjhi1JwdwPRwtLMW4EFPgpuKhbCGOvQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.58.0"
|
||||
"@typescript-eslint/visitor-keys" "5.58.0"
|
||||
"@typescript-eslint/types" "5.59.0"
|
||||
"@typescript-eslint/visitor-keys" "5.59.0"
|
||||
|
||||
"@typescript-eslint/type-utils@5.58.0":
|
||||
version "5.58.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.58.0.tgz#f7d5b3971483d4015a470d8a9e5b8a7d10066e52"
|
||||
integrity sha512-FF5vP/SKAFJ+LmR9PENql7fQVVgGDOS+dq3j+cKl9iW/9VuZC/8CFmzIP0DLKXfWKpRHawJiG70rVH+xZZbp8w==
|
||||
"@typescript-eslint/type-utils@5.59.0":
|
||||
version "5.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.0.tgz#8e8d1420fc2265989fa3a0d897bde37f3851e8c9"
|
||||
integrity sha512-d/B6VSWnZwu70kcKQSCqjcXpVH+7ABKH8P1KNn4K7j5PXXuycZTPXF44Nui0TEm6rbWGi8kc78xRgOC4n7xFgA==
|
||||
dependencies:
|
||||
"@typescript-eslint/typescript-estree" "5.58.0"
|
||||
"@typescript-eslint/utils" "5.58.0"
|
||||
"@typescript-eslint/typescript-estree" "5.59.0"
|
||||
"@typescript-eslint/utils" "5.59.0"
|
||||
debug "^4.3.4"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/types@5.58.0":
|
||||
version "5.58.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.58.0.tgz#54c490b8522c18986004df7674c644ffe2ed77d8"
|
||||
integrity sha512-JYV4eITHPzVQMnHZcYJXl2ZloC7thuUHrcUmxtzvItyKPvQ50kb9QXBkgNAt90OYMqwaodQh2kHutWZl1fc+1g==
|
||||
"@typescript-eslint/types@5.59.0":
|
||||
version "5.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.0.tgz#3fcdac7dbf923ec5251545acdd9f1d42d7c4fe32"
|
||||
integrity sha512-yR2h1NotF23xFFYKHZs17QJnB51J/s+ud4PYU4MqdZbzeNxpgUr05+dNeCN/bb6raslHvGdd6BFCkVhpPk/ZeA==
|
||||
|
||||
"@typescript-eslint/typescript-estree@5.58.0":
|
||||
version "5.58.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.58.0.tgz#4966e6ff57eaf6e0fce2586497edc097e2ab3e61"
|
||||
integrity sha512-cRACvGTodA+UxnYM2uwA2KCwRL7VAzo45syNysqlMyNyjw0Z35Icc9ihPJZjIYuA5bXJYiJ2YGUB59BqlOZT1Q==
|
||||
"@typescript-eslint/typescript-estree@5.59.0":
|
||||
version "5.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.0.tgz#8869156ee1dcfc5a95be3ed0e2809969ea28e965"
|
||||
integrity sha512-sUNnktjmI8DyGzPdZ8dRwW741zopGxltGs/SAPgGL/AAgDpiLsCFLcMNSpbfXfmnNeHmK9h3wGmCkGRGAoUZAg==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.58.0"
|
||||
"@typescript-eslint/visitor-keys" "5.58.0"
|
||||
"@typescript-eslint/types" "5.59.0"
|
||||
"@typescript-eslint/visitor-keys" "5.59.0"
|
||||
debug "^4.3.4"
|
||||
globby "^11.1.0"
|
||||
is-glob "^4.0.3"
|
||||
semver "^7.3.7"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/utils@5.58.0":
|
||||
version "5.58.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.58.0.tgz#430d7c95f23ec457b05be5520c1700a0dfd559d5"
|
||||
integrity sha512-gAmLOTFXMXOC+zP1fsqm3VceKSBQJNzV385Ok3+yzlavNHZoedajjS4UyS21gabJYcobuigQPs/z71A9MdJFqQ==
|
||||
"@typescript-eslint/utils@5.59.0":
|
||||
version "5.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.0.tgz#063d066b3bc4850c18872649ed0da9ee72d833d5"
|
||||
integrity sha512-GGLFd+86drlHSvPgN/el6dRQNYYGOvRSDVydsUaQluwIW3HvbXuxyuD5JETvBt/9qGYe+lOrDk6gRrWOHb/FvA==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.2.0"
|
||||
"@types/json-schema" "^7.0.9"
|
||||
"@types/semver" "^7.3.12"
|
||||
"@typescript-eslint/scope-manager" "5.58.0"
|
||||
"@typescript-eslint/types" "5.58.0"
|
||||
"@typescript-eslint/typescript-estree" "5.58.0"
|
||||
"@typescript-eslint/scope-manager" "5.59.0"
|
||||
"@typescript-eslint/types" "5.59.0"
|
||||
"@typescript-eslint/typescript-estree" "5.59.0"
|
||||
eslint-scope "^5.1.1"
|
||||
semver "^7.3.7"
|
||||
|
||||
"@typescript-eslint/visitor-keys@5.58.0":
|
||||
version "5.58.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.58.0.tgz#eb9de3a61d2331829e6761ce7fd13061781168b4"
|
||||
integrity sha512-/fBraTlPj0jwdyTwLyrRTxv/3lnU2H96pNTVM6z3esTWLtA5MZ9ghSMJ7Rb+TtUAdtEw9EyJzJ0EydIMKxQ9gA==
|
||||
"@typescript-eslint/visitor-keys@5.59.0":
|
||||
version "5.59.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.0.tgz#a59913f2bf0baeb61b5cfcb6135d3926c3854365"
|
||||
integrity sha512-qZ3iXxQhanchCeaExlKPV3gDQFxMUmU35xfd5eCXB6+kUw1TUAbIy2n7QIrwz9s98DQLzNWyHp61fY0da4ZcbA==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.58.0"
|
||||
"@typescript-eslint/types" "5.59.0"
|
||||
eslint-visitor-keys "^3.3.0"
|
||||
|
||||
acorn-jsx@^5.3.2:
|
||||
@@ -1230,9 +1228,9 @@ camelcase@^6.2.0:
|
||||
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
|
||||
|
||||
caniuse-lite@^1.0.30001449:
|
||||
version "1.0.30001478"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001478.tgz#0ef8a1cf8b16be47a0f9fc4ecfc952232724b32a"
|
||||
integrity sha512-gMhDyXGItTHipJj2ApIvR+iVB5hd0KP3svMWWXDvZOmjzJJassGLMfxRkQCSYgGd2gtdL/ReeiyvMSFD1Ss6Mw==
|
||||
version "1.0.30001480"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001480.tgz#9bbd35ee44c2480a1e3a3b9f4496f5066817164a"
|
||||
integrity sha512-q7cpoPPvZYgtyC4VaBSN0Bt+PJ4c4EYRf0DrduInOz2SkFpHD5p3LnvEpqBp7UnJn+8x1Ogl1s38saUxe+ihQQ==
|
||||
|
||||
chalk@^2.0.0, chalk@^2.4.1:
|
||||
version "2.4.2"
|
||||
@@ -1434,9 +1432,9 @@ doctrine@^3.0.0:
|
||||
esutils "^2.0.2"
|
||||
|
||||
electron-to-chromium@^1.4.284:
|
||||
version "1.4.364"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.364.tgz#da9702efd0101627f250f5e90a90a829feb41d7f"
|
||||
integrity sha512-v6GxKdF57qfweXSfnne9nw1vS/86G4+UtscEe+3HQF+zhhrjAY4+9A4gstIQO56gyZvVrt9MZwt9aevCz/tohQ==
|
||||
version "1.4.367"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.367.tgz#d9ddc529ba2315fc852b722c359e4a40e86aa742"
|
||||
integrity sha512-mNuDxb+HpLhPGUKrg0hSxbTjHWw8EziwkwlJNkFUj3W60ypigLDRVz04vU+VRsJPi8Gub+FDhYUpuTm9xiEwRQ==
|
||||
|
||||
emittery@^0.13.1:
|
||||
version "0.13.1"
|
||||
@@ -2182,7 +2180,7 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7:
|
||||
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
|
||||
integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
|
||||
|
||||
is-core-module@^2.12.0, is-core-module@^2.5.0:
|
||||
is-core-module@^2.11.0, is-core-module@^2.5.0:
|
||||
version "2.12.0"
|
||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.0.tgz#36ad62f6f73c8253fd6472517a12483cf03e7ec4"
|
||||
integrity sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==
|
||||
@@ -3354,11 +3352,11 @@ resolve.exports@^2.0.0:
|
||||
integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==
|
||||
|
||||
resolve@^1.10.0, resolve@^1.20.0:
|
||||
version "1.22.3"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.3.tgz#4b4055349ffb962600972da1fdc33c46a4eb3283"
|
||||
integrity sha512-P8ur/gp/AmbEzjr729bZnLjXK5Z+4P0zhIJgBgzqRih7hL7BOukHGtSTA3ACMY467GRFz3duQsi0bDZdR7DKdw==
|
||||
version "1.22.2"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f"
|
||||
integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==
|
||||
dependencies:
|
||||
is-core-module "^2.12.0"
|
||||
is-core-module "^2.11.0"
|
||||
path-parse "^1.0.7"
|
||||
supports-preserve-symlinks-flag "^1.0.0"
|
||||
|
||||
@@ -3396,9 +3394,9 @@ safe-regex-test@^1.0.0:
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
|
||||
semver@7.x, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8:
|
||||
version "7.4.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.4.0.tgz#8481c92feffc531ab1e012a8ffc15bdd3a0f4318"
|
||||
integrity sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0"
|
||||
integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
@@ -3800,9 +3798,9 @@ unbox-primitive@^1.0.2:
|
||||
which-boxed-primitive "^1.0.2"
|
||||
|
||||
update-browserslist-db@^1.0.10:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3"
|
||||
integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940"
|
||||
integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==
|
||||
dependencies:
|
||||
escalade "^3.1.1"
|
||||
picocolors "^1.0.0"
|
||||
|
||||
Reference in New Issue
Block a user