@qntx/nostr 0.0.1

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.
Files changed (146) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +25 -0
  3. package/dist/builder-BphJa-Y-.d.mts +67 -0
  4. package/dist/builder-q0VsvnYE.mjs +158 -0
  5. package/dist/builder-q0VsvnYE.mjs.map +1 -0
  6. package/dist/client-Bd8B1mYm.mjs +835 -0
  7. package/dist/client-Bd8B1mYm.mjs.map +1 -0
  8. package/dist/client.d.mts +2 -0
  9. package/dist/client.mjs +2 -0
  10. package/dist/core.d.mts +8 -0
  11. package/dist/core.mjs +9 -0
  12. package/dist/error-B49rbDzT.d.mts +7 -0
  13. package/dist/error-CwBxle6N.mjs +13 -0
  14. package/dist/error-CwBxle6N.mjs.map +1 -0
  15. package/dist/error-DZEs5OuZ.mjs +24 -0
  16. package/dist/error-DZEs5OuZ.mjs.map +1 -0
  17. package/dist/error-WEFS-R-4.d.mts +20 -0
  18. package/dist/event-B5D2jxt_.d.mts +108 -0
  19. package/dist/event-CzoQEFMF.mjs +188 -0
  20. package/dist/event-CzoQEFMF.mjs.map +1 -0
  21. package/dist/filter-BxwjEkZp.mjs +113 -0
  22. package/dist/filter-BxwjEkZp.mjs.map +1 -0
  23. package/dist/filter-DUEKP3q1.d.mts +40 -0
  24. package/dist/gossip-3xkRRCm_.mjs +220 -0
  25. package/dist/gossip-3xkRRCm_.mjs.map +1 -0
  26. package/dist/gossip-C5RVB1UB.d.mts +71 -0
  27. package/dist/gossip.d.mts +3 -0
  28. package/dist/gossip.mjs +2 -0
  29. package/dist/http-6vevbBlu.d.mts +22 -0
  30. package/dist/http-D2rhokZo.mjs +24 -0
  31. package/dist/http-D2rhokZo.mjs.map +1 -0
  32. package/dist/index-BYMnhRIC.d.mts +161 -0
  33. package/dist/index-C0C4e-Gn.d.mts +55 -0
  34. package/dist/index-CaxrMD1A.d.mts +1 -0
  35. package/dist/index-DaRyjo7O.d.mts +77 -0
  36. package/dist/index-DgOugrpF.d.mts +275 -0
  37. package/dist/index-EwL6fHLo.d.mts +267 -0
  38. package/dist/index-k2YhCgsh.d.mts +57 -0
  39. package/dist/index.d.mts +22 -0
  40. package/dist/index.mjs +23 -0
  41. package/dist/key-BiGxGLhg.d.mts +37 -0
  42. package/dist/key-Dn-aX4x4.mjs +130 -0
  43. package/dist/key-Dn-aX4x4.mjs.map +1 -0
  44. package/dist/kind-BLl4cLOc.mjs +59 -0
  45. package/dist/kind-BLl4cLOc.mjs.map +1 -0
  46. package/dist/loaders-BNQszDAn.mjs +767 -0
  47. package/dist/loaders-BNQszDAn.mjs.map +1 -0
  48. package/dist/loaders.d.mts +3 -0
  49. package/dist/loaders.mjs +2 -0
  50. package/dist/memory-BCmhZPek.mjs +461 -0
  51. package/dist/memory-BCmhZPek.mjs.map +1 -0
  52. package/dist/message-C_Uj-Y-c.mjs +179 -0
  53. package/dist/message-C_Uj-Y-c.mjs.map +1 -0
  54. package/dist/message-DJC-Jz6T.d.mts +32 -0
  55. package/dist/nip17-BWq-Kfq5.d.mts +38 -0
  56. package/dist/nip19-D7905oIv.d.mts +63 -0
  57. package/dist/nip46-C_DoxiS_.d.mts +51 -0
  58. package/dist/nip59-DljW_sYh.d.mts +76 -0
  59. package/dist/nip65-Di5hZRuV.d.mts +21 -0
  60. package/dist/nip77-ByxlgoQO.d.mts +55 -0
  61. package/dist/nips/blossom.d.mts +93 -0
  62. package/dist/nips/blossom.mjs +325 -0
  63. package/dist/nips/blossom.mjs.map +1 -0
  64. package/dist/nips/nip04.d.mts +8 -0
  65. package/dist/nips/nip04.mjs +41 -0
  66. package/dist/nips/nip04.mjs.map +1 -0
  67. package/dist/nips/nip05.d.mts +89 -0
  68. package/dist/nips/nip05.mjs +147 -0
  69. package/dist/nips/nip05.mjs.map +1 -0
  70. package/dist/nips/nip10.d.mts +43 -0
  71. package/dist/nips/nip10.mjs +194 -0
  72. package/dist/nips/nip10.mjs.map +1 -0
  73. package/dist/nips/nip11.d.mts +43 -0
  74. package/dist/nips/nip11.mjs +99 -0
  75. package/dist/nips/nip11.mjs.map +1 -0
  76. package/dist/nips/nip13.d.mts +23 -0
  77. package/dist/nips/nip13.mjs +88 -0
  78. package/dist/nips/nip13.mjs.map +1 -0
  79. package/dist/nips/nip17.d.mts +2 -0
  80. package/dist/nips/nip17.mjs +138 -0
  81. package/dist/nips/nip17.mjs.map +1 -0
  82. package/dist/nips/nip19.d.mts +2 -0
  83. package/dist/nips/nip19.mjs +174 -0
  84. package/dist/nips/nip19.mjs.map +1 -0
  85. package/dist/nips/nip21.d.mts +17 -0
  86. package/dist/nips/nip21.mjs +29 -0
  87. package/dist/nips/nip21.mjs.map +1 -0
  88. package/dist/nips/nip27.d.mts +42 -0
  89. package/dist/nips/nip27.mjs +175 -0
  90. package/dist/nips/nip27.mjs.map +1 -0
  91. package/dist/nips/nip42.d.mts +12 -0
  92. package/dist/nips/nip42.mjs +22 -0
  93. package/dist/nips/nip42.mjs.map +1 -0
  94. package/dist/nips/nip44.d.mts +12 -0
  95. package/dist/nips/nip44.mjs +125 -0
  96. package/dist/nips/nip44.mjs.map +1 -0
  97. package/dist/nips/nip46.d.mts +2 -0
  98. package/dist/nips/nip46.mjs +132 -0
  99. package/dist/nips/nip46.mjs.map +1 -0
  100. package/dist/nips/nip49.d.mts +12 -0
  101. package/dist/nips/nip49.mjs +80 -0
  102. package/dist/nips/nip49.mjs.map +1 -0
  103. package/dist/nips/nip51.d.mts +88 -0
  104. package/dist/nips/nip51.mjs +246 -0
  105. package/dist/nips/nip51.mjs.map +1 -0
  106. package/dist/nips/nip57.d.mts +43 -0
  107. package/dist/nips/nip57.mjs +230 -0
  108. package/dist/nips/nip57.mjs.map +1 -0
  109. package/dist/nips/nip59.d.mts +2 -0
  110. package/dist/nips/nip59.mjs +171 -0
  111. package/dist/nips/nip59.mjs.map +1 -0
  112. package/dist/nips/nip65.d.mts +2 -0
  113. package/dist/nips/nip65.mjs +76 -0
  114. package/dist/nips/nip65.mjs.map +1 -0
  115. package/dist/nips/nip77.d.mts +2 -0
  116. package/dist/nips/nip77.mjs +421 -0
  117. package/dist/nips/nip77.mjs.map +1 -0
  118. package/dist/nips/nip96.d.mts +31 -0
  119. package/dist/nips/nip96.mjs +95 -0
  120. package/dist/nips/nip96.mjs.map +1 -0
  121. package/dist/nips/nip98.d.mts +28 -0
  122. package/dist/nips/nip98.mjs +91 -0
  123. package/dist/nips/nip98.mjs.map +1 -0
  124. package/dist/nostr_crypto_wasm_bg-hi5aXi5P.mjs +4 -0
  125. package/dist/nostr_crypto_wasm_bg.wasm +0 -0
  126. package/dist/pool-C_j1OmBB.mjs +1881 -0
  127. package/dist/pool-C_j1OmBB.mjs.map +1 -0
  128. package/dist/pool-DhRi7l-b.d.mts +297 -0
  129. package/dist/relay.d.mts +4 -0
  130. package/dist/relay.mjs +2 -0
  131. package/dist/signer-CZ6K6FUc.mjs +473 -0
  132. package/dist/signer-CZ6K6FUc.mjs.map +1 -0
  133. package/dist/signer.d.mts +3 -0
  134. package/dist/signer.mjs +2 -0
  135. package/dist/storage-DOy0iLRE.mjs +870 -0
  136. package/dist/storage-DOy0iLRE.mjs.map +1 -0
  137. package/dist/storage.d.mts +4 -0
  138. package/dist/storage.mjs +4 -0
  139. package/dist/types-BzyADqnw.d.mts +19 -0
  140. package/dist/types-Dn24TPFs.d.mts +43 -0
  141. package/dist/util-6tfLQ9JF.mjs +73 -0
  142. package/dist/util-6tfLQ9JF.mjs.map +1 -0
  143. package/dist/wasm.d.mts +19 -0
  144. package/dist/wasm.mjs +237 -0
  145. package/dist/wasm.mjs.map +1 -0
  146. package/package.json +204 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nip57.mjs","names":[],"sources":["../../src/nips/nip57.ts"],"sourcesContent":["/**\n * NIP-57 Lightning Zaps — zap request template (kind 9734) and receipt validation (kind 9735).\n * Does not fetch LNURL. Receipt checks never throw.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/57.md\n */\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { bech32 } from \"@scure/base\";\nimport { EventValidationError } from \"../core/error.ts\";\nimport { validateSignedEvent, type Event, type EventTemplate } from \"../core/event.ts\";\nimport { verifyEvent } from \"../core/key.ts\";\nimport { isAddressableKind, Kind } from \"../core/kind.ts\";\nimport { eventAddress, getDTag, parseEventAddress, Tag } from \"../core/tag.ts\";\nimport { hexToBytes, utf8Encoder } from \"../core/util.ts\";\n\nexport type ProfileZapRequest = {\n pubkey: string;\n /** Amount in millisats. */\n amount: number;\n relays: readonly string[];\n comment?: string;\n lnurl?: string;\n};\n\nexport type EventZapRequest = {\n event: Event;\n /** Amount in millisats. */\n amount: number;\n relays: readonly string[];\n comment?: string;\n lnurl?: string;\n};\n\nexport function makeZapRequest(params: ProfileZapRequest | EventZapRequest): EventTemplate {\n if (params.relays.length === 0) {\n throw new EventValidationError(\"relays tag requires one or more URLs\");\n }\n\n const recipient = \"event\" in params ? params.event.pubkey : params.pubkey;\n const tags: Tag[] = [\n Tag.p(recipient),\n [\"amount\", params.amount.toString()],\n [\"relays\", ...params.relays],\n ];\n\n if (\"event\" in params) {\n const { event } = params;\n tags.push(Tag.e(event.id));\n if (isAddressableKind(event.kind)) {\n const d = getDTag(event.tags);\n if (d === undefined) throw new EventValidationError(\"d tag not found\");\n const addr = eventAddress(event);\n if (addr) tags.push([\"a\", addr]);\n }\n tags.push([\"k\", event.kind.toString()]);\n }\n\n if (params.lnurl) tags.push([\"lnurl\", params.lnurl]);\n\n return {\n kind: Kind.ZapRequest,\n created_at: Math.floor(Date.now() / 1000),\n content: params.comment ?? \"\",\n tags,\n };\n}\n\nexport type ZapReceiptContext = {\n nostrPubkey: string;\n lnurl?: string;\n};\n\nexport type ZapReceiptValidation = {\n valid: boolean;\n reason?: string;\n request?: Event;\n amountMsats?: number;\n};\n\nexport type Bolt11Fields = {\n amountMsats?: number;\n descriptionHash?: Uint8Array;\n paymentHash?: Uint8Array;\n};\n\nfunction firstTagValue(tags: readonly Tag[], name: string): string | undefined {\n for (const tag of tags) {\n if (tag[0] === name && tag[1] !== undefined) return tag[1];\n }\n return undefined;\n}\n\nfunction bytesEq(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n let x = 0;\n for (let i = 0; i < a.length; i++) x |= a[i]! ^ b[i]!;\n return x === 0;\n}\n\nfunction fail(reason: string): ZapReceiptValidation {\n return { valid: false, reason };\n}\n\nconst MSATS_PER_BTC = 100_000_000_000;\nconst MSATS_PER_MILLI = 100_000_000;\nconst MSATS_PER_MICRO = 100_000;\nconst MSATS_PER_NANO = 100;\n\n/** Amount is digits after `ln` + currency letters, optional m/u/n/p. Non-integer pico is omitted. */\nfunction amountMsatsFromHrp(hrp: string): number | undefined {\n if (!hrp.startsWith(\"ln\")) return undefined;\n let i = 2;\n while (i < hrp.length) {\n const c = hrp.charCodeAt(i);\n if (c < 97 || c > 122) break;\n i++;\n }\n if (i === hrp.length) return undefined;\n const rest = hrp.slice(i);\n const m = /^([0-9]+)([munp])?$/.exec(rest);\n if (!m) return undefined;\n const n = Number(m[1]);\n if (!Number.isSafeInteger(n)) return undefined;\n const mul = m[2];\n if (mul === undefined) return n * MSATS_PER_BTC;\n if (mul === \"m\") return n * MSATS_PER_MILLI;\n if (mul === \"u\") return n * MSATS_PER_MICRO;\n if (mul === \"n\") return n * MSATS_PER_NANO;\n // p: 0.1 msat; drop amounts that are not whole millisats\n if (n % 10 !== 0) return undefined;\n return n / 10;\n}\n\nfunction parseMsatsTag(value: string): number | undefined {\n if (!/^[0-9]+$/.test(value)) return undefined;\n const n = Number(value);\n return Number.isSafeInteger(n) ? n : undefined;\n}\n\nfunction countTags(tags: readonly Tag[], name: string): number {\n let n = 0;\n for (const tag of tags) {\n if (tag[0] === name) n++;\n }\n return n;\n}\n\nfunction hasHexTagValue(tags: readonly Tag[], name: string, value: string): boolean {\n const needle = value.toLowerCase();\n for (const tag of tags) {\n if (tag[0] === name && tag[1] !== undefined && tag[1].toLowerCase() === needle) return true;\n }\n return false;\n}\n\nfunction hasAddressTag(tags: readonly Tag[], value: string): boolean {\n const want = parseEventAddress(value);\n if (!want) return false;\n for (const tag of tags) {\n if (tag[0] !== \"a\" || tag[1] === undefined) continue;\n const got = parseEventAddress(tag[1]);\n if (\n got &&\n got.kind === want.kind &&\n got.pubkey === want.pubkey &&\n got.identifier === want.identifier\n ) {\n return true;\n }\n }\n return false;\n}\n\nconst BOLT11_TIMESTAMP_WORDS = 7;\nconst BOLT11_SIGNATURE_WORDS = 104;\nconst BOLT11_HASH_WORDS = 52;\nconst HASH_BYTES = 32;\n\n/** Decode a BOLT11 invoice. Never throws. Requires a 32-byte payment hash (type 1). */\nexport function parseBolt11(pr: string): Bolt11Fields | undefined {\n try {\n // Invoices exceed bech32's default 90-char limit.\n const { prefix, words } = bech32.decode(pr.toLowerCase() as `${string}1${string}`, false);\n if (!prefix.startsWith(\"ln\")) return undefined;\n // timestamp (7) + tagged fields + secp256k1 signature (104)\n if (words.length < BOLT11_TIMESTAMP_WORDS + BOLT11_SIGNATURE_WORDS) return undefined;\n const fields: Bolt11Fields = {};\n const amountMsats = amountMsatsFromHrp(prefix);\n if (amountMsats !== undefined) fields.amountMsats = amountMsats;\n const tlvEnd = words.length - BOLT11_SIGNATURE_WORDS;\n let i = BOLT11_TIMESTAMP_WORDS;\n while (i + 3 <= tlvEnd) {\n const type = words[i]!;\n const dataLen = (words[i + 1]! << 5) | words[i + 2]!;\n i += 3;\n if (i + dataLen > tlvEnd) break;\n const data = words.slice(i, i + dataLen);\n i += dataLen;\n if ((type !== 1 && type !== 23) || dataLen !== BOLT11_HASH_WORDS) continue;\n const bytes = bech32.fromWordsUnsafe(data);\n if (!bytes || bytes.length !== HASH_BYTES) continue;\n if (type === 1) fields.paymentHash ??= bytes;\n else fields.descriptionHash ??= bytes;\n }\n if (!fields.paymentHash) return undefined;\n return fields;\n } catch {\n return undefined;\n }\n}\n\n/** Parse the `description` tag of a kind 9735 receipt as a signed kind 9734. Never throws. */\nexport function parseZapRequestFromReceipt(receipt: Event): Event | undefined {\n try {\n const raw = firstTagValue(receipt.tags, \"description\");\n if (raw === undefined) return undefined;\n const parsed: unknown = JSON.parse(raw);\n if (!validateSignedEvent(parsed) || parsed.kind !== Kind.ZapRequest) return undefined;\n if (!verifyEvent(parsed)) return undefined;\n return parsed;\n } catch {\n return undefined;\n }\n}\n\n/** Validate a kind 9735 zap receipt. Never throws. */\nexport function validateZapReceipt(receipt: Event, ctx: ZapReceiptContext): ZapReceiptValidation {\n try {\n if (receipt.kind !== Kind.Zap || !verifyEvent(receipt)) {\n return fail(\"invalid receipt\");\n }\n if (receipt.pubkey.toLowerCase() !== ctx.nostrPubkey.toLowerCase()) {\n return fail(\"pubkey mismatch\");\n }\n\n const descriptionRaw = firstTagValue(receipt.tags, \"description\");\n const request = parseZapRequestFromReceipt(receipt);\n if (descriptionRaw === undefined || !request) return fail(\"invalid zap request\");\n if (countTags(request.tags, \"p\") !== 1) return fail(\"invalid p count\");\n if (countTags(request.tags, \"e\") > 1) return fail(\"too many e tags\");\n if (!firstTagValue(request.tags, \"relays\")) return fail(\"missing relays\");\n\n for (const tag of request.tags) {\n if (tag[0] !== \"a\") continue;\n if (tag[1] === undefined) return fail(\"invalid a\");\n const parsed = parseEventAddress(tag[1]);\n if (!parsed || !isAddressableKind(parsed.kind)) return fail(\"invalid a\");\n }\n\n // Request P is the LNURL provider (receipt pubkey), not the zap sender.\n const requestPCount = countTags(request.tags, \"P\");\n if (requestPCount > 1) return fail(\"too many P tags\");\n if (requestPCount === 1) {\n const requestP = firstTagValue(request.tags, \"P\");\n if (requestP === undefined || requestP.toLowerCase() !== receipt.pubkey.toLowerCase()) {\n return fail(\"request P mismatch\");\n }\n }\n\n const bolt11Tag = firstTagValue(receipt.tags, \"bolt11\");\n if (bolt11Tag === undefined) return fail(\"missing bolt11\");\n const bolt11 = parseBolt11(bolt11Tag);\n if (!bolt11 || !bolt11.descriptionHash) return fail(\"invalid bolt11\");\n\n const requestAmount = firstTagValue(request.tags, \"amount\");\n if (requestAmount !== undefined) {\n const msats = parseMsatsTag(requestAmount);\n if (msats === undefined || bolt11.amountMsats !== msats) return fail(\"amount mismatch\");\n }\n\n // Hash the tag payload, not JSON.stringify(parsed) (key order may differ).\n const digest = sha256(utf8Encoder.encode(descriptionRaw));\n if (!bytesEq(digest, bolt11.descriptionHash)) return fail(\"description hash mismatch\");\n\n const requestLnurl = firstTagValue(request.tags, \"lnurl\");\n if (requestLnurl !== undefined && ctx.lnurl !== undefined) {\n if (requestLnurl.toLowerCase() !== ctx.lnurl.toLowerCase()) return fail(\"lnurl mismatch\");\n }\n\n const preimageHex = firstTagValue(receipt.tags, \"preimage\");\n if (preimageHex !== undefined && bolt11.paymentHash) {\n let preimage: Uint8Array;\n try {\n preimage = hexToBytes(preimageHex);\n } catch {\n return fail(\"preimage mismatch\");\n }\n if (!bytesEq(sha256(preimage), bolt11.paymentHash)) return fail(\"preimage mismatch\");\n }\n\n const recipient = firstTagValue(request.tags, \"p\");\n if (recipient === undefined || !hasHexTagValue(receipt.tags, \"p\", recipient)) {\n return fail(\"missing p\");\n }\n const requestE = firstTagValue(request.tags, \"e\");\n if (requestE !== undefined && !hasHexTagValue(receipt.tags, \"e\", requestE)) {\n return fail(\"missing e\");\n }\n for (const tag of request.tags) {\n if (tag[0] !== \"a\" || tag[1] === undefined) continue;\n if (!hasAddressTag(receipt.tags, tag[1])) return fail(\"missing a\");\n }\n\n // Receipt P is the zap sender (request pubkey). Do not copy request tag P.\n for (const tag of receipt.tags) {\n if (tag[0] !== \"P\") continue;\n if (tag[1] === undefined || tag[1].toLowerCase() !== request.pubkey.toLowerCase()) {\n return fail(\"receipt P mismatch\");\n }\n }\n\n const amountMsats =\n bolt11.amountMsats ??\n (requestAmount !== undefined ? parseMsatsTag(requestAmount) : undefined);\n return amountMsats === undefined\n ? { valid: true, request }\n : { valid: true, request, amountMsats };\n } catch {\n return fail(\"invalid receipt\");\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { _ as requireNip59Crypto, a as Rumor, b as wrap, c as TimestampRandomize, d as createRumor, f as createSeal, g as requireNip44Decryptor, h as randomPastTimestamp, i as Nip59Error, l as WrapOptions, m as isGiftWrapKind, n as Nip44Decryptor, o as SealOptions, p as eventToJson, r as Nip59Crypto, s as TWO_DAYS_SECS, t as GiftWrapTimestamps, u as createGiftWrap, v as rumorToJson, y as unwrap } from "../nip59-DljW_sYh.mjs";
