@renown/sdk 6.2.2-dev.2 → 6.2.2-dev.21
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/README.md +103 -1
- package/dist/crypto.d.ts +2 -2
- package/dist/crypto.js +1 -1
- package/dist/index-25fTa4yI.d.ts +127 -0
- package/dist/index-25fTa4yI.d.ts.map +1 -0
- package/dist/index.d.ts +9 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -8
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +54 -11
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +94 -12
- package/dist/node.js.map +1 -1
- package/dist/renown-builder-BqWoYtBR.js +841 -0
- package/dist/renown-builder-BqWoYtBR.js.map +1 -0
- package/dist/{signer-XkA-D7Qt.js → signer-CgYhXTV7.js} +2 -2
- package/dist/{signer-XkA-D7Qt.js.map → signer-CgYhXTV7.js.map} +1 -1
- package/dist/types-DG816Qa8.js +27 -0
- package/dist/types-DG816Qa8.js.map +1 -0
- package/dist/{signer-DYKGoYHB.d.ts → types-DsF91dDF.d.ts} +121 -115
- package/dist/types-DsF91dDF.d.ts.map +1 -0
- package/dist/types-Z6jyeonm.d.ts +54 -0
- package/dist/types-Z6jyeonm.d.ts.map +1 -0
- package/dist/{utils-D8C7f9Qs.d.ts → utils-OOrVXHof.d.ts} +39 -65
- package/dist/utils-OOrVXHof.d.ts.map +1 -0
- package/dist/wallet/index.d.ts +30 -0
- package/dist/wallet/index.d.ts.map +1 -0
- package/dist/wallet/index.js +74 -0
- package/dist/wallet/index.js.map +1 -0
- package/dist/wallet/mock/index.d.ts +18 -0
- package/dist/wallet/mock/index.d.ts.map +1 -0
- package/dist/wallet/mock/index.js +38 -0
- package/dist/wallet/mock/index.js.map +1 -0
- package/dist/wallet/privy/index.d.ts +63 -0
- package/dist/wallet/privy/index.d.ts.map +1 -0
- package/dist/wallet/privy/index.js +230 -0
- package/dist/wallet/privy/index.js.map +1 -0
- package/dist/wallet/rainbow/index.d.ts +14 -0
- package/dist/wallet/rainbow/index.d.ts.map +1 -0
- package/dist/wallet/rainbow/index.js +218 -0
- package/dist/wallet/rainbow/index.js.map +1 -0
- package/package.json +57 -3
- package/dist/browser-key-storage-DSkDBAhU.d.ts +0 -14
- package/dist/browser-key-storage-DSkDBAhU.d.ts.map +0 -1
- package/dist/renown-builder-DP0iFzK1.js +0 -433
- package/dist/renown-builder-DP0iFzK1.js.map +0 -1
- package/dist/signer-DYKGoYHB.d.ts.map +0 -1
- package/dist/utils-D8C7f9Qs.d.ts.map +0 -1
package/dist/node.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as signatureHasResultingHash, c as ConnectCrypto, d as createAuthBearerToken, f as getResolver, i as parseSignatureHashField, l as RenownCrypto, m as verifyAuthBearerToken, n as createSignatureVerifier, o as MemoryKeyStorage, p as parsePkhDid, r as extractResultingHashFromSignature, s as RenownCryptoBuilder, t as RenownCryptoSigner, u as assertIsAuthCredential } from "./signer-
|
|
1
|
+
import { S as VERIFIABLE_CREDENTIAL_EIP712_TYPE, _ as DEFAULT_RENOWN_NETWORK_ID, a as BaseStorage, b as ISSUER_TYPE, c as recoverCredentialSigner, d as SwitchboardClient, f as resolveSwitchboardEndpoint, g as DEFAULT_RENOWN_CHAIN_ID, h as CREDENTIAL_TYPES, i as RenownMemoryStorage, l as verifyAuthCredential, m as CREDENTIAL_SUBJECT_TYPE, n as fetchRenownProfile, o as buildAndSignCredential, p as CREDENTIAL_SCHEMA_EIP712_TYPE, r as Renown, s as fetchDelegationCredential, t as BaseRenownBuilder, u as verifyCredentialSignature, v as DEFAULT_RENOWN_URL, x as RENOWN_SESSION_COOKIE, y as DOMAIN_TYPE } from "./renown-builder-BqWoYtBR.js";
|
|
2
|
+
import { a as signatureHasResultingHash, c as ConnectCrypto, d as createAuthBearerToken, f as getResolver, i as parseSignatureHashField, l as RenownCrypto, m as verifyAuthBearerToken, n as createSignatureVerifier, o as MemoryKeyStorage, p as parsePkhDid, r as extractResultingHashFromSignature, s as RenownCryptoBuilder, t as RenownCryptoSigner, u as assertIsAuthCredential } from "./signer-CgYhXTV7.js";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { EventEmitter } from "node:events";
|
|
@@ -128,22 +128,23 @@ var NodeStorage = class extends BaseStorage {
|
|
|
128
128
|
var NodeRenownEventEmitter = class extends NodeEventEmitter {};
|
|
129
129
|
var NodeRenownStorage = class extends NodeStorage {};
|
|
130
130
|
const DEFAULT_RENOWN_STORAGE_PATH = "./.ph/.renown.json";
|
|
131
|
-
/**
|
|
132
|
-
* Node.js-specific Renown builder with pre-configured defaults.
|
|
133
|
-
* Uses file-based storage for both user data and key storage.
|
|
134
|
-
*/
|
|
131
|
+
/** Node.js Renown builder: file-based user + key storage. */
|
|
135
132
|
var RenownBuilder = class extends BaseRenownBuilder {
|
|
136
|
-
|
|
137
|
-
* @param appName - Application name used for signing context
|
|
138
|
-
* @param options - Node.js-specific configuration options
|
|
139
|
-
*/
|
|
133
|
+
#revalidate;
|
|
140
134
|
constructor(appName, options = {}) {
|
|
141
135
|
super(appName);
|
|
142
|
-
const { storagePath = DEFAULT_RENOWN_STORAGE_PATH, keyPath, baseUrl } = options;
|
|
136
|
+
const { storagePath = DEFAULT_RENOWN_STORAGE_PATH, keyPath, baseUrl, switchboardUrl, revalidate = "always" } = options;
|
|
137
|
+
this.#revalidate = revalidate;
|
|
143
138
|
this.withKeyPairStorage(new NodeKeyStorage(keyPath));
|
|
144
139
|
this.withStorage(new NodeRenownStorage(storagePath));
|
|
145
140
|
this.withEventEmitter(new NodeRenownEventEmitter());
|
|
146
141
|
if (baseUrl) this.withBaseUrl(baseUrl);
|
|
142
|
+
if (switchboardUrl) this.withSwitchboardUrl(switchboardUrl);
|
|
143
|
+
}
|
|
144
|
+
async build() {
|
|
145
|
+
const renown = await super.build();
|
|
146
|
+
if (this.#revalidate === "always" && renown.user) await renown.revalidate();
|
|
147
|
+
return renown;
|
|
147
148
|
}
|
|
148
149
|
};
|
|
149
150
|
//#endregion
|
|
@@ -301,6 +302,87 @@ function formatExpiry(expiresIn) {
|
|
|
301
302
|
return `${expiresIn} second${expiresIn !== 1 ? "s" : ""}`;
|
|
302
303
|
}
|
|
303
304
|
//#endregion
|
|
304
|
-
|
|
305
|
+
//#region src/session.ts
|
|
306
|
+
/** Serialize a session cookie value (token + optional display hint). */
|
|
307
|
+
function serializeRenownSessionCookie(value) {
|
|
308
|
+
return JSON.stringify(value);
|
|
309
|
+
}
|
|
310
|
+
function parseSessionCookie(value) {
|
|
311
|
+
try {
|
|
312
|
+
const parsed = JSON.parse(value);
|
|
313
|
+
if (parsed && typeof parsed === "object" && typeof parsed.token === "string") return parsed;
|
|
314
|
+
} catch {}
|
|
315
|
+
}
|
|
316
|
+
function readSessionClaims(value) {
|
|
317
|
+
const parsed = parseSessionCookie(value);
|
|
318
|
+
if (!parsed) return void 0;
|
|
319
|
+
const parts = parsed.token.split(".");
|
|
320
|
+
if (parts.length < 2) return void 0;
|
|
321
|
+
try {
|
|
322
|
+
const json = Buffer.from(parts[1], "base64url").toString("utf8");
|
|
323
|
+
const payload = JSON.parse(json);
|
|
324
|
+
const subject = payload.vc?.credentialSubject;
|
|
325
|
+
return {
|
|
326
|
+
address: subject?.address,
|
|
327
|
+
chainId: subject?.chainId,
|
|
328
|
+
networkId: subject?.networkId,
|
|
329
|
+
expiresAt: payload.exp
|
|
330
|
+
};
|
|
331
|
+
} catch {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
async function verifyRenownSession(value, options = {}) {
|
|
336
|
+
const { verifyCredential = false } = options;
|
|
337
|
+
const parsed = parseSessionCookie(value);
|
|
338
|
+
if (!parsed) return void 0;
|
|
339
|
+
const { token, profile } = parsed;
|
|
340
|
+
let session;
|
|
341
|
+
if (verifyCredential) {
|
|
342
|
+
const verified = await verifyAuthCredential(token, {
|
|
343
|
+
audience: options.audience,
|
|
344
|
+
renownUrl: options.renownUrl,
|
|
345
|
+
switchboardUrl: options.switchboardUrl
|
|
346
|
+
});
|
|
347
|
+
if (verified) session = {
|
|
348
|
+
user: {
|
|
349
|
+
address: verified.address,
|
|
350
|
+
chainId: verified.chainId,
|
|
351
|
+
networkId: verified.networkId,
|
|
352
|
+
did: verified.did,
|
|
353
|
+
credential: verified.credential
|
|
354
|
+
},
|
|
355
|
+
appDid: verified.appDid
|
|
356
|
+
};
|
|
357
|
+
} else {
|
|
358
|
+
const verified = await verifyAuthBearerToken(token, { audience: options.audience });
|
|
359
|
+
if (verified) {
|
|
360
|
+
const subject = verified.verifiableCredential.credentialSubject;
|
|
361
|
+
const did = `did:pkh:${subject.networkId}:${subject.chainId}:${subject.address.toLowerCase()}`;
|
|
362
|
+
session = {
|
|
363
|
+
user: {
|
|
364
|
+
address: subject.address,
|
|
365
|
+
chainId: subject.chainId,
|
|
366
|
+
networkId: subject.networkId,
|
|
367
|
+
did,
|
|
368
|
+
credential: void 0
|
|
369
|
+
},
|
|
370
|
+
appDid: verified.issuer,
|
|
371
|
+
expiresAt: verified.payload.exp
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (!session) return void 0;
|
|
376
|
+
if (profile?.name) session.user = {
|
|
377
|
+
...session.user,
|
|
378
|
+
ens: {
|
|
379
|
+
name: profile.name,
|
|
380
|
+
avatarUrl: profile.avatar ?? void 0
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
return session;
|
|
384
|
+
}
|
|
385
|
+
//#endregion
|
|
386
|
+
export { BaseRenownBuilder, CREDENTIAL_SCHEMA_EIP712_TYPE, CREDENTIAL_SUBJECT_TYPE, CREDENTIAL_TYPES, ConnectCrypto, DEFAULT_KEYPAIR_PATH, DEFAULT_RENOWN_CHAIN_ID, DEFAULT_RENOWN_NETWORK_ID, DEFAULT_RENOWN_STORAGE_PATH, DEFAULT_RENOWN_URL, DOMAIN_TYPE, ISSUER_TYPE, MemoryKeyStorage, NodeEventEmitter, NodeKeyStorage, NodeRenownEventEmitter, NodeRenownStorage, NodeStorage, RENOWN_PRIVATE_KEY_ENV, RENOWN_SESSION_COOKIE, Renown, RenownBuilder, RenownCrypto, RenownCryptoBuilder, RenownCryptoSigner, RenownMemoryStorage, SwitchboardClient, VERIFIABLE_CREDENTIAL_EIP712_TYPE, assertIsAuthCredential, browserLogin, buildAndSignCredential, createAuthBearerToken, createSignatureVerifier, extractResultingHashFromSignature, fetchDelegationCredential, fetchRenownProfile, formatExpiry, generateAccessToken, getAuthStatus, getResolver, openBrowser, parseExpiry, parsePkhDid, parseSignatureHashField, readSessionClaims, recoverCredentialSigner, resolveSwitchboardEndpoint, serializeRenownSessionCookie, signatureHasResultingHash, verifyAuthBearerToken, verifyAuthCredential, verifyCredentialSignature, verifyRenownSession };
|
|
305
387
|
|
|
306
388
|
//# sourceMappingURL=node.js.map
|
package/dist/node.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.js","names":["#filePath","#envKeyName","#logger","#parseKeyPair","#loadFromFile","#saveToFile","#emitter"],"sources":["../src/crypto/node-key-storage.ts","../src/event/event.node.ts","../src/storage/storage.node.ts","../src/init.node.ts","../src/login.node.ts"],"sourcesContent":["import {\n existsSync,\n mkdirSync,\n readFileSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport type { ILogger } from \"../utils.js\";\nimport type { JsonWebKeyPairStorage, JwkKeyPair } from \"./types.js\";\n\nexport const RENOWN_PRIVATE_KEY_ENV = \"PH_RENOWN_PRIVATE_KEY\";\nexport const DEFAULT_KEYPAIR_PATH = \".ph/.keypair.json\";\n\n/**\n * Key storage that supports:\n * 1. PH_RENOWN_PRIVATE_KEY environment variable (JSON-encoded JwkKeyPair)\n * 2. Custom file path passed via options\n * 3. Falls back to file storage at .ph/.keypair.json in current working directory\n */\nexport class NodeKeyStorage implements JsonWebKeyPairStorage {\n #filePath: string;\n #envKeyName: string;\n #logger?: ILogger;\n\n static readonly DEFAULT_KEYPAIR_PATH = DEFAULT_KEYPAIR_PATH;\n static readonly RENOWN_PRIVATE_KEY_ENV = RENOWN_PRIVATE_KEY_ENV;\n\n constructor(\n filePath?: string,\n options: { envKeyName?: string; logger?: ILogger } = {},\n ) {\n this.#filePath = filePath || join(process.cwd(), DEFAULT_KEYPAIR_PATH);\n this.#envKeyName = options.envKeyName || RENOWN_PRIVATE_KEY_ENV;\n this.#logger = options.logger;\n\n // Ensure directory exists\n const dir = dirname(this.#filePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n }\n\n loadKeyPair(): Promise<JwkKeyPair | undefined> {\n // First check environment variable\n const envKey = process.env[this.#envKeyName];\n if (envKey) {\n try {\n const keyPairJson = JSON.parse(envKey) as unknown;\n const keyPair: JwkKeyPair = this.#parseKeyPair(keyPairJson);\n\n // Validate it has the required structure\n this.#logger?.debug(\"Loaded keypair from environment variable\");\n return Promise.resolve(keyPair);\n } catch (e) {\n throw new Error(\n `Failed to parse ${this.#envKeyName}: ${e instanceof Error ? e.message : String(e)}`,\n {\n cause: e,\n },\n );\n }\n }\n\n // Fall back to file storage\n return Promise.resolve(this.#loadFromFile());\n }\n\n async saveKeyPair(keyPair: JwkKeyPair): Promise<void> {\n // Don't save if using env var\n if (process.env[this.#envKeyName]) {\n return;\n }\n\n // Save to file\n this.#saveToFile(keyPair);\n return Promise.resolve();\n }\n\n removeKeyPair(): Promise<void> {\n if (process.env[this.#envKeyName]) {\n delete process.env[this.#envKeyName];\n }\n if (existsSync(this.#filePath)) {\n unlinkSync(this.#filePath);\n }\n return Promise.resolve();\n }\n\n #loadFromFile(): JwkKeyPair | undefined {\n try {\n if (!existsSync(this.#filePath)) {\n return undefined;\n }\n const data = readFileSync(this.#filePath, \"utf-8\");\n const parsed = JSON.parse(data) as unknown;\n const keyPair: JwkKeyPair = this.#parseKeyPair(parsed);\n this.#logger?.debug(`Loaded keypair from ${this.#filePath}`);\n return keyPair;\n } catch (e) {\n throw new Error(\n `Failed to parse ${this.#filePath}: ${e instanceof Error ? e.message : String(e)}`,\n {\n cause: e,\n },\n );\n }\n }\n\n #saveToFile(keyPair: JwkKeyPair): void {\n const data = { keyPair };\n writeFileSync(this.#filePath, JSON.stringify(data, null, 2), \"utf-8\");\n this.#logger?.debug(`Saved keypair to ${this.#filePath}`);\n }\n\n #parseKeyPair(json: unknown): JwkKeyPair {\n if (typeof json !== \"object\") {\n throw new Error(\"Invalid keyPair format:\" + JSON.stringify(json));\n }\n\n const object = json as JwkKeyPair | { keyPair: JwkKeyPair };\n let keyPair: JwkKeyPair;\n if (\"keyPair\" in object) {\n keyPair = object.keyPair;\n } else {\n keyPair = object;\n }\n\n if (\"publicKey\" in keyPair && \"privateKey\" in keyPair) {\n return keyPair;\n } else {\n throw new Error(\"Invalid keyPair format:\" + JSON.stringify(json));\n }\n }\n}\n","import { EventEmitter } from \"node:events\";\nimport type { IEventEmitter } from \"./types.js\";\n\nexport class NodeEventEmitter<\n Events extends Record<string, unknown>,\n> implements IEventEmitter<Events> {\n #emitter = new EventEmitter();\n\n constructor() {\n this.#emitter.setMaxListeners(0);\n }\n\n on<K extends keyof Events>(\n event: K,\n listener: (data: Events[K]) => void,\n ): () => void {\n this.#emitter.on(event as string, listener);\n return () => {\n this.#emitter.removeListener(event.toString(), listener);\n };\n }\n\n emit<K extends keyof Events>(event: K, data: Events[K]): void {\n this.#emitter.emit(event.toString(), data);\n }\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { BaseStorage } from \"./common.js\";\n\nexport class NodeStorage<\n T extends Record<string, unknown> = Record<string, unknown>,\n> extends BaseStorage<T> {\n private readonly filePath: string;\n\n constructor(filePath: string) {\n super();\n this.filePath = filePath;\n\n const parentFolder = dirname(this.filePath);\n mkdirSync(parentFolder, { recursive: true });\n\n if (!existsSync(this.filePath)) {\n writeFileSync(this.filePath, JSON.stringify({}));\n }\n }\n\n private readData(): T {\n const data = readFileSync(this.filePath, \"utf-8\");\n return JSON.parse(data) as T;\n }\n\n private writeData(data: T): void {\n writeFileSync(this.filePath, JSON.stringify(data, null, 2));\n }\n\n get<Key extends keyof T>(key: Key): T[Key] | undefined {\n const data = this.readData();\n return data[key];\n }\n\n set<Key extends keyof T>(key: Key, value?: T[Key]): void {\n const data = this.readData();\n if (value === undefined) {\n delete data[key];\n } else {\n data[key] = value;\n }\n this.writeData(data);\n }\n\n delete(key: keyof T): void {\n const data = this.readData();\n delete data[key];\n this.writeData(data);\n }\n}\n","import { NodeKeyStorage } from \"./crypto/node-key-storage.js\";\nimport { NodeEventEmitter } from \"./event/event.node.js\";\nimport { BaseRenownBuilder } from \"./renown-builder.js\";\nimport { NodeStorage } from \"./storage/storage.node.js\";\nimport type { RenownEvents, RenownStorageMap } from \"./types.js\";\n\nexport class NodeRenownEventEmitter extends NodeEventEmitter<RenownEvents> {}\nexport class NodeRenownStorage extends NodeStorage<RenownStorageMap> {}\n\nexport const DEFAULT_RENOWN_STORAGE_PATH = \"./.ph/.renown.json\";\n\nexport interface NodeRenownBuilderOptions {\n /** File path for user storage. Defaults to \".ph/.renown.json\" in cwd */\n storagePath?: string;\n /** File path for keypair storage. Defaults to \".ph/.keypair.json\" in cwd */\n keyPath?: string;\n /** Renown server URL. Defaults to https://www.renown.id */\n baseUrl?: string;\n}\n\n/**\n * Node.js-specific Renown builder with pre-configured defaults.\n * Uses file-based storage for both user data and key storage.\n */\nexport class RenownBuilder extends BaseRenownBuilder {\n /**\n * @param appName - Application name used for signing context\n * @param options - Node.js-specific configuration options\n */\n constructor(appName: string, options: NodeRenownBuilderOptions = {}) {\n super(appName);\n\n const {\n storagePath = DEFAULT_RENOWN_STORAGE_PATH,\n keyPath,\n baseUrl,\n } = options;\n\n this.withKeyPairStorage(new NodeKeyStorage(keyPath));\n this.withStorage(new NodeRenownStorage(storagePath));\n this.withEventEmitter(new NodeRenownEventEmitter());\n if (baseUrl) {\n this.withBaseUrl(baseUrl);\n }\n }\n}\n","import type { CreateBearerTokenOptions, IRenown, User } from \"./types.js\";\n\nconst DEFAULT_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes\nconst POLL_INTERVAL_MS = 500; // 0.5s: session store is in-memory, cheap to poll\nconst SECONDS_IN_DAY = 24 * 60 * 60;\n\nexport interface BrowserLoginOptions {\n /** Renown server URL */\n renownUrl: string;\n /** Timeout in milliseconds. Defaults to 5 minutes. */\n timeoutMs?: number;\n /** Called with the login URL and session ID before the browser is opened */\n onLoginUrl?: (url: string, sessionId: string) => void;\n /** Called on each poll tick while waiting for authentication */\n onPollTick?: () => void;\n /** Called when the browser failed to open automatically */\n onBrowserOpenFailed?: (url: string) => void;\n /** AbortSignal to cancel the login flow */\n signal?: AbortSignal;\n}\n\nexport interface BrowserLoginResult {\n user: User;\n cliDid: string;\n}\n\nexport interface AuthStatusResult {\n authenticated: boolean;\n address?: string;\n userDid?: string;\n chainId?: number;\n cliDid: string;\n authenticatedAt?: Date;\n baseUrl: string;\n}\n\ninterface PendingSessionResponse {\n sessionId: string;\n status: \"pending\";\n}\n\ninterface ReadySessionResponse {\n sessionId: string;\n status: \"ready\";\n address: string;\n chainId: number;\n did: string;\n credentialId: string;\n userDocumentId: string;\n}\n\ntype SessionResponse = PendingSessionResponse | ReadySessionResponse;\n\nfunction abortReason(signal: AbortSignal): Error {\n if (signal.reason instanceof Error) return signal.reason;\n if (signal.reason) return new Error(String(signal.reason));\n return new DOMException(\"Aborted\", \"AbortError\");\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(abortReason(signal));\n return;\n }\n const onAbort = () => {\n clearTimeout(timer);\n reject(abortReason(signal!));\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\n/**\n * Open a URL in the default browser (cross-platform).\n */\nexport async function openBrowser(url: string): Promise<void> {\n const { execFile } = await import(\"node:child_process\");\n const { promisify } = await import(\"node:util\");\n const execFileAsync = promisify(execFile);\n const platform = process.platform;\n\n if (platform === \"darwin\") {\n await execFileAsync(\"open\", [url]);\n } else if (platform === \"win32\") {\n await execFileAsync(\"cmd\", [\"/c\", \"start\", \"\", url]);\n } else {\n await execFileAsync(\"xdg-open\", [url]);\n }\n}\n\nasync function pollSession(\n renownUrl: string,\n sessionId: string,\n timeoutMs: number,\n onPollTick?: () => void,\n signal?: AbortSignal,\n): Promise<ReadySessionResponse | null> {\n const startTime = Date.now();\n const sessionUrl = new URL(\n `/api/console/session/${sessionId}`,\n renownUrl,\n ).toString();\n\n while (Date.now() - startTime < timeoutMs) {\n signal?.throwIfAborted();\n try {\n const response = await fetch(sessionUrl, { signal });\n if (response.ok) {\n const data = (await response.json()) as SessionResponse;\n if (data.status === \"ready\") {\n return data;\n }\n }\n } catch (error) {\n if (error instanceof DOMException && error.name === \"AbortError\")\n throw error;\n // Network error, will retry\n }\n onPollTick?.();\n await sleep(POLL_INTERVAL_MS, signal);\n }\n\n return null;\n}\n\n/**\n * Perform a browser-based login flow with Renown.\n * Opens the user's browser to authenticate, then polls for completion.\n * Throws if already authenticated or if the flow times out.\n * If the browser fails to open, the flow continues polling — callers\n * can use onBrowserOpenFailed to show the URL as a fallback.\n */\nexport async function browserLogin(\n renown: IRenown,\n options: BrowserLoginOptions,\n): Promise<BrowserLoginResult> {\n if (renown.user?.credential) {\n throw new Error(\n `Already authenticated as ${renown.user.address}. Logout first.`,\n );\n }\n\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const sessionId = crypto.randomUUID();\n\n const loginUrl = new URL(\"/console\", options.renownUrl);\n loginUrl.searchParams.set(\"session\", sessionId);\n loginUrl.searchParams.set(\"connect\", renown.did);\n loginUrl.searchParams.set(\"app\", renown.did);\n\n const url = loginUrl.toString();\n options.onLoginUrl?.(url, sessionId);\n\n try {\n await openBrowser(url);\n } catch {\n options.onBrowserOpenFailed?.(url);\n }\n\n const result = await pollSession(\n options.renownUrl,\n sessionId,\n timeoutMs,\n options.onPollTick,\n options.signal,\n );\n\n if (!result) {\n throw new Error(\"Authentication timed out.\");\n }\n\n const user = await renown.login(result.did);\n return { user, cliDid: renown.did };\n}\n\n/**\n * Get the current authentication status from a Renown instance.\n */\nexport function getAuthStatus(renown: IRenown): AuthStatusResult {\n const user = renown.user;\n const credential = user?.credential;\n\n return {\n authenticated: !!credential,\n address: user?.address,\n userDid: user?.did,\n chainId: user?.chainId,\n cliDid: renown.did,\n authenticatedAt: credential ? new Date(credential.issuanceDate) : undefined,\n baseUrl: renown.baseUrl,\n };\n}\n\nexport interface AccessTokenResult {\n token: string;\n did: string;\n address: string;\n expiresIn: number;\n}\n\n/**\n * Generate a bearer token for API authentication.\n * Requires the user to be authenticated (via browserLogin or equivalent).\n * Throws if not authenticated.\n */\nexport async function generateAccessToken(\n renown: IRenown,\n options?: CreateBearerTokenOptions,\n): Promise<AccessTokenResult> {\n const user = renown.user;\n if (!user?.credential) {\n throw new Error(\n \"Not authenticated. Login first to generate access tokens.\",\n );\n }\n\n const token = await renown.getBearerToken(options ?? {});\n\n return {\n token,\n did: renown.did,\n address: user.address,\n expiresIn: options?.expiresIn ?? 0,\n };\n}\n\n/**\n * Parse a human-readable expiry string to seconds.\n * Supports formats: \"7d\" (days), \"24h\" (hours), \"3600\" (seconds), \"3600s\" (seconds).\n * Only accepts positive integers — decimals like \"1.5h\" are rejected.\n */\nexport function parseExpiry(expiry: string): number {\n const trimmed = expiry.trim().toLowerCase();\n const match = trimmed.match(/^(\\d+)(d|h|s)?$/);\n\n if (!match) {\n throw new Error(\n `Invalid expiry format: ${expiry}. Expected a positive integer with optional suffix: \"7d\", \"24h\", \"3600s\", or \"3600\".`,\n );\n }\n\n const value = Number(match[1]);\n const unit = match[2];\n\n if (value <= 0) {\n throw new Error(\n `Invalid expiry format: ${expiry}. Value must be a positive integer.`,\n );\n }\n\n switch (unit) {\n case \"d\":\n return value * SECONDS_IN_DAY;\n case \"h\":\n return value * 60 * 60;\n default:\n return value;\n }\n}\n\n/**\n * Format an expiry duration in seconds to a human-readable string.\n */\nexport function formatExpiry(expiresIn: number): string {\n const days = Math.floor(expiresIn / SECONDS_IN_DAY);\n const hours = Math.floor((expiresIn % SECONDS_IN_DAY) / 3600);\n\n if (days > 0) {\n const dayStr = `${days} day${days !== 1 ? \"s\" : \"\"}`;\n if (hours > 0) {\n return `${dayStr} and ${hours} hour${hours !== 1 ? \"s\" : \"\"}`;\n }\n return dayStr;\n }\n\n if (hours > 0) {\n return `${hours} hour${hours !== 1 ? \"s\" : \"\"}`;\n }\n\n return `${expiresIn} second${expiresIn !== 1 ? \"s\" : \"\"}`;\n}\n"],"mappings":";;;;;;AAWA,MAAa,yBAAyB;AACtC,MAAa,uBAAuB;;;;;;;AAQpC,IAAa,iBAAb,MAA6D;CAC3D;CACA;CACA;CAEA,OAAgB,uBAAuB;CACvC,OAAgB,yBAAyB;CAEzC,YACE,UACA,UAAqD,EAAE,EACvD;AACA,QAAA,WAAiB,YAAY,KAAK,QAAQ,KAAK,EAAA,oBAAuB;AACtE,QAAA,aAAmB,QAAQ,cAAA;AAC3B,QAAA,SAAe,QAAQ;EAGvB,MAAM,MAAM,QAAQ,MAAA,SAAe;AACnC,MAAI,CAAC,WAAW,IAAI,CAClB,WAAU,KAAK,EAAE,WAAW,MAAM,CAAC;;CAIvC,cAA+C;EAE7C,MAAM,SAAS,QAAQ,IAAI,MAAA;AAC3B,MAAI,OACF,KAAI;GACF,MAAM,cAAc,KAAK,MAAM,OAAO;GACtC,MAAM,UAAsB,MAAA,aAAmB,YAAY;AAG3D,SAAA,QAAc,MAAM,2CAA2C;AAC/D,UAAO,QAAQ,QAAQ,QAAQ;WACxB,GAAG;AACV,SAAM,IAAI,MACR,mBAAmB,MAAA,WAAiB,IAAI,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE,IAClF,EACE,OAAO,GACR,CACF;;AAKL,SAAO,QAAQ,QAAQ,MAAA,cAAoB,CAAC;;CAG9C,MAAM,YAAY,SAAoC;AAEpD,MAAI,QAAQ,IAAI,MAAA,YACd;AAIF,QAAA,WAAiB,QAAQ;AACzB,SAAO,QAAQ,SAAS;;CAG1B,gBAA+B;AAC7B,MAAI,QAAQ,IAAI,MAAA,YACd,QAAO,QAAQ,IAAI,MAAA;AAErB,MAAI,WAAW,MAAA,SAAe,CAC5B,YAAW,MAAA,SAAe;AAE5B,SAAO,QAAQ,SAAS;;CAG1B,gBAAwC;AACtC,MAAI;AACF,OAAI,CAAC,WAAW,MAAA,SAAe,CAC7B;GAEF,MAAM,OAAO,aAAa,MAAA,UAAgB,QAAQ;GAClD,MAAM,SAAS,KAAK,MAAM,KAAK;GAC/B,MAAM,UAAsB,MAAA,aAAmB,OAAO;AACtD,SAAA,QAAc,MAAM,uBAAuB,MAAA,WAAiB;AAC5D,UAAO;WACA,GAAG;AACV,SAAM,IAAI,MACR,mBAAmB,MAAA,SAAe,IAAI,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE,IAChF,EACE,OAAO,GACR,CACF;;;CAIL,YAAY,SAA2B;EACrC,MAAM,OAAO,EAAE,SAAS;AACxB,gBAAc,MAAA,UAAgB,KAAK,UAAU,MAAM,MAAM,EAAE,EAAE,QAAQ;AACrE,QAAA,QAAc,MAAM,oBAAoB,MAAA,WAAiB;;CAG3D,cAAc,MAA2B;AACvC,MAAI,OAAO,SAAS,SAClB,OAAM,IAAI,MAAM,4BAA4B,KAAK,UAAU,KAAK,CAAC;EAGnE,MAAM,SAAS;EACf,IAAI;AACJ,MAAI,aAAa,OACf,WAAU,OAAO;MAEjB,WAAU;AAGZ,MAAI,eAAe,WAAW,gBAAgB,QAC5C,QAAO;MAEP,OAAM,IAAI,MAAM,4BAA4B,KAAK,UAAU,KAAK,CAAC;;;;;AChIvE,IAAa,mBAAb,MAEmC;CACjC,WAAW,IAAI,cAAc;CAE7B,cAAc;AACZ,QAAA,QAAc,gBAAgB,EAAE;;CAGlC,GACE,OACA,UACY;AACZ,QAAA,QAAc,GAAG,OAAiB,SAAS;AAC3C,eAAa;AACX,SAAA,QAAc,eAAe,MAAM,UAAU,EAAE,SAAS;;;CAI5D,KAA6B,OAAU,MAAuB;AAC5D,QAAA,QAAc,KAAK,MAAM,UAAU,EAAE,KAAK;;;;;ACnB9C,IAAa,cAAb,cAEU,YAAe;CACvB;CAEA,YAAY,UAAkB;AAC5B,SAAO;AACP,OAAK,WAAW;AAGhB,YADqB,QAAQ,KAAK,SAAS,EACnB,EAAE,WAAW,MAAM,CAAC;AAE5C,MAAI,CAAC,WAAW,KAAK,SAAS,CAC5B,eAAc,KAAK,UAAU,KAAK,UAAU,EAAE,CAAC,CAAC;;CAIpD,WAAsB;EACpB,MAAM,OAAO,aAAa,KAAK,UAAU,QAAQ;AACjD,SAAO,KAAK,MAAM,KAAK;;CAGzB,UAAkB,MAAe;AAC/B,gBAAc,KAAK,UAAU,KAAK,UAAU,MAAM,MAAM,EAAE,CAAC;;CAG7D,IAAyB,KAA8B;AAErD,SADa,KAAK,UAAU,CAChB;;CAGd,IAAyB,KAAU,OAAsB;EACvD,MAAM,OAAO,KAAK,UAAU;AAC5B,MAAI,UAAU,KAAA,EACZ,QAAO,KAAK;MAEZ,MAAK,OAAO;AAEd,OAAK,UAAU,KAAK;;CAGtB,OAAO,KAAoB;EACzB,MAAM,OAAO,KAAK,UAAU;AAC5B,SAAO,KAAK;AACZ,OAAK,UAAU,KAAK;;;;;AC1CxB,IAAa,yBAAb,cAA4C,iBAA+B;AAC3E,IAAa,oBAAb,cAAuC,YAA8B;AAErE,MAAa,8BAA8B;;;;;AAe3C,IAAa,gBAAb,cAAmC,kBAAkB;;;;;CAKnD,YAAY,SAAiB,UAAoC,EAAE,EAAE;AACnE,QAAM,QAAQ;EAEd,MAAM,EACJ,cAAc,6BACd,SACA,YACE;AAEJ,OAAK,mBAAmB,IAAI,eAAe,QAAQ,CAAC;AACpD,OAAK,YAAY,IAAI,kBAAkB,YAAY,CAAC;AACpD,OAAK,iBAAiB,IAAI,wBAAwB,CAAC;AACnD,MAAI,QACF,MAAK,YAAY,QAAQ;;;;;ACxC/B,MAAM,qBAAqB,MAAS;AACpC,MAAM,mBAAmB;AACzB,MAAM,iBAAiB,OAAU;AAiDjC,SAAS,YAAY,QAA4B;AAC/C,KAAI,OAAO,kBAAkB,MAAO,QAAO,OAAO;AAClD,KAAI,OAAO,OAAQ,QAAO,IAAI,MAAM,OAAO,OAAO,OAAO,CAAC;AAC1D,QAAO,IAAI,aAAa,WAAW,aAAa;;AAGlD,SAAS,MAAM,IAAY,QAAqC;AAC9D,QAAO,IAAI,SAAS,SAAS,WAAW;AACtC,MAAI,QAAQ,SAAS;AACnB,UAAO,YAAY,OAAO,CAAC;AAC3B;;EAEF,MAAM,gBAAgB;AACpB,gBAAa,MAAM;AACnB,UAAO,YAAY,OAAQ,CAAC;;EAE9B,MAAM,QAAQ,iBAAiB;AAC7B,WAAQ,oBAAoB,SAAS,QAAQ;AAC7C,YAAS;KACR,GAAG;AACN,UAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,MAAM,CAAC;GAC1D;;;;;AAMJ,eAAsB,YAAY,KAA4B;CAC5D,MAAM,EAAE,aAAa,MAAM,OAAO;CAClC,MAAM,EAAE,cAAc,MAAM,OAAO;CACnC,MAAM,gBAAgB,UAAU,SAAS;CACzC,MAAM,WAAW,QAAQ;AAEzB,KAAI,aAAa,SACf,OAAM,cAAc,QAAQ,CAAC,IAAI,CAAC;UACzB,aAAa,QACtB,OAAM,cAAc,OAAO;EAAC;EAAM;EAAS;EAAI;EAAI,CAAC;KAEpD,OAAM,cAAc,YAAY,CAAC,IAAI,CAAC;;AAI1C,eAAe,YACb,WACA,WACA,WACA,YACA,QACsC;CACtC,MAAM,YAAY,KAAK,KAAK;CAC5B,MAAM,aAAa,IAAI,IACrB,wBAAwB,aACxB,UACD,CAAC,UAAU;AAEZ,QAAO,KAAK,KAAK,GAAG,YAAY,WAAW;AACzC,UAAQ,gBAAgB;AACxB,MAAI;GACF,MAAM,WAAW,MAAM,MAAM,YAAY,EAAE,QAAQ,CAAC;AACpD,OAAI,SAAS,IAAI;IACf,MAAM,OAAQ,MAAM,SAAS,MAAM;AACnC,QAAI,KAAK,WAAW,QAClB,QAAO;;WAGJ,OAAO;AACd,OAAI,iBAAiB,gBAAgB,MAAM,SAAS,aAClD,OAAM;;AAGV,gBAAc;AACd,QAAM,MAAM,kBAAkB,OAAO;;AAGvC,QAAO;;;;;;;;;AAUT,eAAsB,aACpB,QACA,SAC6B;AAC7B,KAAI,OAAO,MAAM,WACf,OAAM,IAAI,MACR,4BAA4B,OAAO,KAAK,QAAQ,iBACjD;CAGH,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,YAAY,OAAO,YAAY;CAErC,MAAM,WAAW,IAAI,IAAI,YAAY,QAAQ,UAAU;AACvD,UAAS,aAAa,IAAI,WAAW,UAAU;AAC/C,UAAS,aAAa,IAAI,WAAW,OAAO,IAAI;AAChD,UAAS,aAAa,IAAI,OAAO,OAAO,IAAI;CAE5C,MAAM,MAAM,SAAS,UAAU;AAC/B,SAAQ,aAAa,KAAK,UAAU;AAEpC,KAAI;AACF,QAAM,YAAY,IAAI;SAChB;AACN,UAAQ,sBAAsB,IAAI;;CAGpC,MAAM,SAAS,MAAM,YACnB,QAAQ,WACR,WACA,WACA,QAAQ,YACR,QAAQ,OACT;AAED,KAAI,CAAC,OACH,OAAM,IAAI,MAAM,4BAA4B;AAI9C,QAAO;EAAE,MADI,MAAM,OAAO,MAAM,OAAO,IAAI;EAC5B,QAAQ,OAAO;EAAK;;;;;AAMrC,SAAgB,cAAc,QAAmC;CAC/D,MAAM,OAAO,OAAO;CACpB,MAAM,aAAa,MAAM;AAEzB,QAAO;EACL,eAAe,CAAC,CAAC;EACjB,SAAS,MAAM;EACf,SAAS,MAAM;EACf,SAAS,MAAM;EACf,QAAQ,OAAO;EACf,iBAAiB,aAAa,IAAI,KAAK,WAAW,aAAa,GAAG,KAAA;EAClE,SAAS,OAAO;EACjB;;;;;;;AAeH,eAAsB,oBACpB,QACA,SAC4B;CAC5B,MAAM,OAAO,OAAO;AACpB,KAAI,CAAC,MAAM,WACT,OAAM,IAAI,MACR,4DACD;AAKH,QAAO;EACL,OAHY,MAAM,OAAO,eAAe,WAAW,EAAE,CAAC;EAItD,KAAK,OAAO;EACZ,SAAS,KAAK;EACd,WAAW,SAAS,aAAa;EAClC;;;;;;;AAQH,SAAgB,YAAY,QAAwB;CAElD,MAAM,QADU,OAAO,MAAM,CAAC,aAAa,CACrB,MAAM,kBAAkB;AAE9C,KAAI,CAAC,MACH,OAAM,IAAI,MACR,0BAA0B,OAAO,sFAClC;CAGH,MAAM,QAAQ,OAAO,MAAM,GAAG;CAC9B,MAAM,OAAO,MAAM;AAEnB,KAAI,SAAS,EACX,OAAM,IAAI,MACR,0BAA0B,OAAO,qCAClC;AAGH,SAAQ,MAAR;EACE,KAAK,IACH,QAAO,QAAQ;EACjB,KAAK,IACH,QAAO,QAAQ,KAAK;EACtB,QACE,QAAO;;;;;;AAOb,SAAgB,aAAa,WAA2B;CACtD,MAAM,OAAO,KAAK,MAAM,YAAY,eAAe;CACnD,MAAM,QAAQ,KAAK,MAAO,YAAY,iBAAkB,KAAK;AAE7D,KAAI,OAAO,GAAG;EACZ,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,IAAI,MAAM;AAChD,MAAI,QAAQ,EACV,QAAO,GAAG,OAAO,OAAO,MAAM,OAAO,UAAU,IAAI,MAAM;AAE3D,SAAO;;AAGT,KAAI,QAAQ,EACV,QAAO,GAAG,MAAM,OAAO,UAAU,IAAI,MAAM;AAG7C,QAAO,GAAG,UAAU,SAAS,cAAc,IAAI,MAAM"}
|
|
1
|
+
{"version":3,"file":"node.js","names":["#filePath","#envKeyName","#logger","#parseKeyPair","#loadFromFile","#saveToFile","#emitter","#revalidate"],"sources":["../src/crypto/node-key-storage.ts","../src/event/event.node.ts","../src/storage/storage.node.ts","../src/init.node.ts","../src/login.node.ts","../src/session.ts"],"sourcesContent":["import {\n existsSync,\n mkdirSync,\n readFileSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport type { ILogger } from \"../utils.js\";\nimport type { JsonWebKeyPairStorage, JwkKeyPair } from \"./types.js\";\n\nexport const RENOWN_PRIVATE_KEY_ENV = \"PH_RENOWN_PRIVATE_KEY\";\nexport const DEFAULT_KEYPAIR_PATH = \".ph/.keypair.json\";\n\n/**\n * Key storage that supports:\n * 1. PH_RENOWN_PRIVATE_KEY environment variable (JSON-encoded JwkKeyPair)\n * 2. Custom file path passed via options\n * 3. Falls back to file storage at .ph/.keypair.json in current working directory\n */\nexport class NodeKeyStorage implements JsonWebKeyPairStorage {\n #filePath: string;\n #envKeyName: string;\n #logger?: ILogger;\n\n static readonly DEFAULT_KEYPAIR_PATH = DEFAULT_KEYPAIR_PATH;\n static readonly RENOWN_PRIVATE_KEY_ENV = RENOWN_PRIVATE_KEY_ENV;\n\n constructor(\n filePath?: string,\n options: { envKeyName?: string; logger?: ILogger } = {},\n ) {\n this.#filePath = filePath || join(process.cwd(), DEFAULT_KEYPAIR_PATH);\n this.#envKeyName = options.envKeyName || RENOWN_PRIVATE_KEY_ENV;\n this.#logger = options.logger;\n\n // Ensure directory exists\n const dir = dirname(this.#filePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n }\n\n loadKeyPair(): Promise<JwkKeyPair | undefined> {\n // First check environment variable\n const envKey = process.env[this.#envKeyName];\n if (envKey) {\n try {\n const keyPairJson = JSON.parse(envKey) as unknown;\n const keyPair: JwkKeyPair = this.#parseKeyPair(keyPairJson);\n\n // Validate it has the required structure\n this.#logger?.debug(\"Loaded keypair from environment variable\");\n return Promise.resolve(keyPair);\n } catch (e) {\n throw new Error(\n `Failed to parse ${this.#envKeyName}: ${e instanceof Error ? e.message : String(e)}`,\n {\n cause: e,\n },\n );\n }\n }\n\n // Fall back to file storage\n return Promise.resolve(this.#loadFromFile());\n }\n\n async saveKeyPair(keyPair: JwkKeyPair): Promise<void> {\n // Don't save if using env var\n if (process.env[this.#envKeyName]) {\n return;\n }\n\n // Save to file\n this.#saveToFile(keyPair);\n return Promise.resolve();\n }\n\n removeKeyPair(): Promise<void> {\n if (process.env[this.#envKeyName]) {\n delete process.env[this.#envKeyName];\n }\n if (existsSync(this.#filePath)) {\n unlinkSync(this.#filePath);\n }\n return Promise.resolve();\n }\n\n #loadFromFile(): JwkKeyPair | undefined {\n try {\n if (!existsSync(this.#filePath)) {\n return undefined;\n }\n const data = readFileSync(this.#filePath, \"utf-8\");\n const parsed = JSON.parse(data) as unknown;\n const keyPair: JwkKeyPair = this.#parseKeyPair(parsed);\n this.#logger?.debug(`Loaded keypair from ${this.#filePath}`);\n return keyPair;\n } catch (e) {\n throw new Error(\n `Failed to parse ${this.#filePath}: ${e instanceof Error ? e.message : String(e)}`,\n {\n cause: e,\n },\n );\n }\n }\n\n #saveToFile(keyPair: JwkKeyPair): void {\n const data = { keyPair };\n writeFileSync(this.#filePath, JSON.stringify(data, null, 2), \"utf-8\");\n this.#logger?.debug(`Saved keypair to ${this.#filePath}`);\n }\n\n #parseKeyPair(json: unknown): JwkKeyPair {\n if (typeof json !== \"object\") {\n throw new Error(\"Invalid keyPair format:\" + JSON.stringify(json));\n }\n\n const object = json as JwkKeyPair | { keyPair: JwkKeyPair };\n let keyPair: JwkKeyPair;\n if (\"keyPair\" in object) {\n keyPair = object.keyPair;\n } else {\n keyPair = object;\n }\n\n if (\"publicKey\" in keyPair && \"privateKey\" in keyPair) {\n return keyPair;\n } else {\n throw new Error(\"Invalid keyPair format:\" + JSON.stringify(json));\n }\n }\n}\n","import { EventEmitter } from \"node:events\";\nimport type { IEventEmitter } from \"./types.js\";\n\nexport class NodeEventEmitter<\n Events extends Record<string, unknown>,\n> implements IEventEmitter<Events> {\n #emitter = new EventEmitter();\n\n constructor() {\n this.#emitter.setMaxListeners(0);\n }\n\n on<K extends keyof Events>(\n event: K,\n listener: (data: Events[K]) => void,\n ): () => void {\n this.#emitter.on(event as string, listener);\n return () => {\n this.#emitter.removeListener(event.toString(), listener);\n };\n }\n\n emit<K extends keyof Events>(event: K, data: Events[K]): void {\n this.#emitter.emit(event.toString(), data);\n }\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { BaseStorage } from \"./common.js\";\n\nexport class NodeStorage<\n T extends Record<string, unknown> = Record<string, unknown>,\n> extends BaseStorage<T> {\n private readonly filePath: string;\n\n constructor(filePath: string) {\n super();\n this.filePath = filePath;\n\n const parentFolder = dirname(this.filePath);\n mkdirSync(parentFolder, { recursive: true });\n\n if (!existsSync(this.filePath)) {\n writeFileSync(this.filePath, JSON.stringify({}));\n }\n }\n\n private readData(): T {\n const data = readFileSync(this.filePath, \"utf-8\");\n return JSON.parse(data) as T;\n }\n\n private writeData(data: T): void {\n writeFileSync(this.filePath, JSON.stringify(data, null, 2));\n }\n\n get<Key extends keyof T>(key: Key): T[Key] | undefined {\n const data = this.readData();\n return data[key];\n }\n\n set<Key extends keyof T>(key: Key, value?: T[Key]): void {\n const data = this.readData();\n if (value === undefined) {\n delete data[key];\n } else {\n data[key] = value;\n }\n this.writeData(data);\n }\n\n delete(key: keyof T): void {\n const data = this.readData();\n delete data[key];\n this.writeData(data);\n }\n}\n","import type { Renown } from \"./common.js\";\nimport { NodeKeyStorage } from \"./crypto/node-key-storage.js\";\nimport { NodeEventEmitter } from \"./event/event.node.js\";\nimport { BaseRenownBuilder } from \"./renown-builder.js\";\nimport { NodeStorage } from \"./storage/storage.node.js\";\nimport type { RenownEvents, RenownStorageMap } from \"./types.js\";\n\nexport class NodeRenownEventEmitter extends NodeEventEmitter<RenownEvents> {}\nexport class NodeRenownStorage extends NodeStorage<RenownStorageMap> {}\n\nexport const DEFAULT_RENOWN_STORAGE_PATH = \"./.ph/.renown.json\";\n\nexport interface NodeRenownBuilderOptions {\n /** File path for user storage. Defaults to \".ph/.renown.json\" in cwd */\n storagePath?: string;\n /** File path for keypair storage. Defaults to \".ph/.keypair.json\" in cwd */\n keyPath?: string;\n /** Renown server URL. Defaults to https://www.renown.id */\n baseUrl?: string;\n /** Switchboard GraphQL endpoint for the direct-to-reactor flow. */\n switchboardUrl?: string;\n /** Re-check a stored credential on build (default \"always\"). */\n revalidate?: \"always\" | \"never\";\n}\n\n/** Node.js Renown builder: file-based user + key storage. */\nexport class RenownBuilder extends BaseRenownBuilder {\n #revalidate: \"always\" | \"never\";\n\n constructor(appName: string, options: NodeRenownBuilderOptions = {}) {\n super(appName);\n\n const {\n storagePath = DEFAULT_RENOWN_STORAGE_PATH,\n keyPath,\n baseUrl,\n switchboardUrl,\n revalidate = \"always\",\n } = options;\n\n this.#revalidate = revalidate;\n this.withKeyPairStorage(new NodeKeyStorage(keyPath));\n this.withStorage(new NodeRenownStorage(storagePath));\n this.withEventEmitter(new NodeRenownEventEmitter());\n if (baseUrl) {\n this.withBaseUrl(baseUrl);\n }\n if (switchboardUrl) {\n this.withSwitchboardUrl(switchboardUrl);\n }\n }\n\n // Node scripts/CLI act on auth state once and exit, so revalidation must\n // finish before build() resolves — block on it (unlike the browser builder).\n async build(): Promise<Renown> {\n const renown = await super.build();\n if (this.#revalidate === \"always\" && renown.user) {\n await renown.revalidate();\n }\n return renown;\n }\n}\n","import type { CreateBearerTokenOptions, IRenown, User } from \"./types.js\";\n\nconst DEFAULT_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes\nconst POLL_INTERVAL_MS = 500; // 0.5s: session store is in-memory, cheap to poll\nconst SECONDS_IN_DAY = 24 * 60 * 60;\n\nexport interface BrowserLoginOptions {\n /** Renown server URL */\n renownUrl: string;\n /** Timeout in milliseconds. Defaults to 5 minutes. */\n timeoutMs?: number;\n /** Called with the login URL and session ID before the browser is opened */\n onLoginUrl?: (url: string, sessionId: string) => void;\n /** Called on each poll tick while waiting for authentication */\n onPollTick?: () => void;\n /** Called when the browser failed to open automatically */\n onBrowserOpenFailed?: (url: string) => void;\n /** AbortSignal to cancel the login flow */\n signal?: AbortSignal;\n}\n\nexport interface BrowserLoginResult {\n user: User;\n cliDid: string;\n}\n\nexport interface AuthStatusResult {\n authenticated: boolean;\n address?: string;\n userDid?: string;\n chainId?: number;\n cliDid: string;\n authenticatedAt?: Date;\n baseUrl: string;\n}\n\ninterface PendingSessionResponse {\n sessionId: string;\n status: \"pending\";\n}\n\ninterface ReadySessionResponse {\n sessionId: string;\n status: \"ready\";\n address: string;\n chainId: number;\n did: string;\n credentialId: string;\n userDocumentId: string;\n}\n\ntype SessionResponse = PendingSessionResponse | ReadySessionResponse;\n\nfunction abortReason(signal: AbortSignal): Error {\n if (signal.reason instanceof Error) return signal.reason;\n if (signal.reason) return new Error(String(signal.reason));\n return new DOMException(\"Aborted\", \"AbortError\");\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(abortReason(signal));\n return;\n }\n const onAbort = () => {\n clearTimeout(timer);\n reject(abortReason(signal!));\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\n/**\n * Open a URL in the default browser (cross-platform).\n */\nexport async function openBrowser(url: string): Promise<void> {\n const { execFile } = await import(\"node:child_process\");\n const { promisify } = await import(\"node:util\");\n const execFileAsync = promisify(execFile);\n const platform = process.platform;\n\n if (platform === \"darwin\") {\n await execFileAsync(\"open\", [url]);\n } else if (platform === \"win32\") {\n await execFileAsync(\"cmd\", [\"/c\", \"start\", \"\", url]);\n } else {\n await execFileAsync(\"xdg-open\", [url]);\n }\n}\n\nasync function pollSession(\n renownUrl: string,\n sessionId: string,\n timeoutMs: number,\n onPollTick?: () => void,\n signal?: AbortSignal,\n): Promise<ReadySessionResponse | null> {\n const startTime = Date.now();\n const sessionUrl = new URL(\n `/api/console/session/${sessionId}`,\n renownUrl,\n ).toString();\n\n while (Date.now() - startTime < timeoutMs) {\n signal?.throwIfAborted();\n try {\n const response = await fetch(sessionUrl, { signal });\n if (response.ok) {\n const data = (await response.json()) as SessionResponse;\n if (data.status === \"ready\") {\n return data;\n }\n }\n } catch (error) {\n if (error instanceof DOMException && error.name === \"AbortError\")\n throw error;\n // Network error, will retry\n }\n onPollTick?.();\n await sleep(POLL_INTERVAL_MS, signal);\n }\n\n return null;\n}\n\n/**\n * Perform a browser-based login flow with Renown.\n * Opens the user's browser to authenticate, then polls for completion.\n * Throws if already authenticated or if the flow times out.\n * If the browser fails to open, the flow continues polling — callers\n * can use onBrowserOpenFailed to show the URL as a fallback.\n */\nexport async function browserLogin(\n renown: IRenown,\n options: BrowserLoginOptions,\n): Promise<BrowserLoginResult> {\n if (renown.user?.credential) {\n throw new Error(\n `Already authenticated as ${renown.user.address}. Logout first.`,\n );\n }\n\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const sessionId = crypto.randomUUID();\n\n const loginUrl = new URL(\"/console\", options.renownUrl);\n loginUrl.searchParams.set(\"session\", sessionId);\n loginUrl.searchParams.set(\"connect\", renown.did);\n loginUrl.searchParams.set(\"app\", renown.did);\n\n const url = loginUrl.toString();\n options.onLoginUrl?.(url, sessionId);\n\n try {\n await openBrowser(url);\n } catch {\n options.onBrowserOpenFailed?.(url);\n }\n\n const result = await pollSession(\n options.renownUrl,\n sessionId,\n timeoutMs,\n options.onPollTick,\n options.signal,\n );\n\n if (!result) {\n throw new Error(\"Authentication timed out.\");\n }\n\n const user = await renown.login(result.did);\n return { user, cliDid: renown.did };\n}\n\n/**\n * Get the current authentication status from a Renown instance.\n */\nexport function getAuthStatus(renown: IRenown): AuthStatusResult {\n const user = renown.user;\n const credential = user?.credential;\n\n return {\n authenticated: !!credential,\n address: user?.address,\n userDid: user?.did,\n chainId: user?.chainId,\n cliDid: renown.did,\n authenticatedAt: credential ? new Date(credential.issuanceDate) : undefined,\n baseUrl: renown.baseUrl,\n };\n}\n\nexport interface AccessTokenResult {\n token: string;\n did: string;\n address: string;\n expiresIn: number;\n}\n\n/**\n * Generate a bearer token for API authentication.\n * Requires the user to be authenticated (via browserLogin or equivalent).\n * Throws if not authenticated.\n */\nexport async function generateAccessToken(\n renown: IRenown,\n options?: CreateBearerTokenOptions,\n): Promise<AccessTokenResult> {\n const user = renown.user;\n if (!user?.credential) {\n throw new Error(\n \"Not authenticated. Login first to generate access tokens.\",\n );\n }\n\n const token = await renown.getBearerToken(options ?? {});\n\n return {\n token,\n did: renown.did,\n address: user.address,\n expiresIn: options?.expiresIn ?? 0,\n };\n}\n\n/**\n * Parse a human-readable expiry string to seconds.\n * Supports formats: \"7d\" (days), \"24h\" (hours), \"3600\" (seconds), \"3600s\" (seconds).\n * Only accepts positive integers — decimals like \"1.5h\" are rejected.\n */\nexport function parseExpiry(expiry: string): number {\n const trimmed = expiry.trim().toLowerCase();\n const match = trimmed.match(/^(\\d+)(d|h|s)?$/);\n\n if (!match) {\n throw new Error(\n `Invalid expiry format: ${expiry}. Expected a positive integer with optional suffix: \"7d\", \"24h\", \"3600s\", or \"3600\".`,\n );\n }\n\n const value = Number(match[1]);\n const unit = match[2];\n\n if (value <= 0) {\n throw new Error(\n `Invalid expiry format: ${expiry}. Value must be a positive integer.`,\n );\n }\n\n switch (unit) {\n case \"d\":\n return value * SECONDS_IN_DAY;\n case \"h\":\n return value * 60 * 60;\n default:\n return value;\n }\n}\n\n/**\n * Format an expiry duration in seconds to a human-readable string.\n */\nexport function formatExpiry(expiresIn: number): string {\n const days = Math.floor(expiresIn / SECONDS_IN_DAY);\n const hours = Math.floor((expiresIn % SECONDS_IN_DAY) / 3600);\n\n if (days > 0) {\n const dayStr = `${days} day${days !== 1 ? \"s\" : \"\"}`;\n if (hours > 0) {\n return `${dayStr} and ${hours} hour${hours !== 1 ? \"s\" : \"\"}`;\n }\n return dayStr;\n }\n\n if (hours > 0) {\n return `${hours} hour${hours !== 1 ? \"s\" : \"\"}`;\n }\n\n return `${expiresIn} second${expiresIn !== 1 ? \"s\" : \"\"}`;\n}\n","import { verifyAuthCredential } from \"./credential.js\";\nimport type { User } from \"./types.js\";\nimport { verifyAuthBearerToken } from \"./utils.js\";\n\n/** Server-side view of an authenticated Renown session. */\nexport interface RenownSession {\n /** Authenticated user identity derived from the session token. */\n user: User;\n /** App/client DID the token was issued by. */\n appDid: string;\n /** Token expiry (epoch seconds), when present. */\n expiresAt?: number;\n}\n\n/** Display hint carried alongside the token (unverified; not for auth). */\nexport interface RenownSessionProfile {\n name?: string | null;\n avatar?: string | null;\n}\n\n/** The `renown_session` cookie payload: the bearer token + a display hint. */\nexport interface RenownSessionCookie {\n token: string;\n profile?: RenownSessionProfile | null;\n}\n\nexport interface VerifyRenownSessionOptions {\n /** Expected `aud` claim on the token. */\n audience?: string;\n /** Switchboard GraphQL endpoint used when re-verifying the credential. */\n switchboardUrl?: string;\n /** Renown service base URL used when re-verifying the credential. */\n renownUrl?: string;\n /** Also re-verify the credential against Switchboard (default false = token-only, no network). */\n verifyCredential?: boolean;\n}\n\n/** Claims read from a session token without verifying its signature. */\nexport interface RenownSessionClaims {\n address?: string;\n chainId?: number;\n networkId?: string;\n /** Expiry in epoch seconds. */\n expiresAt?: number;\n}\n\n/** Serialize a session cookie value (token + optional display hint). */\nexport function serializeRenownSessionCookie(\n value: RenownSessionCookie,\n): string {\n return JSON.stringify(value);\n}\n\n// Parse the JSON cookie envelope { token, profile }; undefined if malformed.\nfunction parseSessionCookie(value: string): RenownSessionCookie | undefined {\n try {\n const parsed = JSON.parse(value) as unknown;\n if (\n parsed &&\n typeof parsed === \"object\" &&\n typeof (parsed as RenownSessionCookie).token === \"string\"\n ) {\n return parsed as RenownSessionCookie;\n }\n } catch {\n // Malformed cookie.\n }\n return undefined;\n}\n\n// Decode a session cookie's claims WITHOUT verifying the signature — cheap\n// optimistic checks only (e.g. proxy redirect); never trust for access control.\nexport function readSessionClaims(\n value: string,\n): RenownSessionClaims | undefined {\n const parsed = parseSessionCookie(value);\n if (!parsed) return undefined;\n const parts = parsed.token.split(\".\");\n if (parts.length < 2) return undefined;\n try {\n const json = Buffer.from(parts[1], \"base64url\").toString(\"utf8\");\n const payload = JSON.parse(json) as {\n exp?: number;\n vc?: {\n credentialSubject?: {\n address?: string;\n chainId?: number;\n networkId?: string;\n };\n };\n };\n const subject = payload.vc?.credentialSubject;\n return {\n address: subject?.address,\n chainId: subject?.chainId,\n networkId: subject?.networkId,\n expiresAt: payload.exp,\n };\n } catch {\n return undefined;\n }\n}\n\n// Verify a session cookie (envelope or bare token) → user, or undefined. Pass\n// verifyCredential:true to also check vs Switchboard; envelope hint is merged.\nexport async function verifyRenownSession(\n value: string,\n options: VerifyRenownSessionOptions = {},\n): Promise<RenownSession | undefined> {\n const { verifyCredential = false } = options;\n const parsed = parseSessionCookie(value);\n if (!parsed) return undefined;\n const { token, profile } = parsed;\n\n let session: RenownSession | undefined;\n if (verifyCredential) {\n const verified = await verifyAuthCredential(token, {\n audience: options.audience,\n renownUrl: options.renownUrl,\n switchboardUrl: options.switchboardUrl,\n });\n if (verified) {\n session = {\n user: {\n address: verified.address as `0x${string}`,\n chainId: verified.chainId,\n networkId: verified.networkId,\n did: verified.did,\n credential: verified.credential,\n },\n appDid: verified.appDid,\n };\n }\n } else {\n const verified = await verifyAuthBearerToken(token, {\n audience: options.audience,\n });\n if (verified) {\n const subject = verified.verifiableCredential.credentialSubject;\n const did = `did:pkh:${subject.networkId}:${subject.chainId}:${subject.address.toLowerCase()}`;\n session = {\n user: {\n address: subject.address as `0x${string}`,\n chainId: subject.chainId,\n networkId: subject.networkId,\n did,\n credential: undefined,\n },\n appDid: verified.issuer,\n expiresAt: verified.payload.exp,\n };\n }\n }\n\n if (!session) return undefined;\n\n // Merge the (unverified) display hint from the envelope — display only.\n if (profile?.name) {\n session.user = {\n ...session.user,\n ens: { name: profile.name, avatarUrl: profile.avatar ?? undefined },\n };\n }\n return session;\n}\n"],"mappings":"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"}
|