You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
117 lines
3.5 KiB
117 lines
3.5 KiB
// nip19.ts |
|
import { bytesToHex as bytesToHex2, concatBytes, hexToBytes as hexToBytes2 } from "@noble/hashes/utils"; |
|
import { bech32 } from "@scure/base"; |
|
|
|
// utils.ts |
|
import { bytesToHex, hexToBytes } from "@noble/hashes/utils"; |
|
var utf8Decoder = new TextDecoder("utf-8"); |
|
var utf8Encoder = new TextEncoder(); |
|
|
|
// nip19.ts |
|
var Bech32MaxSize = 5e3; |
|
var BECH32_REGEX = /[\x21-\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/; |
|
function decode(code) { |
|
let { prefix, words } = bech32.decode(code, Bech32MaxSize); |
|
let data = new Uint8Array(bech32.fromWords(words)); |
|
switch (prefix) { |
|
case "nprofile": { |
|
let tlv = parseTLV(data); |
|
if (!tlv[0]?.[0]) |
|
throw new Error("missing TLV 0 for nprofile"); |
|
if (tlv[0][0].length !== 32) |
|
throw new Error("TLV 0 should be 32 bytes"); |
|
return { |
|
type: "nprofile", |
|
data: { |
|
pubkey: bytesToHex2(tlv[0][0]), |
|
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [] |
|
} |
|
}; |
|
} |
|
case "nevent": { |
|
let tlv = parseTLV(data); |
|
if (!tlv[0]?.[0]) |
|
throw new Error("missing TLV 0 for nevent"); |
|
if (tlv[0][0].length !== 32) |
|
throw new Error("TLV 0 should be 32 bytes"); |
|
if (tlv[2] && tlv[2][0].length !== 32) |
|
throw new Error("TLV 2 should be 32 bytes"); |
|
if (tlv[3] && tlv[3][0].length !== 4) |
|
throw new Error("TLV 3 should be 4 bytes"); |
|
return { |
|
type: "nevent", |
|
data: { |
|
id: bytesToHex2(tlv[0][0]), |
|
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [], |
|
author: tlv[2]?.[0] ? bytesToHex2(tlv[2][0]) : void 0, |
|
kind: tlv[3]?.[0] ? parseInt(bytesToHex2(tlv[3][0]), 16) : void 0 |
|
} |
|
}; |
|
} |
|
case "naddr": { |
|
let tlv = parseTLV(data); |
|
if (!tlv[0]?.[0]) |
|
throw new Error("missing TLV 0 for naddr"); |
|
if (!tlv[2]?.[0]) |
|
throw new Error("missing TLV 2 for naddr"); |
|
if (tlv[2][0].length !== 32) |
|
throw new Error("TLV 2 should be 32 bytes"); |
|
if (!tlv[3]?.[0]) |
|
throw new Error("missing TLV 3 for naddr"); |
|
if (tlv[3][0].length !== 4) |
|
throw new Error("TLV 3 should be 4 bytes"); |
|
return { |
|
type: "naddr", |
|
data: { |
|
identifier: utf8Decoder.decode(tlv[0][0]), |
|
pubkey: bytesToHex2(tlv[2][0]), |
|
kind: parseInt(bytesToHex2(tlv[3][0]), 16), |
|
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [] |
|
} |
|
}; |
|
} |
|
case "nsec": |
|
return { type: prefix, data }; |
|
case "npub": |
|
case "note": |
|
return { type: prefix, data: bytesToHex2(data) }; |
|
default: |
|
throw new Error(`unknown prefix ${prefix}`); |
|
} |
|
} |
|
function parseTLV(data) { |
|
let result = {}; |
|
let rest = data; |
|
while (rest.length > 0) { |
|
let t = rest[0]; |
|
let l = rest[1]; |
|
let v = rest.slice(2, 2 + l); |
|
rest = rest.slice(2 + l); |
|
if (v.length < l) |
|
throw new Error(`not enough data to read on TLV ${t}`); |
|
result[t] = result[t] || []; |
|
result[t].push(v); |
|
} |
|
return result; |
|
} |
|
|
|
// nip21.ts |
|
var NOSTR_URI_REGEX = new RegExp(`nostr:(${BECH32_REGEX.source})`); |
|
function test(value) { |
|
return typeof value === "string" && new RegExp(`^${NOSTR_URI_REGEX.source}$`).test(value); |
|
} |
|
function parse(uri) { |
|
const match = uri.match(new RegExp(`^${NOSTR_URI_REGEX.source}$`)); |
|
if (!match) |
|
throw new Error(`Invalid Nostr URI: ${uri}`); |
|
return { |
|
uri: match[0], |
|
value: match[1], |
|
decoded: decode(match[1]) |
|
}; |
|
} |
|
export { |
|
NOSTR_URI_REGEX, |
|
parse, |
|
test |
|
};
|
|
|