@private.me/xbind 3.0.0 → 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 +9 -2
- 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/package.json +4 -1
- package/share1.dat +0 -0
|
@@ -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"]}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Native Runtime Support for xBind
|
|
3
|
+
*
|
|
4
|
+
* Provides compatibility layer for React Native environments:
|
|
5
|
+
* - Polyfills for missing Node.js APIs
|
|
6
|
+
* - AsyncStorage adapter for persistence
|
|
7
|
+
* - Native crypto module integration
|
|
8
|
+
* - Cross-platform crypto primitives
|
|
9
|
+
*
|
|
10
|
+
* @module runtime/react-native
|
|
11
|
+
*/
|
|
12
|
+
import type { Result } from '@private.me/shared';
|
|
13
|
+
/**
|
|
14
|
+
* React Native AsyncStorage interface
|
|
15
|
+
*/
|
|
16
|
+
export interface AsyncStorage {
|
|
17
|
+
getItem(key: string): Promise<string | null>;
|
|
18
|
+
setItem(key: string, value: string): Promise<void>;
|
|
19
|
+
removeItem(key: string): Promise<void>;
|
|
20
|
+
getAllKeys(): Promise<string[]>;
|
|
21
|
+
multiGet(keys: string[]): Promise<Array<[string, string | null]>>;
|
|
22
|
+
multiSet(keyValuePairs: Array<[string, string]>): Promise<void>;
|
|
23
|
+
multiRemove(keys: string[]): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* React Native crypto capabilities
|
|
27
|
+
*/
|
|
28
|
+
export interface ReactNativeCrypto {
|
|
29
|
+
getRandomValues(array: Uint8Array): void;
|
|
30
|
+
subtle?: {
|
|
31
|
+
digest(algorithm: string, data: BufferSource): Promise<ArrayBuffer>;
|
|
32
|
+
encrypt(algorithm: AlgorithmIdentifier, key: CryptoKey, data: BufferSource): Promise<ArrayBuffer>;
|
|
33
|
+
decrypt(algorithm: AlgorithmIdentifier, key: CryptoKey, data: BufferSource): Promise<ArrayBuffer>;
|
|
34
|
+
sign(algorithm: AlgorithmIdentifier, key: CryptoKey, data: BufferSource): Promise<ArrayBuffer>;
|
|
35
|
+
verify(algorithm: AlgorithmIdentifier, key: CryptoKey, signature: BufferSource, data: BufferSource): Promise<boolean>;
|
|
36
|
+
generateKey(algorithm: AlgorithmIdentifier, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey | CryptoKeyPair>;
|
|
37
|
+
importKey(format: KeyFormat, keyData: BufferSource | JsonWebKey, algorithm: AlgorithmIdentifier, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey>;
|
|
38
|
+
exportKey(format: KeyFormat, key: CryptoKey): Promise<ArrayBuffer | JsonWebKey>;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* React Native runtime configuration
|
|
43
|
+
*/
|
|
44
|
+
export interface ReactNativeConfig {
|
|
45
|
+
/**
|
|
46
|
+
* AsyncStorage instance (required for persistence)
|
|
47
|
+
*/
|
|
48
|
+
storage: AsyncStorage;
|
|
49
|
+
/**
|
|
50
|
+
* Crypto implementation (defaults to global.crypto or react-native-quick-crypto)
|
|
51
|
+
*/
|
|
52
|
+
crypto?: ReactNativeCrypto;
|
|
53
|
+
/**
|
|
54
|
+
* Storage key prefix (prevents collisions)
|
|
55
|
+
*/
|
|
56
|
+
storagePrefix?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Enable debug logging
|
|
59
|
+
*/
|
|
60
|
+
debug?: boolean;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* AsyncStorage adapter for xBind persistence
|
|
64
|
+
*
|
|
65
|
+
* Provides cross-platform storage for:
|
|
66
|
+
* - Agent identities (Ed25519/X25519 keys)
|
|
67
|
+
* - Nonce store (replay protection)
|
|
68
|
+
* - Trust registry cache
|
|
69
|
+
* - Connection metadata
|
|
70
|
+
*/
|
|
71
|
+
export declare class AsyncStorageAdapter {
|
|
72
|
+
private readonly storage;
|
|
73
|
+
private readonly prefix;
|
|
74
|
+
private readonly debug;
|
|
75
|
+
constructor(config: ReactNativeConfig);
|
|
76
|
+
/**
|
|
77
|
+
* Get value by key
|
|
78
|
+
*/
|
|
79
|
+
get(key: string): Promise<Result<string | null, Error>>;
|
|
80
|
+
/**
|
|
81
|
+
* Set key-value pair
|
|
82
|
+
*/
|
|
83
|
+
set(key: string, value: string): Promise<Result<void, Error>>;
|
|
84
|
+
/**
|
|
85
|
+
* Remove key
|
|
86
|
+
*/
|
|
87
|
+
remove(key: string): Promise<Result<void, Error>>;
|
|
88
|
+
/**
|
|
89
|
+
* Get all keys with prefix
|
|
90
|
+
*/
|
|
91
|
+
keys(): Promise<Result<string[], Error>>;
|
|
92
|
+
/**
|
|
93
|
+
* Get multiple keys (batch operation)
|
|
94
|
+
*/
|
|
95
|
+
multiGet(keys: string[]): Promise<Result<Array<[string, string | null]>, Error>>;
|
|
96
|
+
/**
|
|
97
|
+
* Set multiple key-value pairs (batch operation)
|
|
98
|
+
*/
|
|
99
|
+
multiSet(pairs: Array<[string, string]>): Promise<Result<void, Error>>;
|
|
100
|
+
/**
|
|
101
|
+
* Clear all xBind data
|
|
102
|
+
*/
|
|
103
|
+
clear(): Promise<Result<void, Error>>;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Detect available crypto implementation
|
|
107
|
+
*/
|
|
108
|
+
export declare function detectCrypto(): ReactNativeCrypto | null;
|
|
109
|
+
/**
|
|
110
|
+
* Get random bytes (cross-platform)
|
|
111
|
+
*/
|
|
112
|
+
export declare function getRandomBytes(length: number, crypto?: ReactNativeCrypto): Uint8Array;
|
|
113
|
+
/**
|
|
114
|
+
* SHA-256 hash (cross-platform)
|
|
115
|
+
*/
|
|
116
|
+
export declare function sha256(data: Uint8Array, crypto?: ReactNativeCrypto): Promise<Uint8Array>;
|
|
117
|
+
/**
|
|
118
|
+
* Buffer polyfill for React Native
|
|
119
|
+
*
|
|
120
|
+
* Uses global Buffer if available, otherwise provides minimal implementation
|
|
121
|
+
*/
|
|
122
|
+
export declare class BufferPolyfill {
|
|
123
|
+
static isBuffer(obj: any): boolean;
|
|
124
|
+
static from(data: string | Uint8Array | number[], encoding?: string): Uint8Array;
|
|
125
|
+
static toString(buffer: Uint8Array, encoding?: string): string;
|
|
126
|
+
static concat(buffers: Uint8Array[]): Uint8Array;
|
|
127
|
+
static alloc(size: number, fill?: number): Uint8Array;
|
|
128
|
+
private static fromBase64;
|
|
129
|
+
private static toBase64;
|
|
130
|
+
private static fromHex;
|
|
131
|
+
private static toHex;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Detect React Native runtime
|
|
135
|
+
*/
|
|
136
|
+
export declare function isReactNative(): boolean;
|
|
137
|
+
/**
|
|
138
|
+
* Detect platform (iOS/Android)
|
|
139
|
+
*/
|
|
140
|
+
export declare function detectPlatform(): 'ios' | 'android' | 'unknown';
|
|
141
|
+
/**
|
|
142
|
+
* Initialize React Native runtime environment
|
|
143
|
+
*
|
|
144
|
+
* Sets up polyfills and validates required dependencies
|
|
145
|
+
*/
|
|
146
|
+
export declare function initializeReactNative(config: ReactNativeConfig): Result<void, Error>;
|
|
147
|
+
declare const _default: {
|
|
148
|
+
AsyncStorageAdapter: typeof AsyncStorageAdapter;
|
|
149
|
+
detectCrypto: typeof detectCrypto;
|
|
150
|
+
getRandomBytes: typeof getRandomBytes;
|
|
151
|
+
sha256: typeof sha256;
|
|
152
|
+
BufferPolyfill: typeof BufferPolyfill;
|
|
153
|
+
isReactNative: typeof isReactNative;
|
|
154
|
+
detectPlatform: typeof detectPlatform;
|
|
155
|
+
initializeReactNative: typeof initializeReactNative;
|
|
156
|
+
};
|
|
157
|
+
export default _default;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ok,err}from"../_deps/shared/index.js";export class AsyncStorageAdapter{storage;prefix;debug;constructor(t){this.storage=t.storage,this.prefix=t.storagePrefix||"@xbind:",this.debug=t.debug||!1}async get(t){try{const e=this.prefix+t,r=await this.storage.getItem(e);return this.debug&&console.log(`[AsyncStorage] GET ${e}:`,r?"found":"not found"),ok(r)}catch(t){return err(new Error(`AsyncStorage get failed: ${t}`))}}async set(t,e){try{const r=this.prefix+t;return await this.storage.setItem(r,e),this.debug&&console.log(`[AsyncStorage] SET ${r}:`,e.length,"bytes"),ok(void 0)}catch(t){return err(new Error(`AsyncStorage set failed: ${t}`))}}async remove(t){try{const e=this.prefix+t;return await this.storage.removeItem(e),this.debug&&console.log(`[AsyncStorage] REMOVE ${e}`),ok(void 0)}catch(t){return err(new Error(`AsyncStorage remove failed: ${t}`))}}async keys(){try{const t=(await this.storage.getAllKeys()).filter(t=>t.startsWith(this.prefix)).map(t=>t.substring(this.prefix.length));return this.debug&&console.log("[AsyncStorage] KEYS:",t.length,"items"),ok(t)}catch(t){return err(new Error(`AsyncStorage keys failed: ${t}`))}}async multiGet(t){try{const e=t.map(t=>this.prefix+t),r=(await this.storage.multiGet(e)).map(([t,e])=>[t.substring(this.prefix.length),e]);return this.debug&&console.log("[AsyncStorage] MULTI_GET:",t.length,"keys"),ok(r)}catch(t){return err(new Error(`AsyncStorage multiGet failed: ${t}`))}}async multiSet(t){try{const e=t.map(([t,e])=>[this.prefix+t,e]);return await this.storage.multiSet(e),this.debug&&console.log("[AsyncStorage] MULTI_SET:",t.length,"pairs"),ok(void 0)}catch(t){return err(new Error(`AsyncStorage multiSet failed: ${t}`))}}async clear(){try{const t=await this.keys();if(!t.ok)return err(t.error);const e=t.value.map(t=>this.prefix+t);return e.length>0&&await this.storage.multiRemove(e),this.debug&&console.log("[AsyncStorage] CLEAR:",e.length,"items removed"),ok(void 0)}catch(t){return err(new Error(`AsyncStorage clear failed: ${t}`))}}}export function detectCrypto(){if("undefined"!=typeof global&&global.crypto)return global.crypto;try{return require("react-native-quick-crypto")}catch{}try{const t=require("expo-crypto");return{getRandomValues:e=>{const r=t.getRandomBytes(e.length);e.set(r)}}}catch{}return null}export function getRandomBytes(t,e){const r=e||detectCrypto();if(!r)throw new Error("No crypto implementation available. Install react-native-quick-crypto or expo-crypto.");const o=new Uint8Array(t);return r.getRandomValues(o),o}export async function sha256(t,e){const r=e||detectCrypto();if(!r?.subtle)throw new Error("No SubtleCrypto implementation available. Install react-native-quick-crypto.");const o=new Uint8Array(t).buffer,n=await r.subtle.digest("SHA-256",o);return new Uint8Array(n)}export class BufferPolyfill{static isBuffer(t){return t instanceof Uint8Array}static from(t,e){if("string"==typeof t){if("base64"===e)return this.fromBase64(t);if("hex"===e)return this.fromHex(t);return(new TextEncoder).encode(t)}return Array.isArray(t),new Uint8Array(t)}static toString(t,e){if("base64"===e)return this.toBase64(t);if("hex"===e)return this.toHex(t);return(new TextDecoder).decode(t)}static concat(t){const e=t.reduce((t,e)=>t+e.length,0),r=new Uint8Array(e);let o=0;for(const e of t)r.set(e,o),o+=e.length;return r}static alloc(t,e){const r=new Uint8Array(t);return void 0!==e&&r.fill(e),r}static fromBase64(t){const e=atob(t),r=new Uint8Array(e.length);for(let t=0;t<e.length;t++)r[t]=e.charCodeAt(t);return r}static toBase64(t){let e="";for(let r=0;r<t.length;r++){const o=t[r];void 0!==o&&(e+=String.fromCharCode(o))}return btoa(e)}static fromHex(t){const e=new Uint8Array(t.length/2);for(let r=0;r<e.length;r++)e[r]=parseInt(t.substr(2*r,2),16);return e}static toHex(t){return Array.from(t).map(t=>t.toString(16).padStart(2,"0")).join("")}}export function isReactNative(){return"undefined"!=typeof navigator&&"ReactNative"===navigator.product}export function detectPlatform(){if("undefined"==typeof navigator)return"unknown";const t=navigator.userAgent||"";return/iPhone|iPad|iPod/.test(t)?"ios":/Android/.test(t)?"android":"unknown"}export function initializeReactNative(t){try{if(!t.storage)return err(new Error("AsyncStorage is required for React Native runtime"));const e=t.crypto||detectCrypto();return e?(global.crypto||(global.crypto=e),global.Buffer||(global.Buffer=BufferPolyfill),t.debug&&console.log("[xBind] React Native runtime initialized:",{platform:detectPlatform(),crypto:e.subtle?"full":"basic",storage:"AsyncStorage",prefix:t.storagePrefix||"@xbind:"}),ok(void 0)):err(new Error("No crypto implementation found. Install react-native-quick-crypto or expo-crypto."))}catch(t){return err(new Error(`React Native initialization failed: ${t}`))}}export default{AsyncStorageAdapter:AsyncStorageAdapter,detectCrypto:detectCrypto,getRandomBytes:getRandomBytes,sha256:sha256,BufferPolyfill:BufferPolyfill,isReactNative:isReactNative,detectPlatform:detectPlatform,initializeReactNative:initializeReactNative};
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Response Types
|
|
3
|
+
*
|
|
4
|
+
* Dual-code error response system supporting both AWS standard codes
|
|
5
|
+
* and legacy xBind codes for backward compatibility.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Standardized error response structure with dual-code support
|
|
11
|
+
*
|
|
12
|
+
* Supports multiple code formats:
|
|
13
|
+
* - AWS standard codes (primary): InvalidParameterException, UnauthorizedException
|
|
14
|
+
* - Legacy xBind codes (deprecated): XBIND_INVALID_SIGNATURE, XBIND_AUTH_FAILED
|
|
15
|
+
* - gRPC status codes: INVALID_ARGUMENT, UNAUTHENTICATED
|
|
16
|
+
* - HTTP status codes: 400, 401, 403, etc.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const error: ErrorResponse = {
|
|
21
|
+
* code: 'UnauthorizedException',
|
|
22
|
+
* legacyCode: 'XBIND_AUTH_FAILED',
|
|
23
|
+
* httpStatus: 401,
|
|
24
|
+
* grpcCode: 'UNAUTHENTICATED',
|
|
25
|
+
* message: 'Invalid signature',
|
|
26
|
+
* hint: 'Ensure DID signature is correct',
|
|
27
|
+
* docs: 'https://private.me/docs/xbind#authentication',
|
|
28
|
+
* requestId: 'req_abc123',
|
|
29
|
+
* timestamp: Date.now()
|
|
30
|
+
* };
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export interface ErrorResponse {
|
|
34
|
+
/**
|
|
35
|
+
* AWS standard error code (primary)
|
|
36
|
+
*
|
|
37
|
+
* Examples:
|
|
38
|
+
* - InvalidParameterException
|
|
39
|
+
* - UnauthorizedException
|
|
40
|
+
* - ResourceNotFoundException
|
|
41
|
+
* - InternalServerException
|
|
42
|
+
* - ServiceUnavailableException
|
|
43
|
+
* - ThrottlingException
|
|
44
|
+
*
|
|
45
|
+
* This is the primary code format. All new integrations should use this.
|
|
46
|
+
*/
|
|
47
|
+
code: string;
|
|
48
|
+
/**
|
|
49
|
+
* Legacy xBind error code (deprecated)
|
|
50
|
+
*
|
|
51
|
+
* Examples:
|
|
52
|
+
* - XBIND_INVALID_SIGNATURE
|
|
53
|
+
* - XBIND_AUTH_FAILED
|
|
54
|
+
* - XBIND_DID_NOT_FOUND
|
|
55
|
+
* - XBIND_RATE_LIMIT
|
|
56
|
+
*
|
|
57
|
+
* Provided for backward compatibility only.
|
|
58
|
+
* Will be removed in future major version.
|
|
59
|
+
*
|
|
60
|
+
* @deprecated Use `code` field instead
|
|
61
|
+
*/
|
|
62
|
+
legacyCode?: string;
|
|
63
|
+
/**
|
|
64
|
+
* HTTP status code
|
|
65
|
+
*
|
|
66
|
+
* Standard HTTP status codes:
|
|
67
|
+
* - 400: Bad Request (InvalidParameterException)
|
|
68
|
+
* - 401: Unauthorized (UnauthorizedException)
|
|
69
|
+
* - 403: Forbidden (AccessDeniedException)
|
|
70
|
+
* - 404: Not Found (ResourceNotFoundException)
|
|
71
|
+
* - 429: Too Many Requests (ThrottlingException)
|
|
72
|
+
* - 500: Internal Server Error (InternalServerException)
|
|
73
|
+
* - 503: Service Unavailable (ServiceUnavailableException)
|
|
74
|
+
*/
|
|
75
|
+
httpStatus: number;
|
|
76
|
+
/**
|
|
77
|
+
* gRPC status code (optional)
|
|
78
|
+
*
|
|
79
|
+
* Examples:
|
|
80
|
+
* - INVALID_ARGUMENT
|
|
81
|
+
* - UNAUTHENTICATED
|
|
82
|
+
* - PERMISSION_DENIED
|
|
83
|
+
* - NOT_FOUND
|
|
84
|
+
* - RESOURCE_EXHAUSTED
|
|
85
|
+
* - INTERNAL
|
|
86
|
+
* - UNAVAILABLE
|
|
87
|
+
*
|
|
88
|
+
* Provided for gRPC-based integrations.
|
|
89
|
+
*/
|
|
90
|
+
grpcCode?: string;
|
|
91
|
+
/**
|
|
92
|
+
* Human-readable error message
|
|
93
|
+
*
|
|
94
|
+
* Should be clear and actionable. Avoid technical jargon where possible.
|
|
95
|
+
*
|
|
96
|
+
* Example: "Invalid signature" instead of "ECDSA verification failed"
|
|
97
|
+
*/
|
|
98
|
+
message: string;
|
|
99
|
+
/**
|
|
100
|
+
* Developer hint for resolving the error (optional)
|
|
101
|
+
*
|
|
102
|
+
* Provides actionable guidance for fixing the issue.
|
|
103
|
+
*
|
|
104
|
+
* Example: "Ensure DID signature is correct and not expired"
|
|
105
|
+
*/
|
|
106
|
+
hint?: string;
|
|
107
|
+
/**
|
|
108
|
+
* Documentation URL for detailed error information (optional)
|
|
109
|
+
*
|
|
110
|
+
* Links to relevant documentation section for this error type.
|
|
111
|
+
*
|
|
112
|
+
* Example: "https://private.me/docs/xbind#authentication"
|
|
113
|
+
*/
|
|
114
|
+
docs?: string;
|
|
115
|
+
/**
|
|
116
|
+
* Request ID for correlation and debugging (optional)
|
|
117
|
+
*
|
|
118
|
+
* Unique identifier for this request. Used for:
|
|
119
|
+
* - Log correlation across distributed systems
|
|
120
|
+
* - Customer support troubleshooting
|
|
121
|
+
* - Audit trail tracking
|
|
122
|
+
*
|
|
123
|
+
* Format: "req_" + random alphanumeric (e.g., "req_abc123def456")
|
|
124
|
+
*/
|
|
125
|
+
requestId?: string;
|
|
126
|
+
/**
|
|
127
|
+
* Error timestamp in milliseconds since Unix epoch (optional)
|
|
128
|
+
*
|
|
129
|
+
* When the error occurred. Useful for:
|
|
130
|
+
* - Time-based debugging
|
|
131
|
+
* - Rate limit tracking
|
|
132
|
+
* - Audit logs
|
|
133
|
+
*
|
|
134
|
+
* Format: Unix timestamp in milliseconds (Date.now())
|
|
135
|
+
*/
|
|
136
|
+
timestamp?: number;
|
|
137
|
+
/**
|
|
138
|
+
* Correlation ID for distributed tracing (optional)
|
|
139
|
+
*
|
|
140
|
+
* Tracks a single logical operation across multiple services.
|
|
141
|
+
* Different from requestId (per-request) - this spans multiple requests.
|
|
142
|
+
*
|
|
143
|
+
* Format: UUID or similar distributed trace ID
|
|
144
|
+
*
|
|
145
|
+
* Example: "trace_789xyz" spans: Auth request → DID lookup → Billing check
|
|
146
|
+
*/
|
|
147
|
+
correlationId?: string;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Error response with additional metadata for internal use
|
|
151
|
+
*
|
|
152
|
+
* Extends ErrorResponse with internal fields that should NOT be
|
|
153
|
+
* exposed to external clients.
|
|
154
|
+
*
|
|
155
|
+
* @internal
|
|
156
|
+
*/
|
|
157
|
+
export interface InternalErrorResponse extends ErrorResponse {
|
|
158
|
+
/**
|
|
159
|
+
* Stack trace (internal only, never exposed externally)
|
|
160
|
+
*
|
|
161
|
+
* @internal
|
|
162
|
+
*/
|
|
163
|
+
stack?: string;
|
|
164
|
+
/**
|
|
165
|
+
* Internal error code for debugging (internal only)
|
|
166
|
+
*
|
|
167
|
+
* @internal
|
|
168
|
+
*/
|
|
169
|
+
internalCode?: string;
|
|
170
|
+
/**
|
|
171
|
+
* Service name that generated the error
|
|
172
|
+
*
|
|
173
|
+
* @internal
|
|
174
|
+
*/
|
|
175
|
+
service?: string;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Type guard to check if a value is an ErrorResponse
|
|
179
|
+
*
|
|
180
|
+
* @param value - Value to check
|
|
181
|
+
* @returns True if value is an ErrorResponse
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```typescript
|
|
185
|
+
* if (isErrorResponse(result)) {
|
|
186
|
+
* console.error(result.message);
|
|
187
|
+
* }
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
export declare function isErrorResponse(value: unknown): value is ErrorResponse;
|
|
191
|
+
/**
|
|
192
|
+
* Create a standardized error response
|
|
193
|
+
*
|
|
194
|
+
* @param params - Error response parameters
|
|
195
|
+
* @returns Standardized ErrorResponse object
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```typescript
|
|
199
|
+
* const error = createErrorResponse({
|
|
200
|
+
* code: 'UnauthorizedException',
|
|
201
|
+
* legacyCode: 'XBIND_AUTH_FAILED',
|
|
202
|
+
* httpStatus: 401,
|
|
203
|
+
* message: 'Invalid signature'
|
|
204
|
+
* });
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
export declare function createErrorResponse(params: Omit<ErrorResponse, 'timestamp'> & {
|
|
208
|
+
timestamp?: number;
|
|
209
|
+
}): ErrorResponse;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function isErrorResponse(t){if(!t||"object"!=typeof t)return!1;const e=t;return"string"==typeof e.code&&"number"==typeof e.httpStatus&&"string"==typeof e.message}export function createErrorResponse(t){return{...t,timestamp:t.timestamp??Date.now()}}
|