lumefuse-sdk 1.0.0

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/src/client.ts ADDED
@@ -0,0 +1,391 @@
1
+ /**
2
+ * LumeFuse Client - Main SDK Client
3
+ */
4
+
5
+ import { createHash } from 'crypto';
6
+ import { DataStream, StreamResult, StreamConfig } from './stream';
7
+ import type {
8
+ LumeFuseConfig,
9
+ BitPacket,
10
+ VerificationResult,
11
+ ChainStatus,
12
+ CreditBalance,
13
+ HealingEvent,
14
+ SentinelStatus,
15
+ AuditResult,
16
+ } from './types';
17
+ import {
18
+ LumeFuseError,
19
+ AuthenticationError,
20
+ RateLimitError,
21
+ ChainIntegrityError,
22
+ InsufficientCreditsError,
23
+ NetworkError,
24
+ } from './errors';
25
+
26
+ const DEFAULT_BASE_URL = 'https://api.lumefuse.io/v1';
27
+
28
+ /**
29
+ * LumeFuse SDK Client - The "Born-Signed" Data Integrity Platform
30
+ *
31
+ * This client provides methods to:
32
+ * - Open data streams for Bit-Packet creation
33
+ * - Verify data against the BSV ledger
34
+ * - Check chain integrity
35
+ * - Monitor healing events
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * import { LumeFuse } from '@lumefuse/sdk';
40
+ *
41
+ * const lf = new LumeFuse({ apiKey: 'lf_your_api_key' });
42
+ *
43
+ * // Create a data stream
44
+ * const stream = lf.openStream('sensor_data');
45
+ * await stream.write({ temperature: 72.5 });
46
+ * const result = await stream.close();
47
+ *
48
+ * // Verify chain integrity
49
+ * const status = await lf.verifyChain('sensor_data');
50
+ * console.log(`Chain intact: ${status.chainIntact}`);
51
+ * ```
52
+ */
53
+ export class LumeFuse {
54
+ private apiKey: string;
55
+ private baseUrl: string;
56
+ private timeout: number;
57
+ private autoHeal: boolean;
58
+
59
+ constructor(config: LumeFuseConfig) {
60
+ this.apiKey = config.apiKey || process.env.LUMEFUSE_API_KEY || '';
61
+ this.baseUrl = config.baseUrl || process.env.LUMEFUSE_BASE_URL || DEFAULT_BASE_URL;
62
+ this.timeout = config.timeout || 30000;
63
+ this.autoHeal = config.autoHeal ?? true;
64
+
65
+ if (!this.apiKey) {
66
+ throw new AuthenticationError(
67
+ 'API key is required. Set LUMEFUSE_API_KEY environment variable or pass apiKey in config.'
68
+ );
69
+ }
70
+
71
+ if (!this.apiKey.startsWith('lf_')) {
72
+ throw new AuthenticationError(
73
+ "Invalid API key format. API keys should start with 'lf_'"
74
+ );
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Make an API request
80
+ */
81
+ private async request<T = Record<string, unknown>>(
82
+ method: string,
83
+ endpoint: string,
84
+ data?: Record<string, unknown>,
85
+ params?: Record<string, string>
86
+ ): Promise<T> {
87
+ const url = new URL(endpoint, this.baseUrl);
88
+
89
+ if (params) {
90
+ Object.entries(params).forEach(([key, value]) => {
91
+ url.searchParams.append(key, value);
92
+ });
93
+ }
94
+
95
+ const controller = new AbortController();
96
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
97
+
98
+ try {
99
+ const response = await fetch(url.toString(), {
100
+ method,
101
+ headers: {
102
+ 'Authorization': `Bearer ${this.apiKey}`,
103
+ 'Content-Type': 'application/json',
104
+ 'X-SDK-Version': 'lumefuse-js/1.0.0',
105
+ },
106
+ body: data ? JSON.stringify(data) : undefined,
107
+ signal: controller.signal,
108
+ });
109
+
110
+ clearTimeout(timeoutId);
111
+
112
+ if (response.status === 401) {
113
+ throw new AuthenticationError('Invalid API key');
114
+ }
115
+
116
+ if (response.status === 429) {
117
+ const retryAfter = response.headers.get('Retry-After');
118
+ throw new RateLimitError(
119
+ 'Rate limit exceeded',
120
+ retryAfter ? parseInt(retryAfter) : null
121
+ );
122
+ }
123
+
124
+ if (response.status === 402) {
125
+ throw new InsufficientCreditsError('Insufficient BSV credits');
126
+ }
127
+
128
+ if (!response.ok) {
129
+ const errorData = await response.json().catch(() => ({}));
130
+ throw new LumeFuseError(
131
+ (errorData as Record<string, string>).detail || `Request failed with status ${response.status}`,
132
+ String(response.status)
133
+ );
134
+ }
135
+
136
+ return response.json() as Promise<T>;
137
+ } catch (error) {
138
+ clearTimeout(timeoutId);
139
+
140
+ if (error instanceof LumeFuseError) {
141
+ throw error;
142
+ }
143
+
144
+ if (error instanceof Error && error.name === 'AbortError') {
145
+ throw new NetworkError('Request timeout');
146
+ }
147
+
148
+ throw new NetworkError(`Network request failed: ${error}`);
149
+ }
150
+ }
151
+
152
+ // ==================== STREAM API ====================
153
+
154
+ /**
155
+ * Open a new data stream for Bit-Packet creation.
156
+ *
157
+ * This is the core "Latch" - data written to the returned stream is
158
+ * automatically hashed, linked with Recursive DNA, and anchored to BSV.
159
+ *
160
+ * @param sourceId - Unique identifier for this data source
161
+ * @param config - Stream configuration options
162
+ * @returns DataStream for writing data
163
+ *
164
+ * @example
165
+ * ```typescript
166
+ * const stream = lf.openStream('medical_lab_results');
167
+ * await stream.write({ patient_id: 'P-001', glucose: 95 });
168
+ * await stream.write({ patient_id: 'P-001', glucose: 102 });
169
+ * const result = await stream.close();
170
+ * console.log(`Merkle Root: ${result.merkleRoot}`);
171
+ * ```
172
+ */
173
+ openStream(sourceId: string, config?: StreamConfig): DataStream {
174
+ return new DataStream(sourceId, this, config);
175
+ }
176
+
177
+ // ==================== WRAP API ====================
178
+
179
+ /**
180
+ * Wrap data into a single Bit-Packet with Recursive DNA.
181
+ */
182
+ async wrap(sourceId: string, data: unknown): Promise<BitPacket> {
183
+ let dataStr: string;
184
+
185
+ if (typeof data === 'string') {
186
+ dataStr = data;
187
+ } else {
188
+ dataStr = JSON.stringify(data, Object.keys(data as object).sort());
189
+ }
190
+
191
+ const response = await this.request<Record<string, unknown>>('POST', '/packets/wrap', {
192
+ source_id: sourceId,
193
+ data: dataStr,
194
+ });
195
+
196
+ return this.mapBitPacket(response);
197
+ }
198
+
199
+ // ==================== VERIFY API ====================
200
+
201
+ /**
202
+ * Verify data against the BSV ledger.
203
+ */
204
+ async verify(data: unknown): Promise<VerificationResult> {
205
+ let dataHash: string;
206
+
207
+ if (typeof data === 'string') {
208
+ dataHash = createHash('sha256').update(data).digest('hex');
209
+ } else if (Buffer.isBuffer(data)) {
210
+ dataHash = createHash('sha256').update(data).digest('hex');
211
+ } else {
212
+ const dataStr = JSON.stringify(data, Object.keys(data as object).sort());
213
+ dataHash = createHash('sha256').update(dataStr).digest('hex');
214
+ }
215
+
216
+ const response = await this.request<Record<string, unknown>>('POST', '/verify', {
217
+ data_hash: dataHash,
218
+ });
219
+
220
+ return {
221
+ verified: response.verified as boolean,
222
+ sourceId: response.source_id as string,
223
+ dataHash,
224
+ expectedHash: response.expected_hash as string | null,
225
+ txId: response.tx_id as string | null,
226
+ timestamp: response.timestamp as string | null,
227
+ message: response.message as string || '',
228
+ quantumResistant: response.quantum_resistant as boolean ?? true,
229
+ };
230
+ }
231
+
232
+ /**
233
+ * Verify the entire Recursive DNA chain for a source.
234
+ */
235
+ async verifyChain(sourceId: string, autoHeal?: boolean): Promise<ChainStatus> {
236
+ const heal = autoHeal ?? this.autoHeal;
237
+
238
+ const response = await this.request<Record<string, unknown>>('POST', '/sentinel/verify', {
239
+ source_id: sourceId,
240
+ auto_heal: heal,
241
+ });
242
+
243
+ const breakEvent = response.break_event as Record<string, unknown> | null;
244
+
245
+ const status: ChainStatus = {
246
+ sourceId,
247
+ chainIntact: response.chain_intact as boolean,
248
+ totalPackets: response.total_packets as number,
249
+ verifiedPackets: response.total_packets as number,
250
+ breakSequence: breakEvent?.break_sequence as number | null ?? null,
251
+ breakTimestamp: breakEvent?.break_timestamp as string | null ?? null,
252
+ health: 'healthy',
253
+ quantumResistant: response.quantum_resistant as boolean ?? true,
254
+ healed: response.healed as boolean ?? false,
255
+ forensicMessage: response.message as string | null,
256
+ };
257
+
258
+ if (!status.chainIntact && !heal) {
259
+ throw new ChainIntegrityError(
260
+ `DNA chain broken at sequence ${status.breakSequence}`,
261
+ status.breakSequence,
262
+ status.breakTimestamp
263
+ );
264
+ }
265
+
266
+ return status;
267
+ }
268
+
269
+ // ==================== SENTINEL API ====================
270
+
271
+ /**
272
+ * Get the current status of the Sentinel (self-healing system).
273
+ */
274
+ async getSentinelStatus(): Promise<SentinelStatus> {
275
+ const response = await this.request<Record<string, unknown>>('GET', '/sentinel/status');
276
+
277
+ const features = response.features as Record<string, boolean>;
278
+
279
+ return {
280
+ active: response.active as boolean,
281
+ monitoring: response.monitoring as boolean,
282
+ auditIntervalSeconds: response.audit_interval_seconds as number,
283
+ quarantinedSources: response.quarantined_sources as number,
284
+ features: {
285
+ continuousAudit: features?.continuous_audit ?? false,
286
+ autoHeal: features?.auto_heal ?? false,
287
+ webhooks: features?.webhooks ?? false,
288
+ quantumResistant: features?.quantum_resistant ?? true,
289
+ },
290
+ description: response.description as string,
291
+ };
292
+ }
293
+
294
+ /**
295
+ * Manually trigger a full audit of all data sources.
296
+ */
297
+ async triggerAudit(): Promise<AuditResult> {
298
+ const response = await this.request<Record<string, unknown>>('POST', '/sentinel/audit/run');
299
+
300
+ return {
301
+ auditId: response.audit_id as string,
302
+ timestamp: response.timestamp as string,
303
+ sourcesAudited: response.sources_audited as number,
304
+ packetsVerified: response.packets_verified as number,
305
+ healthySources: response.healthy_sources as number,
306
+ infectedSources: response.infected_sources as number,
307
+ healedSources: response.healed_sources as number,
308
+ auditDurationMs: response.audit_duration_ms as number,
309
+ };
310
+ }
311
+
312
+ /**
313
+ * Manually trigger healing for a specific data break.
314
+ */
315
+ async heal(sourceId: string, breakSequence: number): Promise<Record<string, unknown>> {
316
+ return this.request('POST', '/sentinel/heal', {
317
+ source_id: sourceId,
318
+ break_sequence: breakSequence,
319
+ });
320
+ }
321
+
322
+ /**
323
+ * Get history of healing events.
324
+ */
325
+ async getHealingHistory(sourceId?: string): Promise<HealingEvent[]> {
326
+ const params = sourceId ? { source_id: sourceId } : undefined;
327
+ const response = await this.request<Record<string, unknown>>('GET', '/sentinel/healing-history', undefined, params);
328
+
329
+ const events = response.events as Record<string, unknown>[];
330
+
331
+ return events.map(e => ({
332
+ id: e.id as string,
333
+ sourceId: e.source_id as string,
334
+ breakSequence: e.break_sequence as number,
335
+ originalHash: e.original_payload_hash as string,
336
+ corruptedHash: e.corrupted_payload_hash as string,
337
+ recoverySource: e.recovery_source as string,
338
+ healedAt: e.healed_at as string,
339
+ success: e.success as boolean,
340
+ receiptHash: e.verification_tx_id as string | null,
341
+ message: e.message as string,
342
+ }));
343
+ }
344
+
345
+ // ==================== CREDITS API ====================
346
+
347
+ /**
348
+ * Get current BSV credit balance.
349
+ */
350
+ async getCredits(): Promise<CreditBalance> {
351
+ const response = await this.request<Record<string, unknown>>('GET', '/credits/balance');
352
+
353
+ return {
354
+ satoshiBalance: response.satoshi_balance as number,
355
+ packetsAvailable: response.packets_available as number,
356
+ totalDeposited: response.total_deposited as number,
357
+ totalSpent: response.total_spent as number,
358
+ };
359
+ }
360
+
361
+ // ==================== PACKETS API ====================
362
+
363
+ /**
364
+ * Get all packets for a source.
365
+ */
366
+ async getPackets(sourceId: string, limit: number = 100): Promise<BitPacket[]> {
367
+ const response = await this.request<Record<string, unknown>[]>('GET', '/packets', undefined, {
368
+ source_id: sourceId,
369
+ limit: String(limit),
370
+ });
371
+
372
+ return response.map(p => this.mapBitPacket(p));
373
+ }
374
+
375
+ // ==================== HELPERS ====================
376
+
377
+ private mapBitPacket(data: Record<string, unknown>): BitPacket {
378
+ return {
379
+ sourceId: data.source_id as string,
380
+ sequenceNo: data.sequence_no as number,
381
+ payloadHash: data.payload_hash as string,
382
+ recursiveDna: data.recursive_dna as string,
383
+ prevPacketHash: data.prev_packet_hash as string | null,
384
+ timestamp: data.timestamp as string,
385
+ txId: data.tx_id as string | null,
386
+ status: data.status as any,
387
+ chainValid: data.chain_valid as boolean,
388
+ originalDataPreview: data.original_data_preview as string,
389
+ };
390
+ }
391
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,84 @@
1
+ /**
2
+ * LumeFuse SDK Error Classes
3
+ */
4
+
5
+ export class LumeFuseError extends Error {
6
+ code: string;
7
+ details: Record<string, unknown>;
8
+
9
+ constructor(message: string, code: string = 'LUMEFUSE_ERROR', details: Record<string, unknown> = {}) {
10
+ super(message);
11
+ this.name = 'LumeFuseError';
12
+ this.code = code;
13
+ this.details = details;
14
+ }
15
+ }
16
+
17
+ export class AuthenticationError extends LumeFuseError {
18
+ constructor(message: string = 'Invalid or missing API key') {
19
+ super(message, 'AUTH_ERROR');
20
+ this.name = 'AuthenticationError';
21
+ }
22
+ }
23
+
24
+ export class RateLimitError extends LumeFuseError {
25
+ retryAfter: number | null;
26
+
27
+ constructor(message: string = 'Rate limit exceeded', retryAfter: number | null = null) {
28
+ super(message, 'RATE_LIMIT');
29
+ this.name = 'RateLimitError';
30
+ this.retryAfter = retryAfter;
31
+ }
32
+ }
33
+
34
+ export class ChainIntegrityError extends LumeFuseError {
35
+ breakSequence: number | null;
36
+ breakTimestamp: string | null;
37
+
38
+ constructor(
39
+ message: string = 'Chain integrity compromised',
40
+ breakSequence: number | null = null,
41
+ breakTimestamp: string | null = null
42
+ ) {
43
+ super(message, 'CHAIN_BREAK');
44
+ this.name = 'ChainIntegrityError';
45
+ this.breakSequence = breakSequence;
46
+ this.breakTimestamp = breakTimestamp;
47
+ }
48
+ }
49
+
50
+ export class InsufficientCreditsError extends LumeFuseError {
51
+ required: number | null;
52
+ available: number | null;
53
+
54
+ constructor(
55
+ message: string = 'Insufficient BSV credits',
56
+ required: number | null = null,
57
+ available: number | null = null
58
+ ) {
59
+ super(message, 'INSUFFICIENT_CREDITS');
60
+ this.name = 'InsufficientCreditsError';
61
+ this.required = required;
62
+ this.available = available;
63
+ }
64
+ }
65
+
66
+ export class NetworkError extends LumeFuseError {
67
+ statusCode: number | null;
68
+
69
+ constructor(message: string = 'Network request failed', statusCode: number | null = null) {
70
+ super(message, 'NETWORK_ERROR');
71
+ this.name = 'NetworkError';
72
+ this.statusCode = statusCode;
73
+ }
74
+ }
75
+
76
+ export class ValidationError extends LumeFuseError {
77
+ field: string | null;
78
+
79
+ constructor(message: string = 'Validation failed', field: string | null = null) {
80
+ super(message, 'VALIDATION_ERROR');
81
+ this.name = 'ValidationError';
82
+ this.field = field;
83
+ }
84
+ }
package/src/index.ts ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * LumeFuse SDK - Atomic Veracity Protocol
3
+ *
4
+ * The "Born-Signed" SDK for embedding structural DNA into your data.
5
+ */
6
+
7
+ export { LumeFuse } from './client';
8
+ export { DataStream } from './stream';
9
+ export type { StreamResult } from './stream';
10
+ export type {
11
+ BitPacket,
12
+ VerificationResult,
13
+ ChainStatus,
14
+ CreditBalance,
15
+ HealingEvent,
16
+ LumeFuseConfig,
17
+ PacketStatus,
18
+ ChainHealth,
19
+ } from './types';
20
+ export {
21
+ LumeFuseError,
22
+ AuthenticationError,
23
+ RateLimitError,
24
+ ChainIntegrityError,
25
+ InsufficientCreditsError,
26
+ NetworkError,
27
+ ValidationError,
28
+ } from './errors';