mirror of
https://github.com/nbd-wtf/nostr-tools.git
synced 2026-07-22 07:48:27 +00:00
replace ad-hoc regexes with isHex32().
This commit is contained in:
4
core.ts
4
core.ts
@@ -1,3 +1,5 @@
|
||||
import { isHex32 } from './utils.ts'
|
||||
|
||||
export interface Nostr {
|
||||
generateSecretKey(): Uint8Array
|
||||
getPublicKey(secretKey: Uint8Array): string
|
||||
@@ -36,7 +38,7 @@ export function validateEvent<T>(event: T): event is T & UnsignedEvent {
|
||||
if (typeof event.content !== 'string') return false
|
||||
if (typeof event.created_at !== 'number') return false
|
||||
if (typeof event.pubkey !== 'string') return false
|
||||
if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false
|
||||
if (!isHex32(event.pubkey)) return false
|
||||
|
||||
if (!Array.isArray(event.tags)) return false
|
||||
for (let i = 0; i < event.tags.length; i++) {
|
||||
|
||||
11
nip10.ts
11
nip10.ts
@@ -1,7 +1,6 @@
|
||||
import type { Event } from './core.ts'
|
||||
import type { EventPointer, ProfilePointer } from './nip19.ts'
|
||||
|
||||
const HEX64 = /^[0-9a-fA-F]{64}$/
|
||||
import { isHex32 } from './utils.ts'
|
||||
|
||||
export function parse(event: Pick<Event, 'tags'>): {
|
||||
/**
|
||||
@@ -43,7 +42,7 @@ export function parse(event: Pick<Event, 'tags'>): {
|
||||
for (let i = event.tags.length - 1; i >= 0; i--) {
|
||||
const tag = event.tags[i]
|
||||
|
||||
if (tag[0] === 'e' && tag[1] && HEX64.test(tag[1])) {
|
||||
if (tag[0] === 'e' && tag[1] && isHex32(tag[1])) {
|
||||
const [_, eTagEventId, eTagRelayUrl, eTagMarker, eTagAuthor] = tag as [
|
||||
string,
|
||||
string,
|
||||
@@ -55,7 +54,7 @@ export function parse(event: Pick<Event, 'tags'>): {
|
||||
const eventPointer: EventPointer = {
|
||||
id: eTagEventId,
|
||||
relays: eTagRelayUrl ? [eTagRelayUrl] : [],
|
||||
author: eTagAuthor && HEX64.test(eTagAuthor) ? eTagAuthor : undefined,
|
||||
author: eTagAuthor && isHex32(eTagAuthor) ? eTagAuthor : undefined,
|
||||
}
|
||||
|
||||
if (eTagMarker === 'root') {
|
||||
@@ -83,7 +82,7 @@ export function parse(event: Pick<Event, 'tags'>): {
|
||||
continue
|
||||
}
|
||||
|
||||
if (tag[0] === 'q' && tag[1] && HEX64.test(tag[1])) {
|
||||
if (tag[0] === 'q' && tag[1] && isHex32(tag[1])) {
|
||||
const [_, eTagEventId, eTagRelayUrl] = tag as [string, string, undefined | string]
|
||||
result.quotes.push({
|
||||
id: eTagEventId,
|
||||
@@ -91,7 +90,7 @@ export function parse(event: Pick<Event, 'tags'>): {
|
||||
})
|
||||
}
|
||||
|
||||
if (tag[0] === 'p' && tag[1] && HEX64.test(tag[1])) {
|
||||
if (tag[0] === 'p' && tag[1] && isHex32(tag[1])) {
|
||||
result.profiles.push({
|
||||
pubkey: tag[1],
|
||||
relays: tag[2] ? [tag[2]] : [],
|
||||
|
||||
15
nip22.ts
15
nip22.ts
@@ -1,7 +1,6 @@
|
||||
import type { Event } from './core.ts'
|
||||
import type { AddressPointer, EventPointer, ProfilePointer } from './nip19.ts'
|
||||
|
||||
const HEX64 = /^[0-9a-fA-F]{64}$/
|
||||
import { isHex32 } from './utils.ts'
|
||||
|
||||
export type ExternalPointer = {
|
||||
value: string
|
||||
@@ -22,7 +21,7 @@ function parseAddressPointer(value: string, relayUrl?: string): AddressPointer |
|
||||
if (Number.isNaN(kind)) return undefined
|
||||
|
||||
const pubkey = value.slice(idx + 1, idx2)
|
||||
if (!HEX64.test(pubkey)) return undefined
|
||||
if (!isHex32(pubkey)) return undefined
|
||||
|
||||
return {
|
||||
kind,
|
||||
@@ -36,11 +35,11 @@ function parsePointer(tag: string[]): EventPointer | AddressPointer | ExternalPo
|
||||
switch (tag[0]) {
|
||||
case 'E':
|
||||
case 'e':
|
||||
if (!tag[1] || !HEX64.test(tag[1])) return undefined
|
||||
if (!tag[1] || !isHex32(tag[1])) return undefined
|
||||
return {
|
||||
id: tag[1],
|
||||
relays: tag[2] ? [tag[2]] : [],
|
||||
author: tag[3] && HEX64.test(tag[3]) ? tag[3] : undefined,
|
||||
author: tag[3] && isHex32(tag[3]) ? tag[3] : undefined,
|
||||
}
|
||||
case 'A':
|
||||
case 'a':
|
||||
@@ -63,12 +62,12 @@ function parseQuote(tag: string[]): EventPointer | AddressPointer | ExternalPoin
|
||||
return parseAddressPointer(tag[1], tag[2])
|
||||
}
|
||||
|
||||
if (!HEX64.test(tag[1])) return undefined
|
||||
if (!isHex32(tag[1])) return undefined
|
||||
|
||||
return {
|
||||
id: tag[1],
|
||||
relays: tag[2] ? [tag[2]] : [],
|
||||
author: tag[3] && HEX64.test(tag[3]) ? tag[3] : undefined,
|
||||
author: tag[3] && isHex32(tag[3]) ? tag[3] : undefined,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +178,7 @@ export function parse(event: Pick<Event, 'tags'>): {
|
||||
continue
|
||||
}
|
||||
|
||||
if ((tag[0] === 'P' || tag[0] === 'p') && tag[1] && HEX64.test(tag[1])) {
|
||||
if ((tag[0] === 'P' || tag[0] === 'p') && tag[1] && isHex32(tag[1])) {
|
||||
result.profiles.push({
|
||||
pubkey: tag[1],
|
||||
relays: tag[2] ? [tag[2]] : [],
|
||||
|
||||
13
nip45.ts
13
nip45.ts
@@ -3,6 +3,7 @@ import { bytesToHex } from '@noble/hashes/utils.js'
|
||||
|
||||
import type { Event } from './core.ts'
|
||||
import type { Filter } from './filter.ts'
|
||||
import { isHex32 } from './utils.ts'
|
||||
|
||||
const M = 256
|
||||
const HLL_HEX_LENGTH = M * 2
|
||||
@@ -32,7 +33,7 @@ export function newHll(): Uint8Array {
|
||||
}
|
||||
|
||||
export function hllDecode(hex: string): Uint8Array | undefined {
|
||||
if (hex.length !== HLL_HEX_LENGTH || !/^[0-9a-fA-F]+$/.test(hex)) return undefined
|
||||
if (hex.length !== HLL_HEX_LENGTH || !/^[0-9a-f]+$/.test(hex)) return undefined
|
||||
|
||||
const registers = new Uint8Array(M)
|
||||
for (let i = 0; i < M; i++) {
|
||||
@@ -54,9 +55,9 @@ export function hllEncode(registers: Uint8Array): string {
|
||||
export function computeOffset(filterFirstTagValue: string): number {
|
||||
let hex = filterFirstTagValue
|
||||
|
||||
if (!isHex64(hex)) {
|
||||
if (!isHex32(hex)) {
|
||||
const parts = hex.split(':')
|
||||
if (parts.length === 3 && isHex64(parts[1])) {
|
||||
if (parts.length === 3 && isHex32(parts[1])) {
|
||||
hex = parts[1]
|
||||
} else {
|
||||
hex = bytesToHex(sha256(utf8Encoder.encode(filterFirstTagValue)))
|
||||
@@ -79,7 +80,7 @@ export function getFilterFirstTagValue(filter: Filter): string | undefined {
|
||||
|
||||
export function feedPubkey(hll: Uint8Array, pubkey: string, offset: number): Uint8Array {
|
||||
if (offset < 0 || offset > 24) throw new Error(`invalid offset ${offset}`)
|
||||
if (!isHex64(pubkey)) throw new Error('pubkey must be 32-byte hex')
|
||||
if (!isHex32(pubkey)) throw new Error('pubkey must be 32-byte hex')
|
||||
|
||||
if (hll.length === 0) hll = newHll()
|
||||
if (hll.length !== M) throw new Error(`invalid number of registers ${hll.length}`)
|
||||
@@ -123,10 +124,6 @@ export function estimateCount(hll: Uint8Array): number {
|
||||
return Math.floor(estimate)
|
||||
}
|
||||
|
||||
function isHex64(value: string): boolean {
|
||||
return /^[0-9a-fA-F]{64}$/.test(value)
|
||||
}
|
||||
|
||||
function countLeadingZeroBitsAfterOffset(pubkey: string, offset: number): number {
|
||||
let zeroBits = 0
|
||||
for (let i = offset + 1; i < offset + 8; i++) {
|
||||
|
||||
6
nip57.ts
6
nip57.ts
@@ -1,7 +1,7 @@
|
||||
import { bech32 } from '@scure/base'
|
||||
|
||||
import { NostrEvent, validateEvent, verifyEvent, type Event, type EventTemplate } from './pure.ts'
|
||||
import { utf8Decoder } from './utils.ts'
|
||||
import { isHex32, utf8Decoder } from './utils.ts'
|
||||
import { isReplaceableKind, isAddressableKind } from './kinds.ts'
|
||||
|
||||
var _fetch: any
|
||||
@@ -100,10 +100,10 @@ export function validateZapRequest(zapRequestString: string): string | null {
|
||||
|
||||
let p = zapRequest.tags.find(([t, v]) => t === 'p' && v)
|
||||
if (!p) return "Zap request doesn't have a 'p' tag."
|
||||
if (!p[1].match(/^[a-f0-9]{64}$/)) return "Zap request 'p' tag is not valid hex."
|
||||
if (!isHex32(p[1])) return "Zap request 'p' tag is not valid hex."
|
||||
|
||||
let e = zapRequest.tags.find(([t, v]) => t === 'e' && v)
|
||||
if (e && !e[1].match(/^[a-f0-9]{64}$/)) return "Zap request 'e' tag is not valid hex."
|
||||
if (e && !isHex32(e[1])) return "Zap request 'e' tag is not valid hex."
|
||||
|
||||
let relays = zapRequest.tags.find(([t, v]) => t === 'relays' && v)
|
||||
if (!relays) return "Zap request doesn't have a 'relays' tag."
|
||||
|
||||
11
nipb0.ts
11
nipb0.ts
@@ -3,6 +3,7 @@ import { bytesToHex } from '@noble/hashes/utils.js'
|
||||
import type { EventTemplate } from './core.ts'
|
||||
import type { Signer } from './signer.ts'
|
||||
import { kinds } from './index.ts'
|
||||
import { isHex32 } from './utils.ts'
|
||||
|
||||
export type BlobDescriptor = {
|
||||
url: string
|
||||
@@ -27,10 +28,6 @@ export type AuthEventOptions = {
|
||||
expiration?: number
|
||||
}
|
||||
|
||||
export function isSha256(str: string): boolean {
|
||||
return /^[0-9a-f]{64}$/i.test(str)
|
||||
}
|
||||
|
||||
export function getBlobSize(blob: UploadType): number {
|
||||
if ((typeof File !== 'undefined' && blob instanceof File) || blob instanceof Blob) {
|
||||
return blob.size
|
||||
@@ -213,7 +210,7 @@ export function parseBlossomURI(uri: string): BlossomURI {
|
||||
if (dotIndex === -1) throw new Error('Invalid blossom URI: missing file extension')
|
||||
const sha256 = path.slice(0, dotIndex)
|
||||
const ext = path.slice(dotIndex + 1)
|
||||
if (!isSha256(sha256)) throw new Error('Invalid blossom URI: invalid sha256 hash')
|
||||
if (!isHex32(sha256)) throw new Error('Invalid blossom URI: invalid sha256 hash')
|
||||
if (!ext) throw new Error('Invalid blossom URI: empty file extension')
|
||||
const params = new URLSearchParams(query)
|
||||
const servers = params.getAll('xs')
|
||||
@@ -250,7 +247,7 @@ export function blossomURIFromURL(url: URL): BlossomURI {
|
||||
if (dotIndex === -1) throw new Error('Invalid blossom URL: missing file extension')
|
||||
const sha256 = path.slice(0, dotIndex)
|
||||
const ext = path.slice(dotIndex + 1)
|
||||
if (!isSha256(sha256)) throw new Error('Invalid blossom URL: invalid sha256 hash')
|
||||
if (!isHex32(sha256)) throw new Error('Invalid blossom URL: invalid sha256 hash')
|
||||
if (!ext) throw new Error('Invalid blossom URL: empty file extension')
|
||||
const servers = url.searchParams.getAll('xs')
|
||||
const authors = url.searchParams.getAll('as')
|
||||
@@ -743,7 +740,7 @@ export class BlossomClient {
|
||||
}
|
||||
|
||||
async check(hash: string): Promise<void> {
|
||||
if (!isSha256(hash)) throw new Error(`${hash} is not valid 32-byte hex`)
|
||||
if (!isHex32(hash)) throw new Error(`${hash} is not valid 32-byte hex`)
|
||||
await this.httpCall('HEAD', hash)
|
||||
}
|
||||
|
||||
|
||||
14
nipb7.ts
14
nipb7.ts
@@ -1,7 +1,7 @@
|
||||
import { sha256 } from '@noble/hashes/sha2.js'
|
||||
import { EventTemplate } from './core.ts'
|
||||
import { Signer } from './signer.ts'
|
||||
import { bytesToHex } from './utils.ts'
|
||||
import { bytesToHex, isHex32 } from './utils.ts'
|
||||
|
||||
export type BlobDescriptor = {
|
||||
url: string
|
||||
@@ -84,12 +84,8 @@ export class BlossomClient {
|
||||
}
|
||||
}
|
||||
|
||||
private isValid32ByteHex(hash: string): boolean {
|
||||
return /^[a-f0-9]{64}$/i.test(hash)
|
||||
}
|
||||
|
||||
async check(hash: string): Promise<void> {
|
||||
if (!this.isValid32ByteHex(hash)) {
|
||||
if (!isHex32(hash)) {
|
||||
throw new Error(`${hash} is not a valid 32-byte hex string`)
|
||||
}
|
||||
|
||||
@@ -125,7 +121,7 @@ export class BlossomClient {
|
||||
}
|
||||
|
||||
async download(hash: string): Promise<ArrayBuffer> {
|
||||
if (!this.isValid32ByteHex(hash)) {
|
||||
if (!isHex32(hash)) {
|
||||
throw new Error(`${hash} is not a valid 32-byte hex string`)
|
||||
}
|
||||
|
||||
@@ -156,7 +152,7 @@ export class BlossomClient {
|
||||
async list(): Promise<BlobDescriptor[]> {
|
||||
const pubkey = await this.signer.getPublicKey()
|
||||
|
||||
if (!this.isValid32ByteHex(pubkey)) {
|
||||
if (!isHex32(pubkey)) {
|
||||
throw new Error(`pubkey ${pubkey} is not valid`)
|
||||
}
|
||||
|
||||
@@ -179,7 +175,7 @@ export class BlossomClient {
|
||||
}
|
||||
|
||||
async delete(hash: string): Promise<void> {
|
||||
if (!this.isValid32ByteHex(hash)) {
|
||||
if (!isHex32(hash)) {
|
||||
throw new Error(`${hash} is not a valid 32-byte hex string`)
|
||||
}
|
||||
|
||||
|
||||
13
utils.ts
13
utils.ts
@@ -123,3 +123,16 @@ export function mergeReverseSortedLists(list1: NostrEvent[], list2: NostrEvent[]
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string is a 64-char lowercase hex string as most Nostr ids and pubkeys.
|
||||
*/
|
||||
export function isHex32(input: string): boolean {
|
||||
for (let i = 0; i < 64; i++) {
|
||||
let cc = input.charCodeAt(i)
|
||||
if (isNaN(cc) || cc < 48 || cc > 102 || (cc > 57 && cc < 97)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user