-
Notifications
You must be signed in to change notification settings - Fork 198
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a0b950a
commit 3266b4d
Showing
2 changed files
with
310 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
import { test, expect } from 'bun:test' | ||
import * as nip55 from './nip55.js' | ||
|
||
// Function to parse the NostrSigner URI | ||
function parseNostrSignerUri(uri) { | ||
const [base, query] = uri.split('?') | ||
const basePart = base.replace('nostrsigner:', '') | ||
|
||
let jsonObject = null | ||
if (basePart) { | ||
try { | ||
jsonObject = JSON.parse(decodeURIComponent(basePart)) | ||
} catch (e) { | ||
console.warn('Failed to parse base JSON:', e) | ||
} | ||
} | ||
|
||
const urlSearchParams = new URLSearchParams(query) | ||
const queryParams = Object.fromEntries(urlSearchParams.entries()) | ||
if (queryParams.permissions) { | ||
queryParams.permissions = JSON.parse(decodeURIComponent(queryParams.permissions)) | ||
} | ||
|
||
return { | ||
base: jsonObject, | ||
...queryParams, | ||
} | ||
} | ||
|
||
// Test cases | ||
test('Get Public Key URI', () => { | ||
const permissions = [{ type: 'sign_event', kind: 22242 }, { type: 'nip44_decrypt' }] | ||
const callbackUrl = 'https://example.com/?event=' | ||
|
||
const uri = nip55.getPublicKeyUri({ | ||
permissions, | ||
callbackUrl, | ||
}) | ||
|
||
const jsonObject = parseNostrSignerUri(uri) | ||
|
||
expect(jsonObject).toHaveProperty('type', 'get_public_key') | ||
expect(jsonObject).toHaveProperty('compressionType', 'none') | ||
expect(jsonObject).toHaveProperty('returnType', 'signature') | ||
expect(jsonObject).toHaveProperty('callbackUrl', 'https://example.com/?event=') | ||
expect(jsonObject).toHaveProperty('permissions[0].type', 'sign_event') | ||
expect(jsonObject).toHaveProperty('permissions[0].kind', 22242) | ||
expect(jsonObject).toHaveProperty('permissions[1].type', 'nip44_decrypt') | ||
}) | ||
|
||
test('Sign Event URI', () => { | ||
const eventJson = { kind: 1, content: 'test' } | ||
|
||
const uri = nip55.signEventUri({ | ||
eventJson, | ||
id: 'some_id', | ||
currentUser: 'hex_pub_key', | ||
}) | ||
|
||
const jsonObject = parseNostrSignerUri(uri) | ||
|
||
expect(jsonObject).toHaveProperty('base.kind', 1) | ||
expect(jsonObject).toHaveProperty('base.content', 'test') | ||
expect(jsonObject).toHaveProperty('type', 'sign_event') | ||
expect(jsonObject).toHaveProperty('compressionType', 'none') | ||
expect(jsonObject).toHaveProperty('returnType', 'signature') | ||
expect(jsonObject).toHaveProperty('id', 'some_id') | ||
expect(jsonObject).toHaveProperty('current_user', 'hex_pub_key') | ||
}) | ||
|
||
test('Get Relays URI', () => { | ||
const uri = nip55.getRelaysUri({ | ||
id: 'some_id', | ||
currentUser: 'hex_pub_key', | ||
appName: 'test app name', | ||
}) | ||
|
||
const jsonObject = parseNostrSignerUri(uri) | ||
|
||
expect(jsonObject).toHaveProperty('type', 'get_relays') | ||
expect(jsonObject).toHaveProperty('compressionType', 'none') | ||
expect(jsonObject).toHaveProperty('returnType', 'signature') | ||
expect(jsonObject).toHaveProperty('id', 'some_id') | ||
expect(jsonObject).toHaveProperty('current_user', 'hex_pub_key') | ||
expect(jsonObject).toHaveProperty('appName', 'test app name') | ||
}) | ||
|
||
test('Encrypt NIP-04 URI', () => { | ||
const callbackUrl = 'https://example.com/?event=' | ||
|
||
const uri = nip55.encryptNip04Uri({ | ||
callbackUrl, | ||
pubKey: 'hex_pub_key', | ||
content: 'plainText', | ||
}) | ||
|
||
const jsonObject = parseNostrSignerUri(uri) | ||
|
||
expect(jsonObject).toHaveProperty('type', 'nip04_encrypt') | ||
expect(jsonObject).toHaveProperty('compressionType', 'none') | ||
expect(jsonObject).toHaveProperty('returnType', 'signature') | ||
expect(jsonObject).toHaveProperty('callbackUrl', callbackUrl) | ||
expect(jsonObject).toHaveProperty('pubKey', 'hex_pub_key') | ||
expect(jsonObject).toHaveProperty('plainText', 'plainText') | ||
}) | ||
|
||
test('Decrypt NIP-04 URI', () => { | ||
const uri = nip55.decryptNip04Uri({ | ||
id: 'some_id', | ||
currentUser: 'hex_pub_key', | ||
pubKey: 'hex_pub_key', | ||
content: 'encryptedText', | ||
}) | ||
|
||
const jsonObject = parseNostrSignerUri(uri) | ||
|
||
expect(jsonObject).toHaveProperty('type', 'nip04_decrypt') | ||
expect(jsonObject).toHaveProperty('compressionType', 'none') | ||
expect(jsonObject).toHaveProperty('returnType', 'signature') | ||
expect(jsonObject).toHaveProperty('id', 'some_id') | ||
expect(jsonObject).toHaveProperty('current_user', 'hex_pub_key') | ||
expect(jsonObject).toHaveProperty('pubKey', 'hex_pub_key') | ||
expect(jsonObject).toHaveProperty('encryptedText', 'encryptedText') | ||
}) | ||
|
||
test('Encrypt NIP-44 URI', () => { | ||
const uri = nip55.encryptNip44Uri({ | ||
id: 'some_id', | ||
currentUser: 'hex_pub_key', | ||
pubKey: 'hex_pub_key', | ||
content: 'plainText', | ||
}) | ||
|
||
const jsonObject = parseNostrSignerUri(uri) | ||
|
||
expect(jsonObject).toHaveProperty('type', 'nip44_encrypt') | ||
expect(jsonObject).toHaveProperty('compressionType', 'none') | ||
expect(jsonObject).toHaveProperty('returnType', 'signature') | ||
expect(jsonObject).toHaveProperty('id', 'some_id') | ||
expect(jsonObject).toHaveProperty('current_user', 'hex_pub_key') | ||
expect(jsonObject).toHaveProperty('pubKey', 'hex_pub_key') | ||
expect(jsonObject).toHaveProperty('plainText', 'plainText') | ||
}) | ||
|
||
test('Decrypt NIP-44 URI', () => { | ||
const uri = nip55.decryptNip44Uri({ | ||
id: 'some_id', | ||
currentUser: 'hex_pub_key', | ||
pubKey: 'hex_pub_key', | ||
content: 'encryptedText', | ||
}) | ||
|
||
const jsonObject = parseNostrSignerUri(uri) | ||
|
||
expect(jsonObject).toHaveProperty('type', 'nip44_decrypt') | ||
expect(jsonObject).toHaveProperty('compressionType', 'none') | ||
expect(jsonObject).toHaveProperty('returnType', 'signature') | ||
expect(jsonObject).toHaveProperty('id', 'some_id') | ||
expect(jsonObject).toHaveProperty('current_user', 'hex_pub_key') | ||
expect(jsonObject).toHaveProperty('pubKey', 'hex_pub_key') | ||
expect(jsonObject).toHaveProperty('encryptedText', 'encryptedText') | ||
}) | ||
|
||
test('Decrypt Zap Event URI', () => { | ||
const eventJson = { kind: 1, content: 'test' } | ||
|
||
const uri = nip55.decryptZapEventUri({ | ||
eventJson, | ||
id: 'some_id', | ||
currentUser: 'hex_pub_key', | ||
returnType: 'event', | ||
compressionType: 'gzip', | ||
}) | ||
|
||
const jsonObject = parseNostrSignerUri(uri) | ||
|
||
expect(jsonObject).toHaveProperty('type', 'decrypt_zap_event') | ||
expect(jsonObject).toHaveProperty('compressionType', 'gzip') | ||
expect(jsonObject).toHaveProperty('returnType', 'event') | ||
expect(jsonObject).toHaveProperty('base.kind', 1) | ||
expect(jsonObject).toHaveProperty('id', 'some_id') | ||
expect(jsonObject).toHaveProperty('current_user', 'hex_pub_key') | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
type BaseParams = { | ||
callbackUrl?: string | ||
returnType?: 'signature' | 'event' | ||
compressionType?: 'none' | 'gzip' | ||
} | ||
|
||
type PermissionsParams = BaseParams & { | ||
permissions?: { type: string; kind?: number }[] | ||
} | ||
|
||
type EventUriParams = BaseParams & { | ||
eventJson: Record<string, unknown> | ||
id?: string | ||
currentUser?: string | ||
} | ||
|
||
type EncryptDecryptParams = BaseParams & { | ||
pubKey: string | ||
content: string | ||
id?: string | ||
currentUser?: string | ||
} | ||
|
||
type UriParams = BaseParams & { | ||
base: string | ||
type: string | ||
id?: string | ||
currentUser?: string | ||
permissions?: { type: string; kind?: number }[] | ||
pubKey?: string | ||
plainText?: string | ||
encryptedText?: string | ||
appName?: string | ||
} | ||
|
||
function encodeParams(params: Record<string, unknown>): string { | ||
return new URLSearchParams(params as Record<string, string>).toString() | ||
} | ||
|
||
function filterUndefined<T extends Record<string, unknown>>(obj: T): T { | ||
return Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== undefined)) as T | ||
} | ||
|
||
function buildUri({ | ||
base, | ||
type, | ||
callbackUrl, | ||
returnType = 'signature', | ||
compressionType = 'none', | ||
...params | ||
}: UriParams): string { | ||
const baseParams = { | ||
type, | ||
compressionType, | ||
returnType, | ||
callbackUrl, | ||
id: params.id, | ||
current_user: params.currentUser, | ||
permissions: | ||
params.permissions && params.permissions.length > 0 | ||
? encodeURIComponent(JSON.stringify(params.permissions)) | ||
: undefined, | ||
pubKey: params.pubKey, | ||
plainText: params.plainText, | ||
encryptedText: params.encryptedText, | ||
appName: params.appName, | ||
} | ||
|
||
const filteredParams = filterUndefined(baseParams) | ||
return `${base}?${encodeParams(filteredParams)}` | ||
} | ||
|
||
function buildDefaultUri(type: string, params: Partial<UriParams>): string { | ||
return buildUri({ | ||
base: 'nostrsigner:', | ||
type, | ||
...params, | ||
}) | ||
} | ||
|
||
export function getPublicKeyUri({ permissions = [], ...params }: PermissionsParams): string { | ||
return buildDefaultUri('get_public_key', { permissions, ...params }) | ||
} | ||
|
||
export function signEventUri({ eventJson, ...params }: EventUriParams): string { | ||
return buildUri({ | ||
base: `nostrsigner:${encodeURIComponent(JSON.stringify(eventJson))}`, | ||
type: 'sign_event', | ||
...params, | ||
}) | ||
} | ||
|
||
export function getRelaysUri(params: BaseParams & { id?: string; currentUser?: string }): string { | ||
return buildDefaultUri('get_relays', params) | ||
} | ||
|
||
function encryptUri(type: 'nip44_encrypt' | 'nip04_encrypt', params: EncryptDecryptParams): string { | ||
return buildDefaultUri(type, { ...params, plainText: params.content }) | ||
} | ||
|
||
function decryptUri(type: 'nip44_decrypt' | 'nip04_decrypt', params: EncryptDecryptParams): string { | ||
return buildDefaultUri(type, { ...params, encryptedText: params.content }) | ||
} | ||
|
||
export function encryptNip04Uri(params: EncryptDecryptParams): string { | ||
return encryptUri('nip04_encrypt', params) | ||
} | ||
|
||
export function decryptNip04Uri(params: EncryptDecryptParams): string { | ||
return decryptUri('nip04_decrypt', params) | ||
} | ||
|
||
export function encryptNip44Uri(params: EncryptDecryptParams): string { | ||
return encryptUri('nip44_encrypt', params) | ||
} | ||
|
||
export function decryptNip44Uri(params: EncryptDecryptParams): string { | ||
return decryptUri('nip44_decrypt', params) | ||
} | ||
|
||
export function decryptZapEventUri({ eventJson, ...params }: EventUriParams): string { | ||
return buildUri({ | ||
base: `nostrsigner:${encodeURIComponent(JSON.stringify(eventJson))}`, | ||
type: 'decrypt_zap_event', | ||
...params, | ||
}) | ||
} |