@ultrst-armada/kloks 1.2.1 → 1.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultrst-armada/kloks",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -0,0 +1,338 @@
1
+ import {
2
+ createService,
3
+ createServiceDefinition,
4
+ createServiceProxy
5
+ } from "./chunk-IPP4VURQ.js";
6
+
7
+ // src/services/ServiceBoundary.ts
8
+ function createLocalService(definition) {
9
+ return createService(createServiceDefinition(definition));
10
+ }
11
+ function createRemoteService(definition, adapter, endpoint) {
12
+ return createServiceProxy(definition, adapter, endpoint);
13
+ }
14
+
15
+ // src/services/CompositeBackend.ts
16
+ var CompositeBackend = class {
17
+ id;
18
+ name;
19
+ version;
20
+ services = /* @__PURE__ */ new Map();
21
+ capabilitiesList = [];
22
+ constructor(name, version = "1.0.0") {
23
+ this.name = name;
24
+ this.id = `backend-${name}`;
25
+ this.version = version;
26
+ }
27
+ registerService(service) {
28
+ this.services.set(service.name, service);
29
+ return this;
30
+ }
31
+ getService(name) {
32
+ return this.services.get(name);
33
+ }
34
+ getServices() {
35
+ return Array.from(this.services.values());
36
+ }
37
+ registerCapability(capability) {
38
+ if (!this.capabilitiesList.includes(capability)) {
39
+ this.capabilitiesList.push(capability);
40
+ }
41
+ return this;
42
+ }
43
+ async start() {
44
+ for (const service of this.services.values()) {
45
+ await service.start();
46
+ }
47
+ }
48
+ async stop() {
49
+ for (const service of this.services.values()) {
50
+ await service.stop();
51
+ }
52
+ }
53
+ async call(op, input, ctx) {
54
+ const firstDot = op.indexOf(".");
55
+ if (firstDot === -1) {
56
+ throw new Error(`[CompositeBackend] Invalid operation '${op}'. Expected 'service.operation'.`);
57
+ }
58
+ const serviceName = op.slice(0, firstDot);
59
+ const operationName = op.slice(firstDot + 1);
60
+ const service = this.services.get(serviceName);
61
+ if (!service) {
62
+ throw new Error(`[CompositeBackend] Service '${serviceName}' not registered in '${this.name}'.`);
63
+ }
64
+ return await service.call(operationName, input, ctx);
65
+ }
66
+ getState() {
67
+ const aggregated = {};
68
+ for (const [name, service] of this.services) {
69
+ aggregated[name] = service.getState();
70
+ }
71
+ return Object.freeze(aggregated);
72
+ }
73
+ setState(_mutation) {
74
+ }
75
+ on(event, handler) {
76
+ const [serviceName, eventName] = event.split(".");
77
+ const service = this.services.get(serviceName);
78
+ if (!service) return () => {
79
+ };
80
+ return service.on(eventName, handler);
81
+ }
82
+ emit(event, payload) {
83
+ const [serviceName, eventName] = event.split(".");
84
+ const service = this.services.get(serviceName);
85
+ if (service) {
86
+ service.emit(eventName, payload);
87
+ }
88
+ }
89
+ getDefinition() {
90
+ const ops = {};
91
+ for (const service of this.services.values()) {
92
+ const def = service.getDefinition();
93
+ for (const [opKey, opDef] of Object.entries(def.operations)) {
94
+ ops[`${service.name}.${opKey}`] = opDef;
95
+ }
96
+ }
97
+ return {
98
+ id: this.id,
99
+ name: this.name,
100
+ version: this.version,
101
+ operations: ops,
102
+ capabilities: this.capabilitiesList.map((c) => ({
103
+ id: c,
104
+ name: c,
105
+ protocols: ["http", "ws", "udp", "p2p"]
106
+ })),
107
+ protocols: ["http", "ws", "udp", "p2p"]
108
+ };
109
+ }
110
+ };
111
+
112
+ // src/services/EndpointGenerator.ts
113
+ var EndpointGenerator = class {
114
+ static generateEndpoints(backend, basePort = 8e3) {
115
+ const def = backend.getDefinition();
116
+ const ops = Object.keys(def.operations);
117
+ return {
118
+ httpRoutes: ops.map((op) => `/api/${op.replace(".", "/")}`),
119
+ wsChannels: ops.map((op) => `channel:${op}`),
120
+ udpEndpoints: [`udp://127.0.0.1:${basePort}`],
121
+ p2pBindings: ops.map((op) => `p2p://${backend.name}/${op}`)
122
+ };
123
+ }
124
+ static generateDescriptors(backend, host = "127.0.0.1", port = 8080) {
125
+ return [
126
+ { protocol: "http", address: host, port, capabilities: ["requestResponse"] },
127
+ { protocol: "ws", address: host, port, capabilities: ["requestResponse", "pubSub"] },
128
+ { protocol: "udp", address: host, port, capabilities: ["datagram"] },
129
+ { protocol: "p2p", address: `p2p://${backend.name}`, capabilities: ["peerToPeer"] }
130
+ ];
131
+ }
132
+ };
133
+
134
+ // src/services/ServiceGenerators.ts
135
+ function createBackend(name) {
136
+ return new CompositeBackend(name);
137
+ }
138
+ function createCompositeService(name) {
139
+ return new CompositeBackend(name);
140
+ }
141
+
142
+ // src/services/nodeGraphPersistence/types.ts
143
+ var LOCAL_STORAGE_GRAPHS_PREFIX = "depm_graph_doc_";
144
+
145
+ // src/services/nodeGraphPersistence/helpers.ts
146
+ function validateGraphDocument(data) {
147
+ const errors = [];
148
+ if (!data || typeof data !== "object") {
149
+ return { valid: false, errors: ["Invalid JSON: payload must be a JSON object"] };
150
+ }
151
+ if (!data.appId) errors.push("Missing required property: appId");
152
+ if (!data.appName) errors.push("Missing required property: appName");
153
+ if (!data.graph || typeof data.graph !== "object") {
154
+ errors.push("Missing or invalid property: graph");
155
+ } else {
156
+ if (!Array.isArray(data.graph.nodes)) errors.push("Missing or invalid property: graph.nodes (must be array)");
157
+ if (!Array.isArray(data.graph.edges)) errors.push("Missing or invalid property: graph.edges (must be array)");
158
+ }
159
+ if (errors.length > 0) {
160
+ return { valid: false, errors };
161
+ }
162
+ return {
163
+ valid: true,
164
+ errors: [],
165
+ document: data
166
+ };
167
+ }
168
+ function exportGraphAsJsonFile(graphDoc) {
169
+ if (typeof window === "undefined") return;
170
+ const jsonString = JSON.stringify(graphDoc, null, 2);
171
+ const blob = new Blob([jsonString], { type: "application/json" });
172
+ const url = URL.createObjectURL(blob);
173
+ const safeName = (graphDoc.appName || "graph").toLowerCase().replace(/[^a-z0-9_-]/g, "_");
174
+ const filename = `${safeName}_${graphDoc.appId}.graph.json`;
175
+ const link = window.document.createElement("a");
176
+ link.href = url;
177
+ link.download = filename;
178
+ window.document.body.appendChild(link);
179
+ link.click();
180
+ window.document.body.removeChild(link);
181
+ URL.revokeObjectURL(url);
182
+ }
183
+ async function parseGraphJsonFile(file) {
184
+ try {
185
+ const text = await file.text();
186
+ const parsed = JSON.parse(text);
187
+ return validateGraphDocument(parsed);
188
+ } catch (err) {
189
+ return {
190
+ valid: false,
191
+ errors: [`Failed to parse JSON: ${err.message}`]
192
+ };
193
+ }
194
+ }
195
+
196
+ // src/services/nodeGraphPersistence/nodeGraphPersistence.ts
197
+ var NodeGraphPersistenceService = class {
198
+ /**
199
+ * Generates a fully compliant v1.0.0 JSON document representing the app's complete node graph.
200
+ */
201
+ createGraphDocument(options) {
202
+ const { app, nodes, edges, deployments = [], viewport, storageConnection } = options;
203
+ const now = (/* @__PURE__ */ new Date()).toISOString();
204
+ const targetProvider = storageConnection?.provider || "local_fs";
205
+ const targetBucketOrFolder = storageConnection?.localFolderName || storageConnection?.b2BucketName || storageConnection?.s3BucketName || storageConnection?.r2BucketName || storageConnection?.supabaseBucketName || "Vault";
206
+ const safeAppName = (app.name || "graph").toLowerCase().replace(/[^a-z0-9_-]/g, "_");
207
+ const filePath = `graphs/${safeAppName}_${app.id}.graph.json`;
208
+ return {
209
+ $schema: "https://skipo.dev/schemas/depm-node-graph.v1.json",
210
+ version: "1.0.0",
211
+ appId: app.id,
212
+ appName: app.name,
213
+ appIcon: app.icon || "Layers",
214
+ description: app.description,
215
+ publicLink: app.publicLink || `https://${safeAppName}.ecosystem.network`,
216
+ createdAt: app.createdAt || now,
217
+ updatedAt: now,
218
+ exportedAt: now,
219
+ viewport: viewport || { zoom: 1, pan: { x: 0, y: 0 } },
220
+ graph: {
221
+ nodes: nodes.filter((n) => !n.appId || n.appId === app.id),
222
+ edges: edges.filter((e) => !e.appId || e.appId === app.id)
223
+ },
224
+ deployments: deployments.filter(
225
+ (d) => nodes.some((n) => (!n.appId || n.appId === app.id) && n.deploymentId === d.id)
226
+ ),
227
+ storageMetadata: {
228
+ provider: targetProvider,
229
+ targetName: targetBucketOrFolder,
230
+ targetPath: filePath,
231
+ storageConnectionId: storageConnection?.id,
232
+ syncedAt: now
233
+ }
234
+ };
235
+ }
236
+ /**
237
+ * Validates a parsed JSON object to verify it matches the NodeGraphDocument schema.
238
+ */
239
+ validateGraphDocument(data) {
240
+ return validateGraphDocument(data);
241
+ }
242
+ /**
243
+ * Saves the node graph to the selected user storage option (Local Folder or Cloud Bucket)
244
+ * and caches it in LocalStorage.
245
+ */
246
+ async saveGraphToStorage(options, customDirectoryHandle) {
247
+ const document = this.createGraphDocument(options);
248
+ const jsonString = JSON.stringify(document, null, 2);
249
+ const provider = options.storageConnection?.provider || "local_fs";
250
+ try {
251
+ if (typeof window !== "undefined" && window.localStorage) {
252
+ window.localStorage.setItem(`${LOCAL_STORAGE_GRAPHS_PREFIX}${document.appId}`, jsonString);
253
+ window.localStorage.setItem(`depm_latest_saved_graph_${document.appId}`, JSON.stringify({
254
+ updatedAt: document.updatedAt,
255
+ provider,
256
+ targetName: document.storageMetadata?.targetName
257
+ }));
258
+ }
259
+ } catch {
260
+ }
261
+ if (provider === "local_fs" && typeof window !== "undefined") {
262
+ try {
263
+ if (customDirectoryHandle) {
264
+ const fileName = `${(document.appName || "graph").toLowerCase().replace(/[^a-z0-9_-]/g, "_")}_${document.appId}.graph.json`;
265
+ const fileHandle = await customDirectoryHandle.getFileHandle(fileName, { create: true });
266
+ const writable = await fileHandle.createWritable();
267
+ await writable.write(jsonString);
268
+ await writable.close();
269
+ return {
270
+ success: true,
271
+ document,
272
+ message: `Saved node graph directly to local folder "${customDirectoryHandle.name || "Vault"}"`,
273
+ targetPath: `${customDirectoryHandle.name}/${fileName}`
274
+ };
275
+ }
276
+ } catch (err) {
277
+ console.warn("Native FS Handle save warning:", err);
278
+ }
279
+ }
280
+ const targetPath = document.storageMetadata?.targetPath || `graphs/${document.appId}.graph.json`;
281
+ const targetName = document.storageMetadata?.targetName || "Vault";
282
+ const simulatedLatency = provider === "local_fs" ? 10 : 80;
283
+ await new Promise((resolve) => setTimeout(resolve, simulatedLatency));
284
+ return {
285
+ success: true,
286
+ document,
287
+ message: `Node graph saved to ${(provider || "storage").toUpperCase().replace("_", " ")} (${targetName}: ${targetPath})`,
288
+ targetPath
289
+ };
290
+ }
291
+ /**
292
+ * Loads a cached or previously saved node graph document for an app.
293
+ */
294
+ loadSavedGraph(appId) {
295
+ try {
296
+ if (typeof window !== "undefined" && window.localStorage) {
297
+ const stored = window.localStorage.getItem(`${LOCAL_STORAGE_GRAPHS_PREFIX}${appId}`);
298
+ if (stored) {
299
+ const parsed = JSON.parse(stored);
300
+ const validation = this.validateGraphDocument(parsed);
301
+ if (validation.valid && validation.document) {
302
+ return validation.document;
303
+ }
304
+ }
305
+ }
306
+ } catch {
307
+ }
308
+ return null;
309
+ }
310
+ /**
311
+ * Triggers a browser download of the node graph as a portable JSON file.
312
+ */
313
+ exportGraphAsJsonFile(graphDoc) {
314
+ exportGraphAsJsonFile(graphDoc);
315
+ }
316
+ /**
317
+ * Reads and parses a graph JSON file from an input file element.
318
+ */
319
+ async parseGraphJsonFile(file) {
320
+ return parseGraphJsonFile(file);
321
+ }
322
+ };
323
+ var nodeGraphPersistence = new NodeGraphPersistenceService();
324
+
325
+ export {
326
+ createLocalService,
327
+ createRemoteService,
328
+ CompositeBackend,
329
+ EndpointGenerator,
330
+ createBackend,
331
+ createCompositeService,
332
+ LOCAL_STORAGE_GRAPHS_PREFIX,
333
+ validateGraphDocument,
334
+ exportGraphAsJsonFile,
335
+ parseGraphJsonFile,
336
+ NodeGraphPersistenceService,
337
+ nodeGraphPersistence
338
+ };
@@ -166,7 +166,7 @@ async function getPrimarySrklDeploymentUrl(queryDb) {
166
166
  cachedSrklUrl = { url: fallback, expiresAt: Date.now() + 10 * 60 * 1e3 };
167
167
  return fallback;
168
168
  }
