@sparkvault/sdk 1.9.2 → 1.10.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.
@@ -5102,8 +5102,8 @@ class IdentityRenderer {
5102
5102
  * Implements the Container interface for use with IdentityRenderer.
5103
5103
  */
5104
5104
  const DEFAULT_OPTIONS$1 = {
5105
- showHeader: true,
5106
- showCloseButton: true,
5105
+ showHeader: false,
5106
+ showCloseButton: false,
5107
5107
  showFooter: true,
5108
5108
  };
5109
5109
  class InlineContainer {
@@ -5294,6 +5294,36 @@ function onDomReady(callback) {
5294
5294
  callback();
5295
5295
  }
5296
5296
  }
5297
+ /**
5298
+ * Execute a callback when document.body is available.
5299
+ * Handles edge cases where body might not exist even after DOMContentLoaded
5300
+ * (e.g., scripts with defer/async loading during parsing).
5301
+ */
5302
+ function onBodyReady(callback) {
5303
+ if (document.body) {
5304
+ callback();
5305
+ }
5306
+ else {
5307
+ // Body not yet available - wait for DOMContentLoaded
5308
+ // or poll briefly as a fallback for edge cases
5309
+ const checkBody = () => {
5310
+ if (document.body) {
5311
+ callback();
5312
+ }
5313
+ else {
5314
+ // Rare edge case: poll for body availability
5315
+ window.requestAnimationFrame(checkBody);
5316
+ }
5317
+ };
5318
+ if (document.readyState === 'loading') {
5319
+ document.addEventListener('DOMContentLoaded', checkBody, { once: true });
5320
+ }
5321
+ else {
5322
+ // Document ready but no body yet - poll
5323
+ window.requestAnimationFrame(checkBody);
5324
+ }
5325
+ }
5326
+ }
5297
5327
 
5298
5328
  /* global Element */
5299
5329
  /**
@@ -5455,7 +5485,11 @@ class IdentityModule {
5455
5485
  });
5456
5486
  });
5457
5487
  });
5458
- observer.observe(document.body, { childList: true, subtree: true });
5488
+ // Observe document.body for dynamic elements
5489
+ // Use onBodyReady to handle edge cases where body isn't available yet
5490
+ onBodyReady(() => {
5491
+ observer?.observe(document.body, { childList: true, subtree: true });
5492
+ });
5459
5493
  };
5460
5494
  // Defer attachment until DOM is ready
5461
5495
  onDomReady(attachToElements);
@@ -7106,7 +7140,7 @@ class UploadModalContainer {
7106
7140
  <div class="svu-security-badges">
7107
7141
  <span class="svu-tls-badge">TLS 1.3 In Transit</span>
7108
7142
  <span class="svu-tls-badge">Encrypted At Rest</span>
7109
- <span class="svu-tls-badge">Zero Knowledge</span>
7143
+ <span class="svu-tls-badge">Zero Trust</span>
7110
7144
  </div>
7111
7145
  `;
7112
7146
  sidebar.appendChild(security);
@@ -7172,8 +7206,8 @@ class UploadModalContainer {
7172
7206
  * Implements the UploadContainer interface for use with UploadRenderer.
7173
7207
  */
7174
7208
  const DEFAULT_OPTIONS = {
7175
- showHeader: true,
7176
- showCloseButton: true,
7209
+ showHeader: false,
7210
+ showCloseButton: false,
7177
7211
  showFooter: true,
7178
7212
  };
7179
7213
  class UploadInlineContainer {
@@ -7465,7 +7499,7 @@ class UploadInlineContainer {
7465
7499
  <div class="svu-security-badges">
7466
7500
  <span class="svu-tls-badge">TLS 1.3 In Transit</span>
7467
7501
  <span class="svu-tls-badge">Encrypted At Rest</span>
7468
- <span class="svu-tls-badge">Zero Knowledge</span>
7502
+ <span class="svu-tls-badge">Zero Trust</span>
7469
7503
  </div>
7470
7504
  `;
7471
7505
  sidebar.appendChild(security);
@@ -8116,45 +8150,81 @@ class UploadRenderer {
8116
8150
  }
8117
8151
  }
8118
8152
  async uploadWithTus(file, forgeUrl, ingotId, requestId) {
8119
- // Simple XHR upload with progress (tus-like behavior)
8120
- // Timeout: 10 minutes for large file uploads
8121
- const UPLOAD_TIMEOUT_MS = 10 * 60 * 1000;
8122
- return new Promise((resolve, reject) => {
8123
- const xhr = new XMLHttpRequest();
8124
- xhr.timeout = UPLOAD_TIMEOUT_MS;
8125
- xhr.upload.onprogress = (e) => {
8126
- if (e.lengthComputable) {
8127
- const progress = Math.round((e.loaded / e.total) * 100);
8128
- this.setState({
8129
- view: 'uploading',
8130
- file,
8131
- ingotId,
8132
- requestId,
8133
- progress,
8134
- bytesUploaded: e.loaded,
8135
- });
8136
- this.callbacks.onProgress?.({
8137
- bytesUploaded: e.loaded,
8138
- bytesTotal: e.total,
8139
- percentage: progress,
8140
- phase: 'uploading',
8141
- });
8142
- }
8143
- };
8144
- xhr.onload = () => {
8145
- if (xhr.status >= 200 && xhr.status < 300) {
8146
- resolve();
8147
- }
8148
- else {
8149
- reject(new Error(`Upload failed with status ${xhr.status}`));
8150
- }
8151
- };
8152
- xhr.onerror = () => reject(new Error('Upload failed'));
8153
- xhr.ontimeout = () => reject(new Error('Upload timed out'));
8154
- xhr.open('POST', forgeUrl);
8155
- xhr.setRequestHeader('Content-Type', file.type || 'application/octet-stream');
8156
- xhr.send(file);
8153
+ // tus resumable upload protocol v1.0.0
8154
+ // Uploads file in chunks to avoid Cloudflare's 100MB request limit
8155
+ const TUS_VERSION = '1.0.0';
8156
+ const DEFAULT_CHUNK_SIZE = 50 * 1024 * 1024; // 50MB default, server may override
8157
+ // Extract base URL and ISTK from forge URL
8158
+ const url = new URL(forgeUrl);
8159
+ const istk = url.searchParams.get('istk');
8160
+ const baseUrl = `${url.origin}${url.pathname}`;
8161
+ if (!istk) {
8162
+ throw new Error('Missing ISTK in forge URL');
8163
+ }
8164
+ // Step 1: Create tus upload session
8165
+ const createResponse = await fetch(baseUrl, {
8166
+ method: 'POST',
8167
+ headers: {
8168
+ 'Tus-Resumable': TUS_VERSION,
8169
+ 'Upload-Length': String(file.size),
8170
+ 'X-ISTK': istk,
8171
+ 'Content-Type': 'application/offset+octet-stream',
8172
+ },
8157
8173
  });
8174
+ if (!createResponse.ok) {
8175
+ const errorText = await createResponse.text();
8176
+ throw new Error(`Failed to create upload session: ${createResponse.status} ${errorText}`);
8177
+ }
8178
+ // Get upload location and chunk size from response
8179
+ const location = createResponse.headers.get('Location');
8180
+ const chunkSizeHeader = createResponse.headers.get('X-Chunk-Size');
8181
+ const chunkSize = chunkSizeHeader ? parseInt(chunkSizeHeader, 10) : DEFAULT_CHUNK_SIZE;
8182
+ if (!location) {
8183
+ throw new Error('Server did not return upload location');
8184
+ }
8185
+ // Build full upload URL
8186
+ const uploadUrl = location.startsWith('http') ? location : `${url.origin}${location}`;
8187
+ // Step 2: Upload file in chunks
8188
+ let offset = 0;
8189
+ const totalSize = file.size;
8190
+ while (offset < totalSize) {
8191
+ const end = Math.min(offset + chunkSize, totalSize);
8192
+ const chunk = file.slice(offset, end);
8193
+ // Upload chunk
8194
+ const patchResponse = await fetch(uploadUrl, {
8195
+ method: 'PATCH',
8196
+ headers: {
8197
+ 'Tus-Resumable': TUS_VERSION,
8198
+ 'Upload-Offset': String(offset),
8199
+ 'Content-Type': 'application/offset+octet-stream',
8200
+ },
8201
+ body: chunk,
8202
+ });
8203
+ if (!patchResponse.ok) {
8204
+ const errorText = await patchResponse.text();
8205
+ throw new Error(`Chunk upload failed: ${patchResponse.status} ${errorText}`);
8206
+ }
8207
+ // Update offset from server response
8208
+ const newOffsetHeader = patchResponse.headers.get('Upload-Offset');
8209
+ const newOffset = newOffsetHeader ? parseInt(newOffsetHeader, 10) : end;
8210
+ offset = newOffset;
8211
+ // Update progress
8212
+ const progress = Math.round((offset / totalSize) * 100);
8213
+ this.setState({
8214
+ view: 'uploading',
8215
+ file,
8216
+ ingotId,
8217
+ requestId,
8218
+ progress,
8219
+ bytesUploaded: offset,
8220
+ });
8221
+ this.callbacks.onProgress?.({
8222
+ bytesUploaded: offset,
8223
+ bytesTotal: totalSize,
8224
+ percentage: progress,
8225
+ phase: 'uploading',
8226
+ });
8227
+ }
8158
8228
  }
8159
8229
  async runCeremony(file, ingotId, requestId) {
8160
8230
  for (let i = 0; i < CEREMONY_STEPS.length; i++) {