@sparkvault/sdk 1.11.1 → 1.11.3
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/dist/identity/api.d.ts +11 -0
- package/dist/identity/renderer.d.ts +1 -0
- package/dist/sparkvault.cjs.js +127 -11
- package/dist/sparkvault.cjs.js.map +1 -1
- package/dist/sparkvault.esm.js +127 -11
- package/dist/sparkvault.esm.js.map +1 -1
- package/dist/sparkvault.js +1 -1
- package/dist/sparkvault.js.map +1 -1
- package/dist/vaults/upload/api.d.ts +15 -0
- package/dist/vaults/upload/renderer.d.ts +4 -1
- package/package.json +1 -1
package/dist/identity/api.d.ts
CHANGED
|
@@ -11,7 +11,18 @@ export declare class IdentityApi {
|
|
|
11
11
|
private readonly timeoutMs;
|
|
12
12
|
/** Cached config promise - allows preloading and deduplication */
|
|
13
13
|
private configCache;
|
|
14
|
+
/** Abort controller for cancelling all pending requests on close */
|
|
15
|
+
private closeController;
|
|
14
16
|
constructor(config: ResolvedConfig, timeoutMs?: number);
|
|
17
|
+
/**
|
|
18
|
+
* Abort all pending requests.
|
|
19
|
+
* Call this when the renderer is closed to prevent orphaned requests.
|
|
20
|
+
*/
|
|
21
|
+
abort(): void;
|
|
22
|
+
/**
|
|
23
|
+
* Check if the API has been aborted.
|
|
24
|
+
*/
|
|
25
|
+
isAborted(): boolean;
|
|
15
26
|
private get baseUrl();
|
|
16
27
|
private request;
|
|
17
28
|
/**
|
package/dist/sparkvault.cjs.js
CHANGED
|
@@ -586,27 +586,61 @@ class IdentityApi {
|
|
|
586
586
|
constructor(config, timeoutMs = DEFAULT_TIMEOUT_MS$1) {
|
|
587
587
|
/** Cached config promise - allows preloading and deduplication */
|
|
588
588
|
this.configCache = null;
|
|
589
|
+
/** Abort controller for cancelling all pending requests on close */
|
|
590
|
+
this.closeController = new AbortController();
|
|
589
591
|
this.config = config;
|
|
590
592
|
this.timeoutMs = timeoutMs;
|
|
591
593
|
}
|
|
594
|
+
/**
|
|
595
|
+
* Abort all pending requests.
|
|
596
|
+
* Call this when the renderer is closed to prevent orphaned requests.
|
|
597
|
+
*/
|
|
598
|
+
abort() {
|
|
599
|
+
this.closeController.abort();
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Check if the API has been aborted.
|
|
603
|
+
*/
|
|
604
|
+
isAborted() {
|
|
605
|
+
return this.closeController.signal.aborted;
|
|
606
|
+
}
|
|
592
607
|
get baseUrl() {
|
|
593
608
|
return `${this.config.identityBaseUrl}/${this.config.accountId}`;
|
|
594
609
|
}
|
|
595
610
|
async request(method, endpoint, body) {
|
|
611
|
+
// Don't start new requests if already aborted (renderer closed)
|
|
612
|
+
if (this.closeController.signal.aborted) {
|
|
613
|
+
throw new IdentityApiError('Request cancelled', 'cancelled', 0);
|
|
614
|
+
}
|
|
596
615
|
const url = `${this.baseUrl}${endpoint}`;
|
|
597
616
|
const headers = {
|
|
598
617
|
'Content-Type': 'application/json',
|
|
599
618
|
'Accept': 'application/json',
|
|
600
619
|
};
|
|
601
620
|
// Create abort controller for request timeout
|
|
602
|
-
const
|
|
603
|
-
const timeoutId = setTimeout(() =>
|
|
621
|
+
const timeoutController = new AbortController();
|
|
622
|
+
const timeoutId = setTimeout(() => timeoutController.abort(), this.timeoutMs);
|
|
623
|
+
// Combine timeout and close signals - abort on either
|
|
624
|
+
// Use AbortSignal.any if available, otherwise fall back to manual linking
|
|
625
|
+
let combinedSignal;
|
|
626
|
+
if ('any' in AbortSignal) {
|
|
627
|
+
combinedSignal = AbortSignal.any([
|
|
628
|
+
timeoutController.signal,
|
|
629
|
+
this.closeController.signal,
|
|
630
|
+
]);
|
|
631
|
+
}
|
|
632
|
+
else {
|
|
633
|
+
// Fallback for older browsers: link close signal to timeout controller
|
|
634
|
+
combinedSignal = timeoutController.signal;
|
|
635
|
+
const onClose = () => timeoutController.abort();
|
|
636
|
+
this.closeController.signal.addEventListener('abort', onClose);
|
|
637
|
+
}
|
|
604
638
|
try {
|
|
605
639
|
const response = await fetch(url, {
|
|
606
640
|
method,
|
|
607
641
|
headers,
|
|
608
642
|
body: body ? JSON.stringify(body) : undefined,
|
|
609
|
-
signal:
|
|
643
|
+
signal: combinedSignal,
|
|
610
644
|
});
|
|
611
645
|
const json = await response.json();
|
|
612
646
|
if (!response.ok) {
|
|
@@ -621,8 +655,12 @@ class IdentityApi {
|
|
|
621
655
|
return (json.data ?? json);
|
|
622
656
|
}
|
|
623
657
|
catch (error) {
|
|
624
|
-
// Convert AbortError to a more descriptive
|
|
658
|
+
// Convert AbortError to a more descriptive error
|
|
625
659
|
if (error instanceof Error && error.name === 'AbortError') {
|
|
660
|
+
// Check which signal triggered the abort
|
|
661
|
+
if (this.closeController.signal.aborted) {
|
|
662
|
+
throw new IdentityApiError('Request cancelled', 'cancelled', 0);
|
|
663
|
+
}
|
|
626
664
|
throw new IdentityApiError('Request timed out', 'timeout', 0);
|
|
627
665
|
}
|
|
628
666
|
throw error;
|
|
@@ -1652,6 +1690,7 @@ function getStyles(options) {
|
|
|
1652
1690
|
padding: 14px 20px;
|
|
1653
1691
|
border-bottom: 1px solid ${tokens.border};
|
|
1654
1692
|
background: ${tokens.bg};
|
|
1693
|
+
border-radius: 16px 16px 0 0;
|
|
1655
1694
|
flex-shrink: 0;
|
|
1656
1695
|
}
|
|
1657
1696
|
|
|
@@ -2544,6 +2583,8 @@ function getStyles(options) {
|
|
|
2544
2583
|
justify-content: center;
|
|
2545
2584
|
min-height: 200px;
|
|
2546
2585
|
flex: 1;
|
|
2586
|
+
padding-left: 32px;
|
|
2587
|
+
padding-right: 32px;
|
|
2547
2588
|
}
|
|
2548
2589
|
|
|
2549
2590
|
/* ========================================
|
|
@@ -4432,8 +4473,11 @@ class IdentityRenderer {
|
|
|
4432
4473
|
}
|
|
4433
4474
|
/**
|
|
4434
4475
|
* Close the modal and clean up.
|
|
4476
|
+
* Cancels all pending API requests to prevent orphaned operations.
|
|
4435
4477
|
*/
|
|
4436
4478
|
close() {
|
|
4479
|
+
// Cancel all pending API requests first
|
|
4480
|
+
this.api.abort();
|
|
4437
4481
|
if (this.focusTimeoutId !== null) {
|
|
4438
4482
|
clearTimeout(this.focusTimeoutId);
|
|
4439
4483
|
this.focusTimeoutId = null;
|
|
@@ -5619,9 +5663,30 @@ function isInitiateUploadResponse(data) {
|
|
|
5619
5663
|
}
|
|
5620
5664
|
class UploadApi {
|
|
5621
5665
|
constructor(config) {
|
|
5666
|
+
/** Abort controller for cancelling all pending requests on close */
|
|
5667
|
+
this.closeController = new AbortController();
|
|
5622
5668
|
this.config = config;
|
|
5623
5669
|
this.timeoutMs = config.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
5624
5670
|
}
|
|
5671
|
+
/**
|
|
5672
|
+
* Abort all pending requests.
|
|
5673
|
+
* Call this when the renderer is closed to prevent orphaned requests.
|
|
5674
|
+
*/
|
|
5675
|
+
abort() {
|
|
5676
|
+
this.closeController.abort();
|
|
5677
|
+
}
|
|
5678
|
+
/**
|
|
5679
|
+
* Check if the API has been aborted.
|
|
5680
|
+
*/
|
|
5681
|
+
isAborted() {
|
|
5682
|
+
return this.closeController.signal.aborted;
|
|
5683
|
+
}
|
|
5684
|
+
/**
|
|
5685
|
+
* Get the abort signal for external use (e.g., XHR uploads).
|
|
5686
|
+
*/
|
|
5687
|
+
getAbortSignal() {
|
|
5688
|
+
return this.closeController.signal;
|
|
5689
|
+
}
|
|
5625
5690
|
/**
|
|
5626
5691
|
* Get vault upload info (public endpoint).
|
|
5627
5692
|
*/
|
|
@@ -5674,8 +5739,26 @@ class UploadApi {
|
|
|
5674
5739
|
* Internal request method with timeout handling and error context.
|
|
5675
5740
|
*/
|
|
5676
5741
|
async request(url, options) {
|
|
5677
|
-
|
|
5678
|
-
|
|
5742
|
+
// Don't start new requests if already aborted (renderer closed)
|
|
5743
|
+
if (this.closeController.signal.aborted) {
|
|
5744
|
+
throw new UploadApiError('Request cancelled', 'cancelled', 0);
|
|
5745
|
+
}
|
|
5746
|
+
const timeoutController = new AbortController();
|
|
5747
|
+
const timeoutId = setTimeout(() => timeoutController.abort(), this.timeoutMs);
|
|
5748
|
+
// Combine timeout and close signals - abort on either
|
|
5749
|
+
let combinedSignal;
|
|
5750
|
+
if ('any' in AbortSignal) {
|
|
5751
|
+
combinedSignal = AbortSignal.any([
|
|
5752
|
+
timeoutController.signal,
|
|
5753
|
+
this.closeController.signal,
|
|
5754
|
+
]);
|
|
5755
|
+
}
|
|
5756
|
+
else {
|
|
5757
|
+
// Fallback for older browsers: link close signal to timeout controller
|
|
5758
|
+
combinedSignal = timeoutController.signal;
|
|
5759
|
+
const onClose = () => timeoutController.abort();
|
|
5760
|
+
this.closeController.signal.addEventListener('abort', onClose);
|
|
5761
|
+
}
|
|
5679
5762
|
try {
|
|
5680
5763
|
const response = await fetch(url, {
|
|
5681
5764
|
method: options.method,
|
|
@@ -5684,7 +5767,7 @@ class UploadApi {
|
|
|
5684
5767
|
'Accept': 'application/json',
|
|
5685
5768
|
},
|
|
5686
5769
|
body: options.body,
|
|
5687
|
-
signal:
|
|
5770
|
+
signal: combinedSignal,
|
|
5688
5771
|
});
|
|
5689
5772
|
const json = await response.json();
|
|
5690
5773
|
if (!response.ok) {
|
|
@@ -5710,8 +5793,12 @@ class UploadApi {
|
|
|
5710
5793
|
if (error instanceof SparkVaultError) {
|
|
5711
5794
|
throw error;
|
|
5712
5795
|
}
|
|
5713
|
-
// Convert AbortError to
|
|
5796
|
+
// Convert AbortError to appropriate error
|
|
5714
5797
|
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
5798
|
+
// Check which signal triggered the abort
|
|
5799
|
+
if (this.closeController.signal.aborted) {
|
|
5800
|
+
throw new UploadApiError('Request cancelled', 'cancelled', 0);
|
|
5801
|
+
}
|
|
5715
5802
|
throw new TimeoutError(`Request timed out after ${this.timeoutMs}ms`);
|
|
5716
5803
|
}
|
|
5717
5804
|
// Network errors with context
|
|
@@ -7573,6 +7660,8 @@ class UploadRenderer {
|
|
|
7573
7660
|
this.uploadModal = null;
|
|
7574
7661
|
this.isInlineMode = false;
|
|
7575
7662
|
this.hybridModalInitialized = false;
|
|
7663
|
+
// Track active XHR for cancellation on close
|
|
7664
|
+
this.activeXhr = null;
|
|
7576
7665
|
this.pasteHandler = null;
|
|
7577
7666
|
this.container = container;
|
|
7578
7667
|
this.api = api;
|
|
@@ -7605,8 +7694,16 @@ class UploadRenderer {
|
|
|
7605
7694
|
}
|
|
7606
7695
|
/**
|
|
7607
7696
|
* Close the upload flow.
|
|
7697
|
+
* Cancels all pending API requests and active uploads to prevent orphaned operations.
|
|
7608
7698
|
*/
|
|
7609
7699
|
close() {
|
|
7700
|
+
// Cancel all pending API requests first
|
|
7701
|
+
this.api.abort();
|
|
7702
|
+
// Abort any active XHR upload
|
|
7703
|
+
if (this.activeXhr) {
|
|
7704
|
+
this.activeXhr.abort();
|
|
7705
|
+
this.activeXhr = null;
|
|
7706
|
+
}
|
|
7610
7707
|
this.cleanupFileInput();
|
|
7611
7708
|
this.cleanupPasteHandler();
|
|
7612
7709
|
// Clean up modal if open (hybrid mode)
|
|
@@ -8205,11 +8302,19 @@ class UploadRenderer {
|
|
|
8205
8302
|
}
|
|
8206
8303
|
}
|
|
8207
8304
|
/**
|
|
8208
|
-
* Upload a single chunk using XMLHttpRequest for progress tracking
|
|
8305
|
+
* Upload a single chunk using XMLHttpRequest for progress tracking.
|
|
8306
|
+
* Tracks the XHR so it can be cancelled if the renderer is closed.
|
|
8209
8307
|
*/
|
|
8210
8308
|
uploadChunkWithProgress(uploadUrl, chunk, chunkStart, totalSize, tusVersion, file, ingotId, requestId) {
|
|
8211
8309
|
return new Promise((resolve, reject) => {
|
|
8310
|
+
// Check if already aborted before starting
|
|
8311
|
+
if (this.api.isAborted()) {
|
|
8312
|
+
reject(new Error('Upload cancelled'));
|
|
8313
|
+
return;
|
|
8314
|
+
}
|
|
8212
8315
|
const xhr = new XMLHttpRequest();
|
|
8316
|
+
// Track this XHR so it can be cancelled on close
|
|
8317
|
+
this.activeXhr = xhr;
|
|
8213
8318
|
// Track progress within this chunk
|
|
8214
8319
|
xhr.upload.onprogress = (e) => {
|
|
8215
8320
|
if (e.lengthComputable) {
|
|
@@ -8233,6 +8338,7 @@ class UploadRenderer {
|
|
|
8233
8338
|
}
|
|
8234
8339
|
};
|
|
8235
8340
|
xhr.onload = () => {
|
|
8341
|
+
this.activeXhr = null;
|
|
8236
8342
|
if (xhr.status >= 200 && xhr.status < 300) {
|
|
8237
8343
|
// Get new offset from server response
|
|
8238
8344
|
const newOffsetHeader = xhr.getResponseHeader('Upload-Offset');
|
|
@@ -8243,8 +8349,18 @@ class UploadRenderer {
|
|
|
8243
8349
|
reject(new Error(`Chunk upload failed with status ${xhr.status}`));
|
|
8244
8350
|
}
|
|
8245
8351
|
};
|
|
8246
|
-
xhr.onerror = () =>
|
|
8247
|
-
|
|
8352
|
+
xhr.onerror = () => {
|
|
8353
|
+
this.activeXhr = null;
|
|
8354
|
+
reject(new Error('Chunk upload failed'));
|
|
8355
|
+
};
|
|
8356
|
+
xhr.ontimeout = () => {
|
|
8357
|
+
this.activeXhr = null;
|
|
8358
|
+
reject(new Error('Chunk upload timed out'));
|
|
8359
|
+
};
|
|
8360
|
+
xhr.onabort = () => {
|
|
8361
|
+
this.activeXhr = null;
|
|
8362
|
+
reject(new Error('Upload cancelled'));
|
|
8363
|
+
};
|
|
8248
8364
|
xhr.open('PATCH', uploadUrl);
|
|
8249
8365
|
xhr.setRequestHeader('Tus-Resumable', tusVersion);
|
|
8250
8366
|
xhr.setRequestHeader('Upload-Offset', String(chunkStart));
|