169
- async function getSrklAccounts(userId, service, queryDb) {
169
+ async function getSrklAccounts(userId, service2, queryDb) {
170
170
  const srklUrl = await getPrimarySrklDeploymentUrl(queryDb);
171
171
  const accounts = [];
172
172
  const seenKeys = /* @__PURE__ */ new Set();
@@ -182,7 +182,7 @@ async function getSrklAccounts(userId, service, queryDb) {
182
182
  ...p.loky?.storage_connections || []
183
183
  ];
184
184
  for (const conn of pool) {
185
- if (service && !conn.provider?.toLowerCase().includes(service.toLowerCase())) continue;
185
+ if (service2 && !conn.provider?.toLowerCase().includes(service2.toLowerCase())) continue;
186
186
  const kId = (conn.b2KeyId || conn.keyId || conn.accessKeyId || "").trim();
187
187
  const key = kId ? `key:${kId}` : `id:${conn.id}`;
188
188
  if (!seenKeys.has(key)) {
@@ -216,7 +216,7 @@ async function getSrklAccounts(userId, service, queryDb) {
216
216
  const list = Array.isArray(data.services) ? data.services : [];
217
217
  for (const item of list) {
218
218
  const sId = String(item.serviceId || item.id || "").toLowerCase();
219
- if (service && !sId.includes(service.toLowerCase())) continue;
219
+ if (service2 && !sId.includes(service2.toLowerCase())) continue;
220
220
  const kId = (item.credentials?.keyId || item.credentials?.b2KeyId || "").trim();
221
221
  const key = kId ? `key:${kId}` : `id:${item.id}`;
222
222
  if (!seenKeys.has(key)) {
@@ -325,6 +325,304 @@ function useSrklServices(options) {
325
325
  };
326
326
  }
327
327
 
328
+ // src/srkl/SrklCryptoClient.ts
329
+ var SALT_LENGTH = 16;
330
+ var IV_LENGTH = 12;
331
+ var TAG_LENGTH = 16;
332
+ var HEADER_LENGTH = SALT_LENGTH + IV_LENGTH + TAG_LENGTH;
333
+ var PBKDF2_ITERATIONS = 1e5;
334
+ function hexToBytes(hex) {
335
+ const bytes = new Uint8Array(hex.length / 2);
336
+ for (let i = 0; i < bytes.length; i++) {
337
+ bytes[i] = parseInt(hex.substring(i * 2, i * 2 + 2), 16);
338
+ }
339
+ return bytes;
340
+ }
341
+ async function decryptVaultBufferInBrowser(envelopeBuffer, secretHex) {
342
+ const rawBytes = envelopeBuffer instanceof Uint8Array ? envelopeBuffer : new Uint8Array(envelopeBuffer);
343
+ if (rawBytes.length < HEADER_LENGTH) {
344
+ throw new Error(`Invalid encrypted vault payload: buffer too short (${rawBytes.length} bytes)`);
345
+ }
346
+ const salt = rawBytes.slice(0, SALT_LENGTH);
347
+ const iv = rawBytes.slice(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);
348
+ const authTag = rawBytes.slice(SALT_LENGTH + IV_LENGTH, HEADER_LENGTH);
349
+ const ciphertext = rawBytes.slice(HEADER_LENGTH);
350
+ const ciphertextWithTag = new Uint8Array(ciphertext.length + authTag.length);
351
+ ciphertextWithTag.set(ciphertext, 0);
352
+ ciphertextWithTag.set(authTag, ciphertext.length);
353
+ const cryptoObj = typeof window !== "undefined" ? window.crypto : globalThis.crypto;
354
+ if (!cryptoObj?.subtle) {
355
+ throw new Error("Web Crypto API (crypto.subtle) is not available in this environment.");
356
+ }
357
+ const rawSecretBytes = new TextEncoder().encode(secretHex);
358
+ const baseKey = await cryptoObj.subtle.importKey(
359
+ "raw",
360
+ rawSecretBytes,
361
+ "PBKDF2",
362
+ false,
363
+ ["deriveKey"]
364
+ );
365
+ const aesKey = await cryptoObj.subtle.deriveKey(
366
+ {
367
+ name: "PBKDF2",
368
+ salt,
369
+ iterations: PBKDF2_ITERATIONS,
370
+ hash: "SHA-256"
371
+ },
372
+ baseKey,
373
+ { name: "AES-GCM", length: 256 },
374
+ false,
375
+ ["decrypt"]
376
+ );
377
+ const decryptedBuffer = await cryptoObj.subtle.decrypt(
378
+ {
379
+ name: "AES-GCM",
380
+ iv,
381
+ tagLength: 128
382
+ },
383
+ aesKey,
384
+ ciphertextWithTag
385
+ );
386
+ return new Uint8Array(decryptedBuffer);
387
+ }
388
+
389
+ // src/srkl/SrklCredentialRegistry.ts
390
+ var SrklCredentialRegistry = class {
391
+ registry = /* @__PURE__ */ new Map();
392
+ constructor() {
393
+ }
394
+ /**
395
+ * Populate the in-memory registry directly from an initialized sql.js Database instance.
396
+ * Performs the set intersection: DB.services ∩ DEPM.supported_services.
397
+ */
398
+ populateFromSqlite(db, supportedServices) {
399
+ this.registry.clear();
400
+ const normalizedSupported = new Set(supportedServices.map((s) => s.toLowerCase().trim()));
401
+ try {
402
+ const res = db.exec("SELECT * FROM srkl_service_accounts");
403
+ if (!res || !res.length) return;
404
+ const { columns, values } = res[0];
405
+ for (const row of values) {
406
+ const item = {};
407
+ columns.forEach((col, idx) => {
408
+ item[col] = row[idx];
409
+ });
410
+ const serviceId = (item.service_id || "").toLowerCase().trim();
411
+ if (normalizedSupported.size > 0 && !normalizedSupported.has(serviceId)) {
412
+ continue;
413
+ }
414
+ let credentials = {};
415
+ let metadata = {};
416
+ try {
417
+ credentials = JSON.parse(item.credentials_json || "{}");
418
+ } catch {
419
+ }
420
+ try {
421
+ metadata = JSON.parse(item.metadata_json || "{}");
422
+ } catch {
423
+ }
424
+ const entry = {
425
+ id: item.id,
426
+ serviceId: item.service_id,
427
+ name: item.name,
428
+ credentials,
429
+ status: item.status,
430
+ lastSync: item.last_sync,
431
+ metadata,
432
+ createdAt: item.created_at,
433
+ updatedAt: item.updated_at
434
+ };
435
+ const existing = this.registry.get(entry.serviceId) || [];
436
+ existing.push(entry);
437
+ this.registry.set(entry.serviceId, existing);
438
+ }
439
+ } catch (err) {
440
+ console.warn("[SrklCredentialRegistry] Failed to query srkl_service_accounts table:", err.message);
441
+ }
442
+ }
443
+ /**
444
+ * Register a service account in volatile memory (for manual entry or synced accounts)
445
+ */
446
+ registerServiceAccount(account) {
447
+ const serviceId = (account.serviceId || "").toLowerCase().trim();
448
+ if (!serviceId) return;
449
+ if (!this.registry.has(serviceId)) {
450
+ this.registry.set(serviceId, []);
451
+ }
452
+ const existing = this.registry.get(serviceId);
453
+ const idx = existing.findIndex((a) => a.id === account.id);
454
+ if (idx >= 0) {
455
+ existing[idx] = account;
456
+ } else {
457
+ existing.push(account);
458
+ }
459
+ }
460
+ getAccountsForService(serviceId) {
461
+ return this.registry.get(serviceId.toLowerCase().trim()) || [];
462
+ }
463
+ getPrimaryAccount(serviceId) {
464
+ const list = this.getAccountsForService(serviceId);
465
+ return list[0] || null;
466
+ }
467
+ hasCredentials(serviceId) {
468
+ const list = this.getAccountsForService(serviceId);
469
+ return list.length > 0;
470
+ }
471
+ listAvailableServices() {
472
+ return Array.from(this.registry.keys());
473
+ }
474
+ toMap() {
475
+ const obj = {};
476
+ for (const [sId, accounts] of this.registry.entries()) {
477
+ obj[sId] = accounts;
478
+ }
479
+ return obj;
480
+ }
481
+ clear() {
482
+ this.registry.clear();
483
+ }
484
+ };
485
+ var globalCredentialRegistry = new SrklCredentialRegistry();
486
+
487
+ // src/srkl/SrklVaultAccessClient.ts
488
+ var SrklVaultAccessClient = class {
489
+ constructor(options) {
490
+ this.options = options;
491
+ }
492
+ options;
493
+ registry = new SrklCredentialRegistry();
494
+ activeGrant = null;
495
+ sqlEngine = null;
496
+ /**
497
+ * Initialize sql.js engine (WASM).
498
+ * Consumers can pass pre-loaded initSqlJs or let this load from window or CDN.
499
+ */
500
+ async initSqlEngine(customInitSqlJs) {
501
+ if (this.sqlEngine) return this.sqlEngine;
502
+ const initFn = customInitSqlJs || typeof window !== "undefined" && window.initSqlJs;
503
+ if (!initFn) {
504
+ throw new Error(
505
+ 'sql.js engine not found. Ensure "sql.js" is installed or <script src="sql-wasm.js"> is loaded.'
506
+ );
507
+ }
508
+ this.sqlEngine = await initFn({
509
+ locateFile: (file) => `/${file}`
510
+ });
511
+ return this.sqlEngine;
512
+ }
513
+ /**
514
+ * Full pipeline: Authorize → Presigned URL → Direct Download → Decrypt → SQLite Query → Registry.
515
+ */
516
+ async loadCredentials(userSessionToken, workspaceId, customInitSqlJs) {
517
+ await this.initSqlEngine(customInitSqlJs);
518
+ const baseUrl = this.options.srklApiBaseUrl.replace(/\/+$/, "");
519
+ let res;
520
+ try {
521
+ res = await fetch(`${baseUrl}/api/ecosystem/vault-access`, {
522
+ method: "POST",
523
+ headers: {
524
+ "Content-Type": "application/json",
525
+ "Authorization": `Bearer ${userSessionToken}`,
526
+ "x-app-id": this.options.appId,
527
+ ...workspaceId ? { "x-workspace-id": workspaceId } : {}
528
+ },
529
+ body: JSON.stringify({
530
+ appId: this.options.appId,
531
+ workspaceId,
532
+ supportedServices: this.options.supportedServices
533
+ })
534
+ });
535
+ } catch (fetchErr) {
536
+ try {
537
+ res = await fetch("/api/ecosystem/vault-access", {
538
+ method: "POST",
539
+ headers: {
540
+ "Content-Type": "application/json",
541
+ "Authorization": `Bearer ${userSessionToken}`,
542
+ "x-app-id": this.options.appId,
543
+ ...workspaceId ? { "x-workspace-id": workspaceId } : {}
544
+ },
545
+ body: JSON.stringify({
546
+ appId: this.options.appId,
547
+ workspaceId,
548
+ supportedServices: this.options.supportedServices
549
+ })
550
+ });
551
+ } catch {
552
+ throw new Error(`[SrklVaultAccess] Connection to SRKL Engine (${baseUrl}) failed: ${fetchErr.message}`);
553
+ }
554
+ }
555
+ if (!res.ok) {
556
+ const err = await res.json().catch(() => ({ error: res.statusText }));
557
+ throw new Error(`[SrklVaultAccess] Failed to authorize vault access: ${err.error || res.statusText}`);
558
+ }
559
+ const grant = await res.json();
560
+ this.activeGrant = grant;
561
+ const bucketResponse = await fetch(grant.presignedUrl);
562
+ if (!bucketResponse.ok) {
563
+ throw new Error(
564
+ `[SrklVaultAccess] Direct cloud bucket download failed (${bucketResponse.status}): ${bucketResponse.statusText}`
565
+ );
566
+ }
567
+ const encryptedArrayBuffer = await bucketResponse.arrayBuffer();
568
+ const decryptedSqliteBytes = await decryptVaultBufferInBrowser(
569
+ encryptedArrayBuffer,
570
+ grant.keyMaterial.secretHex
571
+ );
572
+ const db = new this.sqlEngine.Database(decryptedSqliteBytes);
573
+ try {
574
+ this.registry.populateFromSqlite(db, this.options.supportedServices);
575
+ } finally {
576
+ db.close();
577
+ }
578
+ return this.registry;
579
+ }
580
+ getRegistry() {
581
+ return this.registry;
582
+ }
583
+ getActiveGrant() {
584
+ return this.activeGrant;
585
+ }
586
+ clear() {
587
+ this.registry.clear();
588
+ this.activeGrant = null;
589
+ }
590
+ };
591
+
592
+ // src/srkl/srklSyncBridge.ts
593
+ async function syncSrklAccounts(userId, sessionToken, srklUrl) {
594
+ const resp = await fetch("/api/srkl/sync", {
595
+ method: "POST",
596
+ headers: {
597
+ "Content-Type": "application/json",
598
+ Authorization: `Bearer ${sessionToken}`,
599
+ "x-user-id": userId
600
+ },
601
+ body: JSON.stringify({ srklUrl })
602
+ });
603
+ return await resp.json();
604
+ }
605
+
606
+ // src/srkl/srkl.ts
607
+ var runtime = {
608
+ VaultAccessClient: SrklVaultAccessClient,
609
+ CredentialRegistry: SrklCredentialRegistry,
610
+ decryptVaultBufferInBrowser,
611
+ hexToBytes
612
+ };
613
+ var deployment = {
614
+ target: "universal",
615
+ supportedEngines: ["browser", "worker", "node"],
616
+ version: "1.0.0"
617
+ };
618
+ var service = {
619
+ name: "srkl",
620
+ description: "SRKL Ecosystem Volatile Memory Credential Vault Service",
621
+ runtime,
622
+ deployment
623
+ };
624
+ var srklService = service;
625
+
328
626
  export {
329
627
  CANONICAL_SRKL_URL,
330
628
  SrklClient,
@@ -332,5 +630,12 @@ export {
332
630
  getPrimarySrklDeploymentUrl,
333
631
  getSrklAccounts,
334
632
  srklListBackblazeBuckets,
335
- useSrklServices
633
+ useSrklServices,
634
+ hexToBytes,
635
+ decryptVaultBufferInBrowser,
636
+ SrklCredentialRegistry,
637
+ globalCredentialRegistry,
638
+ SrklVaultAccessClient,
639
+ syncSrklAccounts,
640
+ srklService
336
641
  };