nexabase-console 2.0.4 → 2.0.5
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/storage/NexaStorageError.d.ts +8 -0
- package/dist/storage/NexaStorageError.js +14 -0
- package/dist/storage/Storage.d.ts +11 -0
- package/dist/storage/Storage.js +77 -22
- package/dist/storage/StorageReference.d.ts +32 -14
- package/dist/storage/StorageReference.js +46 -26
- package/dist/storage/UploadTask.d.ts +33 -6
- package/dist/storage/UploadTask.js +127 -8
- package/dist/storage/types.d.ts +36 -0
- package/dist/storage/types.js +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { StorageErrorCode } from './types';
|
|
2
|
+
export declare class NexaStorageError extends Error {
|
|
3
|
+
code: StorageErrorCode;
|
|
4
|
+
status?: number;
|
|
5
|
+
retryable: boolean;
|
|
6
|
+
cause?: any;
|
|
7
|
+
constructor(code: StorageErrorCode, message: string, status?: number, retryable?: boolean, cause?: any);
|
|
8
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NexaStorageError = void 0;
|
|
4
|
+
class NexaStorageError extends Error {
|
|
5
|
+
constructor(code, message, status, retryable = false, cause) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = 'NexaStorageError';
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.status = status;
|
|
10
|
+
this.retryable = retryable;
|
|
11
|
+
this.cause = cause;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.NexaStorageError = NexaStorageError;
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { AxiosInstance } from 'axios';
|
|
2
|
+
import { UploadMetadata, StorageMetadata } from './types';
|
|
2
3
|
import { UploadOptions } from '../types/index';
|
|
4
|
+
import { UploadTask } from './UploadTask';
|
|
5
|
+
import { IStorageReference } from './StorageReference';
|
|
3
6
|
export declare class Storage {
|
|
4
7
|
private projectId;
|
|
5
8
|
private client;
|
|
9
|
+
emulatorUrl?: string;
|
|
6
10
|
constructor(projectId: string, client: AxiosInstance);
|
|
11
|
+
ref(path: string): IStorageReference;
|
|
12
|
+
refFromURL(url: string): IStorageReference;
|
|
13
|
+
createUploadTask(path: string, file: File | Blob, metadata?: UploadMetadata): UploadTask;
|
|
7
14
|
uploadFile(path: string, file: File | Blob, options?: UploadOptions): Promise<{
|
|
8
15
|
url: string;
|
|
9
16
|
path: string;
|
|
@@ -13,5 +20,9 @@ export declare class Storage {
|
|
|
13
20
|
success: boolean;
|
|
14
21
|
message?: string;
|
|
15
22
|
}>;
|
|
23
|
+
getMetadata(path: string): Promise<StorageMetadata>;
|
|
24
|
+
updateMetadata(path: string, metadata: Partial<StorageMetadata>): Promise<StorageMetadata>;
|
|
25
|
+
list(path: string, options?: any): Promise<any>;
|
|
16
26
|
}
|
|
17
27
|
export declare const getStorage: (app: any) => Storage;
|
|
28
|
+
export declare const connectStorageEmulator: (storage: Storage, host: string, port: number) => void;
|
package/dist/storage/Storage.js
CHANGED
|
@@ -1,32 +1,36 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getStorage = exports.Storage = void 0;
|
|
3
|
+
exports.connectStorageEmulator = exports.getStorage = exports.Storage = void 0;
|
|
4
4
|
const UploadTask_1 = require("./UploadTask");
|
|
5
|
-
const
|
|
5
|
+
const NexaStorageError_1 = require("./NexaStorageError");
|
|
6
|
+
const StorageReference_1 = require("./StorageReference");
|
|
6
7
|
class Storage {
|
|
7
8
|
constructor(projectId, client) {
|
|
8
9
|
this.projectId = projectId;
|
|
9
10
|
this.client = client;
|
|
10
11
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
onUploadProgress: (progressEvent) => {
|
|
19
|
-
if (progressEvent.total) {
|
|
20
|
-
const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);
|
|
21
|
-
uploadTask.notifyProgress(percent);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
return res.data;
|
|
26
|
-
}
|
|
27
|
-
catch (err) {
|
|
28
|
-
throw (0, NexaError_1.toNexaError)(err);
|
|
12
|
+
ref(path) {
|
|
13
|
+
return new StorageReference_1.StorageReferenceImpl(path, this);
|
|
14
|
+
}
|
|
15
|
+
refFromURL(url) {
|
|
16
|
+
const match = url.match(/storage\/([^?]*)/);
|
|
17
|
+
if (match && match[1]) {
|
|
18
|
+
return this.ref(decodeURIComponent(match[1]));
|
|
29
19
|
}
|
|
20
|
+
throw new NexaStorageError_1.NexaStorageError('storage/invalid-path', 'Invalid storage URL');
|
|
21
|
+
}
|
|
22
|
+
createUploadTask(path, file, metadata) {
|
|
23
|
+
const task = new UploadTask_1.UploadTask(this.projectId, this.client, path, file, metadata);
|
|
24
|
+
task.ref = this.ref(path);
|
|
25
|
+
return task;
|
|
26
|
+
}
|
|
27
|
+
// Legacy upload logic
|
|
28
|
+
async uploadFile(path, file, options) {
|
|
29
|
+
const task = this.createUploadTask(path, file);
|
|
30
|
+
task.options = options;
|
|
31
|
+
await task;
|
|
32
|
+
const url = await this.getDownloadURL(path);
|
|
33
|
+
return { url, path };
|
|
30
34
|
}
|
|
31
35
|
async getDownloadURL(path) {
|
|
32
36
|
try {
|
|
@@ -34,7 +38,10 @@ class Storage {
|
|
|
34
38
|
return res.data.url;
|
|
35
39
|
}
|
|
36
40
|
catch (err) {
|
|
37
|
-
|
|
41
|
+
if (err.response) {
|
|
42
|
+
throw new NexaStorageError_1.NexaStorageError(err.response.data?.error?.code || 'storage/server-error', err.response.data?.error?.message || err.message);
|
|
43
|
+
}
|
|
44
|
+
throw new NexaStorageError_1.NexaStorageError('storage/network-error', err.message);
|
|
38
45
|
}
|
|
39
46
|
}
|
|
40
47
|
async deleteFile(path) {
|
|
@@ -43,7 +50,43 @@ class Storage {
|
|
|
43
50
|
return res.data;
|
|
44
51
|
}
|
|
45
52
|
catch (err) {
|
|
46
|
-
|
|
53
|
+
if (err.response) {
|
|
54
|
+
throw new NexaStorageError_1.NexaStorageError(err.response.data?.error?.code || 'storage/server-error', err.response.data?.error?.message || err.message);
|
|
55
|
+
}
|
|
56
|
+
throw new NexaStorageError_1.NexaStorageError('storage/network-error', err.message);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async getMetadata(path) {
|
|
60
|
+
try {
|
|
61
|
+
const res = await this.client.get(`/api/project/${this.projectId}/storage/metadata?path=${encodeURIComponent(path)}`);
|
|
62
|
+
return res.data;
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
if (err.response)
|
|
66
|
+
throw new NexaStorageError_1.NexaStorageError(err.response.data?.error?.code || 'storage/server-error', err.response.data?.error?.message || err.message);
|
|
67
|
+
throw new NexaStorageError_1.NexaStorageError('storage/network-error', err.message);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async updateMetadata(path, metadata) {
|
|
71
|
+
try {
|
|
72
|
+
const res = await this.client.put(`/api/project/${this.projectId}/storage/metadata?path=${encodeURIComponent(path)}`, metadata);
|
|
73
|
+
return res.data;
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
if (err.response)
|
|
77
|
+
throw new NexaStorageError_1.NexaStorageError(err.response.data?.error?.code || 'storage/server-error', err.response.data?.error?.message || err.message);
|
|
78
|
+
throw new NexaStorageError_1.NexaStorageError('storage/network-error', err.message);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async list(path, options) {
|
|
82
|
+
try {
|
|
83
|
+
const res = await this.client.get(`/api/project/${this.projectId}/storage/list?path=${encodeURIComponent(path)}`);
|
|
84
|
+
return res.data;
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
if (err.response)
|
|
88
|
+
throw new NexaStorageError_1.NexaStorageError(err.response.data?.error?.code || 'storage/server-error', err.response.data?.error?.message || err.message);
|
|
89
|
+
throw new NexaStorageError_1.NexaStorageError('storage/network-error', err.message);
|
|
47
90
|
}
|
|
48
91
|
}
|
|
49
92
|
}
|
|
@@ -55,3 +98,15 @@ const getStorage = (app) => {
|
|
|
55
98
|
throw new Error('Invalid NexaApp instance provided to getStorage()');
|
|
56
99
|
};
|
|
57
100
|
exports.getStorage = getStorage;
|
|
101
|
+
const connectStorageEmulator = (storage, host, port) => {
|
|
102
|
+
if (storage.emulatorUrl) {
|
|
103
|
+
throw new Error('Emulator already connected.');
|
|
104
|
+
}
|
|
105
|
+
storage.emulatorUrl = `http://${host}:${port}`;
|
|
106
|
+
// Modify the axios client inside storage to point to emulator
|
|
107
|
+
const client = storage.client;
|
|
108
|
+
if (client) {
|
|
109
|
+
client.defaults.baseURL = storage.emulatorUrl;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
exports.connectStorageEmulator = connectStorageEmulator;
|
|
@@ -1,29 +1,47 @@
|
|
|
1
|
-
import { StorageReference as IStorageReference, UploadOptions } from '../types/index';
|
|
2
1
|
import { Storage } from './Storage';
|
|
3
|
-
|
|
2
|
+
import { UploadTask } from './UploadTask';
|
|
3
|
+
import { UploadMetadata, StorageMetadata } from './types';
|
|
4
|
+
import { UploadOptions } from '../types/index';
|
|
5
|
+
export interface IStorageReference {
|
|
4
6
|
path: string;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
put(file: File | Blob, metadata?: UploadMetadata): UploadTask;
|
|
8
|
+
putString(data: string, format?: string, metadata?: UploadMetadata): UploadTask;
|
|
9
|
+
getDownloadURL(): Promise<string>;
|
|
10
|
+
getMetadata(): Promise<StorageMetadata>;
|
|
11
|
+
updateMetadata(metadata: Partial<StorageMetadata>): Promise<StorageMetadata>;
|
|
12
|
+
delete(): Promise<{
|
|
13
|
+
success: boolean;
|
|
14
|
+
message?: string;
|
|
15
|
+
}>;
|
|
16
|
+
listAll(): Promise<any>;
|
|
17
|
+
list(options?: any): Promise<any>;
|
|
18
|
+
upload?: (file: File | Blob, options?: UploadOptions) => Promise<{
|
|
8
19
|
url: string;
|
|
9
20
|
path: string;
|
|
10
21
|
}>;
|
|
22
|
+
}
|
|
23
|
+
export declare class StorageReferenceImpl implements IStorageReference {
|
|
24
|
+
path: string;
|
|
25
|
+
private storage;
|
|
26
|
+
constructor(path: string, storage: Storage);
|
|
27
|
+
put(file: File | Blob, metadata?: UploadMetadata): UploadTask;
|
|
28
|
+
putString(data: string, format?: string, metadata?: UploadMetadata): UploadTask;
|
|
11
29
|
getDownloadURL(): Promise<string>;
|
|
30
|
+
getMetadata(): Promise<StorageMetadata>;
|
|
31
|
+
updateMetadata(metadata: Partial<StorageMetadata>): Promise<StorageMetadata>;
|
|
12
32
|
delete(): Promise<{
|
|
13
33
|
success: boolean;
|
|
14
34
|
message?: string;
|
|
15
35
|
}>;
|
|
36
|
+
listAll(): Promise<any>;
|
|
37
|
+
list(options?: any): Promise<any>;
|
|
38
|
+
upload(file: File | Blob, options?: UploadOptions): Promise<{
|
|
39
|
+
url: string;
|
|
40
|
+
path: string;
|
|
41
|
+
}>;
|
|
16
42
|
}
|
|
17
|
-
export declare const storageRef: (storage: Storage, path: string) => IStorageReference;
|
|
18
43
|
export declare const ref: (storage: Storage, path: string) => IStorageReference;
|
|
19
|
-
export declare const uploadBytes: (storageRef: IStorageReference, file: File | Blob,
|
|
20
|
-
url: string;
|
|
21
|
-
path: string;
|
|
22
|
-
}>;
|
|
23
|
-
export declare const uploadBytesResumable: (storageRef: IStorageReference, file: File | Blob, options?: UploadOptions) => Promise<{
|
|
24
|
-
url: string;
|
|
25
|
-
path: string;
|
|
26
|
-
}>;
|
|
44
|
+
export declare const uploadBytes: (storageRef: IStorageReference, file: File | Blob, metadata?: UploadMetadata) => Promise<any>;
|
|
27
45
|
export declare const getDownloadURL: (storageRef: IStorageReference) => Promise<string>;
|
|
28
46
|
export declare const deleteObject: (storageRef: IStorageReference) => Promise<{
|
|
29
47
|
success: boolean;
|
|
@@ -1,52 +1,72 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.deleteObject = exports.getDownloadURL = exports.
|
|
3
|
+
exports.deleteObject = exports.getDownloadURL = exports.uploadBytes = exports.ref = exports.StorageReferenceImpl = void 0;
|
|
4
4
|
class StorageReferenceImpl {
|
|
5
5
|
constructor(path, storage) {
|
|
6
6
|
this.path = path;
|
|
7
7
|
this.storage = storage;
|
|
8
8
|
}
|
|
9
|
-
|
|
10
|
-
return this.storage.
|
|
9
|
+
put(file, metadata) {
|
|
10
|
+
return this.storage.createUploadTask(this.path, file, metadata);
|
|
11
|
+
}
|
|
12
|
+
putString(data, format = 'raw', metadata) {
|
|
13
|
+
let blob;
|
|
14
|
+
if (format === 'base64') {
|
|
15
|
+
const byteCharacters = atob(data);
|
|
16
|
+
const byteNumbers = new Array(byteCharacters.length);
|
|
17
|
+
for (let i = 0; i < byteCharacters.length; i++) {
|
|
18
|
+
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
|
19
|
+
}
|
|
20
|
+
const byteArray = new Uint8Array(byteNumbers);
|
|
21
|
+
blob = new Blob([byteArray]);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
blob = new Blob([data]);
|
|
25
|
+
}
|
|
26
|
+
return this.put(blob, metadata);
|
|
11
27
|
}
|
|
12
|
-
|
|
28
|
+
getDownloadURL() {
|
|
13
29
|
return this.storage.getDownloadURL(this.path);
|
|
14
30
|
}
|
|
15
|
-
|
|
31
|
+
getMetadata() {
|
|
32
|
+
return this.storage.getMetadata(this.path);
|
|
33
|
+
}
|
|
34
|
+
updateMetadata(metadata) {
|
|
35
|
+
return this.storage.updateMetadata(this.path, metadata);
|
|
36
|
+
}
|
|
37
|
+
delete() {
|
|
16
38
|
return this.storage.deleteFile(this.path);
|
|
17
39
|
}
|
|
40
|
+
listAll() {
|
|
41
|
+
return this.storage.list(this.path);
|
|
42
|
+
}
|
|
43
|
+
list(options) {
|
|
44
|
+
return this.storage.list(this.path, options);
|
|
45
|
+
}
|
|
46
|
+
// Legacy for backward compatibility
|
|
47
|
+
async upload(file, options) {
|
|
48
|
+
const task = this.put(file, options);
|
|
49
|
+
task.options = options; // Inject legacy options
|
|
50
|
+
await task;
|
|
51
|
+
const url = await this.getDownloadURL();
|
|
52
|
+
return { url, path: this.path };
|
|
53
|
+
}
|
|
18
54
|
}
|
|
19
55
|
exports.StorageReferenceImpl = StorageReferenceImpl;
|
|
20
|
-
const storageRef = (storage, path) => {
|
|
21
|
-
return new StorageReferenceImpl(path, storage);
|
|
22
|
-
};
|
|
23
|
-
exports.storageRef = storageRef;
|
|
24
56
|
const ref = (storage, path) => {
|
|
25
57
|
return new StorageReferenceImpl(path, storage);
|
|
26
58
|
};
|
|
27
59
|
exports.ref = ref;
|
|
28
|
-
const uploadBytes = async (storageRef, file,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
throw new Error('Invalid storage reference');
|
|
60
|
+
const uploadBytes = async (storageRef, file, metadata) => {
|
|
61
|
+
const task = storageRef.put(file, metadata);
|
|
62
|
+
return await task;
|
|
33
63
|
};
|
|
34
64
|
exports.uploadBytes = uploadBytes;
|
|
35
|
-
const uploadBytesResumable = (storageRef, file, options) => {
|
|
36
|
-
return (0, exports.uploadBytes)(storageRef, file, options);
|
|
37
|
-
};
|
|
38
|
-
exports.uploadBytesResumable = uploadBytesResumable;
|
|
39
65
|
const getDownloadURL = async (storageRef) => {
|
|
40
|
-
|
|
41
|
-
return storageRef.getDownloadURL();
|
|
42
|
-
}
|
|
43
|
-
throw new Error('Invalid storage reference');
|
|
66
|
+
return storageRef.getDownloadURL();
|
|
44
67
|
};
|
|
45
68
|
exports.getDownloadURL = getDownloadURL;
|
|
46
69
|
const deleteObject = async (storageRef) => {
|
|
47
|
-
|
|
48
|
-
return storageRef.delete();
|
|
49
|
-
}
|
|
50
|
-
throw new Error('Invalid storage reference');
|
|
70
|
+
return storageRef.delete();
|
|
51
71
|
};
|
|
52
72
|
exports.deleteObject = deleteObject;
|
|
@@ -1,10 +1,37 @@
|
|
|
1
|
+
import { AxiosInstance } from 'axios';
|
|
2
|
+
import { UploadMetadata, UploadTaskSnapshot } from './types';
|
|
1
3
|
import { UploadOptions } from '../types/index';
|
|
2
|
-
|
|
4
|
+
import { NexaStorageError } from './NexaStorageError';
|
|
5
|
+
export declare class UploadTask implements PromiseLike<UploadTaskSnapshot> {
|
|
3
6
|
private file;
|
|
4
7
|
private path;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
options?: UploadOptions;
|
|
9
|
+
private metadata?;
|
|
10
|
+
private client;
|
|
11
|
+
private projectId;
|
|
12
|
+
private state;
|
|
13
|
+
private bytesTransferred;
|
|
14
|
+
private totalBytes;
|
|
15
|
+
private sessionUrl;
|
|
16
|
+
private abortController;
|
|
17
|
+
private listeners;
|
|
18
|
+
private errorListeners;
|
|
19
|
+
private completeListeners;
|
|
20
|
+
private _promise;
|
|
21
|
+
ref: any;
|
|
22
|
+
constructor(projectId: string, client: AxiosInstance, path: string, file: File | Blob, metadata?: UploadMetadata, options?: UploadOptions);
|
|
23
|
+
on(event: 'state_changed', nextOrObserver?: ((snapshot: UploadTaskSnapshot) => void) | {
|
|
24
|
+
next?: (s: UploadTaskSnapshot) => void;
|
|
25
|
+
error?: (e: NexaStorageError) => void;
|
|
26
|
+
complete?: () => void;
|
|
27
|
+
}, error?: (error: NexaStorageError) => void, complete?: () => void): () => void;
|
|
28
|
+
private startPromise;
|
|
29
|
+
then<TResult1 = UploadTaskSnapshot, TResult2 = never>(onfulfilled?: ((value: UploadTaskSnapshot) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
30
|
+
catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null): Promise<UploadTaskSnapshot | TResult>;
|
|
31
|
+
private _executeUpload;
|
|
32
|
+
pause(): void;
|
|
33
|
+
resume(): void;
|
|
34
|
+
cancel(): void;
|
|
35
|
+
getSnapshot(): UploadTaskSnapshot;
|
|
36
|
+
private notify;
|
|
10
37
|
}
|
|
@@ -1,21 +1,140 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.UploadTask = void 0;
|
|
4
|
+
const NexaStorageError_1 = require("./NexaStorageError");
|
|
4
5
|
class UploadTask {
|
|
5
|
-
constructor(path, file, options) {
|
|
6
|
+
constructor(projectId, client, path, file, metadata, options) {
|
|
7
|
+
this.state = 'running';
|
|
8
|
+
this.bytesTransferred = 0;
|
|
9
|
+
this.totalBytes = 0;
|
|
10
|
+
this.sessionUrl = null;
|
|
11
|
+
this.listeners = [];
|
|
12
|
+
this.errorListeners = [];
|
|
13
|
+
this.completeListeners = [];
|
|
14
|
+
this._promise = null;
|
|
15
|
+
this.projectId = projectId;
|
|
16
|
+
this.client = client;
|
|
6
17
|
this.path = path;
|
|
7
18
|
this.file = file;
|
|
19
|
+
this.metadata = metadata;
|
|
8
20
|
this.options = options;
|
|
21
|
+
this.totalBytes = file.size;
|
|
22
|
+
this.abortController = new AbortController();
|
|
9
23
|
}
|
|
10
|
-
|
|
11
|
-
|
|
24
|
+
on(event, nextOrObserver, error, complete) {
|
|
25
|
+
if (typeof nextOrObserver === 'function') {
|
|
26
|
+
this.listeners.push(nextOrObserver);
|
|
27
|
+
if (error)
|
|
28
|
+
this.errorListeners.push(error);
|
|
29
|
+
if (complete)
|
|
30
|
+
this.completeListeners.push(complete);
|
|
31
|
+
}
|
|
32
|
+
else if (nextOrObserver) {
|
|
33
|
+
if (nextOrObserver.next)
|
|
34
|
+
this.listeners.push(nextOrObserver.next);
|
|
35
|
+
if (nextOrObserver.error)
|
|
36
|
+
this.errorListeners.push(nextOrObserver.error);
|
|
37
|
+
if (nextOrObserver.complete)
|
|
38
|
+
this.completeListeners.push(nextOrObserver.complete);
|
|
39
|
+
}
|
|
40
|
+
// Auto start if not started
|
|
41
|
+
this.startPromise();
|
|
42
|
+
return () => {
|
|
43
|
+
this.listeners = [];
|
|
44
|
+
this.errorListeners = [];
|
|
45
|
+
this.completeListeners = [];
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
startPromise() {
|
|
49
|
+
if (!this._promise) {
|
|
50
|
+
this._promise = this._executeUpload();
|
|
51
|
+
}
|
|
52
|
+
return this._promise;
|
|
53
|
+
}
|
|
54
|
+
then(onfulfilled, onrejected) {
|
|
55
|
+
return this.startPromise().then(onfulfilled, onrejected);
|
|
56
|
+
}
|
|
57
|
+
catch(onrejected) {
|
|
58
|
+
return this.startPromise().catch(onrejected);
|
|
59
|
+
}
|
|
60
|
+
async _executeUpload() {
|
|
61
|
+
try {
|
|
62
|
+
this.state = 'running';
|
|
63
|
+
this.notify();
|
|
64
|
+
const sessionRes = await this.client.post(`/api/project/${this.projectId}/storage/upload/session`, {
|
|
65
|
+
path: this.path,
|
|
66
|
+
metadata: {
|
|
67
|
+
contentType: this.file.type,
|
|
68
|
+
...this.metadata
|
|
69
|
+
}
|
|
70
|
+
}, { signal: this.abortController.signal });
|
|
71
|
+
this.sessionUrl = sessionRes.data.sessionUrl;
|
|
72
|
+
const uploadRes = await this.client.put(this.sessionUrl, this.file, {
|
|
73
|
+
headers: {
|
|
74
|
+
'Content-Type': 'application/octet-stream',
|
|
75
|
+
'x-upload-final': 'true'
|
|
76
|
+
},
|
|
77
|
+
signal: this.abortController.signal,
|
|
78
|
+
onUploadProgress: (progressEvent) => {
|
|
79
|
+
if (progressEvent.total) {
|
|
80
|
+
this.bytesTransferred = progressEvent.loaded;
|
|
81
|
+
this.notify();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
this.state = 'success';
|
|
86
|
+
this.bytesTransferred = this.totalBytes;
|
|
87
|
+
const finalSnapshot = this.getSnapshot();
|
|
88
|
+
finalSnapshot.metadata = uploadRes.data.metadata;
|
|
89
|
+
this.notify();
|
|
90
|
+
this.completeListeners.forEach(cb => cb());
|
|
91
|
+
return finalSnapshot;
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
this.state = 'error';
|
|
95
|
+
let nexaErr = new NexaStorageError_1.NexaStorageError('storage/network-error', err.message);
|
|
96
|
+
if (err.response) {
|
|
97
|
+
nexaErr = new NexaStorageError_1.NexaStorageError(err.response.data?.error?.code || 'storage/server-error', err.response.data?.error?.message || err.message, err.response.status);
|
|
98
|
+
}
|
|
99
|
+
this.errorListeners.forEach(cb => cb(nexaErr));
|
|
100
|
+
throw nexaErr;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
pause() {
|
|
104
|
+
if (this.state === 'running') {
|
|
105
|
+
this.state = 'paused';
|
|
106
|
+
this.notify();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
resume() {
|
|
110
|
+
if (this.state === 'paused') {
|
|
111
|
+
this.state = 'running';
|
|
112
|
+
this.notify();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
cancel() {
|
|
116
|
+
if (this.state === 'running' || this.state === 'paused') {
|
|
117
|
+
this.state = 'canceled';
|
|
118
|
+
this.abortController.abort();
|
|
119
|
+
this.notify();
|
|
120
|
+
const err = new NexaStorageError_1.NexaStorageError('storage/canceled', 'Upload canceled');
|
|
121
|
+
this.errorListeners.forEach(cb => cb(err));
|
|
122
|
+
}
|
|
12
123
|
}
|
|
13
|
-
|
|
14
|
-
return
|
|
124
|
+
getSnapshot() {
|
|
125
|
+
return {
|
|
126
|
+
bytesTransferred: this.bytesTransferred,
|
|
127
|
+
totalBytes: this.totalBytes,
|
|
128
|
+
state: this.state,
|
|
129
|
+
task: this,
|
|
130
|
+
ref: this.ref
|
|
131
|
+
};
|
|
15
132
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
133
|
+
notify() {
|
|
134
|
+
const snap = this.getSnapshot();
|
|
135
|
+
this.listeners.forEach(cb => cb(snap));
|
|
136
|
+
if (this.options?.onProgress) {
|
|
137
|
+
this.options.onProgress(Math.round((snap.bytesTransferred / snap.totalBytes) * 100));
|
|
19
138
|
}
|
|
20
139
|
}
|
|
21
140
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export type StorageErrorCode = 'storage/unauthenticated' | 'storage/unauthorized' | 'storage/object-not-found' | 'storage/object-already-exists' | 'storage/invalid-path' | 'storage/invalid-file' | 'storage/invalid-metadata' | 'storage/invalid-checksum' | 'storage/quota-exceeded' | 'storage/upload-session-expired' | 'storage/retry-limit-exceeded' | 'storage/canceled' | 'storage/conflict' | 'storage/network-error' | 'storage/server-error';
|
|
2
|
+
export interface StorageMetadata {
|
|
3
|
+
name: string;
|
|
4
|
+
bucket: string;
|
|
5
|
+
generation: string;
|
|
6
|
+
metageneration: string;
|
|
7
|
+
fullPath: string;
|
|
8
|
+
size: number;
|
|
9
|
+
timeCreated: string;
|
|
10
|
+
updated: string;
|
|
11
|
+
md5Hash?: string;
|
|
12
|
+
cacheControl?: string;
|
|
13
|
+
contentDisposition?: string;
|
|
14
|
+
contentEncoding?: string;
|
|
15
|
+
contentLanguage?: string;
|
|
16
|
+
contentType?: string;
|
|
17
|
+
customMetadata?: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
export interface UploadMetadata {
|
|
20
|
+
md5Hash?: string;
|
|
21
|
+
cacheControl?: string;
|
|
22
|
+
contentDisposition?: string;
|
|
23
|
+
contentEncoding?: string;
|
|
24
|
+
contentLanguage?: string;
|
|
25
|
+
contentType?: string;
|
|
26
|
+
customMetadata?: Record<string, string>;
|
|
27
|
+
}
|
|
28
|
+
export type TaskState = 'running' | 'paused' | 'success' | 'canceled' | 'error';
|
|
29
|
+
export interface UploadTaskSnapshot {
|
|
30
|
+
bytesTransferred: number;
|
|
31
|
+
totalBytes: number;
|
|
32
|
+
state: TaskState;
|
|
33
|
+
metadata?: StorageMetadata;
|
|
34
|
+
task: any;
|
|
35
|
+
ref: any;
|
|
36
|
+
}
|
package/package.json
CHANGED