From 3ad2e6b7a40ffa36ac66bed491c43efeace56646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Conselheiro?= Date: Thu, 30 May 2024 17:13:43 +0000 Subject: [PATCH 01/13] including nostr types --- core.ts | 33 +++++++++++++++++++++++++++++++++ nip19.ts | 16 +++++++++------- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/core.ts b/core.ts index a92b824..bf5b77b 100644 --- a/core.ts +++ b/core.ts @@ -23,6 +23,39 @@ export type NostrEvent = Event export type EventTemplate = Pick export type UnsignedEvent = Pick +export type NProfile = `nprofile${string}` +export type NRelay = `nrelay${string}` +export type NEvent = `nevent${string}` +export type NAddress = `naddr${string}` +export type NSecret = `nsec${string}` +export type NPublic = `npub${string}` +export type Note = `note${string}` +export type Nip05 = `${string}@${string}` + +export const NostrTypeGuard = { + isNProfile: (value: unknown): value is NProfile => { + return typeof value === 'string' && /^nprofile1[a-z\d]{58}$/.test(value) + }, + isNRelay: (value: unknown): value is NRelay => { + return typeof value === 'string' && /^nrelay1[a-z\d]{58}$/.test(value) + }, + isNEvent: (value: unknown): value is NEvent => { + return typeof value === 'string' && /^nevent1[a-z\d]{58}$/.test(value) + }, + isNAddress: (value: unknown): value is NAddress => { + return typeof value === 'string' && /^naddr1[a-z\d]{58}$/.test(value) + }, + isNSecret: (value: unknown): value is NSecret => { + return typeof value === 'string' && /^nsec1[a-z\d]{58}$/.test(value) + }, + isNPublic: (value: unknown): value is NPublic => { + return typeof value === 'string' && /^npub1[a-z\d]{58}$/.test(value) + }, + isNote: (value: unknown): value is Note => { + return typeof value === 'string' && /^note1[a-z\d]{58}$/.test(value) + } +} + /** An event whose signature has been verified. */ export interface VerifiedEvent extends Event { [verifiedSymbol]: true diff --git a/nip19.ts b/nip19.ts index 8ad7c46..173f99b 100644 --- a/nip19.ts +++ b/nip19.ts @@ -2,6 +2,7 @@ import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils' import { bech32 } from '@scure/base' import { utf8Decoder, utf8Encoder } from './utils.ts' +import { NAddress, NEvent, Note, NProfile, NPublic, NRelay, NSecret } from './core.ts' export const Bech32MaxSize = 5000 @@ -53,6 +54,7 @@ type Prefixes = { note: string } + type DecodeValue = { type: Prefix data: Prefixes[Prefix] @@ -158,15 +160,15 @@ function parseTLV(data: Uint8Array): TLV { return result } -export function nsecEncode(key: Uint8Array): `nsec1${string}` { +export function nsecEncode(key: Uint8Array): NSecret { return encodeBytes('nsec', key) } -export function npubEncode(hex: string): `npub1${string}` { +export function npubEncode(hex: string): NPublic { return encodeBytes('npub', hexToBytes(hex)) } -export function noteEncode(hex: string): `note1${string}` { +export function noteEncode(hex: string): Note { return encodeBytes('note', hexToBytes(hex)) } @@ -179,7 +181,7 @@ export function encodeBytes(prefix: Prefix, bytes: Uint8A return encodeBech32(prefix, bytes) } -export function nprofileEncode(profile: ProfilePointer): `nprofile1${string}` { +export function nprofileEncode(profile: ProfilePointer): NProfile { let data = encodeTLV({ 0: [hexToBytes(profile.pubkey)], 1: (profile.relays || []).map(url => utf8Encoder.encode(url)), @@ -187,7 +189,7 @@ export function nprofileEncode(profile: ProfilePointer): `nprofile1${string}` { return encodeBech32('nprofile', data) } -export function neventEncode(event: EventPointer): `nevent1${string}` { +export function neventEncode(event: EventPointer): NEvent { let kindArray if (event.kind !== undefined) { kindArray = integerToUint8Array(event.kind) @@ -203,7 +205,7 @@ export function neventEncode(event: EventPointer): `nevent1${string}` { return encodeBech32('nevent', data) } -export function naddrEncode(addr: AddressPointer): `naddr1${string}` { +export function naddrEncode(addr: AddressPointer): NAddress { let kind = new ArrayBuffer(4) new DataView(kind).setUint32(0, addr.kind, false) @@ -216,7 +218,7 @@ export function naddrEncode(addr: AddressPointer): `naddr1${string}` { return encodeBech32('naddr', data) } -export function nrelayEncode(url: string): `nrelay1${string}` { +export function nrelayEncode(url: string): NRelay { let data = encodeTLV({ 0: [utf8Encoder.encode(url)], }) From 177f4b299117b7a5da45db1f20dd0b92062cd23d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Conselheiro?= Date: Thu, 30 May 2024 17:57:30 +0000 Subject: [PATCH 02/13] including tests for nostr type guard --- core.test.ts | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++- core.ts | 28 ++++---------- 2 files changed, 110 insertions(+), 23 deletions(-) diff --git a/core.test.ts b/core.test.ts index 7271f80..bf6e33b 100644 --- a/core.test.ts +++ b/core.test.ts @@ -1,6 +1,5 @@ import { test, expect } from 'bun:test' - -import { sortEvents } from './core.ts' +import { NostrTypeGuard, sortEvents } from './core.ts' test('sortEvents', () => { const events = [ @@ -17,3 +16,105 @@ test('sortEvents', () => { { id: 'abc123', pubkey: 'key1', created_at: 1610000000, kind: 1, tags: [], content: 'Hello', sig: 'sig1' }, ]) }) + +test('NostrTypeGuard isNProfile', () => { + const is = NostrTypeGuard.isNProfile('nprofile1qqsvc6ulagpn7kwrcwdqgp797xl7usumqa6s3kgcelwq6m75x8fe8yc5usxdg') + + expect(is).toBeTrue() +}) + +test('NostrTypeGuard isNProfile invalid nprofile', () => { + const is = NostrTypeGuard.isNProfile('nprofile1qqsvc6ulagpn7kwrcwdqgp797xl7usumqa6s3kgcelwq6m75x8fe8yc5usxãg') + + expect(is).toBeFalse() +}) + +test('NostrTypeGuard isNProfile with invalid nprofile', () => { + const is = NostrTypeGuard.isNProfile('nsec1lqw6zqyanj9mz8gwhdam6tqge42vptz4zg93qsfej440xm5h5esqya0juv') + + expect(is).toBeFalse() +}) + +test('NostrTypeGuard isNRelay', () => { + const is = NostrTypeGuard.isNRelay('nrelay1qqt8wumn8ghj7un9d3shjtnwdaehgu3wvfskueq4r295t') + + expect(is).toBeTrue() +}) + +test('NostrTypeGuard isNRelay with invalid nrelay', () => { + const is = NostrTypeGuard.isNRelay('nrelay1qqt8wumn8ghj7un9d3shjtnwdaehgu3wvfskueã4r295t') + + expect(is).toBeFalse() +}) + +test('NostrTypeGuard isNRelay with invalid nrelay', () => { + const is = NostrTypeGuard.isNRelay('nevent1qqst8cujky046negxgwwm5ynqwn53t8aqjr6afd8g59nfqwxpdhylpcpzamhxue69uhhyetvv9ujuetcv9khqmr99e3k7mg8arnc9') + + expect(is).toBeFalse() +}) + +test('NostrTypeGuard isNEvent', () => { + const is = NostrTypeGuard.isNEvent('nevent1qqst8cujky046negxgwwm5ynqwn53t8aqjr6afd8g59nfqwxpdhylpcpzamhxue69uhhyetvv9ujuetcv9khqmr99e3k7mg8arnc9') + + expect(is).toBeTrue() +}) + +test('NostrTypeGuard isNEvent with invalid nevent', () => { + const is = NostrTypeGuard.isNEvent('nevent1qqst8cujky046negxgwwm5ynqwn53t8aqjr6afd8g59nfqwxpdhylpcpzamhxue69uhhyetvv9ujuetcv9khqmr99e3k7mg8ãrnc9') + + expect(is).toBeFalse() +}) + +test('NostrTypeGuard isNEvent with invalid nevent', () => { + const is = NostrTypeGuard.isNEvent('nprofile1qqsvc6ulagpn7kwrcwdqgp797xl7usumqa6s3kgcelwq6m75x8fe8yc5usxdg') + + expect(is).toBeFalse() +}) + +test('NostrTypeGuard isNAddress', () => { + const is = NostrTypeGuard.isNAddress('naddr1qqxnzdesxqmnxvpexqunzvpcqyt8wumn8ghj7un9d3shjtnwdaehgu3wvfskueqzypve7elhmamff3sr5mgxxms4a0rppkmhmn7504h96pfcdkpplvl2jqcyqqq823cnmhuld') + + expect(is).toBeTrue() +}) + +test('NostrTypeGuard isNAddress with invalid nadress', () => { + const is = NostrTypeGuard.isNAddress('nsec1lqw6zqyanj9mz8gwhdam6tqge42vptz4zg93qsfej440xm5h5esqya0juv') + + expect(is).toBeFalse() +}) + +test('NostrTypeGuard isNSecret', () => { + const is = NostrTypeGuard.isNSecret('nsec1lqw6zqyanj9mz8gwhdam6tqge42vptz4zg93qsfej440xm5h5esqya0juv') + + expect(is).toBeTrue() +}) + +test('NostrTypeGuard isNSecret with invalid nsec', () => { + const is = NostrTypeGuard.isNSecret('nsec1lqw6zqyanj9mz8gwhdam6tqge42vptz4zg93qsfej440xm5h5esqya0juã') + + expect(is).toBeFalse() +}) + +test('NostrTypeGuard isNSecret with invalid nsec', () => { + const is = NostrTypeGuard.isNSecret('nprofile1qqsvc6ulagpn7kwrcwdqgp797xl7usumqa6s3kgcelwq6m75x8fe8yc5usxdg') + + expect(is).toBeFalse() +}) + +test('NostrTypeGuard isNPublic', () => { + const is = NostrTypeGuard.isNPublic('npub1jz5mdljkmffmqjshpyjgqgrhdkuxd9ztzasv8xeh5q92fv33sjgqy4pats') + + expect(is).toBeTrue() +}) + +test('NostrTypeGuard isNPublic with invalid npub', () => { + const is = NostrTypeGuard.isNPublic('npub1jz5mdljkmffmqjshpyjgqgrhdkuxd9ztzãsv8xeh5q92fv33sjgqy4pats') + + expect(is).toBeFalse() +}) + +test('NostrTypeGuard isNPublic with invalid npub', () => { + const is = NostrTypeGuard.isNPublic('nsec1lqw6zqyanj9mz8gwhdam6tqge42vptz4zg93qsfej440xm5h5esqya0juv') + + expect(is).toBeFalse() +}) diff --git a/core.ts b/core.ts index bf5b77b..804eba7 100644 --- a/core.ts +++ b/core.ts @@ -33,27 +33,13 @@ export type Note = `note${string}` export type Nip05 = `${string}@${string}` export const NostrTypeGuard = { - isNProfile: (value: unknown): value is NProfile => { - return typeof value === 'string' && /^nprofile1[a-z\d]{58}$/.test(value) - }, - isNRelay: (value: unknown): value is NRelay => { - return typeof value === 'string' && /^nrelay1[a-z\d]{58}$/.test(value) - }, - isNEvent: (value: unknown): value is NEvent => { - return typeof value === 'string' && /^nevent1[a-z\d]{58}$/.test(value) - }, - isNAddress: (value: unknown): value is NAddress => { - return typeof value === 'string' && /^naddr1[a-z\d]{58}$/.test(value) - }, - isNSecret: (value: unknown): value is NSecret => { - return typeof value === 'string' && /^nsec1[a-z\d]{58}$/.test(value) - }, - isNPublic: (value: unknown): value is NPublic => { - return typeof value === 'string' && /^npub1[a-z\d]{58}$/.test(value) - }, - isNote: (value: unknown): value is Note => { - return typeof value === 'string' && /^note1[a-z\d]{58}$/.test(value) - } + isNProfile: (value?: string): value is NProfile => /^nprofile1[a-z\d]{58}$/.test(value || ''), + isNRelay: (value?: string): value is NRelay => /^nrelay1[a-z\d]{45}$/.test(value || ''), + isNEvent: (value?: string): value is NEvent => /^nevent1[a-z\d]+$/.test(value || ''), + isNAddress: (value?: string): value is NAddress => /^naddr1[a-z\d]+$/.test(value || ''), + isNSecret: (value?: string): value is NSecret => /^nsec1[a-z\d]{58}$/.test(value || ''), + isNPublic: (value?: string): value is NPublic => /^npub1[a-z\d]{58}$/.test(value || ''), + isNote: (value?: string): value is Note => /^note1[a-z\d]{58}$/.test(value || '') } /** An event whose signature has been verified. */ From f3a19959d2782a0ad1de46809946db7dc913cc27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Conselheiro?= Date: Thu, 30 May 2024 18:02:31 +0000 Subject: [PATCH 03/13] fix tests for nostr type guard --- core.test.ts | 18 ++++++++++++++++++ core.ts | 6 +++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/core.test.ts b/core.test.ts index bf6e33b..2fd8b05 100644 --- a/core.test.ts +++ b/core.test.ts @@ -118,3 +118,21 @@ test('NostrTypeGuard isNPublic with invalid npub', () => { expect(is).toBeFalse() }) + +test('NostrTypeGuard isNote', () => { + const is = NostrTypeGuard.isNote('note1gmtnz6q2m55epmlpe3semjdcq987av3jvx4emmjsa8g3s9x7tg4sclreky') + + expect(is).toBeTrue() +}) + +test('NostrTypeGuard isNote with invalid note', () => { + const is = NostrTypeGuard.isNote('note1gmtnz6q2m55epmlpe3semjdcq987av3jvx4emmjsa8g3s9x7tg4sçlreky') + + expect(is).toBeFalse() +}) + +test('NostrTypeGuard isNote with invalid note', () => { + const is = NostrTypeGuard.isNote('npub1jz5mdljkmffmqjshpyjgqgrhdkuxd9ztzasv8xeh5q92fv33sjgqy4pats') + + expect(is).toBeFalse() +}) diff --git a/core.ts b/core.ts index 804eba7..f93f4c5 100644 --- a/core.ts +++ b/core.ts @@ -33,13 +33,13 @@ export type Note = `note${string}` export type Nip05 = `${string}@${string}` export const NostrTypeGuard = { - isNProfile: (value?: string): value is NProfile => /^nprofile1[a-z\d]{58}$/.test(value || ''), - isNRelay: (value?: string): value is NRelay => /^nrelay1[a-z\d]{45}$/.test(value || ''), + isNProfile: (value?: string): value is NProfile => /^nprofile1[a-z\d]+$/.test(value || ''), + isNRelay: (value?: string): value is NRelay => /^nrelay1[a-z\d]+$/.test(value || ''), isNEvent: (value?: string): value is NEvent => /^nevent1[a-z\d]+$/.test(value || ''), isNAddress: (value?: string): value is NAddress => /^naddr1[a-z\d]+$/.test(value || ''), isNSecret: (value?: string): value is NSecret => /^nsec1[a-z\d]{58}$/.test(value || ''), isNPublic: (value?: string): value is NPublic => /^npub1[a-z\d]{58}$/.test(value || ''), - isNote: (value?: string): value is Note => /^note1[a-z\d]{58}$/.test(value || '') + isNote: (value?: string): value is Note => /^note1[a-z\d]+$/.test(value || '') } /** An event whose signature has been verified. */ From bbea79bab89b96f9425c58d562334057a0d5c6a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Conselheiro?= Date: Thu, 30 May 2024 18:09:02 +0000 Subject: [PATCH 04/13] fix linter and add eslint and prettier to devcontainer --- .devcontainer/Dockerfile | 2 +- core.test.ts | 16 ++++++++++++---- core.ts | 2 +- nip19.ts | 1 - 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index dbcc2ac..3386a5c 100755 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,6 +1,6 @@ FROM node:20 -RUN npm install typescript -g +RUN npm install typescript eslint prettier -g # Install bun RUN curl -fsSL https://bun.sh/install | bash diff --git a/core.test.ts b/core.test.ts index 2fd8b05..9834086 100644 --- a/core.test.ts +++ b/core.test.ts @@ -48,19 +48,25 @@ test('NostrTypeGuard isNRelay with invalid nrelay', () => { }) test('NostrTypeGuard isNRelay with invalid nrelay', () => { - const is = NostrTypeGuard.isNRelay('nevent1qqst8cujky046negxgwwm5ynqwn53t8aqjr6afd8g59nfqwxpdhylpcpzamhxue69uhhyetvv9ujuetcv9khqmr99e3k7mg8arnc9') + const is = NostrTypeGuard.isNRelay( + 'nevent1qqst8cujky046negxgwwm5ynqwn53t8aqjr6afd8g59nfqwxpdhylpcpzamhxue69uhhyetvv9ujuetcv9khqmr99e3k7mg8arnc9', + ) expect(is).toBeFalse() }) test('NostrTypeGuard isNEvent', () => { - const is = NostrTypeGuard.isNEvent('nevent1qqst8cujky046negxgwwm5ynqwn53t8aqjr6afd8g59nfqwxpdhylpcpzamhxue69uhhyetvv9ujuetcv9khqmr99e3k7mg8arnc9') + const is = NostrTypeGuard.isNEvent( + 'nevent1qqst8cujky046negxgwwm5ynqwn53t8aqjr6afd8g59nfqwxpdhylpcpzamhxue69uhhyetvv9ujuetcv9khqmr99e3k7mg8arnc9', + ) expect(is).toBeTrue() }) test('NostrTypeGuard isNEvent with invalid nevent', () => { - const is = NostrTypeGuard.isNEvent('nevent1qqst8cujky046negxgwwm5ynqwn53t8aqjr6afd8g59nfqwxpdhylpcpzamhxue69uhhyetvv9ujuetcv9khqmr99e3k7mg8ãrnc9') + const is = NostrTypeGuard.isNEvent( + 'nevent1qqst8cujky046negxgwwm5ynqwn53t8aqjr6afd8g59nfqwxpdhylpcpzamhxue69uhhyetvv9ujuetcv9khqmr99e3k7mg8ãrnc9', + ) expect(is).toBeFalse() }) @@ -72,7 +78,9 @@ test('NostrTypeGuard isNEvent with invalid nevent', () => { }) test('NostrTypeGuard isNAddress', () => { - const is = NostrTypeGuard.isNAddress('naddr1qqxnzdesxqmnxvpexqunzvpcqyt8wumn8ghj7un9d3shjtnwdaehgu3wvfskueqzypve7elhmamff3sr5mgxxms4a0rppkmhmn7504h96pfcdkpplvl2jqcyqqq823cnmhuld') + const is = NostrTypeGuard.isNAddress( + 'naddr1qqxnzdesxqmnxvpexqunzvpcqyt8wumn8ghj7un9d3shjtnwdaehgu3wvfskueqzypve7elhmamff3sr5mgxxms4a0rppkmhmn7504h96pfcdkpplvl2jqcyqqq823cnmhuld', + ) expect(is).toBeTrue() }) diff --git a/core.ts b/core.ts index f93f4c5..8556f1e 100644 --- a/core.ts +++ b/core.ts @@ -39,7 +39,7 @@ export const NostrTypeGuard = { isNAddress: (value?: string): value is NAddress => /^naddr1[a-z\d]+$/.test(value || ''), isNSecret: (value?: string): value is NSecret => /^nsec1[a-z\d]{58}$/.test(value || ''), isNPublic: (value?: string): value is NPublic => /^npub1[a-z\d]{58}$/.test(value || ''), - isNote: (value?: string): value is Note => /^note1[a-z\d]+$/.test(value || '') + isNote: (value?: string): value is Note => /^note1[a-z\d]+$/.test(value || ''), } /** An event whose signature has been verified. */ diff --git a/nip19.ts b/nip19.ts index 173f99b..045bdd0 100644 --- a/nip19.ts +++ b/nip19.ts @@ -54,7 +54,6 @@ type Prefixes = { note: string } - type DecodeValue = { type: Prefix data: Prefixes[Prefix] From 442d7ea95f29d855353673c52d47b112ee3856a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Conselheiro?= Date: Thu, 30 May 2024 18:34:50 +0000 Subject: [PATCH 05/13] including null in nostr type guard signature --- core.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core.ts b/core.ts index 8556f1e..b272c78 100644 --- a/core.ts +++ b/core.ts @@ -33,13 +33,13 @@ export type Note = `note${string}` export type Nip05 = `${string}@${string}` export const NostrTypeGuard = { - isNProfile: (value?: string): value is NProfile => /^nprofile1[a-z\d]+$/.test(value || ''), - isNRelay: (value?: string): value is NRelay => /^nrelay1[a-z\d]+$/.test(value || ''), - isNEvent: (value?: string): value is NEvent => /^nevent1[a-z\d]+$/.test(value || ''), - isNAddress: (value?: string): value is NAddress => /^naddr1[a-z\d]+$/.test(value || ''), - isNSecret: (value?: string): value is NSecret => /^nsec1[a-z\d]{58}$/.test(value || ''), - isNPublic: (value?: string): value is NPublic => /^npub1[a-z\d]{58}$/.test(value || ''), - isNote: (value?: string): value is Note => /^note1[a-z\d]+$/.test(value || ''), + isNProfile: (value?: string | null): value is NProfile => /^nprofile1[a-z\d]+$/.test(value || ''), + isNRelay: (value?: string | null): value is NRelay => /^nrelay1[a-z\d]+$/.test(value || ''), + isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\d]+$/.test(value || ''), + isNAddress: (value?: string | null): value is NAddress => /^naddr1[a-z\d]+$/.test(value || ''), + isNSecret: (value?: string | null): value is NSecret => /^nsec1[a-z\d]{58}$/.test(value || ''), + isNPublic: (value?: string | null): value is NPublic => /^npub1[a-z\d]{58}$/.test(value || ''), + isNote: (value?: string | null): value is Note => /^note1[a-z\d]+$/.test(value || ''), } /** An event whose signature has been verified. */ From cc16230b93d89b822b3719cd9726418cf0517ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Conselheiro?= Date: Thu, 30 May 2024 18:46:47 +0000 Subject: [PATCH 06/13] fix type, ops --- core.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core.ts b/core.ts index b272c78..6fe6681 100644 --- a/core.ts +++ b/core.ts @@ -23,13 +23,13 @@ export type NostrEvent = Event export type EventTemplate = Pick export type UnsignedEvent = Pick -export type NProfile = `nprofile${string}` -export type NRelay = `nrelay${string}` -export type NEvent = `nevent${string}` -export type NAddress = `naddr${string}` -export type NSecret = `nsec${string}` -export type NPublic = `npub${string}` -export type Note = `note${string}` +export type NProfile = `nprofile1${string}` +export type NRelay = `nrelay1${string}` +export type NEvent = `nevent1${string}` +export type NAddress = `naddr1${string}` +export type NSecret = `nsec1${string}` +export type NPublic = `npub1${string}` +export type Note = `note1${string}` export type Nip05 = `${string}@${string}` export const NostrTypeGuard = { From d87fe15a7f98dfcb5cc5e56a41b2e513020b3cf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Conselheiro?= Date: Thu, 30 May 2024 19:10:22 +0000 Subject: [PATCH 07/13] including ncryptsec in nostr type guard --- core.test.ts | 18 ++++++++++++++++++ core.ts | 2 ++ 2 files changed, 20 insertions(+) diff --git a/core.test.ts b/core.test.ts index 9834086..83b2edf 100644 --- a/core.test.ts +++ b/core.test.ts @@ -144,3 +144,21 @@ test('NostrTypeGuard isNote with invalid note', () => { expect(is).toBeFalse() }) + +test('NostrTypeGuard isNcryptsec', () => { + const is = NostrTypeGuard.isNcryptsec('ncryptsec1qgg9947rlpvqu76pj5ecreduf9jxhselq2nae2kghhvd5g7dgjtcxfqtd67p9m0w57lspw8gsq6yphnm8623nsl8xn9j4jdzz84zm3frztj3z7s35vpzmqf6ksu8r89qk5z2zxfmu5gv8th8wclt0h4p') + + expect(is).toBeTrue() +}) + +test('NostrTypeGuard isNcryptsec with invalid ncrytpsec', () => { + const is = NostrTypeGuard.isNcryptsec('ncryptsec1qgg9947rlpvqu76pj5ecreduf9jxhselq2nae2kghhvd5g7dgjtcxfqtd67p9m0w57lspw8gsq6yphnm8623nsã8xn9j4jdzz84zm3frztj3z7s35vpzmqf6ksu8r89qk5z2zxfmu5gv8th8wclt0h4p') + + expect(is).toBeFalse() +}) + +test('NostrTypeGuard isNcryptsec with invalid ncrytpsec', () => { + const is = NostrTypeGuard.isNcryptsec('note1gmtnz6q2m55epmlpe3semjdcq987av3jvx4emmjsa8g3s9x7tg4sçlreky') + + expect(is).toBeFalse() +}) diff --git a/core.ts b/core.ts index 6fe6681..9fdc285 100644 --- a/core.ts +++ b/core.ts @@ -30,6 +30,7 @@ export type NAddress = `naddr1${string}` export type NSecret = `nsec1${string}` export type NPublic = `npub1${string}` export type Note = `note1${string}` +export type Ncryptsec = `ncryptsec1${string}`; export type Nip05 = `${string}@${string}` export const NostrTypeGuard = { @@ -40,6 +41,7 @@ export const NostrTypeGuard = { isNSecret: (value?: string | null): value is NSecret => /^nsec1[a-z\d]{58}$/.test(value || ''), isNPublic: (value?: string | null): value is NPublic => /^npub1[a-z\d]{58}$/.test(value || ''), isNote: (value?: string | null): value is Note => /^note1[a-z\d]+$/.test(value || ''), + isNcryptsec: (value?: string | null): value is Note => /^ncryptsec1[a-z\d]+$/.test(value || ''), } /** An event whose signature has been verified. */ From 929ece5b24fb0b96a6f8da10dd34566449e64632 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Conselheiro?= Date: Thu, 30 May 2024 21:55:20 +0000 Subject: [PATCH 08/13] fix linter for ncryptsec --- core.test.ts | 8 ++++++-- core.ts | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/core.test.ts b/core.test.ts index 83b2edf..3260af3 100644 --- a/core.test.ts +++ b/core.test.ts @@ -146,13 +146,17 @@ test('NostrTypeGuard isNote with invalid note', () => { }) test('NostrTypeGuard isNcryptsec', () => { - const is = NostrTypeGuard.isNcryptsec('ncryptsec1qgg9947rlpvqu76pj5ecreduf9jxhselq2nae2kghhvd5g7dgjtcxfqtd67p9m0w57lspw8gsq6yphnm8623nsl8xn9j4jdzz84zm3frztj3z7s35vpzmqf6ksu8r89qk5z2zxfmu5gv8th8wclt0h4p') + const is = NostrTypeGuard.isNcryptsec( + 'ncryptsec1qgg9947rlpvqu76pj5ecreduf9jxhselq2nae2kghhvd5g7dgjtcxfqtd67p9m0w57lspw8gsq6yphnm8623nsl8xn9j4jdzz84zm3frztj3z7s35vpzmqf6ksu8r89qk5z2zxfmu5gv8th8wclt0h4p', + ) expect(is).toBeTrue() }) test('NostrTypeGuard isNcryptsec with invalid ncrytpsec', () => { - const is = NostrTypeGuard.isNcryptsec('ncryptsec1qgg9947rlpvqu76pj5ecreduf9jxhselq2nae2kghhvd5g7dgjtcxfqtd67p9m0w57lspw8gsq6yphnm8623nsã8xn9j4jdzz84zm3frztj3z7s35vpzmqf6ksu8r89qk5z2zxfmu5gv8th8wclt0h4p') + const is = NostrTypeGuard.isNcryptsec( + 'ncryptsec1qgg9947rlpvqu76pj5ecreduf9jxhselq2nae2kghhvd5g7dgjtcxfqtd67p9m0w57lspw8gsq6yphnm8623nsã8xn9j4jdzz84zm3frztj3z7s35vpzmqf6ksu8r89qk5z2zxfmu5gv8th8wclt0h4p', + ) expect(is).toBeFalse() }) diff --git a/core.ts b/core.ts index 9fdc285..a7b0bf0 100644 --- a/core.ts +++ b/core.ts @@ -30,7 +30,7 @@ export type NAddress = `naddr1${string}` export type NSecret = `nsec1${string}` export type NPublic = `npub1${string}` export type Note = `note1${string}` -export type Ncryptsec = `ncryptsec1${string}`; +export type Ncryptsec = `ncryptsec1${string}` export type Nip05 = `${string}@${string}` export const NostrTypeGuard = { From eadfcd0de2dc8019c948d3688edeb9f2056d072f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Conselheiro?= Date: Mon, 17 Jun 2024 11:31:23 +0000 Subject: [PATCH 09/13] including ncryptsec return type for nip49 --- nip49.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nip49.ts b/nip49.ts index bae1a74..83fd246 100644 --- a/nip49.ts +++ b/nip49.ts @@ -3,8 +3,9 @@ import { xchacha20poly1305 } from '@noble/ciphers/chacha' import { concatBytes, randomBytes } from '@noble/hashes/utils' import { Bech32MaxSize, encodeBytes } from './nip19.ts' import { bech32 } from '@scure/base' +import { Ncryptsec } from './core.ts' -export function encrypt(sec: Uint8Array, password: string, logn: number = 16, ksb: 0x00 | 0x01 | 0x02 = 0x02): string { +export function encrypt(sec: Uint8Array, password: string, logn: number = 16, ksb: 0x00 | 0x01 | 0x02 = 0x02): Ncryptsec { let salt = randomBytes(16) let n = 2 ** logn let key = scrypt(password.normalize('NFKC'), salt, { N: n, r: 8, p: 1, dkLen: 32 }) From 5c620baf0fe39ba841d70a7461ef6b442b566289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Conselheiro?= Date: Mon, 17 Jun 2024 15:57:11 +0000 Subject: [PATCH 10/13] fixing names of nostr types and types guards --- core.test.ts | 16 ++++++++-------- core.ts | 12 ++++++------ nip19.ts | 8 ++++---- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/core.test.ts b/core.test.ts index 3260af3..5ae8224 100644 --- a/core.test.ts +++ b/core.test.ts @@ -78,7 +78,7 @@ test('NostrTypeGuard isNEvent with invalid nevent', () => { }) test('NostrTypeGuard isNAddress', () => { - const is = NostrTypeGuard.isNAddress( + const is = NostrTypeGuard.isNAddr( 'naddr1qqxnzdesxqmnxvpexqunzvpcqyt8wumn8ghj7un9d3shjtnwdaehgu3wvfskueqzypve7elhmamff3sr5mgxxms4a0rppkmhmn7504h96pfcdkpplvl2jqcyqqq823cnmhuld', ) @@ -86,43 +86,43 @@ test('NostrTypeGuard isNAddress', () => { }) test('NostrTypeGuard isNAddress with invalid nadress', () => { - const is = NostrTypeGuard.isNAddress('nsec1lqw6zqyanj9mz8gwhdam6tqge42vptz4zg93qsfej440xm5h5esqya0juv') + const is = NostrTypeGuard.isNAddr('nsec1lqw6zqyanj9mz8gwhdam6tqge42vptz4zg93qsfej440xm5h5esqya0juv') expect(is).toBeFalse() }) test('NostrTypeGuard isNSecret', () => { - const is = NostrTypeGuard.isNSecret('nsec1lqw6zqyanj9mz8gwhdam6tqge42vptz4zg93qsfej440xm5h5esqya0juv') + const is = NostrTypeGuard.isNSec('nsec1lqw6zqyanj9mz8gwhdam6tqge42vptz4zg93qsfej440xm5h5esqya0juv') expect(is).toBeTrue() }) test('NostrTypeGuard isNSecret with invalid nsec', () => { - const is = NostrTypeGuard.isNSecret('nsec1lqw6zqyanj9mz8gwhdam6tqge42vptz4zg93qsfej440xm5h5esqya0juã') + const is = NostrTypeGuard.isNSec('nsec1lqw6zqyanj9mz8gwhdam6tqge42vptz4zg93qsfej440xm5h5esqya0juã') expect(is).toBeFalse() }) test('NostrTypeGuard isNSecret with invalid nsec', () => { - const is = NostrTypeGuard.isNSecret('nprofile1qqsvc6ulagpn7kwrcwdqgp797xl7usumqa6s3kgcelwq6m75x8fe8yc5usxdg') + const is = NostrTypeGuard.isNSec('nprofile1qqsvc6ulagpn7kwrcwdqgp797xl7usumqa6s3kgcelwq6m75x8fe8yc5usxdg') expect(is).toBeFalse() }) test('NostrTypeGuard isNPublic', () => { - const is = NostrTypeGuard.isNPublic('npub1jz5mdljkmffmqjshpyjgqgrhdkuxd9ztzasv8xeh5q92fv33sjgqy4pats') + const is = NostrTypeGuard.isNPub('npub1jz5mdljkmffmqjshpyjgqgrhdkuxd9ztzasv8xeh5q92fv33sjgqy4pats') expect(is).toBeTrue() }) test('NostrTypeGuard isNPublic with invalid npub', () => { - const is = NostrTypeGuard.isNPublic('npub1jz5mdljkmffmqjshpyjgqgrhdkuxd9ztzãsv8xeh5q92fv33sjgqy4pats') + const is = NostrTypeGuard.isNPub('npub1jz5mdljkmffmqjshpyjgqgrhdkuxd9ztzãsv8xeh5q92fv33sjgqy4pats') expect(is).toBeFalse() }) test('NostrTypeGuard isNPublic with invalid npub', () => { - const is = NostrTypeGuard.isNPublic('nsec1lqw6zqyanj9mz8gwhdam6tqge42vptz4zg93qsfej440xm5h5esqya0juv') + const is = NostrTypeGuard.isNPub('nsec1lqw6zqyanj9mz8gwhdam6tqge42vptz4zg93qsfej440xm5h5esqya0juv') expect(is).toBeFalse() }) diff --git a/core.ts b/core.ts index a7b0bf0..82b26d7 100644 --- a/core.ts +++ b/core.ts @@ -26,9 +26,9 @@ export type UnsignedEvent = Pick /^nprofile1[a-z\d]+$/.test(value || ''), isNRelay: (value?: string | null): value is NRelay => /^nrelay1[a-z\d]+$/.test(value || ''), isNEvent: (value?: string | null): value is NEvent => /^nevent1[a-z\d]+$/.test(value || ''), - isNAddress: (value?: string | null): value is NAddress => /^naddr1[a-z\d]+$/.test(value || ''), - isNSecret: (value?: string | null): value is NSecret => /^nsec1[a-z\d]{58}$/.test(value || ''), - isNPublic: (value?: string | null): value is NPublic => /^npub1[a-z\d]{58}$/.test(value || ''), + isNAddr: (value?: string | null): value is NAddr => /^naddr1[a-z\d]+$/.test(value || ''), + isNSec: (value?: string | null): value is NSec => /^nsec1[a-z\d]{58}$/.test(value || ''), + isNPub: (value?: string | null): value is NPub => /^npub1[a-z\d]{58}$/.test(value || ''), isNote: (value?: string | null): value is Note => /^note1[a-z\d]+$/.test(value || ''), isNcryptsec: (value?: string | null): value is Note => /^ncryptsec1[a-z\d]+$/.test(value || ''), } diff --git a/nip19.ts b/nip19.ts index 045bdd0..ddcfef7 100644 --- a/nip19.ts +++ b/nip19.ts @@ -2,7 +2,7 @@ import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils' import { bech32 } from '@scure/base' import { utf8Decoder, utf8Encoder } from './utils.ts' -import { NAddress, NEvent, Note, NProfile, NPublic, NRelay, NSecret } from './core.ts' +import { NAddr, NEvent, Note, NProfile, NPub, NRelay, NSec } from './core.ts' export const Bech32MaxSize = 5000 @@ -159,11 +159,11 @@ function parseTLV(data: Uint8Array): TLV { return result } -export function nsecEncode(key: Uint8Array): NSecret { +export function nsecEncode(key: Uint8Array): NSec { return encodeBytes('nsec', key) } -export function npubEncode(hex: string): NPublic { +export function npubEncode(hex: string): NPub { return encodeBytes('npub', hexToBytes(hex)) } @@ -204,7 +204,7 @@ export function neventEncode(event: EventPointer): NEvent { return encodeBech32('nevent', data) } -export function naddrEncode(addr: AddressPointer): NAddress { +export function naddrEncode(addr: AddressPointer): NAddr { let kind = new ArrayBuffer(4) new DataView(kind).setUint32(0, addr.kind, false) From 48cee4e22b0eb3716a3694571621fc70d4c59a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Conselheiro?= Date: Mon, 17 Jun 2024 16:01:29 +0000 Subject: [PATCH 11/13] fixing names of nostr types and types guards in unit tests descriptions --- core.test.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/core.test.ts b/core.test.ts index 5ae8224..20c4385 100644 --- a/core.test.ts +++ b/core.test.ts @@ -77,7 +77,7 @@ test('NostrTypeGuard isNEvent with invalid nevent', () => { expect(is).toBeFalse() }) -test('NostrTypeGuard isNAddress', () => { +test('NostrTypeGuard isNAddr', () => { const is = NostrTypeGuard.isNAddr( 'naddr1qqxnzdesxqmnxvpexqunzvpcqyt8wumn8ghj7un9d3shjtnwdaehgu3wvfskueqzypve7elhmamff3sr5mgxxms4a0rppkmhmn7504h96pfcdkpplvl2jqcyqqq823cnmhuld', ) @@ -85,43 +85,43 @@ test('NostrTypeGuard isNAddress', () => { expect(is).toBeTrue() }) -test('NostrTypeGuard isNAddress with invalid nadress', () => { +test('NostrTypeGuard isNAddr with invalid nadress', () => { const is = NostrTypeGuard.isNAddr('nsec1lqw6zqyanj9mz8gwhdam6tqge42vptz4zg93qsfej440xm5h5esqya0juv') expect(is).toBeFalse() }) -test('NostrTypeGuard isNSecret', () => { +test('NostrTypeGuard isNSec', () => { const is = NostrTypeGuard.isNSec('nsec1lqw6zqyanj9mz8gwhdam6tqge42vptz4zg93qsfej440xm5h5esqya0juv') expect(is).toBeTrue() }) -test('NostrTypeGuard isNSecret with invalid nsec', () => { +test('NostrTypeGuard isNSec with invalid nsec', () => { const is = NostrTypeGuard.isNSec('nsec1lqw6zqyanj9mz8gwhdam6tqge42vptz4zg93qsfej440xm5h5esqya0juã') expect(is).toBeFalse() }) -test('NostrTypeGuard isNSecret with invalid nsec', () => { +test('NostrTypeGuard isNSec with invalid nsec', () => { const is = NostrTypeGuard.isNSec('nprofile1qqsvc6ulagpn7kwrcwdqgp797xl7usumqa6s3kgcelwq6m75x8fe8yc5usxdg') expect(is).toBeFalse() }) -test('NostrTypeGuard isNPublic', () => { +test('NostrTypeGuard isNPub', () => { const is = NostrTypeGuard.isNPub('npub1jz5mdljkmffmqjshpyjgqgrhdkuxd9ztzasv8xeh5q92fv33sjgqy4pats') expect(is).toBeTrue() }) -test('NostrTypeGuard isNPublic with invalid npub', () => { +test('NostrTypeGuard isNPub with invalid npub', () => { const is = NostrTypeGuard.isNPub('npub1jz5mdljkmffmqjshpyjgqgrhdkuxd9ztzãsv8xeh5q92fv33sjgqy4pats') expect(is).toBeFalse() }) -test('NostrTypeGuard isNPublic with invalid npub', () => { +test('NostrTypeGuard isNPub with invalid npub', () => { const is = NostrTypeGuard.isNPub('nsec1lqw6zqyanj9mz8gwhdam6tqge42vptz4zg93qsfej440xm5h5esqya0juv') expect(is).toBeFalse() From 8467a3f4463be0896cb736c6217350345b7dd2ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Conselheiro?= Date: Wed, 17 Jul 2024 12:05:57 +0000 Subject: [PATCH 12/13] fix prettier --- nip49.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nip49.ts b/nip49.ts index 83fd246..203080b 100644 --- a/nip49.ts +++ b/nip49.ts @@ -5,7 +5,12 @@ import { Bech32MaxSize, encodeBytes } from './nip19.ts' import { bech32 } from '@scure/base' import { Ncryptsec } from './core.ts' -export function encrypt(sec: Uint8Array, password: string, logn: number = 16, ksb: 0x00 | 0x01 | 0x02 = 0x02): Ncryptsec { +export function encrypt( + sec: Uint8Array, + password: string, + logn: number = 16, + ksb: 0x00 | 0x01 | 0x02 = 0x02, +): Ncryptsec { let salt = randomBytes(16) let n = 2 ** logn let key = scrypt(password.normalize('NFKC'), salt, { N: n, r: 8, p: 1, dkLen: 32 }) From d51db18f7b9f52b5cb4ef2039e6a3960ec9f042f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Conselheiro?= Date: Mon, 22 Jul 2024 22:37:08 +0000 Subject: [PATCH 13/13] including type guard for nip5 --- nip05.test.ts | 14 +++++++++++++- nip05.ts | 4 +++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/nip05.test.ts b/nip05.test.ts index c4a55a8..2c3f79b 100644 --- a/nip05.test.ts +++ b/nip05.test.ts @@ -1,7 +1,7 @@ import { test, expect } from 'bun:test' import fetch from 'node-fetch' -import { useFetchImplementation, queryProfile } from './nip05.ts' +import { useFetchImplementation, queryProfile, NIP05_REGEX, isNip05 } from './nip05.ts' test('fetch nip05 profiles', async () => { useFetchImplementation(fetch) @@ -18,3 +18,15 @@ test('fetch nip05 profiles', async () => { expect(p3!.pubkey).toEqual('3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d') expect(p3!.relays).toEqual(['wss://pyramid.fiatjaf.com', 'wss://nos.lol']) }) + +test('validate NIP05_REGEX', () => { + expect(NIP05_REGEX.test('_@bob.com.br')).toBeTrue() + expect(NIP05_REGEX.test('bob@bob.com.br')).toBeTrue() + expect(NIP05_REGEX.test('b&b@bob.com.br')).toBeFalse() + + expect('b&b@bob.com.br'.match(NIP05_REGEX)).toBeNull() + expect(Array.from('bob@bob.com.br'.match(NIP05_REGEX) || [])).toEqual(['bob@bob.com.br', 'bob', 'bob.com.br', '.br']) + + expect(isNip05('bob@bob.com.br')).toBeTrue() + expect(isNip05('b&b@bob.com.br')).toBeFalse() +}) diff --git a/nip05.ts b/nip05.ts index b0547f6..2ccbb53 100644 --- a/nip05.ts +++ b/nip05.ts @@ -1,3 +1,4 @@ +import { Nip05 } from './core.ts' import { ProfilePointer } from './nip19.ts' /** @@ -8,6 +9,7 @@ import { ProfilePointer } from './nip19.ts' * - 2: domain */ export const NIP05_REGEX = /^(?:([\w.+-]+)@)?([\w_-]+(\.[\w_-]+)+)$/ +export const isNip05 = (value?: string | null): value is Nip05 => NIP05_REGEX.test(value || '') var _fetch: any @@ -47,7 +49,7 @@ export async function queryProfile(fullname: string): Promise { +export async function isValid(pubkey: string, nip05: Nip05): Promise { let res = await queryProfile(nip05) return res ? res.pubkey === pubkey : false }