@qntx/nostr 0.1.0 → 0.2.0
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/abort-Ba8tDrOI.mjs +38 -0
- package/dist/abort-Ba8tDrOI.mjs.map +1 -0
- package/dist/{builder-BphJa-Y-.d.mts → builder-BWn55Paf.d.mts} +20 -14
- package/dist/{builder-q0VsvnYE.mjs → builder-CyrGKRVP.mjs} +32 -16
- package/dist/builder-CyrGKRVP.mjs.map +1 -0
- package/dist/{client-Bd8B1mYm.mjs → client-BWsX9Cv1.mjs} +354 -195
- package/dist/client-BWsX9Cv1.mjs.map +1 -0
- package/dist/client.d.mts +1 -1
- package/dist/client.mjs +1 -1
- package/dist/core.d.mts +8 -8
- package/dist/core.mjs +8 -9
- package/dist/error-BW7re1UN.d.mts +9 -0
- package/dist/error-B__OMBRY.mjs +14 -0
- package/dist/error-B__OMBRY.mjs.map +1 -0
- package/dist/{error-WEFS-R-4.d.mts → error-CR8Urc84.d.mts} +20 -8
- package/dist/{error-CwBxle6N.mjs → error-DrEIM7FO.mjs} +5 -3
- package/dist/error-DrEIM7FO.mjs.map +1 -0
- package/dist/{event-CzoQEFMF.mjs → event-D8Nfoh4K.mjs} +40 -32
- package/dist/event-D8Nfoh4K.mjs.map +1 -0
- package/dist/{event-B5D2jxt_.d.mts → event-fU3Y01PR.d.mts} +23 -20
- package/dist/{filter-BxwjEkZp.mjs → filter-BQl3GlHt.mjs} +42 -35
- package/dist/filter-BQl3GlHt.mjs.map +1 -0
- package/dist/filter-C0IMByE3.d.mts +37 -0
- package/dist/{gossip-C5RVB1UB.d.mts → gossip-BRQwIM_q.d.mts} +25 -23
- package/dist/{gossip-3xkRRCm_.mjs → gossip-oloDMWEj.mjs} +52 -34
- package/dist/gossip-oloDMWEj.mjs.map +1 -0
- package/dist/gossip.d.mts +1 -1
- package/dist/gossip.mjs +1 -1
- package/dist/{http-6vevbBlu.d.mts → http-C4HwIu4B.d.mts} +7 -7
- package/dist/{http-D2rhokZo.mjs → http-CuazNRIP.mjs} +10 -6
- package/dist/http-CuazNRIP.mjs.map +1 -0
- package/dist/index-B425CAcQ.d.mts +328 -0
- package/dist/index-BVyfUBuG.d.mts +181 -0
- package/dist/{index-BYMnhRIC.d.mts → index-Ba7bJ7DD.d.mts} +68 -50
- package/dist/{index-DaRyjo7O.d.mts → index-Boe8FshM.d.mts} +13 -7
- package/dist/index-CUIvFQdE.d.mts +73 -0
- package/dist/index-JdaS_uce.d.mts +207 -0
- package/dist/index.d.mts +22 -20
- package/dist/index.mjs +19 -18
- package/dist/{key-Dn-aX4x4.mjs → key-Bbtys6o7.mjs} +22 -14
- package/dist/key-Bbtys6o7.mjs.map +1 -0
- package/dist/{key-BiGxGLhg.d.mts → key-BlsYhN1V.d.mts} +9 -6
- package/dist/{kind-BLl4cLOc.mjs → kind-Cxwhhkx8.mjs} +2 -1
- package/dist/kind-Cxwhhkx8.mjs.map +1 -0
- package/dist/{loaders-BNQszDAn.mjs → loaders-o6YXB_Ri.mjs} +329 -305
- package/dist/loaders-o6YXB_Ri.mjs.map +1 -0
- package/dist/loaders.d.mts +3 -3
- package/dist/loaders.mjs +2 -2
- package/dist/memory-C2EVx0_O.mjs +46 -0
- package/dist/memory-C2EVx0_O.mjs.map +1 -0
- package/dist/{memory-BCmhZPek.mjs → memory-index-Do1mz0WF.mjs} +212 -82
- package/dist/memory-index-Do1mz0WF.mjs.map +1 -0
- package/dist/{message-C_Uj-Y-c.mjs → message-DcY6Fi9o.mjs} +38 -24
- package/dist/message-DcY6Fi9o.mjs.map +1 -0
- package/dist/{message-DJC-Jz6T.d.mts → message-iGPIqnn0.d.mts} +15 -8
- package/dist/{nip17-BWq-Kfq5.d.mts → nip17-DNEkz8rM.d.mts} +18 -17
- package/dist/{nip19-D7905oIv.d.mts → nip19-BevCJZsp.d.mts} +23 -3
- package/dist/{nip46-C_DoxiS_.d.mts → nip46-DDV4dC56.d.mts} +13 -12
- package/dist/{nip59-DljW_sYh.d.mts → nip59-zQgdLcEr.d.mts} +28 -25
- package/dist/{nip65-Di5hZRuV.d.mts → nip65-DBfh2smt.d.mts} +8 -3
- package/dist/{nip77-ByxlgoQO.d.mts → nip77-DH4RhS5r.d.mts} +9 -8
- package/dist/nips/blossom.d.mts +37 -37
- package/dist/nips/blossom.mjs +67 -52
- package/dist/nips/blossom.mjs.map +1 -1
- package/dist/nips/nip04.d.mts +2 -3
- package/dist/nips/nip04.mjs +6 -10
- package/dist/nips/nip04.mjs.map +1 -1
- package/dist/nips/nip05.d.mts +37 -43
- package/dist/nips/nip05.mjs +57 -57
- package/dist/nips/nip05.mjs.map +1 -1
- package/dist/nips/nip10.d.mts +18 -19
- package/dist/nips/nip10.mjs +76 -61
- package/dist/nips/nip10.mjs.map +1 -1
- package/dist/nips/nip11.d.mts +9 -8
- package/dist/nips/nip11.mjs +14 -11
- package/dist/nips/nip11.mjs.map +1 -1
- package/dist/nips/nip13.d.mts +9 -9
- package/dist/nips/nip13.mjs +15 -17
- package/dist/nips/nip13.mjs.map +1 -1
- package/dist/nips/nip17.d.mts +1 -1
- package/dist/nips/nip17.mjs +17 -12
- package/dist/nips/nip17.mjs.map +1 -1
- package/dist/nips/nip19.d.mts +1 -1
- package/dist/nips/nip19.mjs +52 -29
- package/dist/nips/nip19.mjs.map +1 -1
- package/dist/nips/nip21.d.mts +5 -6
- package/dist/nips/nip21.mjs +8 -7
- package/dist/nips/nip21.mjs.map +1 -1
- package/dist/nips/nip27.d.mts +7 -8
- package/dist/nips/nip27.mjs +26 -25
- package/dist/nips/nip27.mjs.map +1 -1
- package/dist/nips/nip42.d.mts +5 -6
- package/dist/nips/nip42.mjs +3 -3
- package/dist/nips/nip42.mjs.map +1 -1
- package/dist/nips/nip44.d.mts +24 -7
- package/dist/nips/nip44.mjs +25 -15
- package/dist/nips/nip44.mjs.map +1 -1
- package/dist/nips/nip46.d.mts +1 -1
- package/dist/nips/nip46.mjs +52 -49
- package/dist/nips/nip46.mjs.map +1 -1
- package/dist/nips/nip49.d.mts +8 -7
- package/dist/nips/nip49.mjs +16 -12
- package/dist/nips/nip49.mjs.map +1 -1
- package/dist/nips/nip51.d.mts +25 -27
- package/dist/nips/nip51.mjs +47 -36
- package/dist/nips/nip51.mjs.map +1 -1
- package/dist/nips/nip57.d.mts +12 -13
- package/dist/nips/nip57.mjs +35 -38
- package/dist/nips/nip57.mjs.map +1 -1
- package/dist/nips/nip59.d.mts +1 -1
- package/dist/nips/nip59.mjs +14 -18
- package/dist/nips/nip59.mjs.map +1 -1
- package/dist/nips/nip65.d.mts +1 -1
- package/dist/nips/nip65.mjs +11 -8
- package/dist/nips/nip65.mjs.map +1 -1
- package/dist/nips/nip77.d.mts +1 -1
- package/dist/nips/nip77.mjs +56 -34
- package/dist/nips/nip77.mjs.map +1 -1
- package/dist/nips/nip96.d.mts +21 -12
- package/dist/nips/nip96.mjs +59 -36
- package/dist/nips/nip96.mjs.map +1 -1
- package/dist/nips/nip98.d.mts +12 -12
- package/dist/nips/nip98.mjs +18 -19
- package/dist/nips/nip98.mjs.map +1 -1
- package/dist/{pool-C_j1OmBB.mjs → pool-XmAGaufS.mjs} +611 -487
- package/dist/pool-XmAGaufS.mjs.map +1 -0
- package/dist/pool-tEr7c5bG.d.mts +302 -0
- package/dist/reactive-DDlThDSZ.mjs +324 -0
- package/dist/reactive-DDlThDSZ.mjs.map +1 -0
- package/dist/reactive-DFpqtIf_.d.mts +66 -0
- package/dist/relay.d.mts +5 -4
- package/dist/relay.mjs +2 -2
- package/dist/report-CHld4uWj.mjs +32 -0
- package/dist/report-CHld4uWj.mjs.map +1 -0
- package/dist/{signer-CZ6K6FUc.mjs → signer-CKR9EHce.mjs} +107 -72
- package/dist/signer-CKR9EHce.mjs.map +1 -0
- package/dist/signer.d.mts +3 -3
- package/dist/signer.mjs +3 -2
- package/dist/storage-xXxLGCVb.mjs +1376 -0
- package/dist/storage-xXxLGCVb.mjs.map +1 -0
- package/dist/storage.d.mts +4 -4
- package/dist/storage.mjs +5 -4
- package/dist/store.d.mts +2 -0
- package/dist/store.mjs +2 -0
- package/dist/testing.d.mts +125 -0
- package/dist/testing.mjs +957 -0
- package/dist/testing.mjs.map +1 -0
- package/dist/types-2pCUprRU.d.mts +47 -0
- package/dist/types-CRQEk0Oi.d.mts +19 -0
- package/dist/util-LfQirgDk.mjs +113 -0
- package/dist/util-LfQirgDk.mjs.map +1 -0
- package/dist/wasm.d.mts +4 -4
- package/dist/wasm.mjs +54 -55
- package/dist/wasm.mjs.map +1 -1
- package/dist/websocket-CmeqvAkp.d.mts +27 -0
- package/package.json +31 -9
- package/dist/builder-q0VsvnYE.mjs.map +0 -1
- package/dist/client-Bd8B1mYm.mjs.map +0 -1
- package/dist/error-B49rbDzT.d.mts +0 -7
- package/dist/error-CwBxle6N.mjs.map +0 -1
- package/dist/error-DZEs5OuZ.mjs +0 -24
- package/dist/error-DZEs5OuZ.mjs.map +0 -1
- package/dist/event-CzoQEFMF.mjs.map +0 -1
- package/dist/filter-BxwjEkZp.mjs.map +0 -1
- package/dist/filter-DUEKP3q1.d.mts +0 -40
- package/dist/gossip-3xkRRCm_.mjs.map +0 -1
- package/dist/http-D2rhokZo.mjs.map +0 -1
- package/dist/index-C0C4e-Gn.d.mts +0 -55
- package/dist/index-DgOugrpF.d.mts +0 -275
- package/dist/index-EwL6fHLo.d.mts +0 -267
- package/dist/index-k2YhCgsh.d.mts +0 -57
- package/dist/key-Dn-aX4x4.mjs.map +0 -1
- package/dist/kind-BLl4cLOc.mjs.map +0 -1
- package/dist/loaders-BNQszDAn.mjs.map +0 -1
- package/dist/memory-BCmhZPek.mjs.map +0 -1
- package/dist/message-C_Uj-Y-c.mjs.map +0 -1
- package/dist/pool-C_j1OmBB.mjs.map +0 -1
- package/dist/pool-DhRi7l-b.d.mts +0 -297
- package/dist/signer-CZ6K6FUc.mjs.map +0 -1
- package/dist/storage-DOy0iLRE.mjs +0 -870
- package/dist/storage-DOy0iLRE.mjs.map +0 -1
- package/dist/types-BzyADqnw.d.mts +0 -19
- package/dist/types-Dn24TPFs.d.mts +0 -43
- package/dist/util-6tfLQ9JF.mjs +0 -73
- package/dist/util-6tfLQ9JF.mjs.map +0 -1
package/dist/nips/nip77.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nip77.mjs","names":["#raw","#buf","#sealed","#items","#checkSealed","#checkBounds","#storage","#frameSizeLimit","#initiator","#splitRange","#bound","#lastTimestampIn","#lastTimestampOut","#encodeBound","#decodeBound","#exceededFrameSizeLimit","#minimalBound","#decodeTimestampIn","#encodeTimestampOut"],"sources":["../../src/nips/nip77.ts"],"sourcesContent":["/**\n * NIP-77: Negentropy Syncing.\n * Transport-free V1 algorithm. Does not import relay or client.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/77.md\n */\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { NostrError } from \"../core/error.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { assertHex32, bytesToHex, hexToBytes } from \"../core/util.ts\";\n\nexport const PROTOCOL_VERSION = 0x61;\nexport const DEFAULT_FRAME_SIZE_LIMIT = 60_000;\nexport const MAX_NEG_ROUNDS = 1024;\n\nconst ID_SIZE = 32;\nconst FINGERPRINT_SIZE = 16;\nconst INFINITY = Number.MAX_VALUE;\nconst BUCKETS = 16;\nconst ID_LIST_THRESHOLD = BUCKETS * 2;\n\nconst Mode = {\n Skip: 0,\n Fingerprint: 1,\n IdList: 2,\n} as const;\n\nexport class Nip77Error extends NostrError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n }\n}\n\nexport type NegItem = {\n timestamp: number;\n id: Uint8Array;\n};\n\nexport type ReconcileOutcome = {\n /** Event ids the local side holds that the peer does not. */\n have: string[];\n /** Event ids the peer holds that the local side does not. */\n need: string[];\n /** Next NEG-MSG hex, or null when the session converged. */\n nextMessage: string | null;\n};\n\nclass EncodedBuf {\n #raw: Uint8Array;\n length: number;\n\n constructor(buffer?: Uint8Array | number) {\n if (typeof buffer === \"number\") {\n this.#raw = new Uint8Array(buffer);\n this.length = 0;\n } else if (buffer) {\n this.#raw = new Uint8Array(buffer);\n this.length = buffer.length;\n } else {\n this.#raw = new Uint8Array(512);\n this.length = 0;\n }\n }\n\n unwrap(): Uint8Array {\n return this.#raw.subarray(0, this.length);\n }\n\n get capacity(): number {\n return this.#raw.byteLength;\n }\n\n extend(buf: Uint8Array | EncodedBuf): void {\n const bytes = buf instanceof EncodedBuf ? buf.unwrap() : buf;\n const targetSize = bytes.length + this.length;\n if (this.capacity < targetSize) {\n const old = this.#raw;\n const next = new Uint8Array(Math.max(this.capacity * 2, targetSize));\n next.set(old);\n this.#raw = next;\n }\n this.#raw.set(bytes, this.length);\n this.length += bytes.length;\n }\n\n shift(): number {\n if (this.length === 0) throw new Nip77Error(\"parse ends prematurely\");\n const first = this.#raw[0]!;\n this.#raw = this.#raw.subarray(1);\n this.length -= 1;\n return first;\n }\n\n shiftN(n: number): Uint8Array {\n if (this.length < n) throw new Nip77Error(\"parse ends prematurely\");\n const head = this.#raw.subarray(0, n);\n this.#raw = this.#raw.subarray(n);\n this.length -= n;\n return head;\n }\n}\n\nfunction decodeVarInt(buf: EncodedBuf): number {\n let res = 0;\n for (;;) {\n const byte = buf.shift();\n res = (res << 7) | (byte & 127);\n if ((byte & 128) === 0) break;\n }\n return res;\n}\n\nfunction encodeVarInt(n: number): EncodedBuf {\n if (n === 0) return new EncodedBuf(new Uint8Array([0]));\n const digits: number[] = [];\n let value = n;\n while (value !== 0) {\n digits.push(value & 127);\n value >>>= 7;\n }\n digits.reverse();\n for (let i = 0; i < digits.length - 1; i++) digits[i] |= 128;\n return new EncodedBuf(new Uint8Array(digits));\n}\n\nfunction getBytes(buf: EncodedBuf, n: number): Uint8Array {\n return buf.shiftN(n);\n}\n\nclass Accumulator {\n #buf: Uint8Array;\n\n constructor() {\n this.#buf = new Uint8Array(ID_SIZE);\n }\n\n add(other: Uint8Array): void {\n const view = new DataView(this.#buf.buffer, this.#buf.byteOffset, this.#buf.byteLength);\n const otherView = new DataView(other.buffer, other.byteOffset, other.byteLength);\n let carry = 0;\n for (let i = 0; i < 8; i++) {\n const offset = i * 4;\n const next = view.getUint32(offset, true) + carry + otherView.getUint32(offset, true);\n carry = next > 0xffffffff ? 1 : 0;\n view.setUint32(offset, next >>> 0, true);\n }\n }\n\n fingerprint(n: number): Uint8Array {\n const input = new EncodedBuf();\n input.extend(this.#buf);\n input.extend(encodeVarInt(n));\n return sha256(input.unwrap()).subarray(0, FINGERPRINT_SIZE);\n }\n}\n\nfunction compareBytes(a: Uint8Array, b: Uint8Array): number {\n const n = Math.min(a.byteLength, b.byteLength);\n for (let i = 0; i < n; i++) {\n if (a[i]! < b[i]!) return -1;\n if (a[i]! > b[i]!) return 1;\n }\n if (a.byteLength > b.byteLength) return 1;\n if (a.byteLength < b.byteLength) return -1;\n return 0;\n}\n\nfunction compareNegItems(a: NegItem, b: NegItem): number {\n if (a.timestamp !== b.timestamp) return a.timestamp - b.timestamp;\n return compareBytes(a.id, b.id);\n}\n\nexport class NegentropyStorageVector {\n #items: NegItem[] = [];\n #sealed = false;\n\n insert(timestamp: number, id: string): void {\n if (this.#sealed) throw new Nip77Error(\"already sealed\");\n if (timestamp === INFINITY) throw new Nip77Error(\"timestamp is reserved infinity\");\n const idb = hexToBytes(assertHex32(id, \"event id\"));\n this.#items.push({ timestamp, id: idb });\n }\n\n seal(): void {\n if (this.#sealed) throw new Nip77Error(\"already sealed\");\n this.#sealed = true;\n this.#items.sort(compareNegItems);\n for (let i = 1; i < this.#items.length; i++) {\n if (compareNegItems(this.#items[i - 1]!, this.#items[i]!) === 0) {\n throw new Nip77Error(\"duplicate item inserted\");\n }\n }\n }\n\n size(): number {\n this.#checkSealed();\n return this.#items.length;\n }\n\n iterate(begin: number, end: number, cb: (item: NegItem, i: number) => boolean): void {\n this.#checkSealed();\n this.#checkBounds(begin, end);\n for (let i = begin; i < end; i++) {\n if (!cb(this.#items[i]!, i)) break;\n }\n }\n\n findLowerBound(begin: number, end: number, bound: NegItem): number {\n this.#checkSealed();\n this.#checkBounds(begin, end);\n let first = begin;\n let count = end - begin;\n while (count > 0) {\n const step = Math.floor(count / 2);\n const it = first + step;\n if (compareNegItems(this.#items[it]!, bound) < 0) {\n first = it + 1;\n count -= step + 1;\n } else {\n count = step;\n }\n }\n return first;\n }\n\n fingerprint(begin: number, end: number): Uint8Array {\n const acc = new Accumulator();\n this.iterate(begin, end, (item) => {\n acc.add(item.id);\n return true;\n });\n return acc.fingerprint(end - begin);\n }\n\n #checkSealed(): void {\n if (!this.#sealed) throw new Nip77Error(\"not sealed\");\n }\n\n #checkBounds(begin: number, end: number): void {\n if (begin > end || end > this.#items.length) throw new Nip77Error(\"bad range\");\n }\n}\n\nexport function storageFromItems(\n items: readonly { id: string; created_at: number }[],\n): NegentropyStorageVector {\n const storage = new NegentropyStorageVector();\n const seen = new Set<string>();\n for (const item of items) {\n const id = item.id.toLowerCase();\n if (seen.has(id)) continue;\n seen.add(id);\n storage.insert(item.created_at, id);\n }\n storage.seal();\n return storage;\n}\n\nexport function storageFromEvents(\n events: readonly Pick<Event, \"id\" | \"created_at\">[],\n): NegentropyStorageVector {\n return storageFromItems(events);\n}\n\ntype Bound = { timestamp: number; id: Uint8Array };\n\nexport class Negentropy {\n readonly #storage: NegentropyStorageVector;\n readonly #frameSizeLimit: number;\n #initiator = false;\n #lastTimestampIn = 0;\n #lastTimestampOut = 0;\n\n constructor(storage: NegentropyStorageVector, frameSizeLimit = DEFAULT_FRAME_SIZE_LIMIT) {\n if (frameSizeLimit !== 0 && frameSizeLimit < 4096) {\n throw new Nip77Error(\"frameSizeLimit too small\");\n }\n this.#storage = storage;\n this.#frameSizeLimit = frameSizeLimit;\n }\n\n initiate(): string {\n this.#initiator = true;\n const output = new EncodedBuf();\n output.extend(new Uint8Array([PROTOCOL_VERSION]));\n this.#splitRange(0, this.#storage.size(), this.#bound(INFINITY), output);\n return bytesToHex(output.unwrap());\n }\n\n reconcile(queryMsg: string): ReconcileOutcome {\n const have: string[] = [];\n const need: string[] = [];\n const query = new EncodedBuf(hexToBytes(queryMsg));\n\n this.#lastTimestampIn = 0;\n this.#lastTimestampOut = 0;\n\n const fullOutput = new EncodedBuf();\n fullOutput.extend(new Uint8Array([PROTOCOL_VERSION]));\n\n const protocolVersion = query.shift();\n if (protocolVersion < 0x60 || protocolVersion > 0x6f) {\n throw new Nip77Error(\"invalid negentropy protocol version byte\");\n }\n if (protocolVersion !== PROTOCOL_VERSION) {\n throw new Nip77Error(\n `unsupported negentropy protocol version requested: ${protocolVersion - 0x60}`,\n );\n }\n\n const storageSize = this.#storage.size();\n let prevBound = this.#bound(0);\n let prevIndex = 0;\n let skip = false;\n\n while (query.length !== 0) {\n const o = new EncodedBuf();\n const doSkip = (): void => {\n if (!skip) return;\n skip = false;\n o.extend(this.#encodeBound(prevBound));\n o.extend(encodeVarInt(Mode.Skip));\n };\n\n const currBound = this.#decodeBound(query);\n const mode = decodeVarInt(query);\n const lower = prevIndex;\n const upper = this.#storage.findLowerBound(prevIndex, storageSize, currBound);\n\n if (mode === Mode.Skip) {\n skip = true;\n } else if (mode === Mode.Fingerprint) {\n const theirFingerprint = getBytes(query, FINGERPRINT_SIZE);\n const ourFingerprint = this.#storage.fingerprint(lower, upper);\n if (compareBytes(theirFingerprint, ourFingerprint) !== 0) {\n doSkip();\n this.#splitRange(lower, upper, currBound, o);\n } else {\n skip = true;\n }\n } else if (mode === Mode.IdList) {\n const numIds = decodeVarInt(query);\n const theirElems = new Map<string, Uint8Array>();\n for (let i = 0; i < numIds; i++) {\n const e = getBytes(query, ID_SIZE);\n theirElems.set(bytesToHex(e), e);\n }\n\n if (this.#initiator) {\n skip = true;\n this.#storage.iterate(lower, upper, (item) => {\n const id = bytesToHex(item.id);\n if (!theirElems.has(id)) have.push(id);\n else theirElems.delete(id);\n return true;\n });\n for (const id of theirElems.keys()) need.push(id);\n } else {\n doSkip();\n o.extend(this.#encodeBound(currBound));\n o.extend(encodeVarInt(Mode.IdList));\n const ourIds: Uint8Array[] = [];\n this.#storage.iterate(lower, upper, (item) => {\n ourIds.push(item.id);\n return true;\n });\n o.extend(encodeVarInt(ourIds.length));\n for (const id of ourIds) o.extend(id);\n }\n } else {\n throw new Nip77Error(\"unexpected mode\");\n }\n\n if (this.#exceededFrameSizeLimit(fullOutput.length + o.length)) {\n const remainingFingerprint = this.#storage.fingerprint(upper, storageSize);\n fullOutput.extend(this.#encodeBound(this.#bound(INFINITY)));\n fullOutput.extend(encodeVarInt(Mode.Fingerprint));\n fullOutput.extend(remainingFingerprint);\n break;\n }\n fullOutput.extend(o);\n prevIndex = upper;\n prevBound = currBound;\n }\n\n return {\n have,\n need,\n nextMessage: fullOutput.length === 1 ? null : bytesToHex(fullOutput.unwrap()),\n };\n }\n\n #bound(timestamp: number, id?: Uint8Array): Bound {\n return { timestamp, id: id ?? new Uint8Array(0) };\n }\n\n #splitRange(lower: number, upper: number, upperBound: Bound, o: EncodedBuf): void {\n const numElems = upper - lower;\n if (numElems < ID_LIST_THRESHOLD) {\n o.extend(this.#encodeBound(upperBound));\n o.extend(encodeVarInt(Mode.IdList));\n o.extend(encodeVarInt(numElems));\n this.#storage.iterate(lower, upper, (item) => {\n o.extend(item.id);\n return true;\n });\n return;\n }\n\n const itemsPerBucket = Math.floor(numElems / BUCKETS);\n const bucketsWithExtra = numElems % BUCKETS;\n let curr = lower;\n for (let i = 0; i < BUCKETS; i++) {\n const bucketSize = itemsPerBucket + (i < bucketsWithExtra ? 1 : 0);\n const ourFingerprint = this.#storage.fingerprint(curr, curr + bucketSize);\n curr += bucketSize;\n let nextBound: Bound;\n if (curr === upper) {\n nextBound = upperBound;\n } else {\n let prevItem: NegItem | undefined;\n let currItem: NegItem | undefined;\n this.#storage.iterate(curr - 1, curr + 1, (item, index) => {\n if (index === curr - 1) prevItem = item;\n else currItem = item;\n return true;\n });\n nextBound = this.#minimalBound(prevItem!, currItem!);\n }\n o.extend(this.#encodeBound(nextBound));\n o.extend(encodeVarInt(Mode.Fingerprint));\n o.extend(ourFingerprint);\n }\n }\n\n #exceededFrameSizeLimit(n: number): boolean {\n if (this.#frameSizeLimit === 0) return false;\n return n > this.#frameSizeLimit - 200;\n }\n\n #decodeTimestampIn(encoded: EncodedBuf): number {\n let timestamp = decodeVarInt(encoded);\n timestamp = timestamp === 0 ? INFINITY : timestamp - 1;\n if (this.#lastTimestampIn === INFINITY || timestamp === INFINITY) {\n this.#lastTimestampIn = INFINITY;\n return INFINITY;\n }\n timestamp += this.#lastTimestampIn;\n this.#lastTimestampIn = timestamp;\n return timestamp;\n }\n\n #decodeBound(encoded: EncodedBuf): Bound {\n const timestamp = this.#decodeTimestampIn(encoded);\n const len = decodeVarInt(encoded);\n if (len > ID_SIZE) throw new Nip77Error(\"bound key too long\");\n return { timestamp, id: getBytes(encoded, len) };\n }\n\n #encodeTimestampOut(timestamp: number): EncodedBuf {\n if (timestamp === INFINITY) {\n this.#lastTimestampOut = INFINITY;\n return encodeVarInt(0);\n }\n const temp = timestamp;\n timestamp -= this.#lastTimestampOut;\n this.#lastTimestampOut = temp;\n return encodeVarInt(timestamp + 1);\n }\n\n #encodeBound(key: Bound): EncodedBuf {\n const output = new EncodedBuf();\n output.extend(this.#encodeTimestampOut(key.timestamp));\n output.extend(encodeVarInt(key.id.length));\n output.extend(key.id);\n return output;\n }\n\n #minimalBound(prev: NegItem, curr: NegItem): Bound {\n if (curr.timestamp !== prev.timestamp) return this.#bound(curr.timestamp);\n let shared = 0;\n for (let i = 0; i < ID_SIZE; i++) {\n if (curr.id[i] !== prev.id[i]) break;\n shared += 1;\n }\n return this.#bound(curr.timestamp, curr.id.subarray(0, shared + 1));\n }\n}\n\n/** Drive initiate/reconcile until nextMessage is null. Transport-free. */\nexport async function runNegSession(opts: {\n storage: NegentropyStorageVector;\n frameSizeLimit?: number;\n openingSend: (hex: string) => void;\n msgSend: (hex: string) => void;\n next: () => Promise<string>;\n}): Promise<{ have: string[]; need: string[] }> {\n const neg = new Negentropy(opts.storage, opts.frameSizeLimit);\n const have = new Set<string>();\n const need = new Set<string>();\n opts.openingSend(neg.initiate());\n for (let round = 0; round < MAX_NEG_ROUNDS; round++) {\n const out = neg.reconcile(await opts.next());\n for (const id of out.have) have.add(id);\n for (const id of out.need) need.add(id);\n if (out.nextMessage === null) return { have: [...have], need: [...need] };\n opts.msgSend(out.nextMessage);\n }\n throw new Nip77Error(\"negentropy exceeded max rounds\");\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"nip77.mjs","names":[],"sources":["../../src/nips/nip77.ts"],"sourcesContent":["/**\n * NIP-77: Negentropy Syncing. Transport-free V1 algorithm. Does not import relay or client.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/77.md\n */\nimport { sha256 } from \"@noble/hashes/sha2.js\";\n\nimport { NostrError } from \"../core/error.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { assertHex32, bytesToHex, hexToBytes } from \"../core/util.ts\";\n\nexport const PROTOCOL_VERSION = 0x61;\nexport const DEFAULT_FRAME_SIZE_LIMIT = 60_000;\nexport const MAX_NEG_ROUNDS = 1024;\n\nconst ID_SIZE = 32;\nconst FINGERPRINT_SIZE = 16;\nconst INFINITY = Number.MAX_VALUE;\nconst BUCKETS = 16;\nconst ID_LIST_THRESHOLD = BUCKETS * 2;\n\nconst Mode = {\n Skip: 0,\n Fingerprint: 1,\n IdList: 2,\n} as const;\n\nexport class Nip77Error extends NostrError {\n override name = \"Nip77Error\";\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n }\n}\n\nexport type NegItem = {\n timestamp: number;\n id: Uint8Array;\n};\n\nexport type ReconcileOutcome = {\n /** Event ids the local side holds that the peer does not. */\n have: string[];\n /** Event ids the peer holds that the local side does not. */\n need: string[];\n /** Next NEG-MSG hex, or undefined when the session converged. */\n nextMessage: string | undefined;\n};\n\nclass EncodedBuf {\n #raw: Uint8Array;\n length: number;\n\n constructor(buffer?: Uint8Array | number) {\n if (typeof buffer === \"number\") {\n this.#raw = new Uint8Array(buffer);\n this.length = 0;\n } else if (buffer) {\n this.#raw = new Uint8Array(buffer);\n this.length = buffer.length;\n } else {\n this.#raw = new Uint8Array(512);\n this.length = 0;\n }\n }\n\n unwrap(): Uint8Array {\n return this.#raw.subarray(0, this.length);\n }\n\n get capacity(): number {\n return this.#raw.byteLength;\n }\n\n extend(buf: Uint8Array | EncodedBuf): void {\n const bytes = buf instanceof EncodedBuf ? buf.unwrap() : buf;\n const targetSize = bytes.length + this.length;\n if (this.capacity < targetSize) {\n const old = this.#raw;\n const next = new Uint8Array(Math.max(this.capacity * 2, targetSize));\n next.set(old);\n this.#raw = next;\n }\n this.#raw.set(bytes, this.length);\n this.length += bytes.length;\n }\n\n shift(): number {\n if (this.length === 0) {\n throw new Nip77Error(\"parse ends prematurely\");\n }\n const [first] = this.#raw;\n if (first === undefined) {\n throw new Nip77Error(\"parse ends prematurely\");\n }\n this.#raw = this.#raw.subarray(1);\n this.length -= 1;\n return first;\n }\n\n shiftN(n: number): Uint8Array {\n if (this.length < n) {\n throw new Nip77Error(\"parse ends prematurely\");\n }\n const head = this.#raw.subarray(0, n);\n this.#raw = this.#raw.subarray(n);\n this.length -= n;\n return head;\n }\n}\n\nfunction decodeVarInt(buf: EncodedBuf): number {\n let res = 0;\n for (;;) {\n const byte = buf.shift();\n res = (res << 7) | (byte & 127);\n if ((byte & 128) === 0) {\n break;\n }\n }\n return res;\n}\n\nfunction encodeVarInt(n: number): EncodedBuf {\n if (n === 0) {\n return new EncodedBuf(new Uint8Array([0]));\n }\n const digits: number[] = [];\n let value = n;\n while (value !== 0) {\n digits.push(value & 127);\n value >>>= 7;\n }\n digits.reverse();\n const encoded = digits.map((digit, i) => (i === digits.length - 1 ? digit : digit | 128));\n return new EncodedBuf(new Uint8Array(encoded));\n}\n\nfunction getBytes(buf: EncodedBuf, n: number): Uint8Array {\n return buf.shiftN(n);\n}\n\nclass Accumulator {\n readonly #buf: Uint8Array;\n\n constructor() {\n this.#buf = new Uint8Array(ID_SIZE);\n }\n\n add(other: Uint8Array): void {\n const view = new DataView(this.#buf.buffer, this.#buf.byteOffset, this.#buf.byteLength);\n const otherView = new DataView(other.buffer, other.byteOffset, other.byteLength);\n let carry = 0;\n for (let i = 0; i < 8; i++) {\n const offset = i * 4;\n const next = view.getUint32(offset, true) + carry + otherView.getUint32(offset, true);\n carry = next > 0xffffffff ? 1 : 0;\n view.setUint32(offset, next >>> 0, true);\n }\n }\n\n fingerprint(n: number): Uint8Array {\n const input = new EncodedBuf();\n input.extend(this.#buf);\n input.extend(encodeVarInt(n));\n return sha256(input.unwrap()).subarray(0, FINGERPRINT_SIZE);\n }\n}\n\nfunction compareBytes(a: Uint8Array, b: Uint8Array): number {\n const n = Math.min(a.byteLength, b.byteLength);\n for (let i = 0; i < n; i++) {\n const x = a[i];\n const y = b[i];\n if (x === undefined || y === undefined) {\n break;\n }\n if (x < y) {\n return -1;\n }\n if (x > y) {\n return 1;\n }\n }\n if (a.byteLength > b.byteLength) {\n return 1;\n }\n if (a.byteLength < b.byteLength) {\n return -1;\n }\n return 0;\n}\n\nfunction compareNegItems(a: NegItem, b: NegItem): number {\n if (a.timestamp !== b.timestamp) {\n return a.timestamp - b.timestamp;\n }\n return compareBytes(a.id, b.id);\n}\n\nexport class NegentropyStorageVector {\n readonly #items: NegItem[] = [];\n #sealed = false;\n\n insert(timestamp: number, id: string): void {\n if (this.#sealed) {\n throw new Nip77Error(\"already sealed\");\n }\n if (timestamp === INFINITY) {\n throw new Nip77Error(\"timestamp is reserved infinity\");\n }\n const idb = hexToBytes(assertHex32(id, \"event id\"));\n this.#items.push({ timestamp, id: idb });\n }\n\n seal(): void {\n if (this.#sealed) {\n throw new Nip77Error(\"already sealed\");\n }\n this.#sealed = true;\n this.#items.sort(compareNegItems);\n for (let i = 1; i < this.#items.length; i++) {\n const prev = this.#items[i - 1];\n const item = this.#items[i];\n if (prev !== undefined && item !== undefined && compareNegItems(prev, item) === 0) {\n throw new Nip77Error(\"duplicate item inserted\");\n }\n }\n }\n\n size(): number {\n this.#checkSealed();\n return this.#items.length;\n }\n\n iterate(begin: number, end: number, cb: (item: NegItem, i: number) => boolean): void {\n this.#checkSealed();\n this.#checkBounds(begin, end);\n for (let i = begin; i < end; i++) {\n const item = this.#items[i];\n if (item === undefined || !cb(item, i)) {\n break;\n }\n }\n }\n\n findLowerBound(begin: number, end: number, bound: NegItem): number {\n this.#checkSealed();\n this.#checkBounds(begin, end);\n let first = begin;\n let count = end - begin;\n while (count > 0) {\n const step = Math.floor(count / 2);\n const it = first + step;\n const item = this.#items[it];\n if (item === undefined) {\n break;\n }\n if (compareNegItems(item, bound) < 0) {\n first = it + 1;\n count -= step + 1;\n } else {\n count = step;\n }\n }\n return first;\n }\n\n fingerprint(begin: number, end: number): Uint8Array {\n const acc = new Accumulator();\n this.iterate(begin, end, (item) => {\n acc.add(item.id);\n return true;\n });\n return acc.fingerprint(end - begin);\n }\n\n #checkSealed(): void {\n if (!this.#sealed) {\n throw new Nip77Error(\"not sealed\");\n }\n }\n\n #checkBounds(begin: number, end: number): void {\n if (begin > end || end > this.#items.length) {\n throw new Nip77Error(\"bad range\");\n }\n }\n}\n\nexport function storageFromItems(\n items: ReadonlyArray<{ id: string; created_at: number }>,\n): NegentropyStorageVector {\n const storage = new NegentropyStorageVector();\n const seen = new Set<string>();\n for (const item of items) {\n const id = item.id.toLowerCase();\n if (seen.has(id)) {\n continue;\n }\n seen.add(id);\n storage.insert(item.created_at, id);\n }\n storage.seal();\n return storage;\n}\n\nexport function storageFromEvents(\n events: ReadonlyArray<Pick<Event, \"id\" | \"created_at\">>,\n): NegentropyStorageVector {\n return storageFromItems(events);\n}\n\ntype Bound = { timestamp: number; id: Uint8Array };\n\nexport class Negentropy {\n readonly #storage: NegentropyStorageVector;\n readonly #frameSizeLimit: number;\n #initiator = false;\n #lastTimestampIn = 0;\n #lastTimestampOut = 0;\n\n constructor(storage: NegentropyStorageVector, frameSizeLimit: number = DEFAULT_FRAME_SIZE_LIMIT) {\n if (frameSizeLimit !== 0 && frameSizeLimit < 4096) {\n throw new Nip77Error(\"frameSizeLimit too small\");\n }\n this.#storage = storage;\n this.#frameSizeLimit = frameSizeLimit;\n }\n\n initiate(): string {\n this.#initiator = true;\n const output = new EncodedBuf();\n output.extend(new Uint8Array([PROTOCOL_VERSION]));\n this.#splitRange(0, this.#storage.size(), this.#bound(INFINITY), output);\n return bytesToHex(output.unwrap());\n }\n\n reconcile(queryMsg: string): ReconcileOutcome {\n const have: string[] = [];\n const need: string[] = [];\n const query = new EncodedBuf(hexToBytes(queryMsg));\n\n this.#lastTimestampIn = 0;\n this.#lastTimestampOut = 0;\n\n const fullOutput = new EncodedBuf();\n fullOutput.extend(new Uint8Array([PROTOCOL_VERSION]));\n\n const protocolVersion = query.shift();\n if (protocolVersion < 0x60 || protocolVersion > 0x6f) {\n throw new Nip77Error(\"invalid negentropy protocol version byte\");\n }\n if (protocolVersion !== PROTOCOL_VERSION) {\n // NIP-77: reply with the highest supported version so the peer can downgrade.\n return { have, need, nextMessage: bytesToHex(fullOutput.unwrap()) };\n }\n\n const storageSize = this.#storage.size();\n let prevBound = this.#bound(0);\n let prevIndex = 0;\n let skip = false;\n\n while (query.length > 0) {\n const o = new EncodedBuf();\n\n const currBound = this.#decodeBound(query);\n const mode = decodeVarInt(query);\n const lower = prevIndex;\n const upper = this.#storage.findLowerBound(prevIndex, storageSize, currBound);\n\n if (mode === Mode.Skip) {\n skip = true;\n } else if (mode === Mode.Fingerprint) {\n const theirFingerprint = getBytes(query, FINGERPRINT_SIZE);\n const ourFingerprint = this.#storage.fingerprint(lower, upper);\n if (compareBytes(theirFingerprint, ourFingerprint) === 0) {\n skip = true;\n } else {\n if (skip) {\n skip = false;\n this.#encodeSkipInto(o, prevBound);\n }\n this.#splitRange(lower, upper, currBound, o);\n }\n } else if (mode === Mode.IdList) {\n const numIds = decodeVarInt(query);\n const theirElems = new Map<string, Uint8Array>();\n for (let i = 0; i < numIds; i++) {\n const e = getBytes(query, ID_SIZE);\n theirElems.set(bytesToHex(e), e);\n }\n\n if (this.#initiator) {\n skip = true;\n this.#storage.iterate(lower, upper, (item) => {\n const id = bytesToHex(item.id);\n if (theirElems.has(id)) {\n theirElems.delete(id);\n } else {\n have.push(id);\n }\n return true;\n });\n for (const id of theirElems.keys()) {\n need.push(id);\n }\n } else {\n if (skip) {\n skip = false;\n this.#encodeSkipInto(o, prevBound);\n }\n o.extend(this.#encodeBound(currBound));\n o.extend(encodeVarInt(Mode.IdList));\n const ourIds: Uint8Array[] = [];\n this.#storage.iterate(lower, upper, (item) => {\n ourIds.push(item.id);\n return true;\n });\n o.extend(encodeVarInt(ourIds.length));\n for (const id of ourIds) {\n o.extend(id);\n }\n }\n } else {\n throw new Nip77Error(\"unexpected mode\");\n }\n\n if (this.#exceededFrameSizeLimit(fullOutput.length + o.length)) {\n const remainingFingerprint = this.#storage.fingerprint(upper, storageSize);\n fullOutput.extend(this.#encodeBound(this.#bound(INFINITY)));\n fullOutput.extend(encodeVarInt(Mode.Fingerprint));\n fullOutput.extend(remainingFingerprint);\n break;\n }\n fullOutput.extend(o);\n prevIndex = upper;\n prevBound = currBound;\n }\n\n return {\n have,\n need,\n nextMessage: fullOutput.length === 1 ? undefined : bytesToHex(fullOutput.unwrap()),\n };\n }\n\n #bound(timestamp: number, id?: Uint8Array): Bound {\n return { timestamp, id: id ?? new Uint8Array(0) };\n }\n\n #splitRange(lower: number, upper: number, upperBound: Bound, o: EncodedBuf): void {\n const numElems = upper - lower;\n if (numElems < ID_LIST_THRESHOLD) {\n o.extend(this.#encodeBound(upperBound));\n o.extend(encodeVarInt(Mode.IdList));\n o.extend(encodeVarInt(numElems));\n this.#storage.iterate(lower, upper, (item) => {\n o.extend(item.id);\n return true;\n });\n return;\n }\n\n const itemsPerBucket = Math.floor(numElems / BUCKETS);\n const bucketsWithExtra = numElems % BUCKETS;\n let curr = lower;\n for (let i = 0; i < BUCKETS; i++) {\n const bucketSize = itemsPerBucket + (i < bucketsWithExtra ? 1 : 0);\n const ourFingerprint = this.#storage.fingerprint(curr, curr + bucketSize);\n curr += bucketSize;\n let nextBound: Bound;\n if (curr === upper) {\n nextBound = upperBound;\n } else {\n const at = curr - 1;\n let prevItem: NegItem | undefined;\n let currItem: NegItem | undefined;\n this.#storage.iterate(at, at + 2, (item, index) => {\n if (index === at) {\n prevItem = item;\n } else {\n currItem = item;\n }\n return true;\n });\n if (prevItem === undefined || currItem === undefined) {\n throw new Nip77Error(\"missing bound items\");\n }\n nextBound = this.#minimalBound(prevItem, currItem);\n }\n o.extend(this.#encodeBound(nextBound));\n o.extend(encodeVarInt(Mode.Fingerprint));\n o.extend(ourFingerprint);\n }\n }\n\n #exceededFrameSizeLimit(n: number): boolean {\n if (this.#frameSizeLimit === 0) {\n return false;\n }\n return n > this.#frameSizeLimit - 200;\n }\n\n #decodeTimestampIn(encoded: EncodedBuf): number {\n let timestamp = decodeVarInt(encoded);\n timestamp = timestamp === 0 ? INFINITY : timestamp - 1;\n if (this.#lastTimestampIn === INFINITY || timestamp === INFINITY) {\n this.#lastTimestampIn = INFINITY;\n return INFINITY;\n }\n timestamp += this.#lastTimestampIn;\n this.#lastTimestampIn = timestamp;\n return timestamp;\n }\n\n #decodeBound(encoded: EncodedBuf): Bound {\n const timestamp = this.#decodeTimestampIn(encoded);\n const len = decodeVarInt(encoded);\n if (len > ID_SIZE) {\n throw new Nip77Error(\"bound key too long\");\n }\n return { timestamp, id: getBytes(encoded, len) };\n }\n\n #encodeTimestampOut(timestamp: number): EncodedBuf {\n if (timestamp === INFINITY) {\n this.#lastTimestampOut = INFINITY;\n return encodeVarInt(0);\n }\n const delta = timestamp - this.#lastTimestampOut;\n this.#lastTimestampOut = timestamp;\n return encodeVarInt(delta + 1);\n }\n\n #encodeSkipInto(o: EncodedBuf, bound: Bound): void {\n o.extend(this.#encodeBound(bound));\n o.extend(encodeVarInt(Mode.Skip));\n }\n\n #encodeBound(key: Bound): EncodedBuf {\n const output = new EncodedBuf();\n output.extend(this.#encodeTimestampOut(key.timestamp));\n output.extend(encodeVarInt(key.id.length));\n output.extend(key.id);\n return output;\n }\n\n #minimalBound(prev: NegItem, curr: NegItem): Bound {\n if (curr.timestamp !== prev.timestamp) {\n return this.#bound(curr.timestamp);\n }\n let shared = 0;\n for (let i = 0; i < ID_SIZE; i++) {\n if (curr.id[i] !== prev.id[i]) {\n break;\n }\n shared += 1;\n }\n return this.#bound(curr.timestamp, curr.id.subarray(0, shared + 1));\n }\n}\n\n/** Drive initiate/reconcile until nextMessage is null. Transport-free. */\nexport async function runNegSession(opts: {\n storage: NegentropyStorageVector;\n frameSizeLimit?: number;\n openingSend: (hex: string) => void;\n msgSend: (hex: string) => void;\n next: () => Promise<string>;\n}): Promise<{ have: string[]; need: string[] }> {\n const neg = new Negentropy(opts.storage, opts.frameSizeLimit);\n const have = new Set<string>();\n const need = new Set<string>();\n opts.openingSend(neg.initiate());\n for (let round = 0; round < MAX_NEG_ROUNDS; round++) {\n // oxlint-disable-next-line no-await-in-loop -- protocol rounds are sequential by definition\n const out = neg.reconcile(await opts.next());\n for (const id of out.have) {\n have.add(id);\n }\n for (const id of out.need) {\n need.add(id);\n }\n if (out.nextMessage === undefined) {\n return { have: [...have], need: [...need] };\n }\n opts.msgSend(out.nextMessage);\n }\n throw new Nip77Error(\"negentropy exceeded max rounds\");\n}\n"],"mappings":"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"}
|
package/dist/nips/nip96.d.mts
CHANGED
|
@@ -1,31 +1,40 @@
|
|
|
1
|
-
import { a as NostrError } from "../error-
|
|
2
|
-
import { t as ManualFetch } from "../http-
|
|
1
|
+
import { a as NostrError } from "../error-CR8Urc84.mjs";
|
|
2
|
+
import { t as ManualFetch } from "../http-C4HwIu4B.mjs";
|
|
3
3
|
//#region src/nips/nip96.d.ts
|
|
4
|
-
type Nip96Fetch = ManualFetch;
|
|
5
|
-
type Nip96ServerInfo = {
|
|
4
|
+
export type Nip96Fetch = ManualFetch;
|
|
5
|
+
export type Nip96ServerInfo = {
|
|
6
6
|
api_url: string;
|
|
7
7
|
download_url?: string;
|
|
8
8
|
delegated_to_url?: string;
|
|
9
9
|
content_types?: string[];
|
|
10
10
|
};
|
|
11
|
-
type Nip96UploadResult = {
|
|
11
|
+
export type Nip96UploadResult = {
|
|
12
|
+
status: "success";
|
|
12
13
|
url: string;
|
|
13
14
|
tags: string[][];
|
|
15
|
+
} | {
|
|
16
|
+
status: "processing";
|
|
17
|
+
processingUrl: string;
|
|
18
|
+
tags: string[][];
|
|
14
19
|
};
|
|
15
|
-
declare class Nip96Error extends NostrError {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
+
export declare class Nip96Error extends NostrError {
|
|
21
|
+
name: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Parse `{ status, processing_url, nip94_event.tags }`. A `url` tag is required unless the response
|
|
25
|
+
* reports delayed processing (`status: "processing"` or HTTP 202 with `processing_url`).
|
|
26
|
+
*/
|
|
27
|
+
export declare function parseNip96UploadResponse(json: unknown, httpStatus?: number): Nip96UploadResult;
|
|
28
|
+
/** GET `${service}/.well-known/nostr/nip96.json`; follows `delegated_to_url` one hop. */
|
|
29
|
+
export declare function fetchNip96Info(serviceUrl: string, opts?: {
|
|
20
30
|
fetch?: Nip96Fetch;
|
|
21
31
|
signal?: AbortSignal;
|
|
22
32
|
}): Promise<Nip96ServerInfo>;
|
|
23
33
|
/** POST `apiUrl` as multipart `file`. `authorization` is a prebuilt NIP-98 header. */
|
|
24
|
-
declare function uploadNip96(apiUrl: string, file: Blob, authorization: string, opts?: {
|
|
34
|
+
export declare function uploadNip96(apiUrl: string, file: Blob, authorization: string, opts?: {
|
|
25
35
|
fetch?: Nip96Fetch;
|
|
26
36
|
signal?: AbortSignal;
|
|
27
37
|
extraFields?: Record<string, string>;
|
|
28
38
|
}): Promise<Nip96UploadResult>;
|
|
29
39
|
//#endregion
|
|
30
|
-
export { Nip96Error, Nip96Fetch, Nip96ServerInfo, Nip96UploadResult, fetchNip96Info, parseNip96UploadResponse, uploadNip96 };
|
|
31
40
|
//# sourceMappingURL=nip96.d.mts.map
|
package/dist/nips/nip96.mjs
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as requireGlobalFetch, t as fetchManual } from "../http-
|
|
1
|
+
import { c as isRecord, g as NostrError } from "../util-LfQirgDk.mjs";
|
|
2
|
+
import { n as requireGlobalFetch, t as fetchManual } from "../http-CuazNRIP.mjs";
|
|
3
3
|
//#region src/nips/nip96.ts
|
|
4
4
|
/**
|
|
5
|
-
* NIP-96: HTTP file storage (unrecommended; NIP-B7 preferred).
|
|
6
|
-
*
|
|
5
|
+
* NIP-96: HTTP file storage (unrecommended; NIP-B7 preferred). Authorization is a prebuilt NIP-98
|
|
6
|
+
* header — this module does not import nip98.
|
|
7
7
|
*
|
|
8
8
|
* @see https://github.com/nostr-protocol/nips/blob/master/96.md
|
|
9
9
|
*/
|
|
10
10
|
const WELL_KNOWN_PATH = "/.well-known/nostr/nip96.json";
|
|
11
|
-
var Nip96Error = class extends NostrError {
|
|
11
|
+
var Nip96Error = class extends NostrError {
|
|
12
|
+
name = "Nip96Error";
|
|
13
|
+
};
|
|
12
14
|
function serverInfoUrl(serviceUrl) {
|
|
13
15
|
return `${serviceUrl.replace(/\/+$/, "")}${WELL_KNOWN_PATH}`;
|
|
14
16
|
}
|
|
15
17
|
function errorMessageFromBody(json) {
|
|
16
|
-
if (!
|
|
17
|
-
const message = json
|
|
18
|
-
if (typeof message !== "string") return
|
|
18
|
+
if (!isRecord(json)) return;
|
|
19
|
+
const { message } = json;
|
|
20
|
+
if (typeof message !== "string") return;
|
|
19
21
|
return message.trim() || void 0;
|
|
20
22
|
}
|
|
21
23
|
/** Non-OK: prefer NIP-96 JSON `message`; never require a `url` tag. */
|
|
@@ -24,50 +26,71 @@ async function throwHttpError(prefix, res) {
|
|
|
24
26
|
try {
|
|
25
27
|
detail = errorMessageFromBody(await res.json());
|
|
26
28
|
} catch {}
|
|
27
|
-
throw new Nip96Error(detail ? `${prefix} HTTP ${res.status}
|
|
29
|
+
throw new Nip96Error(detail === void 0 || detail === "" ? `${prefix} HTTP ${res.status}` : `${prefix} HTTP ${res.status}: ${detail}`);
|
|
28
30
|
}
|
|
29
31
|
function parseNip96ServerInfo(json) {
|
|
30
|
-
if (!
|
|
32
|
+
if (!isRecord(json)) throw new Nip96Error("NIP-96 server info must be a JSON object");
|
|
31
33
|
const raw = json;
|
|
32
|
-
if (typeof raw
|
|
33
|
-
const info = { api_url: raw
|
|
34
|
-
if (typeof raw
|
|
35
|
-
if (typeof raw
|
|
36
|
-
if (Array.isArray(raw
|
|
34
|
+
if (typeof raw["api_url"] !== "string") throw new Nip96Error("missing api_url");
|
|
35
|
+
const info = { api_url: raw["api_url"] };
|
|
36
|
+
if (typeof raw["download_url"] === "string") info.download_url = raw["download_url"];
|
|
37
|
+
if (typeof raw["delegated_to_url"] === "string") info.delegated_to_url = raw["delegated_to_url"];
|
|
38
|
+
if (Array.isArray(raw["content_types"]) && raw["content_types"].every((t) => typeof t === "string")) info.content_types = [...raw["content_types"]];
|
|
37
39
|
return info;
|
|
38
40
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
function parseStatus(value) {
|
|
42
|
+
return value === "success" || value === "error" || value === "processing" ? value : void 0;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Parse `{ status, processing_url, nip94_event.tags }`. A `url` tag is required unless the response
|
|
46
|
+
* reports delayed processing (`status: "processing"` or HTTP 202 with `processing_url`).
|
|
47
|
+
*/
|
|
48
|
+
function parseNip96UploadResponse(json, httpStatus = 200) {
|
|
49
|
+
if (!isRecord(json)) throw new Nip96Error("NIP-96 upload response must be a JSON object");
|
|
50
|
+
const raw = json;
|
|
51
|
+
const status = parseStatus(raw["status"]);
|
|
52
|
+
const rawProcessingUrl = raw["processing_url"];
|
|
53
|
+
const processingUrl = typeof rawProcessingUrl === "string" && rawProcessingUrl !== "" ? rawProcessingUrl : void 0;
|
|
54
|
+
const event = raw["nip94_event"];
|
|
55
|
+
const rawTags = isRecord(event) ? event["tags"] : void 0;
|
|
45
56
|
const tags = [];
|
|
46
|
-
for (const tag of rawTags) {
|
|
47
|
-
if (!Array.isArray(tag) || tag.
|
|
57
|
+
if (Array.isArray(rawTags)) for (const tag of rawTags) {
|
|
58
|
+
if (!Array.isArray(tag) || !tag.every((item) => typeof item === "string")) throw new Nip96Error("invalid nip94_event.tags");
|
|
48
59
|
tags.push([...tag]);
|
|
49
60
|
}
|
|
50
|
-
const url = tags.find((tag) => tag[0] === "url")?.
|
|
51
|
-
if (
|
|
52
|
-
|
|
61
|
+
const url = tags.find((tag) => tag[0] === "url")?.at(1);
|
|
62
|
+
if (url !== void 0 && url !== "") return {
|
|
63
|
+
status: "success",
|
|
53
64
|
url,
|
|
54
65
|
tags
|
|
55
66
|
};
|
|
67
|
+
if ((httpStatus === 202 || status === "processing") && processingUrl !== void 0) return {
|
|
68
|
+
status: "processing",
|
|
69
|
+
processingUrl,
|
|
70
|
+
tags
|
|
71
|
+
};
|
|
72
|
+
throw new Nip96Error("upload response without url");
|
|
56
73
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const url = serverInfoUrl(serviceUrl);
|
|
60
|
-
const fetchImpl = opts?.fetch ?? requireGlobalFetch(() => new Nip96Error("no fetch implementation available; pass opts.fetch"));
|
|
61
|
-
const res = await fetchManual(fetchImpl, url, { signal: opts?.signal }, (cause) => new Nip96Error(`NIP-96 server info request failed: ${url}`, { cause: cause instanceof Error ? cause : void 0 }));
|
|
74
|
+
async function fetchServerInfo(fetchImpl, url, signal) {
|
|
75
|
+
const res = await fetchManual(fetchImpl, url, { signal }, (cause) => new Nip96Error(`NIP-96 server info request failed: ${url}`, { cause: cause instanceof Error ? cause : void 0 }));
|
|
62
76
|
if (!res.ok) await throwHttpError("NIP-96 server info", res);
|
|
63
77
|
let json;
|
|
64
78
|
try {
|
|
65
79
|
json = await res.json();
|
|
66
|
-
} catch (
|
|
67
|
-
throw new Nip96Error("invalid NIP-96 server info JSON", { cause:
|
|
80
|
+
} catch (error) {
|
|
81
|
+
throw new Nip96Error("invalid NIP-96 server info JSON", { cause: error instanceof Error ? error : void 0 });
|
|
68
82
|
}
|
|
69
83
|
return parseNip96ServerInfo(json);
|
|
70
84
|
}
|
|
85
|
+
/** GET `${service}/.well-known/nostr/nip96.json`; follows `delegated_to_url` one hop. */
|
|
86
|
+
async function fetchNip96Info(serviceUrl, opts) {
|
|
87
|
+
const fetchImpl = opts?.fetch ?? requireGlobalFetch(() => new Nip96Error("no fetch implementation available; pass opts.fetch"));
|
|
88
|
+
const info = await fetchServerInfo(fetchImpl, serverInfoUrl(serviceUrl), opts?.signal);
|
|
89
|
+
if (info.delegated_to_url === void 0 || info.delegated_to_url === "") return info;
|
|
90
|
+
const delegated = await fetchServerInfo(fetchImpl, serverInfoUrl(info.delegated_to_url), opts?.signal);
|
|
91
|
+
if (delegated.delegated_to_url !== void 0 && delegated.delegated_to_url !== "") throw new Nip96Error("NIP-96 delegation exceeded one hop");
|
|
92
|
+
return delegated;
|
|
93
|
+
}
|
|
71
94
|
/** POST `apiUrl` as multipart `file`. `authorization` is a prebuilt NIP-98 header. */
|
|
72
95
|
async function uploadNip96(apiUrl, file, authorization, opts) {
|
|
73
96
|
const fetchImpl = opts?.fetch ?? requireGlobalFetch(() => new Nip96Error("no fetch implementation available; pass opts.fetch"));
|
|
@@ -84,10 +107,10 @@ async function uploadNip96(apiUrl, file, authorization, opts) {
|
|
|
84
107
|
let json;
|
|
85
108
|
try {
|
|
86
109
|
json = await res.json();
|
|
87
|
-
} catch (
|
|
88
|
-
throw new Nip96Error("invalid NIP-96 upload response JSON", { cause:
|
|
110
|
+
} catch (error) {
|
|
111
|
+
throw new Nip96Error("invalid NIP-96 upload response JSON", { cause: error instanceof Error ? error : void 0 });
|
|
89
112
|
}
|
|
90
|
-
return parseNip96UploadResponse(json);
|
|
113
|
+
return parseNip96UploadResponse(json, res.status);
|
|
91
114
|
}
|
|
92
115
|
//#endregion
|
|
93
116
|
export { Nip96Error, fetchNip96Info, parseNip96UploadResponse, uploadNip96 };
|
package/dist/nips/nip96.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nip96.mjs","names":[],"sources":["../../src/nips/nip96.ts"],"sourcesContent":["/**\n * NIP-96: HTTP file storage (unrecommended; NIP-B7 preferred).\n * Authorization is a prebuilt NIP-98 header — this module does not import nip98.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/96.md\n */\nimport { NostrError } from \"../core/error.ts\";\nimport { fetchManual, requireGlobalFetch, type ManualFetch } from \"./http.ts\";\n\nconst WELL_KNOWN_PATH = \"/.well-known/nostr/nip96.json\";\n\nexport type Nip96Fetch = ManualFetch;\n\nexport type Nip96ServerInfo = {\n api_url: string;\n download_url?: string;\n delegated_to_url?: string;\n content_types?: string[];\n};\n\nexport type Nip96UploadResult = {\n url: string;\n tags: string[][];\n};\n\nexport class Nip96Error extends NostrError {}\n\nfunction serverInfoUrl(serviceUrl: string): string {\n return `${serviceUrl.replace(/\\/+$/, \"\")}${WELL_KNOWN_PATH}`;\n}\n\nfunction errorMessageFromBody(json: unknown): string | undefined {\n if (!json || typeof json !== \"object\" || Array.isArray(json)) return undefined;\n const message = (json as { message?: unknown }).message;\n if (typeof message !== \"string\") return undefined;\n const text = message.trim();\n return text || undefined;\n}\n\n/** Non-OK: prefer NIP-96 JSON `message`; never require a `url` tag. */\nasync function throwHttpError(\n prefix: string,\n res: Awaited<ReturnType<Nip96Fetch>>,\n): Promise<never> {\n let detail: string | undefined;\n try {\n detail = errorMessageFromBody(await res.json());\n } catch {\n // body is optional on error\n }\n throw new Nip96Error(\n detail ? `${prefix} HTTP ${res.status}: ${detail}` : `${prefix} HTTP ${res.status}`,\n );\n}\n\nfunction parseNip96ServerInfo(json: unknown): Nip96ServerInfo {\n if (!json || typeof json !== \"object\" || Array.isArray(json)) {\n throw new Nip96Error(\"NIP-96 server info must be a JSON object\");\n }\n const raw = json as Record<string, unknown>;\n if (typeof raw.api_url !== \"string\") {\n throw new Nip96Error(\"missing api_url\");\n }\n\n const info: Nip96ServerInfo = { api_url: raw.api_url };\n if (typeof raw.download_url === \"string\") info.download_url = raw.download_url;\n if (typeof raw.delegated_to_url === \"string\") info.delegated_to_url = raw.delegated_to_url;\n if (Array.isArray(raw.content_types) && raw.content_types.every((t) => typeof t === \"string\")) {\n info.content_types = [...raw.content_types];\n }\n return info;\n}\n\n/** Parse `{ status, nip94_event.tags }` and require a `url` tag. */\nexport function parseNip96UploadResponse(json: unknown): Nip96UploadResult {\n if (!json || typeof json !== \"object\" || Array.isArray(json)) {\n throw new Nip96Error(\"NIP-96 upload response must be a JSON object\");\n }\n const event = (json as { nip94_event?: unknown }).nip94_event;\n const rawTags =\n event && typeof event === \"object\" && !Array.isArray(event)\n ? (event as { tags?: unknown }).tags\n : undefined;\n if (!Array.isArray(rawTags)) {\n throw new Nip96Error(\"upload response without url\");\n }\n\n const tags: string[][] = [];\n for (const tag of rawTags) {\n if (!Array.isArray(tag) || tag.some((item) => typeof item !== \"string\")) {\n throw new Nip96Error(\"invalid nip94_event.tags\");\n }\n tags.push([...tag]);\n }\n\n const url = tags.find((tag) => tag[0] === \"url\")?.[1];\n if (!url) {\n throw new Nip96Error(\"upload response without url\");\n }\n return { url, tags };\n}\n\n/** GET `${service}/.well-known/nostr/nip96.json` with `redirect: \"manual\"`. */\nexport async function fetchNip96Info(\n serviceUrl: string,\n opts?: { fetch?: Nip96Fetch; signal?: AbortSignal },\n): Promise<Nip96ServerInfo> {\n const url = serverInfoUrl(serviceUrl);\n const fetchImpl =\n opts?.fetch ??\n requireGlobalFetch(() => new Nip96Error(\"no fetch implementation available; pass opts.fetch\"));\n\n const res = await fetchManual(\n fetchImpl,\n url,\n { signal: opts?.signal },\n (cause) =>\n new Nip96Error(`NIP-96 server info request failed: ${url}`, {\n cause: cause instanceof Error ? cause : undefined,\n }),\n );\n if (!res.ok) {\n await throwHttpError(\"NIP-96 server info\", res);\n }\n\n let json: unknown;\n try {\n json = await res.json();\n } catch (cause) {\n throw new Nip96Error(\"invalid NIP-96 server info JSON\", {\n cause: cause instanceof Error ? cause : undefined,\n });\n }\n return parseNip96ServerInfo(json);\n}\n\n/** POST `apiUrl` as multipart `file`. `authorization` is a prebuilt NIP-98 header. */\nexport async function uploadNip96(\n apiUrl: string,\n file: Blob,\n authorization: string,\n opts?: { fetch?: Nip96Fetch; signal?: AbortSignal; extraFields?: Record<string, string> },\n): Promise<Nip96UploadResult> {\n const fetchImpl =\n opts?.fetch ??\n requireGlobalFetch(() => new Nip96Error(\"no fetch implementation available; pass opts.fetch\"));\n const body = new FormData();\n body.append(\"file\", file);\n if (opts?.extraFields) {\n for (const [name, value] of Object.entries(opts.extraFields)) {\n body.append(name, value);\n }\n }\n\n const res = await fetchManual(\n fetchImpl,\n apiUrl,\n {\n method: \"POST\",\n headers: { Authorization: authorization },\n body,\n signal: opts?.signal,\n },\n (cause) =>\n new Nip96Error(`NIP-96 upload request failed: ${apiUrl}`, {\n cause: cause instanceof Error ? cause : undefined,\n }),\n );\n if (!res.ok) {\n await throwHttpError(\"NIP-96 upload\", res);\n }\n\n let json: unknown;\n try {\n json = await res.json();\n } catch (cause) {\n throw new Nip96Error(\"invalid NIP-96 upload response JSON\", {\n cause: cause instanceof Error ? cause : undefined,\n });\n }\n return parseNip96UploadResponse(json);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"nip96.mjs","names":[],"sources":["../../src/nips/nip96.ts"],"sourcesContent":["/**\n * NIP-96: HTTP file storage (unrecommended; NIP-B7 preferred). Authorization is a prebuilt NIP-98\n * header — this module does not import nip98.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/96.md\n */\nimport { NostrError } from \"../core/error.ts\";\nimport { isRecord } from \"../core/util.ts\";\nimport { fetchManual, requireGlobalFetch } from \"./http.ts\";\nimport type { ManualFetch } from \"./http.ts\";\n\nconst WELL_KNOWN_PATH = \"/.well-known/nostr/nip96.json\";\n\nexport type Nip96Fetch = ManualFetch;\n\nexport type Nip96ServerInfo = {\n api_url: string;\n download_url?: string;\n delegated_to_url?: string;\n content_types?: string[];\n};\n\nexport type Nip96UploadResult =\n | { status: \"success\"; url: string; tags: string[][] }\n | { status: \"processing\"; processingUrl: string; tags: string[][] };\n\nexport class Nip96Error extends NostrError {\n override name = \"Nip96Error\";\n}\n\nfunction serverInfoUrl(serviceUrl: string): string {\n return `${serviceUrl.replace(/\\/+$/, \"\")}${WELL_KNOWN_PATH}`;\n}\n\nfunction errorMessageFromBody(json: unknown): string | undefined {\n if (!isRecord(json)) {\n return undefined;\n }\n const { message } = json;\n if (typeof message !== \"string\") {\n return undefined;\n }\n const text = message.trim();\n return text || undefined;\n}\n\n/** Non-OK: prefer NIP-96 JSON `message`; never require a `url` tag. */\nasync function throwHttpError(\n prefix: string,\n res: Awaited<ReturnType<Nip96Fetch>>,\n): Promise<never> {\n let detail: string | undefined;\n try {\n detail = errorMessageFromBody(await res.json());\n } catch {\n // body is optional on error\n }\n throw new Nip96Error(\n detail === undefined || detail === \"\"\n ? `${prefix} HTTP ${res.status}`\n : `${prefix} HTTP ${res.status}: ${detail}`,\n );\n}\n\nfunction parseNip96ServerInfo(json: unknown): Nip96ServerInfo {\n if (!isRecord(json)) {\n throw new Nip96Error(\"NIP-96 server info must be a JSON object\");\n }\n const raw = json;\n if (typeof raw[\"api_url\"] !== \"string\") {\n throw new Nip96Error(\"missing api_url\");\n }\n\n const info: Nip96ServerInfo = { api_url: raw[\"api_url\"] };\n if (typeof raw[\"download_url\"] === \"string\") {\n info.download_url = raw[\"download_url\"];\n }\n if (typeof raw[\"delegated_to_url\"] === \"string\") {\n info.delegated_to_url = raw[\"delegated_to_url\"];\n }\n if (\n Array.isArray(raw[\"content_types\"]) &&\n raw[\"content_types\"].every((t) => typeof t === \"string\")\n ) {\n info.content_types = [...raw[\"content_types\"]];\n }\n return info;\n}\n\nfunction parseStatus(value: unknown): \"success\" | \"error\" | \"processing\" | undefined {\n return value === \"success\" || value === \"error\" || value === \"processing\" ? value : undefined;\n}\n\n/**\n * Parse `{ status, processing_url, nip94_event.tags }`. A `url` tag is required unless the response\n * reports delayed processing (`status: \"processing\"` or HTTP 202 with `processing_url`).\n */\nexport function parseNip96UploadResponse(json: unknown, httpStatus = 200): Nip96UploadResult {\n if (!isRecord(json)) {\n throw new Nip96Error(\"NIP-96 upload response must be a JSON object\");\n }\n const raw = json;\n const status = parseStatus(raw[\"status\"]);\n const rawProcessingUrl = raw[\"processing_url\"];\n const processingUrl =\n typeof rawProcessingUrl === \"string\" && rawProcessingUrl !== \"\" ? rawProcessingUrl : undefined;\n const event = raw[\"nip94_event\"];\n const rawTags: unknown = isRecord(event) ? event[\"tags\"] : undefined;\n\n const tags: string[][] = [];\n if (Array.isArray(rawTags)) {\n for (const tag of rawTags) {\n if (!Array.isArray(tag) || !tag.every((item): item is string => typeof item === \"string\")) {\n throw new Nip96Error(\"invalid nip94_event.tags\");\n }\n tags.push([...tag]);\n }\n }\n\n const url = tags.find((tag) => tag[0] === \"url\")?.at(1);\n if (url !== undefined && url !== \"\") {\n return { status: \"success\", url, tags };\n }\n if ((httpStatus === 202 || status === \"processing\") && processingUrl !== undefined) {\n return { status: \"processing\", processingUrl, tags };\n }\n throw new Nip96Error(\"upload response without url\");\n}\n\nasync function fetchServerInfo(\n fetchImpl: Nip96Fetch,\n url: string,\n signal?: AbortSignal,\n): Promise<Nip96ServerInfo> {\n const res = await fetchManual(\n fetchImpl,\n url,\n { signal },\n (cause) =>\n new Nip96Error(`NIP-96 server info request failed: ${url}`, {\n cause: cause instanceof Error ? cause : undefined,\n }),\n );\n if (!res.ok) {\n await throwHttpError(\"NIP-96 server info\", res);\n }\n\n let json: unknown;\n try {\n json = await res.json();\n } catch (error) {\n throw new Nip96Error(\"invalid NIP-96 server info JSON\", {\n cause: error instanceof Error ? error : undefined,\n });\n }\n return parseNip96ServerInfo(json);\n}\n\n/** GET `${service}/.well-known/nostr/nip96.json`; follows `delegated_to_url` one hop. */\nexport async function fetchNip96Info(\n serviceUrl: string,\n opts?: { fetch?: Nip96Fetch; signal?: AbortSignal },\n): Promise<Nip96ServerInfo> {\n const fetchImpl =\n opts?.fetch ??\n requireGlobalFetch(() => new Nip96Error(\"no fetch implementation available; pass opts.fetch\"));\n\n const info = await fetchServerInfo(fetchImpl, serverInfoUrl(serviceUrl), opts?.signal);\n if (info.delegated_to_url === undefined || info.delegated_to_url === \"\") {\n return info;\n }\n\n const delegated = await fetchServerInfo(\n fetchImpl,\n serverInfoUrl(info.delegated_to_url),\n opts?.signal,\n );\n if (delegated.delegated_to_url !== undefined && delegated.delegated_to_url !== \"\") {\n throw new Nip96Error(\"NIP-96 delegation exceeded one hop\");\n }\n return delegated;\n}\n\n/** POST `apiUrl` as multipart `file`. `authorization` is a prebuilt NIP-98 header. */\nexport async function uploadNip96(\n apiUrl: string,\n file: Blob,\n authorization: string,\n opts?: { fetch?: Nip96Fetch; signal?: AbortSignal; extraFields?: Record<string, string> },\n): Promise<Nip96UploadResult> {\n const fetchImpl =\n opts?.fetch ??\n requireGlobalFetch(() => new Nip96Error(\"no fetch implementation available; pass opts.fetch\"));\n const body = new FormData();\n body.append(\"file\", file);\n if (opts?.extraFields) {\n for (const [name, value] of Object.entries(opts.extraFields)) {\n body.append(name, value);\n }\n }\n\n const res = await fetchManual(\n fetchImpl,\n apiUrl,\n {\n method: \"POST\",\n headers: { Authorization: authorization },\n body,\n signal: opts?.signal,\n },\n (cause) =>\n new Nip96Error(`NIP-96 upload request failed: ${apiUrl}`, {\n cause: cause instanceof Error ? cause : undefined,\n }),\n );\n if (!res.ok) {\n await throwHttpError(\"NIP-96 upload\", res);\n }\n\n let json: unknown;\n try {\n json = await res.json();\n } catch (error) {\n throw new Nip96Error(\"invalid NIP-96 upload response JSON\", {\n cause: error instanceof Error ? error : undefined,\n });\n }\n return parseNip96UploadResponse(json, res.status);\n}\n"],"mappings":"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"}
|
package/dist/nips/nip98.d.mts
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import { n as EventTemplate, t as Event } from "../event-
|
|
2
|
-
import { a as NostrError } from "../error-
|
|
1
|
+
import { n as EventTemplate, t as Event } from "../event-fU3Y01PR.mjs";
|
|
2
|
+
import { a as NostrError } from "../error-CR8Urc84.mjs";
|
|
3
3
|
//#region src/nips/nip98.d.ts
|
|
4
|
-
declare class Nip98Error extends NostrError {
|
|
4
|
+
export declare class Nip98Error extends NostrError {
|
|
5
|
+
name: string;
|
|
6
|
+
}
|
|
5
7
|
/**
|
|
6
|
-
* Sign a kind 27235 auth event and encode it as a NIP-98 token.
|
|
7
|
-
*
|
|
8
|
+
* Sign a kind 27235 auth event and encode it as a NIP-98 token. Uses standard base64
|
|
9
|
+
* (`@scure/base`), not base64url.
|
|
8
10
|
*/
|
|
9
|
-
declare function getToken(url: string, method: string, sign: (template: EventTemplate) => Promise<Event> | Event, opts?: {
|
|
11
|
+
export declare function getToken(url: string, method: string, sign: (template: EventTemplate) => Promise<Event> | Event, opts?: {
|
|
10
12
|
includeAuthorizationScheme?: boolean;
|
|
11
13
|
content?: string;
|
|
12
14
|
payload?: unknown;
|
|
13
15
|
now?: number;
|
|
14
16
|
}): Promise<string>;
|
|
15
17
|
/** Decode a NIP-98 token (with or without the `Nostr ` scheme) into an event. */
|
|
16
|
-
declare function unpackEventFromToken(token: string): Event;
|
|
18
|
+
export declare function unpackEventFromToken(token: string): Event;
|
|
17
19
|
/**
|
|
18
|
-
* True when `event` is a valid NIP-98 auth event for `url` + `method`.
|
|
19
|
-
*
|
|
20
|
-
* and optional `payload` tag.
|
|
20
|
+
* True when `event` is a valid NIP-98 auth event for `url` + `method`. Verifies signature, kind
|
|
21
|
+
* 27235, timestamp window, `u`, `method`, and optional `payload` tag.
|
|
21
22
|
*/
|
|
22
|
-
declare function validateAuthEvent(event: Event, url: string, method: string, opts?: {
|
|
23
|
+
export declare function validateAuthEvent(event: Event, url: string, method: string, opts?: {
|
|
23
24
|
payload?: unknown;
|
|
24
25
|
maxSkewSec?: number;
|
|
25
26
|
}): boolean;
|
|
26
27
|
//#endregion
|
|
27
|
-
export { Nip98Error, getToken, unpackEventFromToken, validateAuthEvent };
|
|
28
28
|
//# sourceMappingURL=nip98.d.mts.map
|
package/dist/nips/nip98.mjs
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { s as verifyEvent } from "../key-Dn-aX4x4.mjs";
|
|
1
|
+
import { d as utf8Encoder, g as NostrError, i as bytesToHex, u as utf8Decoder } from "../util-LfQirgDk.mjs";
|
|
2
|
+
import { t as Kind } from "../kind-Cxwhhkx8.mjs";
|
|
3
|
+
import { m as validateSignedEvent } from "../event-D8Nfoh4K.mjs";
|
|
4
|
+
import { s as verifyEvent } from "../key-Bbtys6o7.mjs";
|
|
6
5
|
import { sha256 } from "@noble/hashes/sha2.js";
|
|
7
6
|
import { base64 } from "@scure/base";
|
|
8
7
|
//#region src/nips/nip98.ts
|
|
9
8
|
/**
|
|
10
|
-
* NIP-98: HTTP Auth (kind 27235).
|
|
11
|
-
* Token is standard base64 of the signed event JSON, not base64url.
|
|
9
|
+
* NIP-98: HTTP Auth (kind 27235). Token is standard base64 of the signed event JSON, not base64url.
|
|
12
10
|
*
|
|
13
11
|
* @see https://github.com/nostr-protocol/nips/blob/master/98.md
|
|
14
12
|
*/
|
|
15
13
|
const AUTHORIZATION_SCHEME = "Nostr ";
|
|
16
14
|
const DEFAULT_MAX_SKEW_SEC = 60;
|
|
17
|
-
var Nip98Error = class extends NostrError {
|
|
15
|
+
var Nip98Error = class extends NostrError {
|
|
16
|
+
name = "Nip98Error";
|
|
17
|
+
};
|
|
18
18
|
/** SHA-256 of the request body: raw bytes for string/Uint8Array, else JSON.stringify. */
|
|
19
19
|
function hashPayload(payload) {
|
|
20
20
|
const bytes = typeof payload === "string" ? utf8Encoder.encode(payload) : payload instanceof Uint8Array ? payload : utf8Encoder.encode(JSON.stringify(payload));
|
|
@@ -31,8 +31,8 @@ function stripAuthorizationScheme(token) {
|
|
|
31
31
|
return prefix ? token.slice(prefix[0].length) : token;
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
|
-
* Sign a kind 27235 auth event and encode it as a NIP-98 token.
|
|
35
|
-
*
|
|
34
|
+
* Sign a kind 27235 auth event and encode it as a NIP-98 token. Uses standard base64
|
|
35
|
+
* (`@scure/base`), not base64url.
|
|
36
36
|
*/
|
|
37
37
|
async function getToken(url, method, sign, opts) {
|
|
38
38
|
const tags = [["u", url], ["method", method]];
|
|
@@ -44,32 +44,31 @@ async function getToken(url, method, sign, opts) {
|
|
|
44
44
|
content: opts?.content ?? ""
|
|
45
45
|
});
|
|
46
46
|
const encoded = base64.encode(utf8Encoder.encode(JSON.stringify(signed)));
|
|
47
|
-
return opts?.includeAuthorizationScheme ? AUTHORIZATION_SCHEME + encoded : encoded;
|
|
47
|
+
return opts?.includeAuthorizationScheme === true ? AUTHORIZATION_SCHEME + encoded : encoded;
|
|
48
48
|
}
|
|
49
49
|
/** Decode a NIP-98 token (with or without the `Nostr ` scheme) into an event. */
|
|
50
50
|
function unpackEventFromToken(token) {
|
|
51
|
-
if (
|
|
51
|
+
if (token === "") throw new Nip98Error("missing token");
|
|
52
52
|
const encoded = stripAuthorizationScheme(token);
|
|
53
53
|
let json;
|
|
54
54
|
try {
|
|
55
55
|
json = utf8Decoder.decode(decodeStandardBase64(encoded));
|
|
56
|
-
} catch (
|
|
57
|
-
throw new Nip98Error("invalid token encoding", { cause });
|
|
56
|
+
} catch (error) {
|
|
57
|
+
throw new Nip98Error("invalid token encoding", { cause: error });
|
|
58
58
|
}
|
|
59
59
|
if (!json.startsWith("{")) throw new Nip98Error("invalid token");
|
|
60
60
|
let parsed;
|
|
61
61
|
try {
|
|
62
62
|
parsed = JSON.parse(json);
|
|
63
|
-
} catch (
|
|
64
|
-
throw new Nip98Error("invalid token JSON", { cause });
|
|
63
|
+
} catch (error) {
|
|
64
|
+
throw new Nip98Error("invalid token JSON", { cause: error });
|
|
65
65
|
}
|
|
66
66
|
if (!validateSignedEvent(parsed)) throw new Nip98Error("token is not a signed event");
|
|
67
67
|
return parsed;
|
|
68
68
|
}
|
|
69
69
|
/**
|
|
70
|
-
* True when `event` is a valid NIP-98 auth event for `url` + `method`.
|
|
71
|
-
*
|
|
72
|
-
* and optional `payload` tag.
|
|
70
|
+
* True when `event` is a valid NIP-98 auth event for `url` + `method`. Verifies signature, kind
|
|
71
|
+
* 27235, timestamp window, `u`, `method`, and optional `payload` tag.
|
|
73
72
|
*/
|
|
74
73
|
function validateAuthEvent(event, url, method, opts) {
|
|
75
74
|
if (!verifyEvent(event)) return false;
|
package/dist/nips/nip98.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nip98.mjs","names":[],"sources":["../../src/nips/nip98.ts"],"sourcesContent":["/**\n * NIP-98: HTTP Auth (kind 27235)
|
|
1
|
+
{"version":3,"file":"nip98.mjs","names":[],"sources":["../../src/nips/nip98.ts"],"sourcesContent":["/**\n * NIP-98: HTTP Auth (kind 27235). Token is standard base64 of the signed event JSON, not base64url.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/98.md\n */\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { base64 } from \"@scure/base\";\n\nimport { NostrError } from \"../core/error.ts\";\nimport type { Event, EventTemplate } from \"../core/event.ts\";\nimport { validateSignedEvent } from \"../core/event.ts\";\nimport { verifyEvent } from \"../core/key.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport type { Tag } from \"../core/tag.ts\";\nimport { bytesToHex, utf8Decoder, utf8Encoder } from \"../core/util.ts\";\n\nconst AUTHORIZATION_SCHEME = \"Nostr \";\nconst DEFAULT_MAX_SKEW_SEC = 60;\n\nexport class Nip98Error extends NostrError {\n override name = \"Nip98Error\";\n}\n\n/** SHA-256 of the request body: raw bytes for string/Uint8Array, else JSON.stringify. */\nfunction hashPayload(payload: unknown): string {\n const bytes =\n typeof payload === \"string\"\n ? utf8Encoder.encode(payload)\n : payload instanceof Uint8Array\n ? payload\n : utf8Encoder.encode(JSON.stringify(payload));\n return bytesToHex(sha256(bytes));\n}\n\n/** RFC 4648 standard base64; accept missing `=` (NIP-98 spec example is unpadded). */\nfunction decodeStandardBase64(encoded: string): Uint8Array {\n const rem = encoded.length % 4;\n const padded = rem === 0 ? encoded : encoded + \"=\".repeat(4 - rem);\n return base64.decode(padded);\n}\n\nfunction stripAuthorizationScheme(token: string): string {\n const prefix = /^nostr\\s+/i.exec(token);\n return prefix ? token.slice(prefix[0].length) : token;\n}\n\n/**\n * Sign a kind 27235 auth event and encode it as a NIP-98 token. Uses standard base64\n * (`@scure/base`), not base64url.\n */\nexport async function getToken(\n url: string,\n method: string,\n sign: (template: EventTemplate) => Promise<Event> | Event,\n opts?: {\n includeAuthorizationScheme?: boolean;\n content?: string;\n payload?: unknown;\n now?: number;\n },\n): Promise<string> {\n const tags: Tag[] = [\n [\"u\", url],\n [\"method\", method],\n ];\n if (opts?.payload !== undefined) {\n tags.push([\"payload\", hashPayload(opts.payload)]);\n }\n\n const signed = await sign({\n kind: Kind.HttpAuth,\n created_at: opts?.now ?? Math.floor(Date.now() / 1000),\n tags,\n content: opts?.content ?? \"\",\n });\n\n const encoded = base64.encode(utf8Encoder.encode(JSON.stringify(signed)));\n return opts?.includeAuthorizationScheme === true ? AUTHORIZATION_SCHEME + encoded : encoded;\n}\n\n/** Decode a NIP-98 token (with or without the `Nostr ` scheme) into an event. */\nexport function unpackEventFromToken(token: string): Event {\n if (token === \"\") {\n throw new Nip98Error(\"missing token\");\n }\n\n const encoded = stripAuthorizationScheme(token);\n\n let json: string;\n try {\n json = utf8Decoder.decode(decodeStandardBase64(encoded));\n } catch (error) {\n throw new Nip98Error(\"invalid token encoding\", { cause: error });\n }\n if (!json.startsWith(\"{\")) {\n throw new Nip98Error(\"invalid token\");\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(json);\n } catch (error) {\n throw new Nip98Error(\"invalid token JSON\", { cause: error });\n }\n if (!validateSignedEvent(parsed)) {\n throw new Nip98Error(\"token is not a signed event\");\n }\n return parsed;\n}\n\n/**\n * True when `event` is a valid NIP-98 auth event for `url` + `method`. Verifies signature, kind\n * 27235, timestamp window, `u`, `method`, and optional `payload` tag.\n */\nexport function validateAuthEvent(\n event: Event,\n url: string,\n method: string,\n opts?: { payload?: unknown; maxSkewSec?: number },\n): boolean {\n if (!verifyEvent(event)) {\n return false;\n }\n if (event.kind !== Kind.HttpAuth) {\n return false;\n }\n\n const maxSkewSec = opts?.maxSkewSec ?? DEFAULT_MAX_SKEW_SEC;\n const now = Math.floor(Date.now() / 1000);\n if (Math.abs(now - event.created_at) > maxSkewSec) {\n return false;\n }\n\n const u = event.tags.find((t) => t[0] === \"u\")?.[1];\n if (u !== url) {\n return false;\n }\n\n const m = event.tags.find((t) => t[0] === \"method\")?.[1];\n if (m === undefined || m.toLowerCase() !== method.toLowerCase()) {\n return false;\n }\n\n if (opts?.payload !== undefined) {\n const payloadTag = event.tags.find((t) => t[0] === \"payload\")?.[1];\n if (payloadTag !== hashPayload(opts.payload)) {\n return false;\n }\n }\n\n return true;\n}\n"],"mappings":";;;;;;;;;;;;AAgBA,MAAM,uBAAuB;AAC7B,MAAM,uBAAuB;AAE7B,IAAa,aAAb,cAAgC,WAAW;CACzC,OAAgB;AAClB;;AAGA,SAAS,YAAY,SAA0B;CAC7C,MAAM,QACJ,OAAO,YAAY,WACf,YAAY,OAAO,OAAO,IAC1B,mBAAmB,aACjB,UACA,YAAY,OAAO,KAAK,UAAU,OAAO,CAAC;CAClD,OAAO,WAAW,OAAO,KAAK,CAAC;AACjC;;AAGA,SAAS,qBAAqB,SAA6B;CACzD,MAAM,MAAM,QAAQ,SAAS;CAC7B,MAAM,SAAS,QAAQ,IAAI,UAAU,UAAU,IAAI,OAAO,IAAI,GAAG;CACjE,OAAO,OAAO,OAAO,MAAM;AAC7B;AAEA,SAAS,yBAAyB,OAAuB;CACvD,MAAM,SAAS,aAAa,KAAK,KAAK;CACtC,OAAO,SAAS,MAAM,MAAM,OAAO,EAAE,CAAC,MAAM,IAAI;AAClD;;;;;AAMA,eAAsB,SACpB,KACA,QACA,MACA,MAMiB;CACjB,MAAM,OAAc,CAClB,CAAC,KAAK,GAAG,GACT,CAAC,UAAU,MAAM,CACnB;CACA,IAAI,MAAM,YAAY,KAAA,GACpB,KAAK,KAAK,CAAC,WAAW,YAAY,KAAK,OAAO,CAAC,CAAC;CAGlD,MAAM,SAAS,MAAM,KAAK;EACxB,MAAM,KAAK;EACX,YAAY,MAAM,OAAO,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;EACrD;EACA,SAAS,MAAM,WAAW;CAC5B,CAAC;CAED,MAAM,UAAU,OAAO,OAAO,YAAY,OAAO,KAAK,UAAU,MAAM,CAAC,CAAC;CACxE,OAAO,MAAM,+BAA+B,OAAO,uBAAuB,UAAU;AACtF;;AAGA,SAAgB,qBAAqB,OAAsB;CACzD,IAAI,UAAU,IACZ,MAAM,IAAI,WAAW,eAAe;CAGtC,MAAM,UAAU,yBAAyB,KAAK;CAE9C,IAAI;CACJ,IAAI;EACF,OAAO,YAAY,OAAO,qBAAqB,OAAO,CAAC;CACzD,SAAS,OAAO;EACd,MAAM,IAAI,WAAW,0BAA0B,EAAE,OAAO,MAAM,CAAC;CACjE;CACA,IAAI,CAAC,KAAK,WAAW,GAAG,GACtB,MAAM,IAAI,WAAW,eAAe;CAGtC,IAAI;CACJ,IAAI;EACF,SAAS,KAAK,MAAM,IAAI;CAC1B,SAAS,OAAO;EACd,MAAM,IAAI,WAAW,sBAAsB,EAAE,OAAO,MAAM,CAAC;CAC7D;CACA,IAAI,CAAC,oBAAoB,MAAM,GAC7B,MAAM,IAAI,WAAW,6BAA6B;CAEpD,OAAO;AACT;;;;;AAMA,SAAgB,kBACd,OACA,KACA,QACA,MACS;CACT,IAAI,CAAC,YAAY,KAAK,GACpB,OAAO;CAET,IAAI,MAAM,SAAS,KAAK,UACtB,OAAO;CAGT,MAAM,aAAa,MAAM,cAAc;CACvC,MAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;CACxC,IAAI,KAAK,IAAI,MAAM,MAAM,UAAU,IAAI,YACrC,OAAO;CAIT,IADU,MAAM,KAAK,MAAM,MAAM,EAAE,OAAO,GAAG,CAAC,GAAG,OACvC,KACR,OAAO;CAGT,MAAM,IAAI,MAAM,KAAK,MAAM,MAAM,EAAE,OAAO,QAAQ,CAAC,GAAG;CACtD,IAAI,MAAM,KAAA,KAAa,EAAE,YAAY,MAAM,OAAO,YAAY,GAC5D,OAAO;CAGT,IAAI,MAAM,YAAY,KAAA,GACD;MAAA,MAAM,KAAK,MAAM,MAAM,EAAE,OAAO,SAAS,CAAC,GAAG,OAC7C,YAAY,KAAK,OAAO,GACzC,OAAO;CAAA;CAIX,OAAO;AACT"}
|