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