@openclaw/buzz 2026.9.1 → 2026.9.3
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/dist/api.js +1 -1
- package/dist/{channel-B50upNWy.js → channel-C6rro-f7.js} +30 -13
- package/dist/channel-plugin-api.js +1 -1
- package/node_modules/nostr-tools/README.md +23 -0
- package/node_modules/nostr-tools/lib/cjs/index.js +25 -15
- package/node_modules/nostr-tools/lib/cjs/index.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nip27.js +24 -14
- package/node_modules/nostr-tools/lib/cjs/nip27.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nipb7.js +1003 -98
- package/node_modules/nostr-tools/lib/cjs/nipb7.js.map +4 -4
- package/node_modules/nostr-tools/lib/cjs/relay.js +1 -1
- package/node_modules/nostr-tools/lib/cjs/relay.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/index.js +25 -15
- package/node_modules/nostr-tools/lib/esm/index.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nip27.js +24 -14
- package/node_modules/nostr-tools/lib/esm/nip27.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nipb7.js +1009 -98
- package/node_modules/nostr-tools/lib/esm/nipb7.js.map +4 -4
- package/node_modules/nostr-tools/lib/esm/relay.js +1 -1
- package/node_modules/nostr-tools/lib/esm/relay.js.map +2 -2
- package/node_modules/nostr-tools/lib/nostr.bundle.js +25 -15
- package/node_modules/nostr-tools/lib/nostr.bundle.js.map +2 -2
- package/node_modules/nostr-tools/lib/types/nip27.d.ts +18 -0
- package/node_modules/nostr-tools/lib/types/nipb7.d.ts +112 -5
- package/node_modules/nostr-tools/lib/types/relay.d.ts +1 -1
- package/node_modules/nostr-tools/package.json +1 -7
- package/package.json +5 -5
- package/node_modules/nostr-tools/lib/cjs/nipb0.js +0 -1087
- package/node_modules/nostr-tools/lib/cjs/nipb0.js.map +0 -7
- package/node_modules/nostr-tools/lib/esm/nipb0.js +0 -1072
- package/node_modules/nostr-tools/lib/esm/nipb0.js.map +0 -7
- package/node_modules/nostr-tools/lib/types/nipb0.d.ts +0 -128
- package/node_modules/nostr-tools/lib/types/nipb0.test.d.ts +0 -1
|
@@ -125,8 +125,8 @@ function* parse(content) {
|
|
|
125
125
|
if (h === 0 || content[h - 1].match(noCharacter)) {
|
|
126
126
|
const m = content.slice(h + 1, h + MAX_HASHTAG_LENGTH).match(noCharacter);
|
|
127
127
|
const end = m ? h + 1 + m.index : max;
|
|
128
|
-
yield { type: "text", text: content.slice(prevIndex, h) };
|
|
129
|
-
yield { type: "hashtag", value: content.slice(h + 1, end) };
|
|
128
|
+
yield { type: "text", text: content.slice(prevIndex, h), start: prevIndex, end: h };
|
|
129
|
+
yield { type: "hashtag", value: content.slice(h + 1, end), start: h, end };
|
|
130
130
|
index = end;
|
|
131
131
|
prevIndex = index;
|
|
132
132
|
continue mainloop;
|
|
@@ -154,9 +154,9 @@ function* parse(content) {
|
|
|
154
154
|
pointer = data;
|
|
155
155
|
}
|
|
156
156
|
if (prevIndex !== u - 5) {
|
|
157
|
-
yield { type: "text", text: content.slice(prevIndex, u - 5) };
|
|
157
|
+
yield { type: "text", text: content.slice(prevIndex, u - 5), start: prevIndex, end: u - 5 };
|
|
158
158
|
}
|
|
159
|
-
yield { type: "reference", pointer };
|
|
159
|
+
yield { type: "reference", pointer, start: u - 5, end };
|
|
160
160
|
index = end;
|
|
161
161
|
prevIndex = index;
|
|
162
162
|
continue mainloop;
|
|
@@ -174,27 +174,32 @@ function* parse(content) {
|
|
|
174
174
|
throw new Error("invalid url");
|
|
175
175
|
}
|
|
176
176
|
if (prevIndex !== u - prefixLen) {
|
|
177
|
-
yield {
|
|
177
|
+
yield {
|
|
178
|
+
type: "text",
|
|
179
|
+
text: content.slice(prevIndex, u - prefixLen),
|
|
180
|
+
start: prevIndex,
|
|
181
|
+
end: u - prefixLen
|
|
182
|
+
};
|
|
178
183
|
}
|
|
179
184
|
if (/\.(png|jpe?g|gif|webp|heic|svg)$/i.test(url.pathname)) {
|
|
180
|
-
yield { type: "image", url: url.toString() };
|
|
185
|
+
yield { type: "image", url: url.toString(), start: u - prefixLen, end };
|
|
181
186
|
index = end;
|
|
182
187
|
prevIndex = index;
|
|
183
188
|
continue mainloop;
|
|
184
189
|
}
|
|
185
190
|
if (/\.(mp4|avi|webm|mkv|mov)$/i.test(url.pathname)) {
|
|
186
|
-
yield { type: "video", url: url.toString() };
|
|
191
|
+
yield { type: "video", url: url.toString(), start: u - prefixLen, end };
|
|
187
192
|
index = end;
|
|
188
193
|
prevIndex = index;
|
|
189
194
|
continue mainloop;
|
|
190
195
|
}
|
|
191
196
|
if (/\.(mp3|aac|ogg|opus|wav|flac)$/i.test(url.pathname)) {
|
|
192
|
-
yield { type: "audio", url: url.toString() };
|
|
197
|
+
yield { type: "audio", url: url.toString(), start: u - prefixLen, end };
|
|
193
198
|
index = end;
|
|
194
199
|
prevIndex = index;
|
|
195
200
|
continue mainloop;
|
|
196
201
|
}
|
|
197
|
-
yield { type: "url", url: url.toString() };
|
|
202
|
+
yield { type: "url", url: url.toString(), start: u - prefixLen, end };
|
|
198
203
|
index = end;
|
|
199
204
|
prevIndex = index;
|
|
200
205
|
continue mainloop;
|
|
@@ -212,9 +217,14 @@ function* parse(content) {
|
|
|
212
217
|
throw new Error("invalid ws url");
|
|
213
218
|
}
|
|
214
219
|
if (prevIndex !== u - prefixLen) {
|
|
215
|
-
yield {
|
|
220
|
+
yield {
|
|
221
|
+
type: "text",
|
|
222
|
+
text: content.slice(prevIndex, u - prefixLen),
|
|
223
|
+
start: prevIndex,
|
|
224
|
+
end: u - prefixLen
|
|
225
|
+
};
|
|
216
226
|
}
|
|
217
|
-
yield { type: "relay", url: url.toString() };
|
|
227
|
+
yield { type: "relay", url: url.toString(), start: u - prefixLen, end };
|
|
218
228
|
index = end;
|
|
219
229
|
prevIndex = index;
|
|
220
230
|
continue mainloop;
|
|
@@ -227,9 +237,9 @@ function* parse(content) {
|
|
|
227
237
|
const emoji = emojis[e];
|
|
228
238
|
if (content[u + emoji.shortcode.length + 1] === ":" && content.slice(u + 1, u + emoji.shortcode.length + 1) === emoji.shortcode) {
|
|
229
239
|
if (prevIndex !== u) {
|
|
230
|
-
yield { type: "text", text: content.slice(prevIndex, u) };
|
|
240
|
+
yield { type: "text", text: content.slice(prevIndex, u), start: prevIndex, end: u };
|
|
231
241
|
}
|
|
232
|
-
yield emoji;
|
|
242
|
+
yield { ...emoji, start: u, end: u + emoji.shortcode.length + 2 };
|
|
233
243
|
index = u + emoji.shortcode.length + 2;
|
|
234
244
|
prevIndex = index;
|
|
235
245
|
continue mainloop;
|
|
@@ -240,7 +250,7 @@ function* parse(content) {
|
|
|
240
250
|
}
|
|
241
251
|
}
|
|
242
252
|
if (prevIndex !== max) {
|
|
243
|
-
yield { type: "text", text: content.slice(prevIndex) };
|
|
253
|
+
yield { type: "text", text: content.slice(prevIndex), start: prevIndex, end: max };
|
|
244
254
|
}
|
|
245
255
|
}
|
|
246
256
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../nip19.ts", "../../utils.ts", "../../nip27.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 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\n/**\n * Checks if a string is a 64-char lowercase hex string as most Nostr ids and pubkeys.\n */\nexport function isHex32(input: string): boolean {\n if (input.length !== 64) return false\n for (let i = 0; i < 64; i++) {\n let cc = input.charCodeAt(i)\n if (isNaN(cc) || cc < 48 || cc > 102 || (cc > 57 && cc < 97)) {\n return false\n }\n }\n return true\n}\n", "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"],
|
|
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\n/**\n * Checks if a string is a 64-char lowercase hex string as most Nostr ids and pubkeys.\n */\nexport function isHex32(input: string): boolean {\n if (input.length !== 64) return false\n for (let i = 0; i < 64; i++) {\n let cc = input.charCodeAt(i)\n if (isNaN(cc) || cc < 48 || cc > 102 || (cc > 57 && cc < 97)) {\n return false\n }\n }\n return true\n}\n", "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 start: number\n end: number\n }\n | {\n type: 'reference'\n pointer: ProfilePointer | AddressPointer | EventPointer\n start: number\n end: number\n }\n | {\n type: 'url'\n url: string\n start: number\n end: number\n }\n | {\n type: 'relay'\n url: string\n start: number\n end: number\n }\n | {\n type: 'image'\n url: string\n start: number\n end: number\n }\n | {\n type: 'video'\n url: string\n start: number\n end: number\n }\n | {\n type: 'audio'\n url: string\n start: number\n end: number\n }\n | {\n type: 'emoji'\n shortcode: string\n url: string\n start: number\n end: number\n }\n | {\n type: 'hashtag'\n value: string\n start: number\n end: number\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), start: prevIndex, end: h }\n yield { type: 'hashtag', value: content.slice(h + 1, end), start: h, 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), start: prevIndex, end: u - 5 }\n }\n yield { type: 'reference', pointer, start: u - 5, end }\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 {\n type: 'text',\n text: content.slice(prevIndex, u - prefixLen),\n start: prevIndex,\n end: u - prefixLen,\n }\n }\n\n if (/\\.(png|jpe?g|gif|webp|heic|svg)$/i.test(url.pathname)) {\n yield { type: 'image', url: url.toString(), start: u - prefixLen, end }\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(), start: u - prefixLen, end }\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(), start: u - prefixLen, end }\n index = end\n prevIndex = index\n continue mainloop\n }\n\n yield { type: 'url', url: url.toString(), start: u - prefixLen, end }\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 {\n type: 'text',\n text: content.slice(prevIndex, u - prefixLen),\n start: prevIndex,\n end: u - prefixLen,\n }\n }\n yield { type: 'relay', url: url.toString(), start: u - prefixLen, end }\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), start: prevIndex, end: u }\n }\n yield { ...emoji, start: u, end: u + emoji.shortcode.length + 2 }\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), start: prevIndex, end: max }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["bytesToHex", "hexToBytes", "bytesToHex"]
|
|
7
7
|
}
|