nostr-tools 2.23.5 → 2.23.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -15
- package/lib/cjs/abstract-pool.js +6 -2
- package/lib/cjs/abstract-pool.js.map +2 -2
- package/lib/cjs/abstract-relay.js +6 -2
- package/lib/cjs/abstract-relay.js.map +2 -2
- package/lib/cjs/index.js +24 -11
- package/lib/cjs/index.js.map +3 -3
- package/lib/cjs/nip10.js +5 -4
- package/lib/cjs/nip10.js.map +2 -2
- package/lib/cjs/nip19.js +2 -0
- package/lib/cjs/nip19.js.map +2 -2
- package/lib/cjs/nip21.js +2 -0
- package/lib/cjs/nip21.js.map +2 -2
- package/lib/cjs/nip22.js +11 -5
- package/lib/cjs/nip22.js.map +2 -2
- package/lib/cjs/nip27.js +2 -0
- package/lib/cjs/nip27.js.map +2 -2
- package/lib/cjs/nip29.js +2 -0
- package/lib/cjs/nip29.js.map +2 -2
- package/lib/cjs/nip46.js +21 -5
- package/lib/cjs/nip46.js.map +2 -2
- package/lib/cjs/nip49.js.map +2 -2
- package/lib/cjs/pool.js +6 -2
- package/lib/cjs/pool.js.map +2 -2
- package/lib/cjs/references.js +2 -0
- package/lib/cjs/references.js.map +2 -2
- package/lib/cjs/relay.js +6 -2
- package/lib/cjs/relay.js.map +2 -2
- package/lib/esm/abstract-pool.js +6 -2
- package/lib/esm/abstract-pool.js.map +2 -2
- package/lib/esm/abstract-relay.js +6 -2
- package/lib/esm/abstract-relay.js.map +2 -2
- package/lib/esm/index.js +24 -11
- package/lib/esm/index.js.map +3 -3
- package/lib/esm/nip10.js +5 -4
- package/lib/esm/nip10.js.map +2 -2
- package/lib/esm/nip19.js +2 -0
- package/lib/esm/nip19.js.map +2 -2
- package/lib/esm/nip21.js +2 -0
- package/lib/esm/nip21.js.map +2 -2
- package/lib/esm/nip22.js +11 -5
- package/lib/esm/nip22.js.map +2 -2
- package/lib/esm/nip27.js +2 -0
- package/lib/esm/nip27.js.map +2 -2
- package/lib/esm/nip29.js +2 -0
- package/lib/esm/nip29.js.map +2 -2
- package/lib/esm/nip46.js +21 -5
- package/lib/esm/nip46.js.map +2 -2
- package/lib/esm/nip49.js.map +2 -2
- package/lib/esm/pool.js +6 -2
- package/lib/esm/pool.js.map +2 -2
- package/lib/esm/references.js +2 -0
- package/lib/esm/references.js.map +2 -2
- package/lib/esm/relay.js +6 -2
- package/lib/esm/relay.js.map +2 -2
- package/lib/nostr.bundle.js +24 -11
- package/lib/nostr.bundle.js.map +3 -3
- package/lib/types/nip46.d.ts +12 -5
- package/package.json +1 -1
package/lib/cjs/nip10.js
CHANGED
|
@@ -23,6 +23,7 @@ __export(nip10_exports, {
|
|
|
23
23
|
parse: () => parse
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(nip10_exports);
|
|
26
|
+
var HEX64 = /^[0-9a-fA-F]{64}$/;
|
|
26
27
|
function parse(event) {
|
|
27
28
|
const result = {
|
|
28
29
|
reply: void 0,
|
|
@@ -35,12 +36,12 @@ function parse(event) {
|
|
|
35
36
|
let maybeRoot;
|
|
36
37
|
for (let i = event.tags.length - 1; i >= 0; i--) {
|
|
37
38
|
const tag = event.tags[i];
|
|
38
|
-
if (tag[0] === "e" && tag[1]) {
|
|
39
|
+
if (tag[0] === "e" && tag[1] && HEX64.test(tag[1])) {
|
|
39
40
|
const [_, eTagEventId, eTagRelayUrl, eTagMarker, eTagAuthor] = tag;
|
|
40
41
|
const eventPointer = {
|
|
41
42
|
id: eTagEventId,
|
|
42
43
|
relays: eTagRelayUrl ? [eTagRelayUrl] : [],
|
|
43
|
-
author: eTagAuthor
|
|
44
|
+
author: eTagAuthor && HEX64.test(eTagAuthor) ? eTagAuthor : void 0
|
|
44
45
|
};
|
|
45
46
|
if (eTagMarker === "root") {
|
|
46
47
|
result.root = eventPointer;
|
|
@@ -62,14 +63,14 @@ function parse(event) {
|
|
|
62
63
|
result.mentions.push(eventPointer);
|
|
63
64
|
continue;
|
|
64
65
|
}
|
|
65
|
-
if (tag[0] === "q" && tag[1]) {
|
|
66
|
+
if (tag[0] === "q" && tag[1] && HEX64.test(tag[1])) {
|
|
66
67
|
const [_, eTagEventId, eTagRelayUrl] = tag;
|
|
67
68
|
result.quotes.push({
|
|
68
69
|
id: eTagEventId,
|
|
69
70
|
relays: eTagRelayUrl ? [eTagRelayUrl] : []
|
|
70
71
|
});
|
|
71
72
|
}
|
|
72
|
-
if (tag[0] === "p" && tag[1]) {
|
|
73
|
+
if (tag[0] === "p" && tag[1] && HEX64.test(tag[1])) {
|
|
73
74
|
result.profiles.push({
|
|
74
75
|
pubkey: tag[1],
|
|
75
76
|
relays: tag[2] ? [tag[2]] : []
|
package/lib/cjs/nip10.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../nip10.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Event } from './core.ts'\nimport type { EventPointer, ProfilePointer } from './nip19.ts'\n\nexport function parse(event: Pick<Event, 'tags'>): {\n /**\n * Pointer to the root of the thread.\n */\n root: EventPointer | undefined\n\n /**\n * Pointer to a \"parent\" event that parsed event replies to (responded to).\n */\n reply: EventPointer | undefined\n\n /**\n * Pointers to events that may or may not be in the reply chain.\n */\n mentions: EventPointer[]\n\n /**\n * Pointers to events that were directly quoted.\n */\n quotes: EventPointer[]\n\n /**\n * List of pubkeys that are involved in the thread in no particular order.\n */\n profiles: ProfilePointer[]\n} {\n const result: ReturnType<typeof parse> = {\n reply: undefined,\n root: undefined,\n mentions: [],\n profiles: [],\n quotes: [],\n }\n\n let maybeParent: EventPointer | undefined\n let maybeRoot: EventPointer | undefined\n\n for (let i = event.tags.length - 1; i >= 0; i--) {\n const tag = event.tags[i]\n\n if (tag[0] === 'e' && tag[1]) {\n const [_, eTagEventId, eTagRelayUrl, eTagMarker, eTagAuthor] = tag as [\n string,\n string,\n undefined | string,\n undefined | string,\n undefined | string,\n ]\n\n const eventPointer: EventPointer = {\n id: eTagEventId,\n relays: eTagRelayUrl ? [eTagRelayUrl] : [],\n author: eTagAuthor,\n }\n\n if (eTagMarker === 'root') {\n result.root = eventPointer\n continue\n }\n\n if (eTagMarker === 'reply') {\n result.reply = eventPointer\n continue\n }\n\n if (eTagMarker === 'mention') {\n result.mentions.push(eventPointer)\n continue\n }\n\n if (!maybeParent) {\n maybeParent = eventPointer\n } else {\n maybeRoot = eventPointer\n }\n\n result.mentions.push(eventPointer)\n continue\n }\n\n if (tag[0] === 'q' && tag[1]) {\n const [_, eTagEventId, eTagRelayUrl] = tag as [string, string, undefined | string]\n result.quotes.push({\n id: eTagEventId,\n relays: eTagRelayUrl ? [eTagRelayUrl] : [],\n })\n }\n\n if (tag[0] === 'p' && tag[1]) {\n result.profiles.push({\n pubkey: tag[1],\n relays: tag[2] ? [tag[2]] : [],\n })\n continue\n }\n }\n\n // get legacy (positional) markers, set reply to root and vice-versa if one of them is missing\n if (!result.root) {\n result.root = maybeRoot || maybeParent || result.reply\n }\n if (!result.reply) {\n result.reply = maybeParent || result.root\n }\n\n // remove root and reply from mentions, inherit relay hints from authors if any\n ;[result.reply, result.root].forEach(ref => {\n if (!ref) return\n\n let idx = result.mentions.indexOf(ref)\n if (idx !== -1) {\n result.mentions.splice(idx, 1)\n }\n if (ref.author) {\n let author = result.profiles.find(p => p.pubkey === ref.author)\n if (author && author.relays) {\n if (!ref.relays) {\n ref.relays = []\n }\n author.relays.forEach(url => {\n if (ref.relays!?.indexOf(url) === -1) ref.relays!.push(url)\n })\n author.relays = ref.relays\n }\n }\n })\n\n result.mentions.forEach(ref => {\n if (ref!.author) {\n let author = result.profiles.find(p => p.pubkey === ref.author)\n if (author && author.relays) {\n if (!ref.relays) {\n ref.relays = []\n }\n author.relays.forEach(url => {\n if (ref.relays!.indexOf(url) === -1) ref.relays!.push(url)\n })\n author.relays = ref.relays\n }\n }\n })\n\n return result\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
4
|
+
"sourcesContent": ["import type { Event } from './core.ts'\nimport type { EventPointer, ProfilePointer } from './nip19.ts'\n\nconst HEX64 = /^[0-9a-fA-F]{64}$/\n\nexport function parse(event: Pick<Event, 'tags'>): {\n /**\n * Pointer to the root of the thread.\n */\n root: EventPointer | undefined\n\n /**\n * Pointer to a \"parent\" event that parsed event replies to (responded to).\n */\n reply: EventPointer | undefined\n\n /**\n * Pointers to events that may or may not be in the reply chain.\n */\n mentions: EventPointer[]\n\n /**\n * Pointers to events that were directly quoted.\n */\n quotes: EventPointer[]\n\n /**\n * List of pubkeys that are involved in the thread in no particular order.\n */\n profiles: ProfilePointer[]\n} {\n const result: ReturnType<typeof parse> = {\n reply: undefined,\n root: undefined,\n mentions: [],\n profiles: [],\n quotes: [],\n }\n\n let maybeParent: EventPointer | undefined\n let maybeRoot: EventPointer | undefined\n\n for (let i = event.tags.length - 1; i >= 0; i--) {\n const tag = event.tags[i]\n\n if (tag[0] === 'e' && tag[1] && HEX64.test(tag[1])) {\n const [_, eTagEventId, eTagRelayUrl, eTagMarker, eTagAuthor] = tag as [\n string,\n string,\n undefined | string,\n undefined | string,\n undefined | string,\n ]\n\n const eventPointer: EventPointer = {\n id: eTagEventId,\n relays: eTagRelayUrl ? [eTagRelayUrl] : [],\n author: eTagAuthor && HEX64.test(eTagAuthor) ? eTagAuthor : undefined,\n }\n\n if (eTagMarker === 'root') {\n result.root = eventPointer\n continue\n }\n\n if (eTagMarker === 'reply') {\n result.reply = eventPointer\n continue\n }\n\n if (eTagMarker === 'mention') {\n result.mentions.push(eventPointer)\n continue\n }\n\n if (!maybeParent) {\n maybeParent = eventPointer\n } else {\n maybeRoot = eventPointer\n }\n\n result.mentions.push(eventPointer)\n continue\n }\n\n if (tag[0] === 'q' && tag[1] && HEX64.test(tag[1])) {\n const [_, eTagEventId, eTagRelayUrl] = tag as [string, string, undefined | string]\n result.quotes.push({\n id: eTagEventId,\n relays: eTagRelayUrl ? [eTagRelayUrl] : [],\n })\n }\n\n if (tag[0] === 'p' && tag[1] && HEX64.test(tag[1])) {\n result.profiles.push({\n pubkey: tag[1],\n relays: tag[2] ? [tag[2]] : [],\n })\n continue\n }\n }\n\n // get legacy (positional) markers, set reply to root and vice-versa if one of them is missing\n if (!result.root) {\n result.root = maybeRoot || maybeParent || result.reply\n }\n if (!result.reply) {\n result.reply = maybeParent || result.root\n }\n\n // remove root and reply from mentions, inherit relay hints from authors if any\n ;[result.reply, result.root].forEach(ref => {\n if (!ref) return\n\n let idx = result.mentions.indexOf(ref)\n if (idx !== -1) {\n result.mentions.splice(idx, 1)\n }\n if (ref.author) {\n let author = result.profiles.find(p => p.pubkey === ref.author)\n if (author && author.relays) {\n if (!ref.relays) {\n ref.relays = []\n }\n author.relays.forEach(url => {\n if (ref.relays!?.indexOf(url) === -1) ref.relays!.push(url)\n })\n author.relays = ref.relays\n }\n }\n })\n\n result.mentions.forEach(ref => {\n if (ref!.author) {\n let author = result.profiles.find(p => p.pubkey === ref.author)\n if (author && author.relays) {\n if (!ref.relays) {\n ref.relays = []\n }\n author.relays.forEach(url => {\n if (ref.relays!.indexOf(url) === -1) ref.relays!.push(url)\n })\n author.relays = ref.relays\n }\n }\n })\n\n return result\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/lib/cjs/nip19.js
CHANGED
package/lib/cjs/nip19.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../nip19.ts", "../../utils.ts"],
|
|
4
|
-
"sourcesContent": ["import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils.js'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n const uint8Array = new Uint8Array(4)\n\n // Use bitwise operations to extract the bytes.\n uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n uint8Array[1] = (number >> 16) & 0xff\n uint8Array[2] = (number >> 8) & 0xff\n uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n return uint8Array\n}\n\nexport type ProfilePointer = {\n pubkey: string // hex\n relays?: string[]\n}\n\nexport type EventPointer = {\n id: string // hex\n relays?: string[]\n author?: string\n kind?: number\n}\n\nexport type AddressPointer = {\n identifier: string\n pubkey: string\n kind: number\n relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n try {\n if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n return decode(nip19code)\n } catch (_err) {\n return { type: 'invalid', data: null }\n }\n}\n\nexport type DecodedNevent = {\n type: 'nevent'\n data: EventPointer\n}\n\nexport type DecodedNprofile = {\n type: 'nprofile'\n data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n type: 'naddr'\n data: AddressPointer\n}\n\nexport type DecodedNsec = {\n type: 'nsec'\n data: Uint8Array\n}\n\nexport type DecodedNpub = {\n type: 'npub'\n data: string\n}\n\nexport type DecodedNote = {\n type: 'note'\n data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n let { prefix, words } = bech32.decode(code as `${string}1${string}`, Bech32MaxSize)\n let data = new Uint8Array(bech32.fromWords(words))\n\n switch (prefix) {\n case 'nprofile': {\n let tlv = parseTLV(data)\n if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n\n return {\n type: 'nprofile',\n data: {\n pubkey: bytesToHex(tlv[0][0]),\n relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n },\n }\n }\n case 'nevent': {\n let tlv = parseTLV(data)\n if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n return {\n type: 'nevent',\n data: {\n id: bytesToHex(tlv[0][0]),\n relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n },\n }\n }\n\n case 'naddr': {\n let tlv = parseTLV(data)\n if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n return {\n type: 'naddr',\n data: {\n identifier: utf8Decoder.decode(tlv[0][0]),\n pubkey: bytesToHex(tlv[2][0]),\n kind: parseInt(bytesToHex(tlv[3][0]), 16),\n relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n },\n }\n }\n\n case 'nsec':\n return { type: prefix, data }\n\n case 'npub':\n case 'note':\n return { type: prefix, data: bytesToHex(data) }\n\n default:\n throw new Error(`unknown prefix ${prefix}`)\n }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n let result: TLV = {}\n let rest = data\n while (rest.length > 0) {\n let t = rest[0]\n let l = rest[1]\n let v = rest.slice(2, 2 + l)\n rest = rest.slice(2 + l)\n if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n result[t] = result[t] || []\n result[t].push(v)\n }\n return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n let words = bech32.toWords(data)\n return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n let data = encodeTLV({\n 0: [hexToBytes(profile.pubkey)],\n 1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n })\n return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n let kindArray\n if (event.kind !== undefined) {\n kindArray = integerToUint8Array(event.kind)\n }\n\n let data = encodeTLV({\n 0: [hexToBytes(event.id)],\n 1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n 2: event.author ? [hexToBytes(event.author)] : [],\n 3: kindArray ? [new Uint8Array(kindArray)] : [],\n })\n\n return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n let kind = new ArrayBuffer(4)\n new DataView(kind).setUint32(0, addr.kind, false)\n\n let data = encodeTLV({\n 0: [utf8Encoder.encode(addr.identifier)],\n 1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n 2: [hexToBytes(addr.pubkey)],\n 3: [new Uint8Array(kind)],\n })\n return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n let entries: Uint8Array[] = []\n\n Object.entries(tlv)\n .reverse()\n .forEach(([t, vs]) => {\n vs.forEach(v => {\n let entry = new Uint8Array(v.length + 2)\n entry.set([parseInt(t)], 0)\n entry.set([v.length], 1)\n entry.set(v, 2)\n entries.push(entry)\n })\n })\n\n return concatBytes(...entries)\n}\n", "import type { NostrEvent } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n try {\n if (url.indexOf('://') === -1) url = 'wss://' + url\n let p = new URL(url)\n if (p.protocol === 'http:') p.protocol = 'ws:'\n else if (p.protocol === 'https:') p.protocol = 'wss:'\n p.pathname = p.pathname.replace(/\\/+/g, '/')\n if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n p.searchParams.sort()\n p.hash = ''\n return p.toString()\n } catch (e) {\n throw new Error(`Invalid URL: ${url}`)\n }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return b.created_at - event.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return event.created_at - b.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n let start = 0\n let end = arr.length - 1\n\n while (start <= end) {\n const mid = Math.floor((start + end) / 2)\n const cmp = compare(arr[mid])\n\n if (cmp === 0) {\n return [mid, true]\n }\n\n if (cmp < 0) {\n end = mid - 1\n } else {\n start = mid + 1\n }\n }\n\n return [start, false]\n}\n\nexport function mergeReverseSortedLists(list1: NostrEvent[], list2: NostrEvent[]): NostrEvent[] {\n const result: NostrEvent[] = new Array(list1.length + list2.length)\n result.length = 0\n let i1 = 0\n let i2 = 0\n let sameTimestampIds: string[] = []\n\n while (i1 < list1.length && i2 < list2.length) {\n let next: NostrEvent\n if (list1[i1]?.created_at > list2[i2]?.created_at) {\n next = list1[i1]\n i1++\n } else {\n next = list2[i2]\n i2++\n }\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i1 < list1.length) {\n const next = list1[i1]\n i1++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i2 < list2.length) {\n const next = list2[i2]\n i2++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n return result\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils.js'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n const uint8Array = new Uint8Array(4)\n\n // Use bitwise operations to extract the bytes.\n uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n uint8Array[1] = (number >> 16) & 0xff\n uint8Array[2] = (number >> 8) & 0xff\n uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n return uint8Array\n}\n\nexport type ProfilePointer = {\n pubkey: string // hex\n relays?: string[]\n}\n\nexport type EventPointer = {\n id: string // hex\n relays?: string[]\n author?: string\n kind?: number\n}\n\nexport type AddressPointer = {\n identifier: string\n pubkey: string\n kind: number\n relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n try {\n if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n return decode(nip19code)\n } catch (_err) {\n return { type: 'invalid', data: null }\n }\n}\n\nexport type DecodedNevent = {\n type: 'nevent'\n data: EventPointer\n}\n\nexport type DecodedNprofile = {\n type: 'nprofile'\n data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n type: 'naddr'\n data: AddressPointer\n}\n\nexport type DecodedNsec = {\n type: 'nsec'\n data: Uint8Array\n}\n\nexport type DecodedNpub = {\n type: 'npub'\n data: string\n}\n\nexport type DecodedNote = {\n type: 'note'\n data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n let { prefix, words } = bech32.decode(code as `${string}1${string}`, Bech32MaxSize)\n let data = new Uint8Array(bech32.fromWords(words))\n\n switch (prefix) {\n case 'nprofile': {\n let tlv = parseTLV(data)\n if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n\n return {\n type: 'nprofile',\n data: {\n pubkey: bytesToHex(tlv[0][0]),\n relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n },\n }\n }\n case 'nevent': {\n let tlv = parseTLV(data)\n if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n return {\n type: 'nevent',\n data: {\n id: bytesToHex(tlv[0][0]),\n relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n },\n }\n }\n\n case 'naddr': {\n let tlv = parseTLV(data)\n if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n return {\n type: 'naddr',\n data: {\n identifier: utf8Decoder.decode(tlv[0][0]),\n pubkey: bytesToHex(tlv[2][0]),\n kind: parseInt(bytesToHex(tlv[3][0]), 16),\n relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n },\n }\n }\n\n case 'nsec':\n return { type: prefix, data }\n\n case 'npub':\n case 'note':\n return { type: prefix, data: bytesToHex(data) }\n\n default:\n throw new Error(`unknown prefix ${prefix}`)\n }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n let result: TLV = {}\n let rest = data\n while (rest.length > 0) {\n if (rest.length < 2) throw new Error('not enough data to read TLV')\n let t = rest[0]\n let l = rest[1]\n let v = rest.slice(2, 2 + l)\n rest = rest.slice(2 + l)\n if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n result[t] = result[t] || []\n result[t].push(v)\n }\n return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n let words = bech32.toWords(data)\n return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n let data = encodeTLV({\n 0: [hexToBytes(profile.pubkey)],\n 1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n })\n return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n let kindArray\n if (event.kind !== undefined) {\n kindArray = integerToUint8Array(event.kind)\n }\n\n let data = encodeTLV({\n 0: [hexToBytes(event.id)],\n 1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n 2: event.author ? [hexToBytes(event.author)] : [],\n 3: kindArray ? [new Uint8Array(kindArray)] : [],\n })\n\n return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n let kind = new ArrayBuffer(4)\n new DataView(kind).setUint32(0, addr.kind, false)\n\n let data = encodeTLV({\n 0: [utf8Encoder.encode(addr.identifier)],\n 1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n 2: [hexToBytes(addr.pubkey)],\n 3: [new Uint8Array(kind)],\n })\n return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n let entries: Uint8Array[] = []\n\n Object.entries(tlv)\n .reverse()\n .forEach(([t, vs]) => {\n vs.forEach(v => {\n let entry = new Uint8Array(v.length + 2)\n entry.set([parseInt(t)], 0)\n entry.set([v.length], 1)\n entry.set(v, 2)\n entries.push(entry)\n })\n })\n\n return concatBytes(...entries)\n}\n", "import type { NostrEvent } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n try {\n if (url.indexOf('://') === -1) url = 'wss://' + url\n let p = new URL(url)\n if (p.protocol === 'http:') p.protocol = 'ws:'\n else if (p.protocol === 'https:') p.protocol = 'wss:'\n p.pathname = p.pathname.replace(/\\/+/g, '/')\n if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n p.searchParams.sort()\n p.hash = ''\n return p.toString()\n } catch (e) {\n throw new Error(`Invalid URL: ${url}`)\n }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return b.created_at - event.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return event.created_at - b.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n let start = 0\n let end = arr.length - 1\n\n while (start <= end) {\n const mid = Math.floor((start + end) / 2)\n const cmp = compare(arr[mid])\n\n if (cmp === 0) {\n return [mid, true]\n }\n\n if (cmp < 0) {\n end = mid - 1\n } else {\n start = mid + 1\n }\n }\n\n return [start, false]\n}\n\nexport function mergeReverseSortedLists(list1: NostrEvent[], list2: NostrEvent[]): NostrEvent[] {\n const result: NostrEvent[] = new Array(list1.length + list2.length)\n result.length = 0\n let i1 = 0\n let i2 = 0\n let sameTimestampIds: string[] = []\n\n while (i1 < list1.length && i2 < list2.length) {\n let next: NostrEvent\n if (list1[i1]?.created_at > list2[i2]?.created_at) {\n next = list1[i1]\n i1++\n } else {\n next = list2[i2]\n i2++\n }\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i1 < list1.length) {\n const next = list1[i1]\n i1++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i2 < list2.length) {\n const next = list2[i2]\n i2++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n return result\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["import_utils"]
|
|
7
7
|
}
|
package/lib/cjs/nip21.js
CHANGED
package/lib/cjs/nip21.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../nip21.ts", "../../nip19.ts", "../../utils.ts"],
|
|
4
|
-
"sourcesContent": ["import { AddressPointer, BECH32_REGEX, decode, EventPointer, ProfilePointer } from './nip19.ts'\n\n/** Nostr URI regex, eg `nostr:npub1...` */\nexport const NOSTR_URI_REGEX: RegExp = new RegExp(`nostr:(${BECH32_REGEX.source})`)\n\n/** Test whether the value is a Nostr URI. */\nexport function test(value: unknown): value is `nostr:${string}` {\n return typeof value === 'string' && new RegExp(`^${NOSTR_URI_REGEX.source}$`).test(value)\n}\n\n/** Parsed Nostr URI data. */\nexport interface NostrURI {\n /** Full URI including the `nostr:` protocol. */\n uri: `nostr:${string}`\n /** The bech32-encoded data (eg `npub1...`). */\n value: string\n /** Decoded bech32 string, according to NIP-19. */\n decoded:\n | {\n type: 'nevent'\n data: EventPointer\n }\n | {\n type: 'nprofile'\n data: ProfilePointer\n }\n | {\n type: 'naddr'\n data: AddressPointer\n }\n | {\n type: 'npub'\n data: string\n }\n | {\n type: 'nsec'\n data: Uint8Array\n }\n | {\n type: 'note'\n data: string\n }\n}\n\n/** Parse and decode a Nostr URI. */\nexport function parse(uri: string): NostrURI {\n const match = uri.match(new RegExp(`^${NOSTR_URI_REGEX.source}$`))\n if (!match) throw new Error(`Invalid Nostr URI: ${uri}`)\n return {\n uri: match[0] as `nostr:${string}`,\n value: match[1],\n decoded: decode(match[1]),\n }\n}\n", "import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils.js'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n const uint8Array = new Uint8Array(4)\n\n // Use bitwise operations to extract the bytes.\n uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n uint8Array[1] = (number >> 16) & 0xff\n uint8Array[2] = (number >> 8) & 0xff\n uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n return uint8Array\n}\n\nexport type ProfilePointer = {\n pubkey: string // hex\n relays?: string[]\n}\n\nexport type EventPointer = {\n id: string // hex\n relays?: string[]\n author?: string\n kind?: number\n}\n\nexport type AddressPointer = {\n identifier: string\n pubkey: string\n kind: number\n relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n try {\n if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n return decode(nip19code)\n } catch (_err) {\n return { type: 'invalid', data: null }\n }\n}\n\nexport type DecodedNevent = {\n type: 'nevent'\n data: EventPointer\n}\n\nexport type DecodedNprofile = {\n type: 'nprofile'\n data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n type: 'naddr'\n data: AddressPointer\n}\n\nexport type DecodedNsec = {\n type: 'nsec'\n data: Uint8Array\n}\n\nexport type DecodedNpub = {\n type: 'npub'\n data: string\n}\n\nexport type DecodedNote = {\n type: 'note'\n data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n let { prefix, words } = bech32.decode(code as `${string}1${string}`, Bech32MaxSize)\n let data = new Uint8Array(bech32.fromWords(words))\n\n switch (prefix) {\n case 'nprofile': {\n let tlv = parseTLV(data)\n if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n\n return {\n type: 'nprofile',\n data: {\n pubkey: bytesToHex(tlv[0][0]),\n relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n },\n }\n }\n case 'nevent': {\n let tlv = parseTLV(data)\n if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n return {\n type: 'nevent',\n data: {\n id: bytesToHex(tlv[0][0]),\n relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n },\n }\n }\n\n case 'naddr': {\n let tlv = parseTLV(data)\n if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n return {\n type: 'naddr',\n data: {\n identifier: utf8Decoder.decode(tlv[0][0]),\n pubkey: bytesToHex(tlv[2][0]),\n kind: parseInt(bytesToHex(tlv[3][0]), 16),\n relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n },\n }\n }\n\n case 'nsec':\n return { type: prefix, data }\n\n case 'npub':\n case 'note':\n return { type: prefix, data: bytesToHex(data) }\n\n default:\n throw new Error(`unknown prefix ${prefix}`)\n }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n let result: TLV = {}\n let rest = data\n while (rest.length > 0) {\n let t = rest[0]\n let l = rest[1]\n let v = rest.slice(2, 2 + l)\n rest = rest.slice(2 + l)\n if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n result[t] = result[t] || []\n result[t].push(v)\n }\n return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n let words = bech32.toWords(data)\n return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n let data = encodeTLV({\n 0: [hexToBytes(profile.pubkey)],\n 1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n })\n return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n let kindArray\n if (event.kind !== undefined) {\n kindArray = integerToUint8Array(event.kind)\n }\n\n let data = encodeTLV({\n 0: [hexToBytes(event.id)],\n 1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n 2: event.author ? [hexToBytes(event.author)] : [],\n 3: kindArray ? [new Uint8Array(kindArray)] : [],\n })\n\n return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n let kind = new ArrayBuffer(4)\n new DataView(kind).setUint32(0, addr.kind, false)\n\n let data = encodeTLV({\n 0: [utf8Encoder.encode(addr.identifier)],\n 1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n 2: [hexToBytes(addr.pubkey)],\n 3: [new Uint8Array(kind)],\n })\n return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n let entries: Uint8Array[] = []\n\n Object.entries(tlv)\n .reverse()\n .forEach(([t, vs]) => {\n vs.forEach(v => {\n let entry = new Uint8Array(v.length + 2)\n entry.set([parseInt(t)], 0)\n entry.set([v.length], 1)\n entry.set(v, 2)\n entries.push(entry)\n })\n })\n\n return concatBytes(...entries)\n}\n", "import type { NostrEvent } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n try {\n if (url.indexOf('://') === -1) url = 'wss://' + url\n let p = new URL(url)\n if (p.protocol === 'http:') p.protocol = 'ws:'\n else if (p.protocol === 'https:') p.protocol = 'wss:'\n p.pathname = p.pathname.replace(/\\/+/g, '/')\n if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n p.searchParams.sort()\n p.hash = ''\n return p.toString()\n } catch (e) {\n throw new Error(`Invalid URL: ${url}`)\n }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return b.created_at - event.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return event.created_at - b.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n let start = 0\n let end = arr.length - 1\n\n while (start <= end) {\n const mid = Math.floor((start + end) / 2)\n const cmp = compare(arr[mid])\n\n if (cmp === 0) {\n return [mid, true]\n }\n\n if (cmp < 0) {\n end = mid - 1\n } else {\n start = mid + 1\n }\n }\n\n return [start, false]\n}\n\nexport function mergeReverseSortedLists(list1: NostrEvent[], list2: NostrEvent[]): NostrEvent[] {\n const result: NostrEvent[] = new Array(list1.length + list2.length)\n result.length = 0\n let i1 = 0\n let i2 = 0\n let sameTimestampIds: string[] = []\n\n while (i1 < list1.length && i2 < list2.length) {\n let next: NostrEvent\n if (list1[i1]?.created_at > list2[i2]?.created_at) {\n next = list1[i1]\n i1++\n } else {\n next = list2[i2]\n i2++\n }\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i1 < list1.length) {\n const next = list1[i1]\n i1++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i2 < list2.length) {\n const next = list2[i2]\n i2++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n return result\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { AddressPointer, BECH32_REGEX, decode, EventPointer, ProfilePointer } from './nip19.ts'\n\n/** Nostr URI regex, eg `nostr:npub1...` */\nexport const NOSTR_URI_REGEX: RegExp = new RegExp(`nostr:(${BECH32_REGEX.source})`)\n\n/** Test whether the value is a Nostr URI. */\nexport function test(value: unknown): value is `nostr:${string}` {\n return typeof value === 'string' && new RegExp(`^${NOSTR_URI_REGEX.source}$`).test(value)\n}\n\n/** Parsed Nostr URI data. */\nexport interface NostrURI {\n /** Full URI including the `nostr:` protocol. */\n uri: `nostr:${string}`\n /** The bech32-encoded data (eg `npub1...`). */\n value: string\n /** Decoded bech32 string, according to NIP-19. */\n decoded:\n | {\n type: 'nevent'\n data: EventPointer\n }\n | {\n type: 'nprofile'\n data: ProfilePointer\n }\n | {\n type: 'naddr'\n data: AddressPointer\n }\n | {\n type: 'npub'\n data: string\n }\n | {\n type: 'nsec'\n data: Uint8Array\n }\n | {\n type: 'note'\n data: string\n }\n}\n\n/** Parse and decode a Nostr URI. */\nexport function parse(uri: string): NostrURI {\n const match = uri.match(new RegExp(`^${NOSTR_URI_REGEX.source}$`))\n if (!match) throw new Error(`Invalid Nostr URI: ${uri}`)\n return {\n uri: match[0] as `nostr:${string}`,\n value: match[1],\n decoded: decode(match[1]),\n }\n}\n", "import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils.js'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n const uint8Array = new Uint8Array(4)\n\n // Use bitwise operations to extract the bytes.\n uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n uint8Array[1] = (number >> 16) & 0xff\n uint8Array[2] = (number >> 8) & 0xff\n uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n return uint8Array\n}\n\nexport type ProfilePointer = {\n pubkey: string // hex\n relays?: string[]\n}\n\nexport type EventPointer = {\n id: string // hex\n relays?: string[]\n author?: string\n kind?: number\n}\n\nexport type AddressPointer = {\n identifier: string\n pubkey: string\n kind: number\n relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n try {\n if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n return decode(nip19code)\n } catch (_err) {\n return { type: 'invalid', data: null }\n }\n}\n\nexport type DecodedNevent = {\n type: 'nevent'\n data: EventPointer\n}\n\nexport type DecodedNprofile = {\n type: 'nprofile'\n data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n type: 'naddr'\n data: AddressPointer\n}\n\nexport type DecodedNsec = {\n type: 'nsec'\n data: Uint8Array\n}\n\nexport type DecodedNpub = {\n type: 'npub'\n data: string\n}\n\nexport type DecodedNote = {\n type: 'note'\n data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n let { prefix, words } = bech32.decode(code as `${string}1${string}`, Bech32MaxSize)\n let data = new Uint8Array(bech32.fromWords(words))\n\n switch (prefix) {\n case 'nprofile': {\n let tlv = parseTLV(data)\n if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n\n return {\n type: 'nprofile',\n data: {\n pubkey: bytesToHex(tlv[0][0]),\n relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n },\n }\n }\n case 'nevent': {\n let tlv = parseTLV(data)\n if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n return {\n type: 'nevent',\n data: {\n id: bytesToHex(tlv[0][0]),\n relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n },\n }\n }\n\n case 'naddr': {\n let tlv = parseTLV(data)\n if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n return {\n type: 'naddr',\n data: {\n identifier: utf8Decoder.decode(tlv[0][0]),\n pubkey: bytesToHex(tlv[2][0]),\n kind: parseInt(bytesToHex(tlv[3][0]), 16),\n relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n },\n }\n }\n\n case 'nsec':\n return { type: prefix, data }\n\n case 'npub':\n case 'note':\n return { type: prefix, data: bytesToHex(data) }\n\n default:\n throw new Error(`unknown prefix ${prefix}`)\n }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n let result: TLV = {}\n let rest = data\n while (rest.length > 0) {\n if (rest.length < 2) throw new Error('not enough data to read TLV')\n let t = rest[0]\n let l = rest[1]\n let v = rest.slice(2, 2 + l)\n rest = rest.slice(2 + l)\n if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n result[t] = result[t] || []\n result[t].push(v)\n }\n return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n let words = bech32.toWords(data)\n return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n let data = encodeTLV({\n 0: [hexToBytes(profile.pubkey)],\n 1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n })\n return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n let kindArray\n if (event.kind !== undefined) {\n kindArray = integerToUint8Array(event.kind)\n }\n\n let data = encodeTLV({\n 0: [hexToBytes(event.id)],\n 1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n 2: event.author ? [hexToBytes(event.author)] : [],\n 3: kindArray ? [new Uint8Array(kindArray)] : [],\n })\n\n return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n let kind = new ArrayBuffer(4)\n new DataView(kind).setUint32(0, addr.kind, false)\n\n let data = encodeTLV({\n 0: [utf8Encoder.encode(addr.identifier)],\n 1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n 2: [hexToBytes(addr.pubkey)],\n 3: [new Uint8Array(kind)],\n })\n return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n let entries: Uint8Array[] = []\n\n Object.entries(tlv)\n .reverse()\n .forEach(([t, vs]) => {\n vs.forEach(v => {\n let entry = new Uint8Array(v.length + 2)\n entry.set([parseInt(t)], 0)\n entry.set([v.length], 1)\n entry.set(v, 2)\n entries.push(entry)\n })\n })\n\n return concatBytes(...entries)\n}\n", "import type { NostrEvent } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n try {\n if (url.indexOf('://') === -1) url = 'wss://' + url\n let p = new URL(url)\n if (p.protocol === 'http:') p.protocol = 'ws:'\n else if (p.protocol === 'https:') p.protocol = 'wss:'\n p.pathname = p.pathname.replace(/\\/+/g, '/')\n if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n p.searchParams.sort()\n p.hash = ''\n return p.toString()\n } catch (e) {\n throw new Error(`Invalid URL: ${url}`)\n }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return b.created_at - event.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return event.created_at - b.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n let start = 0\n let end = arr.length - 1\n\n while (start <= end) {\n const mid = Math.floor((start + end) / 2)\n const cmp = compare(arr[mid])\n\n if (cmp === 0) {\n return [mid, true]\n }\n\n if (cmp < 0) {\n end = mid - 1\n } else {\n start = mid + 1\n }\n }\n\n return [start, false]\n}\n\nexport function mergeReverseSortedLists(list1: NostrEvent[], list2: NostrEvent[]): NostrEvent[] {\n const result: NostrEvent[] = new Array(list1.length + list2.length)\n result.length = 0\n let i1 = 0\n let i2 = 0\n let sameTimestampIds: string[] = []\n\n while (i1 < list1.length && i2 < list2.length) {\n let next: NostrEvent\n if (list1[i1]?.created_at > list2[i2]?.created_at) {\n next = list1[i1]\n i1++\n } else {\n next = list2[i2]\n i2++\n }\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i1 < list1.length) {\n const next = list1[i1]\n i1++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i2 < list2.length) {\n const next = list2[i2]\n i2++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n return result\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["import_utils"]
|
|
7
7
|
}
|
package/lib/cjs/nip22.js
CHANGED
|
@@ -23,6 +23,7 @@ __export(nip22_exports, {
|
|
|
23
23
|
parse: () => parse
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(nip22_exports);
|
|
26
|
+
var HEX64 = /^[0-9a-fA-F]{64}$/;
|
|
26
27
|
function parseKind(kind) {
|
|
27
28
|
if (!kind)
|
|
28
29
|
return void 0;
|
|
@@ -36,9 +37,12 @@ function parseAddressPointer(value, relayUrl) {
|
|
|
36
37
|
const kind = parseInt(value.slice(0, idx), 10);
|
|
37
38
|
if (Number.isNaN(kind))
|
|
38
39
|
return void 0;
|
|
40
|
+
const pubkey = value.slice(idx + 1, idx2);
|
|
41
|
+
if (!HEX64.test(pubkey))
|
|
42
|
+
return void 0;
|
|
39
43
|
return {
|
|
40
44
|
kind,
|
|
41
|
-
pubkey
|
|
45
|
+
pubkey,
|
|
42
46
|
identifier: value.slice(idx2 + 1),
|
|
43
47
|
relays: relayUrl ? [relayUrl] : []
|
|
44
48
|
};
|
|
@@ -47,12 +51,12 @@ function parsePointer(tag) {
|
|
|
47
51
|
switch (tag[0]) {
|
|
48
52
|
case "E":
|
|
49
53
|
case "e":
|
|
50
|
-
if (!tag[1])
|
|
54
|
+
if (!tag[1] || !HEX64.test(tag[1]))
|
|
51
55
|
return void 0;
|
|
52
56
|
return {
|
|
53
57
|
id: tag[1],
|
|
54
58
|
relays: tag[2] ? [tag[2]] : [],
|
|
55
|
-
author: tag[3]
|
|
59
|
+
author: tag[3] && HEX64.test(tag[3]) ? tag[3] : void 0
|
|
56
60
|
};
|
|
57
61
|
case "A":
|
|
58
62
|
case "a":
|
|
@@ -75,10 +79,12 @@ function parseQuote(tag) {
|
|
|
75
79
|
if (tag[1].includes(":")) {
|
|
76
80
|
return parseAddressPointer(tag[1], tag[2]);
|
|
77
81
|
}
|
|
82
|
+
if (!HEX64.test(tag[1]))
|
|
83
|
+
return void 0;
|
|
78
84
|
return {
|
|
79
85
|
id: tag[1],
|
|
80
86
|
relays: tag[2] ? [tag[2]] : [],
|
|
81
|
-
author: tag[3]
|
|
87
|
+
author: tag[3] && HEX64.test(tag[3]) ? tag[3] : void 0
|
|
82
88
|
};
|
|
83
89
|
}
|
|
84
90
|
function choosePointer(candidates) {
|
|
@@ -138,7 +144,7 @@ function parse(event) {
|
|
|
138
144
|
result.quotes.push(pointer);
|
|
139
145
|
continue;
|
|
140
146
|
}
|
|
141
|
-
if ((tag[0] === "P" || tag[0] === "p") && tag[1]) {
|
|
147
|
+
if ((tag[0] === "P" || tag[0] === "p") && tag[1] && HEX64.test(tag[1])) {
|
|
142
148
|
result.profiles.push({
|
|
143
149
|
pubkey: tag[1],
|
|
144
150
|
relays: tag[2] ? [tag[2]] : []
|
package/lib/cjs/nip22.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../nip22.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Event } from './core.ts'\nimport type { AddressPointer, EventPointer, ProfilePointer } from './nip19.ts'\n\nexport type ExternalPointer = {\n value: string\n hint?: string\n}\n\nfunction parseKind(kind: string | undefined): number | string | undefined {\n if (!kind) return undefined\n return /^\\d+$/.test(kind) ? parseInt(kind, 10) : kind\n}\n\nfunction parseAddressPointer(value: string, relayUrl?: string): AddressPointer | undefined {\n const idx = value.indexOf(':')\n const idx2 = value.indexOf(':', idx + 1)\n if (idx === -1 || idx2 === -1) return undefined\n\n const kind = parseInt(value.slice(0, idx), 10)\n if (Number.isNaN(kind)) return undefined\n\n
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
4
|
+
"sourcesContent": ["import type { Event } from './core.ts'\nimport type { AddressPointer, EventPointer, ProfilePointer } from './nip19.ts'\n\nconst HEX64 = /^[0-9a-fA-F]{64}$/\n\nexport type ExternalPointer = {\n value: string\n hint?: string\n}\n\nfunction parseKind(kind: string | undefined): number | string | undefined {\n if (!kind) return undefined\n return /^\\d+$/.test(kind) ? parseInt(kind, 10) : kind\n}\n\nfunction parseAddressPointer(value: string, relayUrl?: string): AddressPointer | undefined {\n const idx = value.indexOf(':')\n const idx2 = value.indexOf(':', idx + 1)\n if (idx === -1 || idx2 === -1) return undefined\n\n const kind = parseInt(value.slice(0, idx), 10)\n if (Number.isNaN(kind)) return undefined\n\n const pubkey = value.slice(idx + 1, idx2)\n if (!HEX64.test(pubkey)) return undefined\n\n return {\n kind,\n pubkey,\n identifier: value.slice(idx2 + 1),\n relays: relayUrl ? [relayUrl] : [],\n }\n}\n\nfunction parsePointer(tag: string[]): EventPointer | AddressPointer | ExternalPointer | undefined {\n switch (tag[0]) {\n case 'E':\n case 'e':\n if (!tag[1] || !HEX64.test(tag[1])) return undefined\n return {\n id: tag[1],\n relays: tag[2] ? [tag[2]] : [],\n author: tag[3] && HEX64.test(tag[3]) ? tag[3] : undefined,\n }\n case 'A':\n case 'a':\n if (!tag[1]) return undefined\n return parseAddressPointer(tag[1], tag[2])\n case 'I':\n case 'i':\n if (!tag[1]) return undefined\n return {\n value: tag[1],\n hint: tag[2],\n }\n }\n}\n\nfunction parseQuote(tag: string[]): EventPointer | AddressPointer | ExternalPointer | undefined {\n if (!tag[1]) return undefined\n\n if (tag[1].includes(':')) {\n return parseAddressPointer(tag[1], tag[2])\n }\n\n if (!HEX64.test(tag[1])) return undefined\n\n return {\n id: tag[1],\n relays: tag[2] ? [tag[2]] : [],\n author: tag[3] && HEX64.test(tag[3]) ? tag[3] : undefined,\n }\n}\n\nfunction choosePointer(\n candidates: Array<{ tagName: string; pointer: EventPointer | AddressPointer | ExternalPointer }>,\n): EventPointer | AddressPointer | ExternalPointer | undefined {\n return (\n candidates.findLast(candidate => candidate.tagName === 'A' || candidate.tagName === 'a')?.pointer ||\n candidates.findLast(candidate => candidate.tagName === 'I' || candidate.tagName === 'i')?.pointer ||\n candidates.findLast(candidate => candidate.tagName === 'E' || candidate.tagName === 'e')?.pointer\n )\n}\n\nfunction inheritRelayHints(\n pointer: EventPointer | AddressPointer | ExternalPointer | undefined,\n profiles: ProfilePointer[],\n) {\n if (!pointer || !('id' in pointer) || !pointer.author) return\n\n const author = profiles.find(profile => profile.pubkey === pointer.author)\n if (!author || !author.relays) return\n\n if (!pointer.relays) {\n pointer.relays = []\n }\n\n author.relays.forEach(url => {\n if (pointer.relays!.indexOf(url) === -1) pointer.relays!.push(url)\n })\n author.relays = pointer.relays\n}\n\nexport function parse(event: Pick<Event, 'tags'>): {\n /**\n * Pointer to root scope.\n */\n root: EventPointer | AddressPointer | ExternalPointer | undefined\n\n /**\n * Kind of root scope from `K` tag.\n */\n rootKind: number | string | undefined\n\n /**\n * Pointer to parent item being replied to.\n */\n reply: EventPointer | AddressPointer | ExternalPointer | undefined\n\n /**\n * Kind of parent item from `k` tag.\n */\n replyKind: number | string | undefined\n\n /**\n * Reserved for extra referenced items.\n */\n mentions: (EventPointer | AddressPointer | ExternalPointer)[]\n\n /**\n * Pointers directly quoted with `q` tags.\n */\n quotes: (EventPointer | AddressPointer | ExternalPointer)[]\n\n /**\n * Root and parent authors.\n */\n profiles: ProfilePointer[]\n} {\n const result: ReturnType<typeof parse> = {\n root: undefined,\n rootKind: undefined,\n reply: undefined,\n replyKind: undefined,\n mentions: [],\n quotes: [],\n profiles: [],\n }\n\n const rootCandidates: Array<{ tagName: string; pointer: EventPointer | AddressPointer | ExternalPointer }> = []\n const replyCandidates: Array<{ tagName: string; pointer: EventPointer | AddressPointer | ExternalPointer }> = []\n\n for (const tag of event.tags) {\n if ((tag[0] === 'E' || tag[0] === 'A' || tag[0] === 'I') && tag[1]) {\n const pointer = parsePointer(tag)\n if (pointer) rootCandidates.push({ tagName: tag[0], pointer })\n continue\n }\n\n if ((tag[0] === 'e' || tag[0] === 'a' || tag[0] === 'i') && tag[1]) {\n const pointer = parsePointer(tag)\n if (pointer) replyCandidates.push({ tagName: tag[0], pointer })\n continue\n }\n\n if (tag[0] === 'K') {\n result.rootKind = parseKind(tag[1])\n continue\n }\n\n if (tag[0] === 'k') {\n result.replyKind = parseKind(tag[1])\n continue\n }\n\n if (tag[0] === 'q') {\n const pointer = parseQuote(tag)\n if (pointer) result.quotes.push(pointer)\n continue\n }\n\n if ((tag[0] === 'P' || tag[0] === 'p') && tag[1] && HEX64.test(tag[1])) {\n result.profiles.push({\n pubkey: tag[1],\n relays: tag[2] ? [tag[2]] : [],\n })\n }\n }\n\n result.root = choosePointer(rootCandidates)\n result.reply = choosePointer(replyCandidates)\n\n inheritRelayHints(result.root, result.profiles)\n inheritRelayHints(result.reply, result.profiles)\n result.quotes.forEach(pointer => inheritRelayHints(pointer, result.profiles))\n\n return result\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/lib/cjs/nip27.js
CHANGED
package/lib/cjs/nip27.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../nip27.ts", "../../nip19.ts", "../../utils.ts"],
|
|
4
|
-
"sourcesContent": ["import { NostrEvent } from './core.ts'\nimport { AddressPointer, EventPointer, ProfilePointer, decode } from './nip19.ts'\n\nexport type Block =\n | {\n type: 'text'\n text: string\n }\n | {\n type: 'reference'\n pointer: ProfilePointer | AddressPointer | EventPointer\n }\n | {\n type: 'url'\n url: string\n }\n | {\n type: 'relay'\n url: string\n }\n | {\n type: 'image'\n url: string\n }\n | {\n type: 'video'\n url: string\n }\n | {\n type: 'audio'\n url: string\n }\n | {\n type: 'emoji'\n shortcode: string\n url: string\n }\n | {\n type: 'hashtag'\n value: string\n }\n\nconst noCharacter = /\\W/m\nconst noURLCharacter = /[^\\w\\/] |[^\\w\\/]$|$|,| /m\nconst MAX_HASHTAG_LENGTH = 42\n\nexport function* parse(content: string | NostrEvent): Iterable<Block> {\n let emojis: { type: 'emoji'; shortcode: string; url: string }[] = []\n if (typeof content !== 'string') {\n for (let i = 0; i < content.tags.length; i++) {\n const tag = content.tags[i]\n if (tag[0] === 'emoji' && tag.length >= 3) {\n emojis.push({ type: 'emoji', shortcode: tag[1], url: tag[2] })\n }\n }\n content = content.content\n }\n\n const max = content.length\n let prevIndex = 0\n let index = 0\n mainloop: while (index < max) {\n const u = content.indexOf(':', index)\n const h = content.indexOf('#', index)\n if (u === -1 && h === -1) {\n // reached end\n break mainloop\n }\n\n if (u === -1 || (h >= 0 && h < u)) {\n // parse hashtag\n if (h === 0 || content[h - 1].match(noCharacter)) {\n const m = content.slice(h + 1, h + MAX_HASHTAG_LENGTH).match(noCharacter)\n const end = m ? h + 1 + m.index! : max\n yield { type: 'text', text: content.slice(prevIndex, h) }\n yield { type: 'hashtag', value: content.slice(h + 1, end) }\n index = end\n prevIndex = index\n continue mainloop\n }\n\n // ignore this, it is nothing\n index = h + 1\n continue mainloop\n }\n\n // otherwise parse things that have an \":\"\n if (content.slice(u - 5, u) === 'nostr') {\n const m = content.slice(u + 60).match(noCharacter)\n const end = m ? u + 60 + m.index! : max\n try {\n let pointer: ProfilePointer | AddressPointer | EventPointer\n let { data, type } = decode(content.slice(u + 1, end))\n\n switch (type) {\n case 'npub':\n pointer = { pubkey: data } as ProfilePointer\n break\n case 'note':\n pointer = { id: data } as EventPointer\n break\n case 'nsec':\n // ignore this, treat it as not a valid uri\n index = end + 1\n continue\n default:\n pointer = data as any\n }\n\n if (prevIndex !== u - 5) {\n yield { type: 'text', text: content.slice(prevIndex, u - 5) }\n }\n yield { type: 'reference', pointer }\n index = end\n prevIndex = index\n continue mainloop\n } catch (_err) {\n // ignore this, not a valid nostr uri\n index = u + 1\n continue mainloop\n }\n } else if (content.slice(u - 5, u) === 'https' || content.slice(u - 4, u) === 'http') {\n const m = content.slice(u + 4).match(noURLCharacter)\n const end = m ? u + 4 + m.index! : max\n const prefixLen = content[u - 1] === 's' ? 5 : 4\n try {\n let url = new URL(content.slice(u - prefixLen, end))\n if (url.hostname.indexOf('.') === -1) {\n throw new Error('invalid url')\n }\n\n if (prevIndex !== u - prefixLen) {\n yield { type: 'text', text: content.slice(prevIndex, u - prefixLen) }\n }\n\n if (/\\.(png|jpe?g|gif|webp|heic|svg)$/i.test(url.pathname)) {\n yield { type: 'image', url: url.toString() }\n index = end\n prevIndex = index\n continue mainloop\n }\n if (/\\.(mp4|avi|webm|mkv|mov)$/i.test(url.pathname)) {\n yield { type: 'video', url: url.toString() }\n index = end\n prevIndex = index\n continue mainloop\n }\n if (/\\.(mp3|aac|ogg|opus|wav|flac)$/i.test(url.pathname)) {\n yield { type: 'audio', url: url.toString() }\n index = end\n prevIndex = index\n continue mainloop\n }\n\n yield { type: 'url', url: url.toString() }\n index = end\n prevIndex = index\n continue mainloop\n } catch (_err) {\n // ignore this, not a valid url\n index = end + 1\n continue mainloop\n }\n } else if (content.slice(u - 3, u) === 'wss' || content.slice(u - 2, u) === 'ws') {\n const m = content.slice(u + 4).match(noURLCharacter)\n const end = m ? u + 4 + m.index! : max\n const prefixLen = content[u - 1] === 's' ? 3 : 2\n try {\n let url = new URL(content.slice(u - prefixLen, end))\n if (url.hostname.indexOf('.') === -1) {\n throw new Error('invalid ws url')\n }\n\n if (prevIndex !== u - prefixLen) {\n yield { type: 'text', text: content.slice(prevIndex, u - prefixLen) }\n }\n yield { type: 'relay', url: url.toString() }\n index = end\n prevIndex = index\n continue mainloop\n } catch (_err) {\n // ignore this, not a valid url\n index = end + 1\n continue mainloop\n }\n } else {\n // try to parse an emoji shortcode\n for (let e = 0; e < emojis.length; e++) {\n const emoji = emojis[e]\n if (\n content[u + emoji.shortcode.length + 1] === ':' &&\n content.slice(u + 1, u + emoji.shortcode.length + 1) === emoji.shortcode\n ) {\n // found an emoji\n if (prevIndex !== u) {\n yield { type: 'text', text: content.slice(prevIndex, u) }\n }\n yield emoji\n index = u + emoji.shortcode.length + 2\n prevIndex = index\n continue mainloop\n }\n }\n\n // ignore this, it is nothing\n index = u + 1\n continue mainloop\n }\n }\n\n if (prevIndex !== max) {\n yield { type: 'text', text: content.slice(prevIndex) }\n }\n}\n", "import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils.js'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n const uint8Array = new Uint8Array(4)\n\n // Use bitwise operations to extract the bytes.\n uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n uint8Array[1] = (number >> 16) & 0xff\n uint8Array[2] = (number >> 8) & 0xff\n uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n return uint8Array\n}\n\nexport type ProfilePointer = {\n pubkey: string // hex\n relays?: string[]\n}\n\nexport type EventPointer = {\n id: string // hex\n relays?: string[]\n author?: string\n kind?: number\n}\n\nexport type AddressPointer = {\n identifier: string\n pubkey: string\n kind: number\n relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n try {\n if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n return decode(nip19code)\n } catch (_err) {\n return { type: 'invalid', data: null }\n }\n}\n\nexport type DecodedNevent = {\n type: 'nevent'\n data: EventPointer\n}\n\nexport type DecodedNprofile = {\n type: 'nprofile'\n data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n type: 'naddr'\n data: AddressPointer\n}\n\nexport type DecodedNsec = {\n type: 'nsec'\n data: Uint8Array\n}\n\nexport type DecodedNpub = {\n type: 'npub'\n data: string\n}\n\nexport type DecodedNote = {\n type: 'note'\n data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n let { prefix, words } = bech32.decode(code as `${string}1${string}`, Bech32MaxSize)\n let data = new Uint8Array(bech32.fromWords(words))\n\n switch (prefix) {\n case 'nprofile': {\n let tlv = parseTLV(data)\n if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n\n return {\n type: 'nprofile',\n data: {\n pubkey: bytesToHex(tlv[0][0]),\n relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n },\n }\n }\n case 'nevent': {\n let tlv = parseTLV(data)\n if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n return {\n type: 'nevent',\n data: {\n id: bytesToHex(tlv[0][0]),\n relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n },\n }\n }\n\n case 'naddr': {\n let tlv = parseTLV(data)\n if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n return {\n type: 'naddr',\n data: {\n identifier: utf8Decoder.decode(tlv[0][0]),\n pubkey: bytesToHex(tlv[2][0]),\n kind: parseInt(bytesToHex(tlv[3][0]), 16),\n relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n },\n }\n }\n\n case 'nsec':\n return { type: prefix, data }\n\n case 'npub':\n case 'note':\n return { type: prefix, data: bytesToHex(data) }\n\n default:\n throw new Error(`unknown prefix ${prefix}`)\n }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n let result: TLV = {}\n let rest = data\n while (rest.length > 0) {\n let t = rest[0]\n let l = rest[1]\n let v = rest.slice(2, 2 + l)\n rest = rest.slice(2 + l)\n if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n result[t] = result[t] || []\n result[t].push(v)\n }\n return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n let words = bech32.toWords(data)\n return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n let data = encodeTLV({\n 0: [hexToBytes(profile.pubkey)],\n 1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n })\n return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n let kindArray\n if (event.kind !== undefined) {\n kindArray = integerToUint8Array(event.kind)\n }\n\n let data = encodeTLV({\n 0: [hexToBytes(event.id)],\n 1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n 2: event.author ? [hexToBytes(event.author)] : [],\n 3: kindArray ? [new Uint8Array(kindArray)] : [],\n })\n\n return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n let kind = new ArrayBuffer(4)\n new DataView(kind).setUint32(0, addr.kind, false)\n\n let data = encodeTLV({\n 0: [utf8Encoder.encode(addr.identifier)],\n 1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n 2: [hexToBytes(addr.pubkey)],\n 3: [new Uint8Array(kind)],\n })\n return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n let entries: Uint8Array[] = []\n\n Object.entries(tlv)\n .reverse()\n .forEach(([t, vs]) => {\n vs.forEach(v => {\n let entry = new Uint8Array(v.length + 2)\n entry.set([parseInt(t)], 0)\n entry.set([v.length], 1)\n entry.set(v, 2)\n entries.push(entry)\n })\n })\n\n return concatBytes(...entries)\n}\n", "import type { NostrEvent } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n try {\n if (url.indexOf('://') === -1) url = 'wss://' + url\n let p = new URL(url)\n if (p.protocol === 'http:') p.protocol = 'ws:'\n else if (p.protocol === 'https:') p.protocol = 'wss:'\n p.pathname = p.pathname.replace(/\\/+/g, '/')\n if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n p.searchParams.sort()\n p.hash = ''\n return p.toString()\n } catch (e) {\n throw new Error(`Invalid URL: ${url}`)\n }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return b.created_at - event.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return event.created_at - b.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n let start = 0\n let end = arr.length - 1\n\n while (start <= end) {\n const mid = Math.floor((start + end) / 2)\n const cmp = compare(arr[mid])\n\n if (cmp === 0) {\n return [mid, true]\n }\n\n if (cmp < 0) {\n end = mid - 1\n } else {\n start = mid + 1\n }\n }\n\n return [start, false]\n}\n\nexport function mergeReverseSortedLists(list1: NostrEvent[], list2: NostrEvent[]): NostrEvent[] {\n const result: NostrEvent[] = new Array(list1.length + list2.length)\n result.length = 0\n let i1 = 0\n let i2 = 0\n let sameTimestampIds: string[] = []\n\n while (i1 < list1.length && i2 < list2.length) {\n let next: NostrEvent\n if (list1[i1]?.created_at > list2[i2]?.created_at) {\n next = list1[i1]\n i1++\n } else {\n next = list2[i2]\n i2++\n }\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i1 < list1.length) {\n const next = list1[i1]\n i1++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i2 < list2.length) {\n const next = list2[i2]\n i2++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n return result\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { NostrEvent } from './core.ts'\nimport { AddressPointer, EventPointer, ProfilePointer, decode } from './nip19.ts'\n\nexport type Block =\n | {\n type: 'text'\n text: string\n }\n | {\n type: 'reference'\n pointer: ProfilePointer | AddressPointer | EventPointer\n }\n | {\n type: 'url'\n url: string\n }\n | {\n type: 'relay'\n url: string\n }\n | {\n type: 'image'\n url: string\n }\n | {\n type: 'video'\n url: string\n }\n | {\n type: 'audio'\n url: string\n }\n | {\n type: 'emoji'\n shortcode: string\n url: string\n }\n | {\n type: 'hashtag'\n value: string\n }\n\nconst noCharacter = /\\W/m\nconst noURLCharacter = /[^\\w\\/] |[^\\w\\/]$|$|,| /m\nconst MAX_HASHTAG_LENGTH = 42\n\nexport function* parse(content: string | NostrEvent): Iterable<Block> {\n let emojis: { type: 'emoji'; shortcode: string; url: string }[] = []\n if (typeof content !== 'string') {\n for (let i = 0; i < content.tags.length; i++) {\n const tag = content.tags[i]\n if (tag[0] === 'emoji' && tag.length >= 3) {\n emojis.push({ type: 'emoji', shortcode: tag[1], url: tag[2] })\n }\n }\n content = content.content\n }\n\n const max = content.length\n let prevIndex = 0\n let index = 0\n mainloop: while (index < max) {\n const u = content.indexOf(':', index)\n const h = content.indexOf('#', index)\n if (u === -1 && h === -1) {\n // reached end\n break mainloop\n }\n\n if (u === -1 || (h >= 0 && h < u)) {\n // parse hashtag\n if (h === 0 || content[h - 1].match(noCharacter)) {\n const m = content.slice(h + 1, h + MAX_HASHTAG_LENGTH).match(noCharacter)\n const end = m ? h + 1 + m.index! : max\n yield { type: 'text', text: content.slice(prevIndex, h) }\n yield { type: 'hashtag', value: content.slice(h + 1, end) }\n index = end\n prevIndex = index\n continue mainloop\n }\n\n // ignore this, it is nothing\n index = h + 1\n continue mainloop\n }\n\n // otherwise parse things that have an \":\"\n if (content.slice(u - 5, u) === 'nostr') {\n const m = content.slice(u + 60).match(noCharacter)\n const end = m ? u + 60 + m.index! : max\n try {\n let pointer: ProfilePointer | AddressPointer | EventPointer\n let { data, type } = decode(content.slice(u + 1, end))\n\n switch (type) {\n case 'npub':\n pointer = { pubkey: data } as ProfilePointer\n break\n case 'note':\n pointer = { id: data } as EventPointer\n break\n case 'nsec':\n // ignore this, treat it as not a valid uri\n index = end + 1\n continue\n default:\n pointer = data as any\n }\n\n if (prevIndex !== u - 5) {\n yield { type: 'text', text: content.slice(prevIndex, u - 5) }\n }\n yield { type: 'reference', pointer }\n index = end\n prevIndex = index\n continue mainloop\n } catch (_err) {\n // ignore this, not a valid nostr uri\n index = u + 1\n continue mainloop\n }\n } else if (content.slice(u - 5, u) === 'https' || content.slice(u - 4, u) === 'http') {\n const m = content.slice(u + 4).match(noURLCharacter)\n const end = m ? u + 4 + m.index! : max\n const prefixLen = content[u - 1] === 's' ? 5 : 4\n try {\n let url = new URL(content.slice(u - prefixLen, end))\n if (url.hostname.indexOf('.') === -1) {\n throw new Error('invalid url')\n }\n\n if (prevIndex !== u - prefixLen) {\n yield { type: 'text', text: content.slice(prevIndex, u - prefixLen) }\n }\n\n if (/\\.(png|jpe?g|gif|webp|heic|svg)$/i.test(url.pathname)) {\n yield { type: 'image', url: url.toString() }\n index = end\n prevIndex = index\n continue mainloop\n }\n if (/\\.(mp4|avi|webm|mkv|mov)$/i.test(url.pathname)) {\n yield { type: 'video', url: url.toString() }\n index = end\n prevIndex = index\n continue mainloop\n }\n if (/\\.(mp3|aac|ogg|opus|wav|flac)$/i.test(url.pathname)) {\n yield { type: 'audio', url: url.toString() }\n index = end\n prevIndex = index\n continue mainloop\n }\n\n yield { type: 'url', url: url.toString() }\n index = end\n prevIndex = index\n continue mainloop\n } catch (_err) {\n // ignore this, not a valid url\n index = end + 1\n continue mainloop\n }\n } else if (content.slice(u - 3, u) === 'wss' || content.slice(u - 2, u) === 'ws') {\n const m = content.slice(u + 4).match(noURLCharacter)\n const end = m ? u + 4 + m.index! : max\n const prefixLen = content[u - 1] === 's' ? 3 : 2\n try {\n let url = new URL(content.slice(u - prefixLen, end))\n if (url.hostname.indexOf('.') === -1) {\n throw new Error('invalid ws url')\n }\n\n if (prevIndex !== u - prefixLen) {\n yield { type: 'text', text: content.slice(prevIndex, u - prefixLen) }\n }\n yield { type: 'relay', url: url.toString() }\n index = end\n prevIndex = index\n continue mainloop\n } catch (_err) {\n // ignore this, not a valid url\n index = end + 1\n continue mainloop\n }\n } else {\n // try to parse an emoji shortcode\n for (let e = 0; e < emojis.length; e++) {\n const emoji = emojis[e]\n if (\n content[u + emoji.shortcode.length + 1] === ':' &&\n content.slice(u + 1, u + emoji.shortcode.length + 1) === emoji.shortcode\n ) {\n // found an emoji\n if (prevIndex !== u) {\n yield { type: 'text', text: content.slice(prevIndex, u) }\n }\n yield emoji\n index = u + emoji.shortcode.length + 2\n prevIndex = index\n continue mainloop\n }\n }\n\n // ignore this, it is nothing\n index = u + 1\n continue mainloop\n }\n }\n\n if (prevIndex !== max) {\n yield { type: 'text', text: content.slice(prevIndex) }\n }\n}\n", "import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils.js'\nimport { bech32 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport type NProfile = `nprofile1${string}`\nexport type NEvent = `nevent1${string}`\nexport type NAddr = `naddr1${string}`\nexport type NSec = `nsec1${string}`\nexport type NPub = `npub1${string}`\nexport type Note = `note1${string}`\nexport type Ncryptsec = `ncryptsec1${string}`\n\nexport const NostrTypeGuard = {\n isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\\d]+$/.test(value || ''),\n isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\\d]+$/.test(value || ''),\n isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\\d]+$/.test(value || ''),\n isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\\d]{58}$/.test(value || ''),\n isNPub: (value?: string | null): value is NPub => /^npub1[a-z\\d]{58}$/.test(value || ''),\n isNote: (value?: string | null): value is Note => /^note1[a-z\\d]+$/.test(value || ''),\n isNcryptsec: (value?: string | null): value is Ncryptsec => /^ncryptsec1[a-z\\d]+$/.test(value || ''),\n}\n\nexport const Bech32MaxSize = 5000\n\n/**\n * Bech32 regex.\n * @see https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32\n */\nexport const BECH32_REGEX = /[\\x21-\\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/\n\nfunction integerToUint8Array(number: number) {\n // Create a Uint8Array with enough space to hold a 32-bit integer (4 bytes).\n const uint8Array = new Uint8Array(4)\n\n // Use bitwise operations to extract the bytes.\n uint8Array[0] = (number >> 24) & 0xff // Most significant byte (MSB)\n uint8Array[1] = (number >> 16) & 0xff\n uint8Array[2] = (number >> 8) & 0xff\n uint8Array[3] = number & 0xff // Least significant byte (LSB)\n\n return uint8Array\n}\n\nexport type ProfilePointer = {\n pubkey: string // hex\n relays?: string[]\n}\n\nexport type EventPointer = {\n id: string // hex\n relays?: string[]\n author?: string\n kind?: number\n}\n\nexport type AddressPointer = {\n identifier: string\n pubkey: string\n kind: number\n relays?: string[]\n}\n\nexport function decodeNostrURI(nip19code: string): ReturnType<typeof decode> | { type: 'invalid'; data: null } {\n try {\n if (nip19code.startsWith('nostr:')) nip19code = nip19code.substring(6)\n return decode(nip19code)\n } catch (_err) {\n return { type: 'invalid', data: null }\n }\n}\n\nexport type DecodedNevent = {\n type: 'nevent'\n data: EventPointer\n}\n\nexport type DecodedNprofile = {\n type: 'nprofile'\n data: ProfilePointer\n}\n\nexport type DecodedNaddr = {\n type: 'naddr'\n data: AddressPointer\n}\n\nexport type DecodedNsec = {\n type: 'nsec'\n data: Uint8Array\n}\n\nexport type DecodedNpub = {\n type: 'npub'\n data: string\n}\n\nexport type DecodedNote = {\n type: 'note'\n data: string\n}\n\nexport type DecodedResult = DecodedNevent | DecodedNprofile | DecodedNaddr | DecodedNpub | DecodedNsec | DecodedNote\n\nexport function decode(nip19: NEvent): DecodedNevent\nexport function decode(nip19: NProfile): DecodedNprofile\nexport function decode(nip19: NAddr): DecodedNaddr\nexport function decode(nip19: NSec): DecodedNsec\nexport function decode(nip19: NPub): DecodedNpub\nexport function decode(nip19: Note): DecodedNote\nexport function decode(code: string): DecodedResult\nexport function decode(code: string): DecodedResult {\n let { prefix, words } = bech32.decode(code as `${string}1${string}`, Bech32MaxSize)\n let data = new Uint8Array(bech32.fromWords(words))\n\n switch (prefix) {\n case 'nprofile': {\n let tlv = parseTLV(data)\n if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')\n if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n\n return {\n type: 'nprofile',\n data: {\n pubkey: bytesToHex(tlv[0][0]),\n relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n },\n }\n }\n case 'nevent': {\n let tlv = parseTLV(data)\n if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nevent')\n if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')\n if (tlv[2] && tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n if (tlv[3] && tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n return {\n type: 'nevent',\n data: {\n id: bytesToHex(tlv[0][0]),\n relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n kind: tlv[3]?.[0] ? parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n },\n }\n }\n\n case 'naddr': {\n let tlv = parseTLV(data)\n if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for naddr')\n if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for naddr')\n if (tlv[2][0].length !== 32) throw new Error('TLV 2 should be 32 bytes')\n if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for naddr')\n if (tlv[3][0].length !== 4) throw new Error('TLV 3 should be 4 bytes')\n\n return {\n type: 'naddr',\n data: {\n identifier: utf8Decoder.decode(tlv[0][0]),\n pubkey: bytesToHex(tlv[2][0]),\n kind: parseInt(bytesToHex(tlv[3][0]), 16),\n relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],\n },\n }\n }\n\n case 'nsec':\n return { type: prefix, data }\n\n case 'npub':\n case 'note':\n return { type: prefix, data: bytesToHex(data) }\n\n default:\n throw new Error(`unknown prefix ${prefix}`)\n }\n}\n\ntype TLV = { [t: number]: Uint8Array[] }\n\nfunction parseTLV(data: Uint8Array): TLV {\n let result: TLV = {}\n let rest = data\n while (rest.length > 0) {\n if (rest.length < 2) throw new Error('not enough data to read TLV')\n let t = rest[0]\n let l = rest[1]\n let v = rest.slice(2, 2 + l)\n rest = rest.slice(2 + l)\n if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)\n result[t] = result[t] || []\n result[t].push(v)\n }\n return result\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n return encodeBytes('nsec', key)\n}\n\nexport function npubEncode(hex: string): NPub {\n return encodeBytes('npub', hexToBytes(hex))\n}\n\nexport function noteEncode(hex: string): Note {\n return encodeBytes('note', hexToBytes(hex))\n}\n\nfunction encodeBech32<Prefix extends string>(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` {\n let words = bech32.toWords(data)\n return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`\n}\n\nexport function encodeBytes<Prefix extends string>(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` {\n return encodeBech32(prefix, bytes)\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n let data = encodeTLV({\n 0: [hexToBytes(profile.pubkey)],\n 1: (profile.relays || []).map(url => utf8Encoder.encode(url)),\n })\n return encodeBech32('nprofile', data)\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n let kindArray\n if (event.kind !== undefined) {\n kindArray = integerToUint8Array(event.kind)\n }\n\n let data = encodeTLV({\n 0: [hexToBytes(event.id)],\n 1: (event.relays || []).map(url => utf8Encoder.encode(url)),\n 2: event.author ? [hexToBytes(event.author)] : [],\n 3: kindArray ? [new Uint8Array(kindArray)] : [],\n })\n\n return encodeBech32('nevent', data)\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n let kind = new ArrayBuffer(4)\n new DataView(kind).setUint32(0, addr.kind, false)\n\n let data = encodeTLV({\n 0: [utf8Encoder.encode(addr.identifier)],\n 1: (addr.relays || []).map(url => utf8Encoder.encode(url)),\n 2: [hexToBytes(addr.pubkey)],\n 3: [new Uint8Array(kind)],\n })\n return encodeBech32('naddr', data)\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n let entries: Uint8Array[] = []\n\n Object.entries(tlv)\n .reverse()\n .forEach(([t, vs]) => {\n vs.forEach(v => {\n let entry = new Uint8Array(v.length + 2)\n entry.set([parseInt(t)], 0)\n entry.set([v.length], 1)\n entry.set(v, 2)\n entries.push(entry)\n })\n })\n\n return concatBytes(...entries)\n}\n", "import type { NostrEvent } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n try {\n if (url.indexOf('://') === -1) url = 'wss://' + url\n let p = new URL(url)\n if (p.protocol === 'http:') p.protocol = 'ws:'\n else if (p.protocol === 'https:') p.protocol = 'wss:'\n p.pathname = p.pathname.replace(/\\/+/g, '/')\n if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n p.searchParams.sort()\n p.hash = ''\n return p.toString()\n } catch (e) {\n throw new Error(`Invalid URL: ${url}`)\n }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return b.created_at - event.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return event.created_at - b.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n let start = 0\n let end = arr.length - 1\n\n while (start <= end) {\n const mid = Math.floor((start + end) / 2)\n const cmp = compare(arr[mid])\n\n if (cmp === 0) {\n return [mid, true]\n }\n\n if (cmp < 0) {\n end = mid - 1\n } else {\n start = mid + 1\n }\n }\n\n return [start, false]\n}\n\nexport function mergeReverseSortedLists(list1: NostrEvent[], list2: NostrEvent[]): NostrEvent[] {\n const result: NostrEvent[] = new Array(list1.length + list2.length)\n result.length = 0\n let i1 = 0\n let i2 = 0\n let sameTimestampIds: string[] = []\n\n while (i1 < list1.length && i2 < list2.length) {\n let next: NostrEvent\n if (list1[i1]?.created_at > list2[i2]?.created_at) {\n next = list1[i1]\n i1++\n } else {\n next = list2[i2]\n i2++\n }\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i1 < list1.length) {\n const next = list1[i1]\n i1++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i2 < list2.length) {\n const next = list2[i2]\n i2++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n return result\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["import_utils"]
|
|
7
7
|
}
|
package/lib/cjs/nip29.js
CHANGED