2
+ export { GiftWrapTimestamps, Nip44Decryptor, Nip59Crypto, Nip59Error, Rumor, SealOptions, TWO_DAYS_SECS, TimestampRandomize, WrapOptions, createGiftWrap, createRumor, createSeal, eventToJson, isGiftWrapKind, randomPastTimestamp, requireNip44Decryptor, requireNip59Crypto, rumorToJson, unwrap, wrap };
@@ -0,0 +1,171 @@
1
+ import { t as assertHex32 } from "../util-6tfLQ9JF.mjs";
2
+ import { a as NostrError } from "../error-DZEs5OuZ.mjs";
3
+ import { t as Kind } from "../kind-BLl4cLOc.mjs";
4
+ import { f as validateEvent, m as Tag, p as validateSignedEvent, t as getEventHash } from "../event-CzoQEFMF.mjs";
5
+ import { r as finalizeEvent, s as verifyEvent, t as Keys } from "../key-Dn-aX4x4.mjs";
6
+ import { encryptToPubkey } from "./nip44.mjs";
7
+ import { randomBytes } from "@noble/hashes/utils.js";
8
+ //#region src/nips/nip59.ts
9
+ /**
10
+ * NIP-59: Gift Wrap.
11
+ * Rumor → NIP-44 seal (kind 13) → gift wrap (kind 1059).
12
+ * Does not import signer, relay, or client. Crypto is a structural type.
13
+ *
14
+ * @see https://github.com/nostr-protocol/nips/blob/master/59.md
15
+ */
16
+ var Nip59Error = class extends NostrError {
17
+ constructor(message, options) {
18
+ super(message, options);
19
+ }
20
+ };
21
+ const TWO_DAYS_SECS = 172800;
22
+ function isGiftWrapKind(kind) {
23
+ return kind === Kind.GiftWrap || kind === Kind.GiftWrapEphemeral;
24
+ }
25
+ /** Narrow a signer-shaped object to {@link Nip59Crypto}, or throw. */
26
+ function requireNip59Crypto(crypto) {
27
+ const encrypt = crypto.nip44Encrypt;
28
+ const decrypt = crypto.nip44Decrypt;
29
+ if (!encrypt || !decrypt) throw new Nip59Error("NIP-44 is required");
30
+ return {
31
+ getPublicKey: () => crypto.getPublicKey(),
32
+ signEvent: (unsigned) => crypto.signEvent(unsigned),
33
+ nip44Encrypt: (peer, plaintext) => encrypt.call(crypto, peer, plaintext),
34
+ nip44Decrypt: (peer, payload) => decrypt.call(crypto, peer, payload)
35
+ };
36
+ }
37
+ function requireNip44Decryptor(crypto) {
38
+ const decrypt = crypto.nip44Decrypt;
39
+ if (!decrypt) throw new Nip59Error("NIP-44 is required");
40
+ return { nip44Decrypt: (peer, payload) => decrypt.call(crypto, peer, payload) };
41
+ }
42
+ function rumorToJson(rumor) {
43
+ return JSON.stringify({
44
+ id: rumor.id,
45
+ pubkey: rumor.pubkey,
46
+ created_at: rumor.created_at,
47
+ kind: rumor.kind,
48
+ tags: rumor.tags,
49
+ content: rumor.content
50
+ });
51
+ }
52
+ function eventToJson(event) {
53
+ return JSON.stringify({
54
+ id: event.id,
55
+ pubkey: event.pubkey,
56
+ created_at: event.created_at,
57
+ kind: event.kind,
58
+ tags: event.tags,
59
+ content: event.content,
60
+ sig: event.sig
61
+ });
62
+ }
63
+ function defaultRandomInt(maxExclusive) {
64
+ if (!Number.isSafeInteger(maxExclusive) || maxExclusive <= 0) throw new Nip59Error("randomInt bound must be a positive integer");
65
+ const bytes = randomBytes(4);
66
+ return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getUint32(0) % maxExclusive;
67
+ }
68
+ function randomPastTimestamp(opts) {
69
+ return (opts?.now ?? Math.floor(Date.now() / 1e3)) - (opts?.randomInt ?? defaultRandomInt)(TWO_DAYS_SECS);
70
+ }
71
+ function createRumor(pubkey, template) {
72
+ const unsigned = {
73
+ kind: template.kind,
74
+ content: template.content ?? "",
75
+ tags: template.tags ? [...template.tags] : [],
76
+ created_at: template.created_at ?? Math.floor(Date.now() / 1e3),
77
+ pubkey: assertHex32(pubkey, "public key")
78
+ };
79
+ return {
80
+ ...unsigned,
81
+ id: getEventHash(unsigned)
82
+ };
83
+ }
84
+ async function createSeal(crypto, recipient, rumor, opts) {
85
+ const sealer = requireNip59Crypto(crypto);
86
+ const recipientPk = assertHex32(recipient, "public key");
87
+ let content;
88
+ try {
89
+ content = await sealer.nip44Encrypt(recipientPk, rumorToJson(rumor));
90
+ } catch (error) {
91
+ throw new Nip59Error("failed to encrypt", { cause: error });
92
+ }
93
+ const created_at = opts?.timestamps?.seal ?? (opts?.randomize === "wrap" ? rumor.created_at : randomPastTimestamp(opts));
94
+ const pubkey = await sealer.getPublicKey();
95
+ return sealer.signEvent({
96
+ kind: Kind.Seal,
97
+ content,
98
+ created_at,
99
+ tags: [],
100
+ pubkey
101
+ });
102
+ }
103
+ function createGiftWrap(seal, recipient, opts) {
104
+ const recipientPk = assertHex32(recipient, "public key");
105
+ const ephemeral = Keys.generate();
106
+ const content = encryptToPubkey(eventToJson(seal), ephemeral.secretKey.bytes, recipientPk);
107
+ const created_at = opts?.timestamps?.wrap ?? randomPastTimestamp(opts);
108
+ const tags = [Tag.p(recipientPk, opts?.relayHint), ...opts?.extraTags ?? []];
109
+ return finalizeEvent({
110
+ kind: Kind.GiftWrap,
111
+ content,
112
+ created_at,
113
+ tags
114
+ }, ephemeral.secretKey);
115
+ }
116
+ async function wrap(crypto, recipient, rumor, opts) {
117
+ return createGiftWrap(await createSeal(crypto, recipient, rumor, opts), recipient, opts);
118
+ }
119
+ function parseJson(text) {
120
+ try {
121
+ return JSON.parse(text);
122
+ } catch (error) {
123
+ throw new Nip59Error("invalid JSON", { cause: error });
124
+ }
125
+ }
126
+ function isRecord(value) {
127
+ return typeof value === "object" && value !== null && !Array.isArray(value);
128
+ }
129
+ function parseRumor(value) {
130
+ if (!isRecord(value)) throw new Nip59Error("invalid rumor");
131
+ if (Object.hasOwn(value, "sig")) throw new Nip59Error("rumor must be unsigned");
132
+ const givenId = value.id;
133
+ if (!validateEvent(value)) throw new Nip59Error("invalid rumor");
134
+ const unsigned = {
135
+ kind: value.kind,
136
+ tags: value.tags,
137
+ content: value.content,
138
+ created_at: value.created_at,
139
+ pubkey: value.pubkey.toLowerCase()
140
+ };
141
+ const expected = getEventHash(unsigned);
142
+ if (typeof givenId === "string" && givenId.toLowerCase() !== expected) throw new Nip59Error("invalid rumor");
143
+ return {
144
+ ...unsigned,
145
+ id: expected
146
+ };
147
+ }
148
+ async function decryptLayer(crypto, peer, payload) {
149
+ try {
150
+ return await crypto.nip44Decrypt(peer, payload);
151
+ } catch (error) {
152
+ throw new Nip59Error("failed to decrypt", { cause: error });
153
+ }
154
+ }
155
+ async function unwrap(crypto, giftWrap) {
156
+ const decryptor = requireNip44Decryptor(crypto);
157
+ if (!validateSignedEvent(giftWrap) || !isGiftWrapKind(giftWrap.kind)) throw new Nip59Error("expected gift wrap");
158
+ if (!verifyEvent(giftWrap)) throw new Nip59Error("gift wrap signature");
159
+ const sealRaw = parseJson(await decryptLayer(decryptor, giftWrap.pubkey, giftWrap.content));
160
+ if (!validateSignedEvent(sealRaw)) throw new Nip59Error("expected seal");
161
+ if (sealRaw.kind !== Kind.Seal) throw new Nip59Error("expected seal");
162
+ if (sealRaw.tags.length !== 0) throw new Nip59Error("seal tags must be empty");
163
+ if (!verifyEvent(sealRaw)) throw new Nip59Error("seal signature");
164
+ const rumor = parseRumor(parseJson(await decryptLayer(decryptor, sealRaw.pubkey, sealRaw.content)));
165
+ if (sealRaw.pubkey.toLowerCase() !== rumor.pubkey.toLowerCase()) throw new Nip59Error("seal pubkey does not match rumor pubkey");
166
+ return rumor;
167
+ }
168
+ //#endregion
169
+ export { Nip59Error, TWO_DAYS_SECS, createGiftWrap, createRumor, createSeal, eventToJson, isGiftWrapKind, randomPastTimestamp, requireNip44Decryptor, requireNip59Crypto, rumorToJson, unwrap, wrap };
170
+
171
+ //# sourceMappingURL=nip59.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nip59.mjs","names":["TagBuilder"],"sources":["../../src/nips/nip59.ts"],"sourcesContent":["/**\n * NIP-59: Gift Wrap.\n * Rumor → NIP-44 seal (kind 13) → gift wrap (kind 1059).\n * Does not import signer, relay, or client. Crypto is a structural type.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/59.md\n */\nimport { randomBytes } from \"@noble/hashes/utils.js\";\nimport type { Event, UnsignedEvent } from \"../core/event.ts\";\nimport { getEventHash, validateEvent, validateSignedEvent } from \"../core/event.ts\";\nimport { NostrError } from \"../core/error.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { Keys, finalizeEvent, verifyEvent } from \"../core/key.ts\";\nimport type { Tag } from \"../core/tag.ts\";\nimport { Tag as TagBuilder } from \"../core/tag.ts\";\nimport { assertHex32 } from \"../core/util.ts\";\nimport { encryptToPubkey } from \"./nip44.ts\";\n\n/** Unsigned event with a computed id. Never has `sig`. */\nexport type Rumor = UnsignedEvent & {\n readonly id: string;\n};\n\n/**\n * Structural crypto used by NIP-59.\n * Satisfied by NostrSigner when nip44Encrypt/nip44Decrypt are present.\n * This module must not import src/signer/.\n */\nexport type Nip59Crypto = {\n getPublicKey(): Promise<string>;\n signEvent(unsigned: UnsignedEvent): Promise<Event>;\n nip44Encrypt(peer: string, plaintext: string): Promise<string>;\n nip44Decrypt(peer: string, payload: string): Promise<string>;\n};\n\nexport type Nip44Decryptor = {\n nip44Decrypt(peer: string, payload: string): Promise<string>;\n};\n\nexport type GiftWrapTimestamps = {\n readonly seal: number;\n readonly wrap: number;\n};\n\n/** `\"wrap\"` randomizes only the gift wrap; `\"seal+wrap\"` (default) randomizes both. */\nexport type TimestampRandomize = \"wrap\" | \"seal+wrap\";\n\nexport type WrapOptions = {\n /** Unix seconds used as the randomization window end. Default: floor(Date.now()/1000). */\n readonly now?: number;\n /** Uniform integer in [0, maxExclusive). Default: CSPRNG via @noble/hashes randomBytes. */\n readonly randomInt?: (maxExclusive: number) => number;\n /** When set, used as-is. Overrides now/randomInt for this call. */\n readonly timestamps?: GiftWrapTimestamps;\n readonly relayHint?: string;\n /** Appended after the required wrap `p` tag. Never applied to the seal. */\n readonly extraTags?: readonly Tag[];\n /** Default `\"seal+wrap\"` (NIP-59). `\"wrap\"` = only wrap timestamp is randomized; seal uses rumor.created_at. */\n readonly randomize?: TimestampRandomize;\n};\n\nexport type SealOptions = Pick<WrapOptions, \"now\" | \"randomInt\" | \"timestamps\" | \"randomize\">;\n\nexport class Nip59Error extends NostrError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n }\n}\n\nexport const TWO_DAYS_SECS = 2 * 24 * 60 * 60;\n\nexport function isGiftWrapKind(kind: number): boolean {\n return kind === Kind.GiftWrap || kind === Kind.GiftWrapEphemeral;\n}\n\ntype Nip59CryptoInput = {\n getPublicKey(): Promise<string>;\n signEvent(unsigned: UnsignedEvent): Promise<Event>;\n nip44Encrypt?: (peer: string, plaintext: string) => Promise<string>;\n nip44Decrypt?: (peer: string, payload: string) => Promise<string>;\n};\n\n/** Narrow a signer-shaped object to {@link Nip59Crypto}, or throw. */\nexport function requireNip59Crypto(crypto: Nip59CryptoInput): Nip59Crypto {\n const encrypt = crypto.nip44Encrypt;\n const decrypt = crypto.nip44Decrypt;\n if (!encrypt || !decrypt) {\n throw new Nip59Error(\"NIP-44 is required\");\n }\n return {\n getPublicKey: () => crypto.getPublicKey(),\n signEvent: (unsigned) => crypto.signEvent(unsigned),\n nip44Encrypt: (peer, plaintext) => encrypt.call(crypto, peer, plaintext),\n nip44Decrypt: (peer, payload) => decrypt.call(crypto, peer, payload),\n };\n}\n\nexport function requireNip44Decryptor(crypto: {\n nip44Decrypt?: (peer: string, payload: string) => Promise<string>;\n}): Nip44Decryptor {\n const decrypt = crypto.nip44Decrypt;\n if (!decrypt) {\n throw new Nip59Error(\"NIP-44 is required\");\n }\n return {\n nip44Decrypt: (peer, payload) => decrypt.call(crypto, peer, payload),\n };\n}\n\nexport function rumorToJson(rumor: Rumor): string {\n return JSON.stringify({\n id: rumor.id,\n pubkey: rumor.pubkey,\n created_at: rumor.created_at,\n kind: rumor.kind,\n tags: rumor.tags,\n content: rumor.content,\n });\n}\n\nexport function eventToJson(event: Event): string {\n return JSON.stringify({\n id: event.id,\n pubkey: event.pubkey,\n created_at: event.created_at,\n kind: event.kind,\n tags: event.tags,\n content: event.content,\n sig: event.sig,\n });\n}\n\nfunction defaultRandomInt(maxExclusive: number): number {\n if (!Number.isSafeInteger(maxExclusive) || maxExclusive <= 0) {\n throw new Nip59Error(\"randomInt bound must be a positive integer\");\n }\n const bytes = randomBytes(4);\n const n = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getUint32(0);\n return n % maxExclusive;\n}\n\nexport function randomPastTimestamp(opts?: {\n now?: number;\n randomInt?: (maxExclusive: number) => number;\n}): number {\n const now = opts?.now ?? Math.floor(Date.now() / 1000);\n const offset = (opts?.randomInt ?? defaultRandomInt)(TWO_DAYS_SECS);\n return now - offset;\n}\n\nexport function createRumor(\n pubkey: string,\n template: {\n kind: number;\n content?: string;\n tags?: readonly Tag[];\n created_at?: number;\n },\n): Rumor {\n const unsigned: UnsignedEvent = {\n kind: template.kind,\n content: template.content ?? \"\",\n tags: template.tags ? [...template.tags] : [],\n created_at: template.created_at ?? Math.floor(Date.now() / 1000),\n pubkey: assertHex32(pubkey, \"public key\"),\n };\n return { ...unsigned, id: getEventHash(unsigned) };\n}\n\nexport async function createSeal(\n crypto: Nip59Crypto,\n recipient: string,\n rumor: Rumor,\n opts?: SealOptions,\n): Promise<Event> {\n const sealer = requireNip59Crypto(crypto);\n const recipientPk = assertHex32(recipient, \"public key\");\n let content: string;\n try {\n content = await sealer.nip44Encrypt(recipientPk, rumorToJson(rumor));\n } catch (error) {\n throw new Nip59Error(\"failed to encrypt\", { cause: error });\n }\n const created_at =\n opts?.timestamps?.seal ??\n (opts?.randomize === \"wrap\" ? rumor.created_at : randomPastTimestamp(opts));\n const pubkey = await sealer.getPublicKey();\n return sealer.signEvent({\n kind: Kind.Seal,\n content,\n created_at,\n tags: [],\n pubkey,\n });\n}\n\nexport function createGiftWrap(seal: Event, recipient: string, opts?: WrapOptions): Event {\n const recipientPk = assertHex32(recipient, \"public key\");\n const ephemeral = Keys.generate();\n const content = encryptToPubkey(eventToJson(seal), ephemeral.secretKey.bytes, recipientPk);\n const created_at = opts?.timestamps?.wrap ?? randomPastTimestamp(opts);\n const tags: Tag[] = [TagBuilder.p(recipientPk, opts?.relayHint), ...(opts?.extraTags ?? [])];\n return finalizeEvent(\n {\n kind: Kind.GiftWrap,\n content,\n created_at,\n tags,\n },\n ephemeral.secretKey,\n );\n}\n\nexport async function wrap(\n crypto: Nip59Crypto,\n recipient: string,\n rumor: Rumor,\n opts?: WrapOptions,\n): Promise<Event> {\n const seal = await createSeal(crypto, recipient, rumor, opts);\n return createGiftWrap(seal, recipient, opts);\n}\n\nfunction parseJson(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch (error) {\n throw new Nip59Error(\"invalid JSON\", { cause: error });\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction parseRumor(value: unknown): Rumor {\n if (!isRecord(value)) {\n throw new Nip59Error(\"invalid rumor\");\n }\n if (Object.hasOwn(value, \"sig\")) {\n throw new Nip59Error(\"rumor must be unsigned\");\n }\n const givenId = value.id;\n if (!validateEvent(value)) {\n throw new Nip59Error(\"invalid rumor\");\n }\n const unsigned: UnsignedEvent = {\n kind: value.kind,\n tags: value.tags,\n content: value.content,\n created_at: value.created_at,\n pubkey: value.pubkey.toLowerCase(),\n };\n const expected = getEventHash(unsigned);\n if (typeof givenId === \"string\" && givenId.toLowerCase() !== expected) {\n throw new Nip59Error(\"invalid rumor\");\n }\n return { ...unsigned, id: expected };\n}\n\nasync function decryptLayer(\n crypto: Nip44Decryptor,\n peer: string,\n payload: string,\n): Promise<string> {\n try {\n return await crypto.nip44Decrypt(peer, payload);\n } catch (error) {\n throw new Nip59Error(\"failed to decrypt\", { cause: error });\n }\n}\n\nexport async function unwrap(crypto: Nip44Decryptor, giftWrap: Event): Promise<Rumor> {\n const decryptor = requireNip44Decryptor(crypto);\n if (!validateSignedEvent(giftWrap) || !isGiftWrapKind(giftWrap.kind)) {\n throw new Nip59Error(\"expected gift wrap\");\n }\n if (!verifyEvent(giftWrap)) {\n throw new Nip59Error(\"gift wrap signature\");\n }\n\n const sealJson = await decryptLayer(decryptor, giftWrap.pubkey, giftWrap.content);\n const sealRaw = parseJson(sealJson);\n if (!validateSignedEvent(sealRaw)) {\n throw new Nip59Error(\"expected seal\");\n }\n if (sealRaw.kind !== Kind.Seal) {\n throw new Nip59Error(\"expected seal\");\n }\n if (sealRaw.tags.length !== 0) {\n throw new Nip59Error(\"seal tags must be empty\");\n }\n if (!verifyEvent(sealRaw)) {\n throw new Nip59Error(\"seal signature\");\n }\n\n const rumorJson = await decryptLayer(decryptor, sealRaw.pubkey, sealRaw.content);\n const rumor = parseRumor(parseJson(rumorJson));\n if (sealRaw.pubkey.toLowerCase() !== rumor.pubkey.toLowerCase()) {\n throw new Nip59Error(\"seal pubkey does not match rumor pubkey\");\n }\n return rumor;\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { a as readRelays, c as writeRelays, i as parseRelayList, n as RelayMarker, o as relayListEventBuilder, r as markerOf, s as relayListToTags, t as RelayListItem } from "../nip65-Di5hZRuV.mjs";
2
+ export { RelayListItem, RelayMarker, markerOf, parseRelayList, readRelays, relayListEventBuilder, relayListToTags, writeRelays };
@@ -0,0 +1,76 @@
1
+ import { s as normalizeURL } from "../util-6tfLQ9JF.mjs";
2
+ import { n as EventValidationError } from "../error-DZEs5OuZ.mjs";
3
+ import { t as Kind } from "../kind-BLl4cLOc.mjs";
4
+ import { t as EventBuilder } from "../builder-q0VsvnYE.mjs";
5
+ //#region src/nips/nip65.ts
6
+ function markerOf(item) {
7
+ if (item.read && item.write) return "readwrite";
8
+ if (item.read) return "read";
9
+ if (item.write) return "write";
10
+ return "readwrite";
11
+ }
12
+ /** Parse a kind:10002 NIP-65 event into relay list entries. */
13
+ function parseRelayList(event) {
14
+ if (event.kind !== Kind.RelayList) throw new EventValidationError(`expected kind ${Kind.RelayList}, got ${event.kind}`);
15
+ const out = [];
16
+ const seen = /* @__PURE__ */ new Set();
17
+ for (const tag of event.tags) {
18
+ if (tag[0] !== "r" || !tag[1]) continue;
19
+ let url;
20
+ try {
21
+ url = normalizeURL(tag[1]);
22
+ } catch {
23
+ continue;
24
+ }
25
+ if (seen.has(url)) continue;
26
+ seen.add(url);
27
+ const marker = tag[2];
28
+ if (marker === "read") out.push({
29
+ url,
30
+ read: true,
31
+ write: false
32
+ });
33
+ else if (marker === "write") out.push({
34
+ url,
35
+ read: false,
36
+ write: true
37
+ });
38
+ else out.push({
39
+ url,
40
+ read: true,
41
+ write: true
42
+ });
43
+ }
44
+ return out;
45
+ }
46
+ /** Encode relay list items as NIP-65 `r` tags. */
47
+ function relayListToTags(items) {
48
+ return items.map((item) => {
49
+ if (item.read && item.write) return ["r", item.url];
50
+ if (item.read) return [
51
+ "r",
52
+ item.url,
53
+ "read"
54
+ ];
55
+ if (item.write) return [
56
+ "r",
57
+ item.url,
58
+ "write"
59
+ ];
60
+ return ["r", item.url];
61
+ });
62
+ }
63
+ /** Build an unsigned kind:10002 EventBuilder from relay list items. */
64
+ function relayListEventBuilder(items) {
65
+ return new EventBuilder(Kind.RelayList, "").tags(relayListToTags(items));
66
+ }
67
+ function readRelays(items) {
68
+ return items.filter((i) => i.read).map((i) => i.url);
69
+ }
70
+ function writeRelays(items) {
71
+ return items.filter((i) => i.write).map((i) => i.url);
72
+ }
73
+ //#endregion
74
+ export { markerOf, parseRelayList, readRelays, relayListEventBuilder, relayListToTags, writeRelays };
75
+
76
+ //# sourceMappingURL=nip65.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nip65.mjs","names":[],"sources":["../../src/nips/nip65.ts"],"sourcesContent":["import type { Event } from \"../core/event.ts\";\nimport { EventValidationError } from \"../core/error.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport type { Tag } from \"../core/tag.ts\";\nimport { EventBuilder } from \"../core/builder.ts\";\nimport { normalizeURL } from \"../core/util.ts\";\n\nexport type RelayMarker = \"read\" | \"write\" | \"readwrite\";\n\nexport type RelayListItem = {\n url: string;\n read: boolean;\n write: boolean;\n};\n\nexport function markerOf(item: RelayListItem): RelayMarker {\n if (item.read && item.write) return \"readwrite\";\n if (item.read) return \"read\";\n if (item.write) return \"write\";\n return \"readwrite\";\n}\n\n/** Parse a kind:10002 NIP-65 event into relay list entries. */\nexport function parseRelayList(event: Event): RelayListItem[] {\n if (event.kind !== Kind.RelayList) {\n throw new EventValidationError(`expected kind ${Kind.RelayList}, got ${event.kind}`);\n }\n const out: RelayListItem[] = [];\n const seen = new Set<string>();\n\n for (const tag of event.tags) {\n if (tag[0] !== \"r\" || !tag[1]) continue;\n let url: string;\n try {\n url = normalizeURL(tag[1]);\n } catch {\n continue;\n }\n if (seen.has(url)) continue;\n seen.add(url);\n\n const marker = tag[2];\n if (marker === \"read\") {\n out.push({ url, read: true, write: false });\n } else if (marker === \"write\") {\n out.push({ url, read: false, write: true });\n } else {\n out.push({ url, read: true, write: true });\n }\n }\n return out;\n}\n\n/** Encode relay list items as NIP-65 `r` tags. */\nexport function relayListToTags(items: RelayListItem[]): Tag[] {\n return items.map((item) => {\n if (item.read && item.write) return [\"r\", item.url];\n if (item.read) return [\"r\", item.url, \"read\"];\n if (item.write) return [\"r\", item.url, \"write\"];\n return [\"r\", item.url];\n });\n}\n\n/** Build an unsigned kind:10002 EventBuilder from relay list items. */\nexport function relayListEventBuilder(items: RelayListItem[]): EventBuilder {\n return new EventBuilder(Kind.RelayList, \"\").tags(relayListToTags(items));\n}\n\nexport function readRelays(items: RelayListItem[]): string[] {\n return items.filter((i) => i.read).map((i) => i.url);\n}\n\nexport function writeRelays(items: RelayListItem[]): string[] {\n return items.filter((i) => i.write).map((i) => i.url);\n}\n"],"mappings":";;;;;AAeA,SAAgB,SAAS,MAAkC;CACzD,IAAI,KAAK,QAAQ,KAAK,OAAO,OAAO;CACpC,IAAI,KAAK,MAAM,OAAO;CACtB,IAAI,KAAK,OAAO,OAAO;CACvB,OAAO;AACT;;AAGA,SAAgB,eAAe,OAA+B;CAC5D,IAAI,MAAM,SAAS,KAAK,WACtB,MAAM,IAAI,qBAAqB,iBAAiB,KAAK,UAAU,QAAQ,MAAM,MAAM;CAErF,MAAM,MAAuB,CAAC;CAC9B,MAAM,uBAAO,IAAI,IAAY;CAE7B,KAAK,MAAM,OAAO,MAAM,MAAM;EAC5B,IAAI,IAAI,OAAO,OAAO,CAAC,IAAI,IAAI;EAC/B,IAAI;EACJ,IAAI;GACF,MAAM,aAAa,IAAI,EAAE;EAC3B,QAAQ;GACN;EACF;EACA,IAAI,KAAK,IAAI,GAAG,GAAG;EACnB,KAAK,IAAI,GAAG;EAEZ,MAAM,SAAS,IAAI;EACnB,IAAI,WAAW,QACb,IAAI,KAAK;GAAE;GAAK,MAAM;GAAM,OAAO;EAAM,CAAC;OACrC,IAAI,WAAW,SACpB,IAAI,KAAK;GAAE;GAAK,MAAM;GAAO,OAAO;EAAK,CAAC;OAE1C,IAAI,KAAK;GAAE;GAAK,MAAM;GAAM,OAAO;EAAK,CAAC;CAE7C;CACA,OAAO;AACT;;AAGA,SAAgB,gBAAgB,OAA+B;CAC7D,OAAO,MAAM,KAAK,SAAS;EACzB,IAAI,KAAK,QAAQ,KAAK,OAAO,OAAO,CAAC,KAAK,KAAK,GAAG;EAClD,IAAI,KAAK,MAAM,OAAO;GAAC;GAAK,KAAK;GAAK;EAAM;EAC5C,IAAI,KAAK,OAAO,OAAO;GAAC;GAAK,KAAK;GAAK;EAAO;EAC9C,OAAO,CAAC,KAAK,KAAK,GAAG;CACvB,CAAC;AACH;;AAGA,SAAgB,sBAAsB,OAAsC;CAC1E,OAAO,IAAI,aAAa,KAAK,WAAW,EAAE,CAAC,CAAC,KAAK,gBAAgB,KAAK,CAAC;AACzE;AAEA,SAAgB,WAAW,OAAkC;CAC3D,OAAO,MAAM,QAAQ,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,MAAM,EAAE,GAAG;AACrD;AAEA,SAAgB,YAAY,OAAkC;CAC5D,OAAO,MAAM,QAAQ,MAAM,EAAE,KAAK,CAAC,CAAC,KAAK,MAAM,EAAE,GAAG;AACtD"}
@@ -0,0 +1,2 @@
1
+ import { a as NegentropyStorageVector, c as ReconcileOutcome, d as storageFromItems, i as Negentropy, l as runNegSession, n as MAX_NEG_ROUNDS, o as Nip77Error, r as NegItem, s as PROTOCOL_VERSION, t as DEFAULT_FRAME_SIZE_LIMIT, u as storageFromEvents } from "../nip77-ByxlgoQO.mjs";
2
+ export { DEFAULT_FRAME_SIZE_LIMIT, MAX_NEG_ROUNDS, NegItem, Negentropy, NegentropyStorageVector, Nip77Error, PROTOCOL_VERSION, ReconcileOutcome, runNegSession, storageFromEvents, storageFromItems };