@private.me/xbind 2.3.4 → 3.0.1
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 +10 -3
- package/dist-standalone/cjs/version-info.js +1 -1
- package/dist-standalone/plugins/logging.d.ts +84 -0
- package/dist-standalone/plugins/logging.js +1 -0
- package/dist-standalone/plugins/metrics.d.ts +111 -0
- package/dist-standalone/plugins/metrics.js +1 -0
- package/dist-standalone/plugins/validation.d.ts +104 -0
- package/dist-standalone/plugins/validation.js +1 -0
- package/dist-standalone/runtime/browser.d.ts +311 -0
- package/dist-standalone/runtime/browser.js +1 -0
- package/dist-standalone/runtime/edge.d.ts +282 -0
- package/dist-standalone/runtime/edge.js +1 -0
- package/dist-standalone/runtime/react-native.d.ts +157 -0
- package/dist-standalone/runtime/react-native.js +1 -0
- package/dist-standalone/types/error-response.d.ts +209 -0
- package/dist-standalone/types/error-response.js +1 -0
- package/dist-standalone/version-info.js +1 -1
- package/package.json +4 -1
- package/share1.dat +0 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser Runtime Compatibility Layer
|
|
3
|
+
*
|
|
4
|
+
* Provides polyfills and runtime detection for browser environments.
|
|
5
|
+
* Ensures xBind works seamlessly in Chrome, Firefox, Safari, and service workers.
|
|
6
|
+
*
|
|
7
|
+
* @module runtime/browser
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
import type { Result } from '@private.me/shared';
|
|
11
|
+
/**
|
|
12
|
+
* Runtime environment type.
|
|
13
|
+
*/
|
|
14
|
+
export type RuntimeEnvironment = 'browser' | 'node' | 'service-worker' | 'web-worker' | 'unknown';
|
|
15
|
+
/**
|
|
16
|
+
* Detect the current runtime environment.
|
|
17
|
+
*
|
|
18
|
+
* @returns The detected runtime environment type
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* import { detectRuntime } from '@private.me/xbind/runtime/browser';
|
|
23
|
+
*
|
|
24
|
+
* const runtime = detectRuntime();
|
|
25
|
+
* console.log('Running in:', runtime);
|
|
26
|
+
* // => 'browser' | 'node' | 'service-worker' | 'web-worker' | 'unknown'
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function detectRuntime(): RuntimeEnvironment;
|
|
30
|
+
/**
|
|
31
|
+
* Check if running in a browser environment.
|
|
32
|
+
*
|
|
33
|
+
* @returns True if running in browser, service worker, or web worker
|
|
34
|
+
*/
|
|
35
|
+
export declare function isBrowser(): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Check if running in Node.js environment.
|
|
38
|
+
*
|
|
39
|
+
* @returns True if running in Node.js
|
|
40
|
+
*/
|
|
41
|
+
export declare function isNode(): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Check if running in a service worker.
|
|
44
|
+
*
|
|
45
|
+
* @returns True if running in service worker context
|
|
46
|
+
*/
|
|
47
|
+
export declare function isServiceWorker(): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Browser-compatible random bytes generator.
|
|
50
|
+
* Uses Web Crypto API's getRandomValues.
|
|
51
|
+
*
|
|
52
|
+
* @param length - Number of random bytes to generate
|
|
53
|
+
* @returns Uint8Array of cryptographically secure random bytes
|
|
54
|
+
*
|
|
55
|
+
* @throws {Error} If Web Crypto API is not available
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* import { getRandomBytes } from '@private.me/xbind/runtime/browser';
|
|
60
|
+
*
|
|
61
|
+
* const nonce = getRandomBytes(16);
|
|
62
|
+
* console.log('Random nonce:', nonce);
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export declare function getRandomBytes(length: number): Uint8Array;
|
|
66
|
+
/**
|
|
67
|
+
* Generate a cryptographically secure random UUID.
|
|
68
|
+
* Uses Web Crypto API's randomUUID if available, falls back to manual generation.
|
|
69
|
+
*
|
|
70
|
+
* @returns UUID v4 string (e.g., "550e8400-e29b-41d4-a716-446655440000")
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* import { generateUUID } from '@private.me/xbind/runtime/browser';
|
|
75
|
+
*
|
|
76
|
+
* const id = generateUUID();
|
|
77
|
+
* console.log('Generated UUID:', id);
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export declare function generateUUID(): string;
|
|
81
|
+
/**
|
|
82
|
+
* Browser storage interface for persisting agent identity and state.
|
|
83
|
+
*/
|
|
84
|
+
export interface BrowserStorage {
|
|
85
|
+
/**
|
|
86
|
+
* Store a value.
|
|
87
|
+
* @param key - Storage key
|
|
88
|
+
* @param value - Value to store (will be JSON serialized)
|
|
89
|
+
*/
|
|
90
|
+
setItem(key: string, value: unknown): Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* Retrieve a value.
|
|
93
|
+
* @param key - Storage key
|
|
94
|
+
* @returns Stored value or null if not found
|
|
95
|
+
*/
|
|
96
|
+
getItem<T = unknown>(key: string): Promise<T | null>;
|
|
97
|
+
/**
|
|
98
|
+
* Remove a value.
|
|
99
|
+
* @param key - Storage key
|
|
100
|
+
*/
|
|
101
|
+
removeItem(key: string): Promise<void>;
|
|
102
|
+
/**
|
|
103
|
+
* Clear all stored values.
|
|
104
|
+
*/
|
|
105
|
+
clear(): Promise<void>;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* LocalStorage-based implementation of BrowserStorage.
|
|
109
|
+
* Suitable for browser environments with persistent storage needs.
|
|
110
|
+
*/
|
|
111
|
+
export declare class LocalStorageAdapter implements BrowserStorage {
|
|
112
|
+
private prefix;
|
|
113
|
+
/**
|
|
114
|
+
* Create a new LocalStorage adapter.
|
|
115
|
+
*
|
|
116
|
+
* @param prefix - Key prefix to avoid collisions (default: 'xbind:')
|
|
117
|
+
*/
|
|
118
|
+
constructor(prefix?: string);
|
|
119
|
+
setItem(key: string, value: unknown): Promise<void>;
|
|
120
|
+
getItem<T = unknown>(key: string): Promise<T | null>;
|
|
121
|
+
removeItem(key: string): Promise<void>;
|
|
122
|
+
clear(): Promise<void>;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* IndexedDB-based implementation of BrowserStorage.
|
|
126
|
+
* Suitable for larger datasets and service workers.
|
|
127
|
+
*/
|
|
128
|
+
export declare class IndexedDBAdapter implements BrowserStorage {
|
|
129
|
+
private dbName;
|
|
130
|
+
private storeName;
|
|
131
|
+
private version;
|
|
132
|
+
private db;
|
|
133
|
+
/**
|
|
134
|
+
* Create a new IndexedDB adapter.
|
|
135
|
+
*
|
|
136
|
+
* @param dbName - Database name (default: 'xbind-storage')
|
|
137
|
+
* @param storeName - Object store name (default: 'keyval')
|
|
138
|
+
* @param version - Database version (default: 1)
|
|
139
|
+
*/
|
|
140
|
+
constructor(dbName?: string, storeName?: string, version?: number);
|
|
141
|
+
/**
|
|
142
|
+
* Initialize the database connection.
|
|
143
|
+
* Called automatically by storage operations.
|
|
144
|
+
*/
|
|
145
|
+
private ensureDB;
|
|
146
|
+
setItem(key: string, value: unknown): Promise<void>;
|
|
147
|
+
getItem<T = unknown>(key: string): Promise<T | null>;
|
|
148
|
+
removeItem(key: string): Promise<void>;
|
|
149
|
+
clear(): Promise<void>;
|
|
150
|
+
/**
|
|
151
|
+
* Close the database connection.
|
|
152
|
+
* Should be called when storage is no longer needed.
|
|
153
|
+
*/
|
|
154
|
+
close(): void;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* In-memory storage implementation for testing and ephemeral use cases.
|
|
158
|
+
*/
|
|
159
|
+
export declare class MemoryStorageAdapter implements BrowserStorage {
|
|
160
|
+
private store;
|
|
161
|
+
setItem(key: string, value: unknown): Promise<void>;
|
|
162
|
+
getItem<T = unknown>(key: string): Promise<T | null>;
|
|
163
|
+
removeItem(key: string): Promise<void>;
|
|
164
|
+
clear(): Promise<void>;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Error codes for WASM operations.
|
|
168
|
+
*/
|
|
169
|
+
export type WasmError = 'WASM_NOT_SUPPORTED' | 'WASM_LOAD_FAILED' | 'WASM_INIT_FAILED';
|
|
170
|
+
/**
|
|
171
|
+
* Check if WebAssembly is supported in the current environment.
|
|
172
|
+
*
|
|
173
|
+
* @returns True if WebAssembly is supported
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```typescript
|
|
177
|
+
* import { isWasmSupported } from '@private.me/xbind/runtime/browser';
|
|
178
|
+
*
|
|
179
|
+
* if (isWasmSupported()) {
|
|
180
|
+
* console.log('WASM is supported - using post-quantum crypto');
|
|
181
|
+
* } else {
|
|
182
|
+
* console.log('WASM not supported - falling back to classical crypto');
|
|
183
|
+
* }
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
export declare function isWasmSupported(): boolean;
|
|
187
|
+
/**
|
|
188
|
+
* Load and instantiate a WebAssembly module from a URL.
|
|
189
|
+
*
|
|
190
|
+
* @param url - URL to WASM module
|
|
191
|
+
* @param importObject - Optional imports for WASM module
|
|
192
|
+
* @returns Result containing the WASM instance or error
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* ```typescript
|
|
196
|
+
* import { loadWasmModule } from '@private.me/xbind/runtime/browser';
|
|
197
|
+
*
|
|
198
|
+
* const result = await loadWasmModule('/mldsa.wasm');
|
|
199
|
+
* if (!result.ok) {
|
|
200
|
+
* console.error('Failed to load WASM:', result.error);
|
|
201
|
+
* return;
|
|
202
|
+
* }
|
|
203
|
+
*
|
|
204
|
+
* const instance = result.value;
|
|
205
|
+
* // Use WASM exports...
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
export declare function loadWasmModule(url: string, importObject?: WebAssembly.Imports): Promise<Result<WebAssembly.Instance, WasmError>>;
|
|
209
|
+
/**
|
|
210
|
+
* Browser capabilities and feature support.
|
|
211
|
+
*/
|
|
212
|
+
export interface BrowserCapabilities {
|
|
213
|
+
/** WebCrypto API available */
|
|
214
|
+
webCrypto: boolean;
|
|
215
|
+
/** SubtleCrypto API available */
|
|
216
|
+
subtleCrypto: boolean;
|
|
217
|
+
/** Ed25519 support in WebCrypto */
|
|
218
|
+
ed25519: boolean;
|
|
219
|
+
/** X25519 support in WebCrypto */
|
|
220
|
+
x25519: boolean;
|
|
221
|
+
/** WebAssembly support */
|
|
222
|
+
wasm: boolean;
|
|
223
|
+
/** IndexedDB support */
|
|
224
|
+
indexedDB: boolean;
|
|
225
|
+
/** LocalStorage support */
|
|
226
|
+
localStorage: boolean;
|
|
227
|
+
/** Service Worker support */
|
|
228
|
+
serviceWorker: boolean;
|
|
229
|
+
/** Web Worker support */
|
|
230
|
+
webWorker: boolean;
|
|
231
|
+
/** Fetch API support */
|
|
232
|
+
fetch: boolean;
|
|
233
|
+
/** TextEncoder/TextDecoder support */
|
|
234
|
+
textEncoding: boolean;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Detect browser capabilities and feature support.
|
|
238
|
+
*
|
|
239
|
+
* @returns Object describing available browser features
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```typescript
|
|
243
|
+
* import { detectCapabilities } from '@private.me/xbind/runtime/browser';
|
|
244
|
+
*
|
|
245
|
+
* const caps = await detectCapabilities();
|
|
246
|
+
*
|
|
247
|
+
* if (!caps.webCrypto) {
|
|
248
|
+
* throw new Error('WebCrypto required for xBind');
|
|
249
|
+
* }
|
|
250
|
+
*
|
|
251
|
+
* if (!caps.ed25519) {
|
|
252
|
+
* console.warn('Ed25519 not supported - using polyfill');
|
|
253
|
+
* }
|
|
254
|
+
*
|
|
255
|
+
* if (caps.serviceWorker) {
|
|
256
|
+
* console.log('Service workers supported - can run in background');
|
|
257
|
+
* }
|
|
258
|
+
* ```
|
|
259
|
+
*/
|
|
260
|
+
export declare function detectCapabilities(): Promise<BrowserCapabilities>;
|
|
261
|
+
/**
|
|
262
|
+
* Install Node.js compatibility shims for browser environments.
|
|
263
|
+
* Only installs missing APIs - does not override existing implementations.
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* ```typescript
|
|
267
|
+
* import { installNodePolyfills } from '@private.me/xbind/runtime/browser';
|
|
268
|
+
*
|
|
269
|
+
* // Install polyfills at application startup
|
|
270
|
+
* installNodePolyfills();
|
|
271
|
+
*
|
|
272
|
+
* // Now Node.js-style code works in browser
|
|
273
|
+
* import { randomBytes } from 'crypto'; // Works!
|
|
274
|
+
* ```
|
|
275
|
+
*/
|
|
276
|
+
export declare function installNodePolyfills(): void;
|
|
277
|
+
/**
|
|
278
|
+
* Configuration for service worker compatibility mode.
|
|
279
|
+
*/
|
|
280
|
+
export interface ServiceWorkerConfig {
|
|
281
|
+
/** Cache strategy for WASM modules */
|
|
282
|
+
wasmCacheStrategy: 'cache-first' | 'network-first' | 'no-cache';
|
|
283
|
+
/** Maximum age for cached WASM modules (milliseconds) */
|
|
284
|
+
wasmCacheMaxAge: number;
|
|
285
|
+
/** Enable background sync for pending messages */
|
|
286
|
+
enableBackgroundSync: boolean;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Default service worker configuration.
|
|
290
|
+
*/
|
|
291
|
+
export declare const DEFAULT_SERVICE_WORKER_CONFIG: ServiceWorkerConfig;
|
|
292
|
+
/**
|
|
293
|
+
* Initialize xBind for service worker environment.
|
|
294
|
+
* Sets up caching strategies and background sync handlers.
|
|
295
|
+
*
|
|
296
|
+
* @param config - Service worker configuration
|
|
297
|
+
*
|
|
298
|
+
* @example
|
|
299
|
+
* ```typescript
|
|
300
|
+
* // In your service worker (sw.js):
|
|
301
|
+
* import { initServiceWorker } from '@private.me/xbind/runtime/browser';
|
|
302
|
+
*
|
|
303
|
+
* initServiceWorker({
|
|
304
|
+
* wasmCacheStrategy: 'cache-first',
|
|
305
|
+
* wasmCacheMaxAge: 24 * 60 * 60 * 1000,
|
|
306
|
+
* enableBackgroundSync: true,
|
|
307
|
+
* });
|
|
308
|
+
* ```
|
|
309
|
+
*/
|
|
310
|
+
export declare function initServiceWorker(config?: Partial<ServiceWorkerConfig>): void;
|
|
311
|
+
export type { Result, };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ok,err}from"../_deps/shared/index.js";export function detectRuntime(){return"undefined"!=typeof self&&"ServiceWorkerGlobalScope"in self?"service-worker":"undefined"!=typeof self&&"WorkerGlobalScope"in self&&"importScripts"in self?"web-worker":"undefined"!=typeof window&&"undefined"!=typeof document?"browser":"undefined"!=typeof process&&process.versions&&process.versions.node?"node":"unknown"}export function isBrowser(){const e=detectRuntime();return"browser"===e||"service-worker"===e||"web-worker"===e}export function isNode(){return"node"===detectRuntime()}export function isServiceWorker(){return"service-worker"===detectRuntime()}export function getRandomBytes(e){if("undefined"==typeof crypto||!crypto.getRandomValues)throw new Error("Web Crypto API not available - secure random generation impossible");const t=new Uint8Array(e);return crypto.getRandomValues(t),t}export function generateUUID(){if("undefined"!=typeof crypto&&crypto.randomUUID)return crypto.randomUUID();const e=getRandomBytes(16),t=e[6],r=e[8];void 0!==t&&(e[6]=15&t|64),void 0!==r&&(e[8]=63&r|128);const o=Array.from(e).map(e=>e.toString(16).padStart(2,"0")).join("");return[o.slice(0,8),o.slice(8,12),o.slice(12,16),o.slice(16,20),o.slice(20,32)].join("-")}export class LocalStorageAdapter{prefix;constructor(e="xbind:"){if(this.prefix=e,"undefined"==typeof localStorage)throw new Error("localStorage not available in this environment")}async setItem(e,t){const r=this.prefix+e,o=JSON.stringify(t);localStorage.setItem(r,o)}async getItem(e){const t=this.prefix+e,r=localStorage.getItem(t);if(null===r)return null;try{return JSON.parse(r)}catch{return null}}async removeItem(e){const t=this.prefix+e;localStorage.removeItem(t)}async clear(){Object.keys(localStorage).filter(e=>e.startsWith(this.prefix)).forEach(e=>localStorage.removeItem(e))}}export class IndexedDBAdapter{dbName;storeName;version;db=null;constructor(e="xbind-storage",t="keyval",r=1){this.dbName=e,this.storeName=t,this.version=r}async ensureDB(){return this.db?this.db:new Promise((e,t)=>{const r=indexedDB.open(this.dbName,this.version);r.onerror=()=>t(r.error),r.onsuccess=()=>{this.db=r.result,e(r.result)},r.onupgradeneeded=e=>{const t=e.target.result;t.objectStoreNames.contains(this.storeName)||t.createObjectStore(this.storeName)}})}async setItem(e,t){const r=(await this.ensureDB()).transaction(this.storeName,"readwrite").objectStore(this.storeName);return new Promise((o,n)=>{const s=r.put(t,e);s.onerror=()=>n(s.error),s.onsuccess=()=>o()})}async getItem(e){const t=(await this.ensureDB()).transaction(this.storeName,"readonly").objectStore(this.storeName);return new Promise((r,o)=>{const n=t.get(e);n.onerror=()=>o(n.error),n.onsuccess=()=>{const e=n.result;r(void 0===e?null:e)}})}async removeItem(e){const t=(await this.ensureDB()).transaction(this.storeName,"readwrite").objectStore(this.storeName);return new Promise((r,o)=>{const n=t.delete(e);n.onerror=()=>o(n.error),n.onsuccess=()=>r()})}async clear(){const e=(await this.ensureDB()).transaction(this.storeName,"readwrite").objectStore(this.storeName);return new Promise((t,r)=>{const o=e.clear();o.onerror=()=>r(o.error),o.onsuccess=()=>t()})}close(){this.db&&(this.db.close(),this.db=null)}}export class MemoryStorageAdapter{store=new Map;async setItem(e,t){this.store.set(e,t)}async getItem(e){const t=this.store.get(e);return void 0===t?null:t}async removeItem(e){this.store.delete(e)}async clear(){this.store.clear()}}export function isWasmSupported(){try{if("undefined"==typeof WebAssembly)return!1;const e=new WebAssembly.Module(new Uint8Array([0,97,115,109,1,0,0,0]));if(!(e instanceof WebAssembly.Module))return!1;return new WebAssembly.Instance(e)instanceof WebAssembly.Instance}catch{return!1}}export async function loadWasmModule(e,t){if(!isWasmSupported())return err("WASM_NOT_SUPPORTED");try{if(void 0!==WebAssembly.instantiateStreaming){const r=await fetch(e),o=await WebAssembly.instantiateStreaming(r,t);return ok(o.instance)}const r=await fetch(e),o=await r.arrayBuffer(),n=await WebAssembly.instantiate(o,t);return ok(n.instance)}catch(e){return console.error("WASM load failed:",e),err("WASM_LOAD_FAILED")}}export async function detectCapabilities(){const e={webCrypto:"undefined"!=typeof crypto&&!!crypto.subtle,subtleCrypto:"undefined"!=typeof crypto&&!!crypto.subtle,ed25519:!1,x25519:!1,wasm:isWasmSupported(),indexedDB:"undefined"!=typeof indexedDB,localStorage:"undefined"!=typeof localStorage,serviceWorker:"undefined"!=typeof navigator&&"serviceWorker"in navigator,webWorker:"undefined"!=typeof Worker,fetch:"undefined"!=typeof fetch,textEncoding:"undefined"!=typeof TextEncoder&&"undefined"!=typeof TextDecoder};if(e.subtleCrypto)try{await crypto.subtle.generateKey({name:"Ed25519"},!0,["sign","verify"]),e.ed25519=!0}catch{e.ed25519=!1}if(e.subtleCrypto)try{await crypto.subtle.generateKey({name:"X25519"},!0,["deriveBits"]),e.x25519=!0}catch{e.x25519=!1}return e}export function installNodePolyfills(){isBrowser()&&(void 0===globalThis.process&&(globalThis.process={env:{},versions:{},nextTick:e=>Promise.resolve().then(e)}),void 0===globalThis.Buffer&&(globalThis.Buffer={from:(e,t)=>{if(e instanceof Uint8Array)return e;if("base64"===t){const t=atob(e);return new Uint8Array(t.split("").map(e=>e.charCodeAt(0)))}return(new TextEncoder).encode(e)},alloc:e=>new Uint8Array(e)}),void 0===globalThis.global&&(globalThis.global=globalThis))}export const DEFAULT_SERVICE_WORKER_CONFIG={wasmCacheStrategy:"cache-first",wasmCacheMaxAge:864e5,enableBackgroundSync:!0};export function initServiceWorker(e={}){const t={...DEFAULT_SERVICE_WORKER_CONFIG,...e};if(!isServiceWorker())throw new Error("initServiceWorker() must be called from service worker context");self.__xbind_sw_config=t,console.log("[xBind] Service worker initialized with config:",t)}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edge Runtime Support for xBind
|
|
3
|
+
*
|
|
4
|
+
* Enables xBind to run on edge compute platforms:
|
|
5
|
+
* - Cloudflare Workers
|
|
6
|
+
* - Vercel Edge Functions
|
|
7
|
+
* - Deno Deploy
|
|
8
|
+
*
|
|
9
|
+
* Key Features:
|
|
10
|
+
* - Edge-compatible crypto operations (Web Crypto API only)
|
|
11
|
+
* - KV storage integration for nonce stores and trust registry
|
|
12
|
+
* - Size-optimized build (<1MB constraint)
|
|
13
|
+
* - No Node.js dependencies (no fs, crypto, process)
|
|
14
|
+
*/
|
|
15
|
+
import type { Result } from '@private.me/shared';
|
|
16
|
+
import type { NonceStore, ShareContext } from '../nonce-store.js';
|
|
17
|
+
import type { TrustRegistry, RegistryError } from '../trust-registry.js';
|
|
18
|
+
import type { XailTransportAdapter, EnvelopeHandler, TransportError } from '../transport.js';
|
|
19
|
+
import type { AnyTransportEnvelope } from '../envelope.js';
|
|
20
|
+
/**
|
|
21
|
+
* Detected edge runtime environment.
|
|
22
|
+
*/
|
|
23
|
+
export type EdgeRuntime = 'cloudflare' | 'vercel' | 'deno' | 'node' | 'unknown';
|
|
24
|
+
/**
|
|
25
|
+
* Detect the current edge runtime environment.
|
|
26
|
+
*
|
|
27
|
+
* Detection order:
|
|
28
|
+
* 1. Cloudflare Workers: globalThis.caches + EdgeRuntime
|
|
29
|
+
* 2. Vercel Edge: process.env.VERCEL + EdgeRuntime
|
|
30
|
+
* 3. Deno Deploy: globalThis.Deno
|
|
31
|
+
* 4. Node.js: process.versions.node
|
|
32
|
+
* 5. Unknown: fallback
|
|
33
|
+
*/
|
|
34
|
+
export declare function detectRuntime(): EdgeRuntime;
|
|
35
|
+
/**
|
|
36
|
+
* Check if the current runtime is an edge environment.
|
|
37
|
+
*/
|
|
38
|
+
export declare function isEdgeRuntime(): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Validate that the runtime supports required Web Crypto APIs.
|
|
41
|
+
*
|
|
42
|
+
* Edge runtimes MUST provide:
|
|
43
|
+
* - crypto.subtle (Ed25519, X25519, AES-GCM, HMAC)
|
|
44
|
+
* - crypto.getRandomValues()
|
|
45
|
+
* - TextEncoder/TextDecoder
|
|
46
|
+
*/
|
|
47
|
+
export declare function validateEdgeRuntime(): Result<void, string>;
|
|
48
|
+
/**
|
|
49
|
+
* Generic KV storage interface for edge runtimes.
|
|
50
|
+
*
|
|
51
|
+
* Implementations:
|
|
52
|
+
* - Cloudflare KV
|
|
53
|
+
* - Vercel KV (Redis)
|
|
54
|
+
* - Deno KV
|
|
55
|
+
*/
|
|
56
|
+
export interface EdgeKVStore {
|
|
57
|
+
/**
|
|
58
|
+
* Get a value by key.
|
|
59
|
+
* @returns value or null if not found
|
|
60
|
+
*/
|
|
61
|
+
get(key: string): Promise<string | null>;
|
|
62
|
+
/**
|
|
63
|
+
* Set a value with optional TTL.
|
|
64
|
+
* @param key Storage key
|
|
65
|
+
* @param value String value
|
|
66
|
+
* @param ttlSeconds Time-to-live in seconds (optional)
|
|
67
|
+
*/
|
|
68
|
+
put(key: string, value: string, ttlSeconds?: number): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Delete a key.
|
|
71
|
+
*/
|
|
72
|
+
delete(key: string): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* List keys with optional prefix.
|
|
75
|
+
* @param prefix Key prefix for filtering
|
|
76
|
+
* @param limit Maximum number of keys to return
|
|
77
|
+
* @returns Array of matching keys
|
|
78
|
+
*/
|
|
79
|
+
list(prefix?: string, limit?: number): Promise<string[]>;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Cloudflare Workers KV namespace binding.
|
|
83
|
+
* Available via env.NAMESPACE in Workers.
|
|
84
|
+
*/
|
|
85
|
+
export interface CloudflareKVNamespace {
|
|
86
|
+
get(key: string): Promise<string | null>;
|
|
87
|
+
put(key: string, value: string, options?: {
|
|
88
|
+
expirationTtl?: number;
|
|
89
|
+
}): Promise<void>;
|
|
90
|
+
delete(key: string): Promise<void>;
|
|
91
|
+
list(options?: {
|
|
92
|
+
prefix?: string;
|
|
93
|
+
limit?: number;
|
|
94
|
+
}): Promise<{
|
|
95
|
+
keys: Array<{
|
|
96
|
+
name: string;
|
|
97
|
+
}>;
|
|
98
|
+
}>;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Adapter for Cloudflare Workers KV.
|
|
102
|
+
*/
|
|
103
|
+
export declare class CloudflareKVAdapter implements EdgeKVStore {
|
|
104
|
+
private readonly namespace;
|
|
105
|
+
constructor(namespace: CloudflareKVNamespace);
|
|
106
|
+
get(key: string): Promise<string | null>;
|
|
107
|
+
put(key: string, value: string, ttlSeconds?: number): Promise<void>;
|
|
108
|
+
delete(key: string): Promise<void>;
|
|
109
|
+
list(prefix?: string, limit?: number): Promise<string[]>;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Vercel KV client interface (Redis-compatible).
|
|
113
|
+
*/
|
|
114
|
+
export interface VercelKVClient {
|
|
115
|
+
get(key: string): Promise<string | null>;
|
|
116
|
+
set(key: string, value: string, options?: {
|
|
117
|
+
ex?: number;
|
|
118
|
+
}): Promise<void>;
|
|
119
|
+
del(key: string): Promise<void>;
|
|
120
|
+
keys(pattern: string): Promise<string[]>;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Adapter for Vercel KV (Redis).
|
|
124
|
+
*/
|
|
125
|
+
export declare class VercelKVAdapter implements EdgeKVStore {
|
|
126
|
+
private readonly client;
|
|
127
|
+
constructor(client: VercelKVClient);
|
|
128
|
+
get(key: string): Promise<string | null>;
|
|
129
|
+
put(key: string, value: string, ttlSeconds?: number): Promise<void>;
|
|
130
|
+
delete(key: string): Promise<void>;
|
|
131
|
+
list(prefix?: string, limit?: number): Promise<string[]>;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Deno KV interface.
|
|
135
|
+
*/
|
|
136
|
+
export interface DenoKV {
|
|
137
|
+
get(key: string[]): Promise<{
|
|
138
|
+
value: string | null;
|
|
139
|
+
}>;
|
|
140
|
+
set(key: string[], value: string, options?: {
|
|
141
|
+
expireIn?: number;
|
|
142
|
+
}): Promise<void>;
|
|
143
|
+
delete(key: string[]): Promise<void>;
|
|
144
|
+
list(selector: {
|
|
145
|
+
prefix: string[];
|
|
146
|
+
}): AsyncIterableIterator<{
|
|
147
|
+
key: string[];
|
|
148
|
+
value: string;
|
|
149
|
+
}>;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Adapter for Deno KV.
|
|
153
|
+
*/
|
|
154
|
+
export declare class DenoKVAdapter implements EdgeKVStore {
|
|
155
|
+
private readonly kv;
|
|
156
|
+
constructor(kv: DenoKV);
|
|
157
|
+
get(key: string): Promise<string | null>;
|
|
158
|
+
put(key: string, value: string, ttlSeconds?: number): Promise<void>;
|
|
159
|
+
delete(key: string): Promise<void>;
|
|
160
|
+
list(prefix?: string, limit?: number): Promise<string[]>;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Nonce store implementation using KV storage.
|
|
164
|
+
*
|
|
165
|
+
* Suitable for edge runtimes where in-memory storage
|
|
166
|
+
* is ephemeral across invocations.
|
|
167
|
+
*/
|
|
168
|
+
export declare class KVNonceStore implements NonceStore {
|
|
169
|
+
private readonly kv;
|
|
170
|
+
private readonly ttlSeconds;
|
|
171
|
+
constructor(kv: EdgeKVStore, ttlMs?: number);
|
|
172
|
+
/**
|
|
173
|
+
* Build composite key: xbind:nonce:{senderDid}:{nonce}:{shareGroupId}:{shareIndex}
|
|
174
|
+
*/
|
|
175
|
+
private buildKey;
|
|
176
|
+
check(nonce: string, senderDid: string, shareContext?: ShareContext): Promise<boolean>;
|
|
177
|
+
cleanup(): void;
|
|
178
|
+
dispose(): void;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Trust registry implementation using KV storage.
|
|
182
|
+
*
|
|
183
|
+
* Stores DID registrations in edge KV for distributed lookups.
|
|
184
|
+
* Simplified version suitable for edge runtimes - stores minimal metadata.
|
|
185
|
+
*/
|
|
186
|
+
export declare class KVTrustRegistry implements TrustRegistry {
|
|
187
|
+
private readonly kv;
|
|
188
|
+
constructor(kv: EdgeKVStore);
|
|
189
|
+
/**
|
|
190
|
+
* Build key: xbind:trust:{did}
|
|
191
|
+
*/
|
|
192
|
+
private buildKey;
|
|
193
|
+
register(did: string, publicKey: Uint8Array, name: string, scopes?: string[], x25519PublicKey?: Uint8Array, mlKemPublicKey?: Uint8Array, mlDsaPublicKey?: Uint8Array, xchange?: boolean, receiveScopes?: string[], sdkVersion?: string, minEnvelopeVersion?: number, maxEnvelopeVersion?: number, ttlMs?: number): Promise<Result<void, RegistryError>>;
|
|
194
|
+
resolve(did: string): Promise<Result<Uint8Array, RegistryError>>;
|
|
195
|
+
hasScope(did: string, scope: string): Promise<boolean>;
|
|
196
|
+
hasReceiveScope(did: string, scope: string): Promise<boolean>;
|
|
197
|
+
revoke(did: string): Promise<Result<void, RegistryError>>;
|
|
198
|
+
list(): Promise<string[]>;
|
|
199
|
+
getEntry(did: string): Promise<Result<import('../trust-registry.js').RegistryEntry, RegistryError>>;
|
|
200
|
+
/** Additional methods for edge compatibility */
|
|
201
|
+
getFullEntry(did: string): Promise<Result<Record<string, unknown>, RegistryError>>;
|
|
202
|
+
dispose(): void;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Edge-compatible HTTPS transport adapter.
|
|
206
|
+
*
|
|
207
|
+
* Uses standard fetch API (available in all edge runtimes).
|
|
208
|
+
* Optimized for edge constraints:
|
|
209
|
+
* - No Node.js http/https modules
|
|
210
|
+
* - No setTimeout (uses AbortSignal.timeout)
|
|
211
|
+
* - Minimal allocations
|
|
212
|
+
*/
|
|
213
|
+
export declare class EdgeTransportAdapter implements XailTransportAdapter {
|
|
214
|
+
private readonly baseUrl;
|
|
215
|
+
private readonly timeoutMs;
|
|
216
|
+
private handlers;
|
|
217
|
+
constructor(opts: {
|
|
218
|
+
baseUrl: string;
|
|
219
|
+
timeoutMs?: number;
|
|
220
|
+
});
|
|
221
|
+
send(envelope: AnyTransportEnvelope, recipientDid: string): Promise<Result<void, TransportError>>;
|
|
222
|
+
onReceive(handler: EnvelopeHandler): void;
|
|
223
|
+
/**
|
|
224
|
+
* Dispatch received envelope to all handlers.
|
|
225
|
+
* Called by edge function handler when processing incoming requests.
|
|
226
|
+
*/
|
|
227
|
+
dispatch(envelope: AnyTransportEnvelope): void;
|
|
228
|
+
dispose(): void;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Options for creating an edge-compatible agent.
|
|
232
|
+
*/
|
|
233
|
+
export interface EdgeAgentOptions {
|
|
234
|
+
/** KV store for nonce deduplication and trust registry */
|
|
235
|
+
readonly kv: EdgeKVStore;
|
|
236
|
+
/** Base URL for message transport */
|
|
237
|
+
readonly baseUrl: string;
|
|
238
|
+
/** Request timeout in milliseconds */
|
|
239
|
+
readonly timeoutMs?: number;
|
|
240
|
+
/** Nonce TTL in milliseconds */
|
|
241
|
+
readonly nonceTtlMs?: number;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Create edge-compatible xBind components.
|
|
245
|
+
*
|
|
246
|
+
* Returns nonce store, trust registry, and transport adapter
|
|
247
|
+
* configured for the edge runtime.
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```typescript
|
|
251
|
+
* // Cloudflare Workers
|
|
252
|
+
* const kv = new CloudflareKVAdapter(env.XBIND_KV);
|
|
253
|
+
* const { nonceStore, trustRegistry, transport } = createEdgeAgent({
|
|
254
|
+
* kv,
|
|
255
|
+
* baseUrl: 'https://private.me/relay',
|
|
256
|
+
* });
|
|
257
|
+
*
|
|
258
|
+
* const agent = new Agent({
|
|
259
|
+
* identity: 'persistent',
|
|
260
|
+
* nonceStore,
|
|
261
|
+
* trustRegistry,
|
|
262
|
+
* transport,
|
|
263
|
+
* });
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
export declare function createEdgeAgent(opts: EdgeAgentOptions): {
|
|
267
|
+
nonceStore: KVNonceStore;
|
|
268
|
+
trustRegistry: KVTrustRegistry;
|
|
269
|
+
transport: EdgeTransportAdapter;
|
|
270
|
+
};
|
|
271
|
+
/**
|
|
272
|
+
* Check if the bundled size is within edge runtime limits.
|
|
273
|
+
*
|
|
274
|
+
* Cloudflare Workers: 1MB compressed limit
|
|
275
|
+
* Vercel Edge: 1MB limit
|
|
276
|
+
* Deno Deploy: 1MB limit
|
|
277
|
+
*/
|
|
278
|
+
export declare function checkBundleSize(bundleSizeBytes: number): Result<void, string>;
|
|
279
|
+
/**
|
|
280
|
+
* Get edge-optimized build recommendations.
|
|
281
|
+
*/
|
|
282
|
+
export declare function getOptimizationTips(): string[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ok,err}from"../_deps/shared/index.js";export function detectRuntime(){const e=globalThis;return void 0!==e.caches&&"string"==typeof e.EdgeRuntime?"cloudflare":"string"==typeof e.EdgeRuntime&&"undefined"!=typeof process&&"1"===process.env?.VERCEL?"vercel":void 0!==e.Deno?"deno":"undefined"!=typeof process&&"string"==typeof process.versions?.node?"node":"unknown"}export function isEdgeRuntime(){const e=detectRuntime();return"cloudflare"===e||"vercel"===e||"deno"===e}export function validateEdgeRuntime(){if(void 0===globalThis.crypto)return err("Missing globalThis.crypto");if(void 0===globalThis.crypto.subtle)return err("Missing crypto.subtle");if("function"!=typeof globalThis.crypto.getRandomValues)return err("Missing crypto.getRandomValues");const e=["generateKey","sign","verify","encrypt","decrypt","deriveBits","importKey","exportKey"];for(const t of e)if("function"!=typeof globalThis.crypto.subtle[t])return err(`Missing crypto.subtle.${t}`);return void 0===globalThis.TextEncoder?err("Missing TextEncoder"):void 0===globalThis.TextDecoder?err("Missing TextDecoder"):"function"!=typeof globalThis.fetch?err("Missing fetch API"):ok(void 0)}export class CloudflareKVAdapter{namespace;constructor(e){this.namespace=e}async get(e){return await this.namespace.get(e)}async put(e,t,r){const s=r?{expirationTtl:r}:void 0;await this.namespace.put(e,t,s)}async delete(e){await this.namespace.delete(e)}async list(e,t){return(await this.namespace.list({prefix:e,limit:t})).keys.map(e=>e.name)}}export class VercelKVAdapter{client;constructor(e){this.client=e}async get(e){return await this.client.get(e)}async put(e,t,r){const s=r?{ex:r}:void 0;await this.client.set(e,t,s)}async delete(e){await this.client.del(e)}async list(e,t){const r=e?`${e}*`:"*",s=await this.client.keys(r);return t?s.slice(0,t):s}}export class DenoKVAdapter{kv;constructor(e){this.kv=e}async get(e){return(await this.kv.get([e])).value}async put(e,t,r){const s=r?{expireIn:1e3*r}:void 0;await this.kv.set([e],t,s)}async delete(e){await this.kv.delete([e])}async list(e,t){const r={prefix:e?[e]:[]},s=[];for await(const e of this.kv.list(r))if(s.push(e.key.join("/")),t&&s.length>=t)break;return s}}export class KVNonceStore{kv;ttlSeconds;constructor(e,t=6e5){this.kv=e,this.ttlSeconds=Math.floor(t/1e3)}buildKey(e,t,r){const s=`xbind:nonce:${t}:${e}`;if(!r)return s;return`${s}:${r.shareGroupId??""}:${void 0!==r.shareIndex?String(r.shareIndex):""}`}async check(e,t,r){const s=this.buildKey(e,t,r);return null===await this.kv.get(s)&&(await this.kv.put(s,Date.now().toString(),this.ttlSeconds),!0)}cleanup(){}dispose(){}}export class KVTrustRegistry{kv;constructor(e){this.kv=e}buildKey(e){return`xbind:trust:${e}`}async register(e,t,r,s,n,i,o,c,a,l,u,d,p){const y=this.buildKey(e),h=e=>btoa(String.fromCharCode(...Array.from(e))),m={did:e,publicKey:h(t),name:r,scopes:s??[],x25519PublicKey:n?h(n):void 0,mlKemPublicKey:i?h(i):void 0,mlDsaPublicKey:o?h(o):void 0,xchange:c??!1,receiveScopes:a??[],sdkVersion:l,minEnvelopeVersion:u,maxEnvelopeVersion:d,registeredAt:Date.now(),rotation_sequence:0};try{const e=p?Math.floor(p/1e3):void 0;return await this.kv.put(y,JSON.stringify(m),e),ok(void 0)}catch(e){return err("NETWORK_ERROR")}}async resolve(e){const t=this.buildKey(e);try{const e=await this.kv.get(t);if(null===e)return err("NOT_FOUND");const r=JSON.parse(e);return ok((e=>Uint8Array.from(atob(e),e=>e.charCodeAt(0)))(r.publicKey))}catch(e){return err("NETWORK_ERROR")}}async hasScope(e,t){const r=this.buildKey(e);try{const e=await this.kv.get(r);if(null===e)return!1;const s=JSON.parse(e);return s.scopes?.includes(t)??!1}catch{return!1}}async hasReceiveScope(e,t){const r=this.buildKey(e);try{const e=await this.kv.get(r);if(null===e)return!1;const s=JSON.parse(e);return s.receiveScopes?.includes(t)??!1}catch{return!1}}async revoke(e){const t=this.buildKey(e);try{return await this.kv.delete(t),ok(void 0)}catch(e){return err("NETWORK_ERROR")}}async list(){try{return(await this.kv.list("xbind:trust:",1e3)).map(e=>e.replace("xbind:trust:",""))}catch{return[]}}async getEntry(e){const t=this.buildKey(e);try{const e=await this.kv.get(t);if(null===e)return err("NOT_FOUND");const r=JSON.parse(e),s=e=>Uint8Array.from(atob(e),e=>e.charCodeAt(0)),n={did:r.did,publicKey:s(r.publicKey),name:r.name,scopes:new Set(r.scopes),receiveScopes:r.receiveScopes?new Set(r.receiveScopes):void 0,revoked:!1,x25519PublicKey:r.x25519PublicKey?s(r.x25519PublicKey):void 0,mlKemPublicKey:r.mlKemPublicKey?s(r.mlKemPublicKey):void 0,mlDsaPublicKey:r.mlDsaPublicKey?s(r.mlDsaPublicKey):void 0,xchange:r.xchange,rotation_sequence:r.rotation_sequence,sdkVersion:r.sdkVersion,minEnvelopeVersion:r.minEnvelopeVersion,maxEnvelopeVersion:r.maxEnvelopeVersion};return ok(n)}catch(e){return err("NETWORK_ERROR")}}async getFullEntry(e){const t=this.buildKey(e);try{const e=await this.kv.get(t);if(null===e)return err("NOT_FOUND");const r=JSON.parse(e);return ok(r)}catch(e){return err("NETWORK_ERROR")}}dispose(){}}export class EdgeTransportAdapter{baseUrl;timeoutMs;handlers=[];constructor(e){this.baseUrl=e.baseUrl.replace(/\/$/,""),this.timeoutMs=e.timeoutMs??1e4}async send(e,t){const r=`${this.baseUrl}/deliver/${encodeURIComponent(t)}`;try{const t=new AbortController,s=setTimeout(()=>t.abort(),this.timeoutMs),n=await globalThis.fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),signal:t.signal});return clearTimeout(s),n.ok?ok(void 0):err(404===n.status?"RECIPIENT_UNREACHABLE":"SEND_FAILED")}catch(e){return e instanceof DOMException&&"AbortError"===e.name?err("TIMEOUT"):err("NETWORK_ERROR")}}onReceive(e){this.handlers.push(e)}dispatch(e){for(const t of this.handlers)t(e)}dispose(){this.handlers=[]}}export function createEdgeAgent(e){return{nonceStore:new KVNonceStore(e.kv,e.nonceTtlMs),trustRegistry:new KVTrustRegistry(e.kv),transport:new EdgeTransportAdapter({baseUrl:e.baseUrl,timeoutMs:e.timeoutMs})}}export function checkBundleSize(e){const t=1048576;if(e>t){const t=(e/1024/1024).toFixed(2),r=1..toFixed(2);return err(`Bundle size ${t}MB exceeds edge runtime limit of ${r}MB. Consider using tree-shaking or dynamic imports.`)}return ok(void 0)}export function getOptimizationTips(){return["1. Use tree-shaking: Import only required functions","2. Enable minification in bundler config","3. Use dynamic imports for large dependencies (mlkem, mldsa)","4. Exclude Node.js polyfills (crypto, fs, process)","5. Use Brotli compression for Cloudflare Workers","6. Consider splitting post-quantum crypto into separate bundle","7. Use KV storage instead of in-memory caches"]}
|