nexabase-console 2.0.4 → 2.0.6

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.
@@ -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, SettableMetadata, ListOptions, ListResult } 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: 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: SettableMetadata): Promise<StorageMetadata>;
25
+ list(path: string, options?: ListOptions): Promise<ListResult>;
16
26
  }
17
- export declare const getStorage: (app: any) => Storage;
27
+ export declare const getStorage: (app?: any) => Storage;
28
+ export declare const connectStorageEmulator: (storage: Storage, host: string, port: number) => void;
@@ -1,49 +1,114 @@
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 NexaError_1 = require("../errors/NexaError");
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
  }
12
+ ref(path = '') {
13
+ return new StorageReference_1.StorageReferenceImpl(path, this);
14
+ }
15
+ refFromURL(url) {
16
+ // Handle both NexaBase and standard URL paths
17
+ const match = url.match(/[?&]path=([^&]+)/) || url.match(/storage\/file\/([^?]+)/) || url.match(/storage\/([^?]+)/);
18
+ if (match && match[1]) {
19
+ return this.ref(decodeURIComponent(match[1]));
20
+ }
21
+ throw new NexaStorageError_1.NexaStorageError('storage/invalid-path', 'Invalid storage URL format');
22
+ }
23
+ createUploadTask(path, file, metadata) {
24
+ const task = new UploadTask_1.UploadTask(this.projectId, this.client, path, file, metadata);
25
+ task.ref = this.ref(path);
26
+ return task;
27
+ }
28
+ // Legacy upload logic
11
29
  async uploadFile(path, file, options) {
30
+ const task = this.createUploadTask(path, file);
31
+ task.options = options;
32
+ await task;
33
+ const url = await this.getDownloadURL(path);
34
+ return { url, path };
35
+ }
36
+ async getDownloadURL(path) {
12
37
  try {
13
- const uploadTask = new UploadTask_1.UploadTask(path, file, options);
14
- const formData = new FormData();
15
- formData.append('file', file);
16
- formData.append('path', path);
17
- const res = await this.client.post(`/api/project/${this.projectId}/storage/upload`, formData, {
18
- onUploadProgress: (progressEvent) => {
19
- if (progressEvent.total) {
20
- const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);
21
- uploadTask.notifyProgress(percent);
22
- }
38
+ const res = await this.client.get(`/api/project/${this.projectId}/storage/url?path=${encodeURIComponent(path)}`);
39
+ let url = res.data.downloadUrl || res.data.url;
40
+ // If the backend returns a relative URL, build full absolute URL using client baseURL or window.location.origin
41
+ if (url && !url.startsWith('http://') && !url.startsWith('https://')) {
42
+ let baseUrl = this.client.defaults.baseURL || '';
43
+ if (!baseUrl && typeof window !== 'undefined' && window.location) {
44
+ baseUrl = window.location.origin;
45
+ }
46
+ if (baseUrl) {
47
+ const cleanBase = baseUrl.replace(/\/+$/, '');
48
+ const cleanPath = url.startsWith('/') ? url : `/${url}`;
49
+ url = `${cleanBase}${cleanPath}`;
23
50
  }
24
- });
51
+ }
52
+ return url;
53
+ }
54
+ catch (err) {
55
+ if (err.response) {
56
+ throw new NexaStorageError_1.NexaStorageError(err.response.data?.error?.code || 'storage/server-error', err.response.data?.error?.message || err.message, err.response.status);
57
+ }
58
+ throw new NexaStorageError_1.NexaStorageError('storage/network-error', err.message);
59
+ }
60
+ }
61
+ async deleteFile(path) {
62
+ try {
63
+ const res = await this.client.delete(`/api/project/${this.projectId}/storage/file?path=${encodeURIComponent(path)}`);
25
64
  return res.data;
26
65
  }
27
66
  catch (err) {
28
- throw (0, NexaError_1.toNexaError)(err);
67
+ if (err.response) {
68
+ throw new NexaStorageError_1.NexaStorageError(err.response.data?.error?.code || 'storage/server-error', err.response.data?.error?.message || err.message, err.response.status);
69
+ }
70
+ throw new NexaStorageError_1.NexaStorageError('storage/network-error', err.message);
29
71
  }
30
72
  }
31
- async getDownloadURL(path) {
73
+ async getMetadata(path) {
32
74
  try {
33
- const res = await this.client.get(`/api/project/${this.projectId}/storage/url?path=${encodeURIComponent(path)}`);
34
- return res.data.url;
75
+ const res = await this.client.get(`/api/project/${this.projectId}/storage/metadata?path=${encodeURIComponent(path)}`);
76
+ return res.data;
35
77
  }
36
78
  catch (err) {
37
- throw (0, NexaError_1.toNexaError)(err);
79
+ if (err.response) {
80
+ throw new NexaStorageError_1.NexaStorageError(err.response.data?.error?.code || 'storage/server-error', err.response.data?.error?.message || err.message, err.response.status);
81
+ }
82
+ throw new NexaStorageError_1.NexaStorageError('storage/network-error', err.message);
38
83
  }
39
84
  }
40
- async deleteFile(path) {
85
+ async updateMetadata(path, metadata) {
41
86
  try {
42
- const res = await this.client.delete(`/api/project/${this.projectId}/storage/file?path=${encodeURIComponent(path)}`);
87
+ const res = await this.client.put(`/api/project/${this.projectId}/storage/metadata?path=${encodeURIComponent(path)}`, metadata);
43
88
  return res.data;
44
89
  }
45
90
  catch (err) {
46
- throw (0, NexaError_1.toNexaError)(err);
91
+ if (err.response) {
92
+ throw new NexaStorageError_1.NexaStorageError(err.response.data?.error?.code || 'storage/server-error', err.response.data?.error?.message || err.message, err.response.status);
93
+ }
94
+ throw new NexaStorageError_1.NexaStorageError('storage/network-error', err.message);
95
+ }
96
+ }
97
+ async list(path, options) {
98
+ try {
99
+ const params = new URLSearchParams();
100
+ if (path)
101
+ params.append('path', path);
102
+ if (options?.maxResults)
103
+ params.append('maxResults', options.maxResults.toString());
104
+ const res = await this.client.get(`/api/project/${this.projectId}/storage/list?${params.toString()}`);
105
+ return res.data;
106
+ }
107
+ catch (err) {
108
+ if (err.response) {
109
+ throw new NexaStorageError_1.NexaStorageError(err.response.data?.error?.code || 'storage/server-error', err.response.data?.error?.message || err.message, err.response.status);
110
+ }
111
+ throw new NexaStorageError_1.NexaStorageError('storage/network-error', err.message);
47
112
  }
48
113
  }
49
114
  }
@@ -52,6 +117,23 @@ const getStorage = (app) => {
52
117
  if (app && typeof app.storage === 'function') {
53
118
  return app.storage();
54
119
  }
55
- throw new Error('Invalid NexaApp instance provided to getStorage()');
120
+ if (app && app.storageService) {
121
+ return app.storageService;
122
+ }
123
+ if (app instanceof Storage) {
124
+ return app;
125
+ }
126
+ throw new Error('NexaApp instance is required for getStorage(app)');
56
127
  };
57
128
  exports.getStorage = getStorage;
129
+ const connectStorageEmulator = (storage, host, port) => {
130
+ if (storage.emulatorUrl) {
131
+ throw new Error('Emulator already connected.');
132
+ }
133
+ storage.emulatorUrl = `http://${host}:${port}`;
134
+ const client = storage.client;
135
+ if (client) {
136
+ client.defaults.baseURL = storage.emulatorUrl;
137
+ }
138
+ };
139
+ exports.connectStorageEmulator = connectStorageEmulator;
@@ -1,31 +1,66 @@
1
- import { StorageReference as IStorageReference, UploadOptions } from '../types/index';
2
1
  import { Storage } from './Storage';
3
- export declare class StorageReferenceImpl implements IStorageReference {
2
+ import { UploadTask } from './UploadTask';
3
+ import { UploadMetadata, StorageMetadata, SettableMetadata, StringFormat, UploadResult, ListOptions, ListResult } from './types';
4
+ import { UploadOptions } from '../types/index';
5
+ export interface IStorageReference {
6
+ name: string;
7
+ bucket: string;
8
+ fullPath: string;
4
9
  path: string;
5
- private storage;
6
- constructor(path: string, storage: Storage);
7
- upload(file: File | Blob, options?: UploadOptions): Promise<{
10
+ root: IStorageReference;
11
+ parent: IStorageReference | null;
12
+ storage: Storage;
13
+ put(file: Blob | Uint8Array | ArrayBuffer, metadata?: UploadMetadata): UploadTask;
14
+ putString(data: string, format?: StringFormat, metadata?: UploadMetadata): UploadTask;
15
+ getDownloadURL(): Promise<string>;
16
+ getMetadata(): Promise<StorageMetadata>;
17
+ updateMetadata(metadata: SettableMetadata): Promise<StorageMetadata>;
18
+ delete(): Promise<{
19
+ success: boolean;
20
+ message?: string;
21
+ }>;
22
+ listAll(): Promise<ListResult>;
23
+ list(options?: ListOptions): Promise<ListResult>;
24
+ upload?: (file: File | Blob, options?: UploadOptions) => Promise<{
8
25
  url: string;
9
26
  path: string;
10
27
  }>;
28
+ }
29
+ export declare class StorageReferenceImpl implements IStorageReference {
30
+ path: string;
31
+ fullPath: string;
32
+ name: string;
33
+ bucket: string;
34
+ storage: Storage;
35
+ constructor(path: string, storage: Storage);
36
+ get root(): IStorageReference;
37
+ get parent(): IStorageReference | null;
38
+ put(data: Blob | Uint8Array | ArrayBuffer, metadata?: UploadMetadata): UploadTask;
39
+ putString(data: string, format?: StringFormat, metadata?: UploadMetadata): UploadTask;
11
40
  getDownloadURL(): Promise<string>;
41
+ getMetadata(): Promise<StorageMetadata>;
42
+ updateMetadata(metadata: SettableMetadata): Promise<StorageMetadata>;
12
43
  delete(): Promise<{
13
44
  success: boolean;
14
45
  message?: string;
15
46
  }>;
47
+ listAll(): Promise<ListResult>;
48
+ list(options?: ListOptions): Promise<ListResult>;
49
+ upload(file: File | Blob, options?: UploadOptions): Promise<{
50
+ url: string;
51
+ path: string;
52
+ }>;
16
53
  }
17
- export declare const storageRef: (storage: Storage, path: string) => IStorageReference;
18
- export declare const ref: (storage: Storage, path: string) => IStorageReference;
19
- export declare const uploadBytes: (storageRef: IStorageReference, file: File | Blob, options?: UploadOptions) => Promise<{
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
- }>;
54
+ /**
55
+ * 1:1 Modular Firebase Storage API functions
56
+ */
57
+ export declare const ref: (storageOrRef: Storage | IStorageReference, path?: string) => IStorageReference;
58
+ export declare const uploadBytes: (storageRef: IStorageReference, data: Blob | Uint8Array | ArrayBuffer, metadata?: UploadMetadata) => Promise<UploadResult>;
59
+ export declare const uploadBytesResumable: (storageRef: IStorageReference, data: Blob | Uint8Array | ArrayBuffer, metadata?: UploadMetadata) => UploadTask;
60
+ export declare const uploadString: (storageRef: IStorageReference, value: string, format?: StringFormat, metadata?: UploadMetadata) => Promise<UploadResult>;
27
61
  export declare const getDownloadURL: (storageRef: IStorageReference) => Promise<string>;
28
- export declare const deleteObject: (storageRef: IStorageReference) => Promise<{
29
- success: boolean;
30
- message?: string;
31
- }>;
62
+ export declare const getMetadata: (storageRef: IStorageReference) => Promise<StorageMetadata>;
63
+ export declare const updateMetadata: (storageRef: IStorageReference, metadata: SettableMetadata) => Promise<StorageMetadata>;
64
+ export declare const deleteObject: (storageRef: IStorageReference) => Promise<void>;
65
+ export declare const listAll: (storageRef: IStorageReference) => Promise<ListResult>;
66
+ export declare const list: (storageRef: IStorageReference, options?: ListOptions) => Promise<ListResult>;
@@ -1,52 +1,177 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.deleteObject = exports.getDownloadURL = exports.uploadBytesResumable = exports.uploadBytes = exports.ref = exports.storageRef = exports.StorageReferenceImpl = void 0;
3
+ exports.list = exports.listAll = exports.deleteObject = exports.updateMetadata = exports.getMetadata = exports.getDownloadURL = exports.uploadString = exports.uploadBytesResumable = exports.uploadBytes = exports.ref = exports.StorageReferenceImpl = void 0;
4
+ const NexaStorageError_1 = require("./NexaStorageError");
4
5
  class StorageReferenceImpl {
5
6
  constructor(path, storage) {
6
- this.path = path;
7
+ // Normalize path by stripping leading slashes
8
+ this.path = (path || '').replace(/^\/+/, '');
9
+ this.fullPath = this.path;
10
+ this.name = this.path.split('/').filter(Boolean).pop() || '';
11
+ this.bucket = storage.projectId || '';
7
12
  this.storage = storage;
8
13
  }
9
- async upload(file, options) {
10
- return this.storage.uploadFile(this.path, file, options);
14
+ get root() {
15
+ return new StorageReferenceImpl('', this.storage);
16
+ }
17
+ get parent() {
18
+ const parts = this.path.split('/').filter(Boolean);
19
+ if (parts.length <= 1)
20
+ return null;
21
+ parts.pop();
22
+ return new StorageReferenceImpl(parts.join('/'), this.storage);
23
+ }
24
+ put(data, metadata) {
25
+ let blob;
26
+ if (data instanceof Blob) {
27
+ blob = data;
28
+ }
29
+ else if (data instanceof Uint8Array || data instanceof ArrayBuffer) {
30
+ blob = new Blob([data], { type: metadata?.contentType || 'application/octet-stream' });
31
+ }
32
+ else {
33
+ blob = new Blob([data]);
34
+ }
35
+ return this.storage.createUploadTask(this.path, blob, metadata);
11
36
  }
12
- async getDownloadURL() {
37
+ putString(data, format = 'raw', metadata) {
38
+ let blob;
39
+ try {
40
+ if (format === 'base64') {
41
+ const byteCharacters = atob(data);
42
+ const byteNumbers = new Uint8Array(byteCharacters.length);
43
+ for (let i = 0; i < byteCharacters.length; i++) {
44
+ byteNumbers[i] = byteCharacters.charCodeAt(i);
45
+ }
46
+ blob = new Blob([byteNumbers], { type: metadata?.contentType || 'application/octet-stream' });
47
+ }
48
+ else if (format === 'base64url') {
49
+ let base64 = data.replace(/-/g, '+').replace(/_/g, '/');
50
+ while (base64.length % 4) {
51
+ base64 += '=';
52
+ }
53
+ const byteCharacters = atob(base64);
54
+ const byteNumbers = new Uint8Array(byteCharacters.length);
55
+ for (let i = 0; i < byteCharacters.length; i++) {
56
+ byteNumbers[i] = byteCharacters.charCodeAt(i);
57
+ }
58
+ blob = new Blob([byteNumbers], { type: metadata?.contentType || 'application/octet-stream' });
59
+ }
60
+ else if (format === 'data_url') {
61
+ const parts = data.split(',');
62
+ const mimeMatch = parts[0]?.match(/:(.*?);/);
63
+ const detectedMime = mimeMatch ? mimeMatch[1] : 'application/octet-stream';
64
+ const b64Data = parts[1] || '';
65
+ const byteCharacters = atob(b64Data);
66
+ const byteNumbers = new Uint8Array(byteCharacters.length);
67
+ for (let i = 0; i < byteCharacters.length; i++) {
68
+ byteNumbers[i] = byteCharacters.charCodeAt(i);
69
+ }
70
+ blob = new Blob([byteNumbers], { type: metadata?.contentType || detectedMime });
71
+ }
72
+ else {
73
+ blob = new Blob([data], { type: metadata?.contentType || 'text/plain;charset=utf-8' });
74
+ }
75
+ }
76
+ catch (err) {
77
+ throw new NexaStorageError_1.NexaStorageError('storage/invalid-format', `Failed to parse data as ${format}: ${err.message}`);
78
+ }
79
+ return this.put(blob, metadata);
80
+ }
81
+ getDownloadURL() {
13
82
  return this.storage.getDownloadURL(this.path);
14
83
  }
15
- async delete() {
84
+ getMetadata() {
85
+ return this.storage.getMetadata(this.path);
86
+ }
87
+ updateMetadata(metadata) {
88
+ return this.storage.updateMetadata(this.path, metadata);
89
+ }
90
+ delete() {
16
91
  return this.storage.deleteFile(this.path);
17
92
  }
93
+ listAll() {
94
+ return this.storage.list(this.path);
95
+ }
96
+ list(options) {
97
+ return this.storage.list(this.path, options);
98
+ }
99
+ // Legacy for backward compatibility
100
+ async upload(file, options) {
101
+ const task = this.put(file, options);
102
+ task.options = options;
103
+ await task;
104
+ const url = await this.getDownloadURL();
105
+ return { url, path: this.path };
106
+ }
18
107
  }
19
108
  exports.StorageReferenceImpl = StorageReferenceImpl;
20
- const storageRef = (storage, path) => {
21
- return new StorageReferenceImpl(path, storage);
22
- };
23
- exports.storageRef = storageRef;
24
- const ref = (storage, path) => {
25
- return new StorageReferenceImpl(path, storage);
109
+ /**
110
+ * 1:1 Modular Firebase Storage API functions
111
+ */
112
+ const ref = (storageOrRef, path) => {
113
+ if (!storageOrRef) {
114
+ throw new NexaStorageError_1.NexaStorageError('storage/invalid-path', 'No storage instance or reference provided');
115
+ }
116
+ if ('storage' in storageOrRef && typeof storageOrRef.storage?.ref === 'function') {
117
+ // First argument is a StorageReference, child path is appended
118
+ const parent = storageOrRef;
119
+ const combinedPath = path ? `${parent.path.replace(/\/+$/, '')}/${path.replace(/^\/+/, '')}` : parent.path;
120
+ return new StorageReferenceImpl(combinedPath, parent.storage);
121
+ }
122
+ const storage = storageOrRef;
123
+ return new StorageReferenceImpl(path || '', storage);
26
124
  };
27
125
  exports.ref = ref;
28
- const uploadBytes = async (storageRef, file, options) => {
29
- if (storageRef.upload) {
30
- return storageRef.upload(file, options);
31
- }
32
- throw new Error('Invalid storage reference');
126
+ const uploadBytes = async (storageRef, data, metadata) => {
127
+ const task = storageRef.put(data, metadata);
128
+ const snapshot = await task;
129
+ return {
130
+ bytesTransferred: snapshot.bytesTransferred,
131
+ totalBytes: snapshot.totalBytes,
132
+ state: snapshot.state,
133
+ metadata: snapshot.metadata,
134
+ ref: storageRef
135
+ };
33
136
  };
34
137
  exports.uploadBytes = uploadBytes;
35
- const uploadBytesResumable = (storageRef, file, options) => {
36
- return (0, exports.uploadBytes)(storageRef, file, options);
138
+ const uploadBytesResumable = (storageRef, data, metadata) => {
139
+ return storageRef.put(data, metadata);
37
140
  };
38
141
  exports.uploadBytesResumable = uploadBytesResumable;
142
+ const uploadString = async (storageRef, value, format = 'raw', metadata) => {
143
+ const task = storageRef.putString(value, format, metadata);
144
+ const snapshot = await task;
145
+ return {
146
+ bytesTransferred: snapshot.bytesTransferred,
147
+ totalBytes: snapshot.totalBytes,
148
+ state: snapshot.state,
149
+ metadata: snapshot.metadata,
150
+ ref: storageRef
151
+ };
152
+ };
153
+ exports.uploadString = uploadString;
39
154
  const getDownloadURL = async (storageRef) => {
40
- if (storageRef.getDownloadURL) {
41
- return storageRef.getDownloadURL();
42
- }
43
- throw new Error('Invalid storage reference');
155
+ return storageRef.getDownloadURL();
44
156
  };
45
157
  exports.getDownloadURL = getDownloadURL;
158
+ const getMetadata = async (storageRef) => {
159
+ return storageRef.getMetadata();
160
+ };
161
+ exports.getMetadata = getMetadata;
162
+ const updateMetadata = async (storageRef, metadata) => {
163
+ return storageRef.updateMetadata(metadata);
164
+ };
165
+ exports.updateMetadata = updateMetadata;
46
166
  const deleteObject = async (storageRef) => {
47
- if (storageRef.delete) {
48
- return storageRef.delete();
49
- }
50
- throw new Error('Invalid storage reference');
167
+ await storageRef.delete();
51
168
  };
52
169
  exports.deleteObject = deleteObject;
170
+ const listAll = async (storageRef) => {
171
+ return storageRef.listAll();
172
+ };
173
+ exports.listAll = listAll;
174
+ const list = async (storageRef, options) => {
175
+ return storageRef.list(options);
176
+ };
177
+ exports.list = list;
@@ -1,10 +1,37 @@
1
+ import { AxiosInstance } from 'axios';
2
+ import { UploadMetadata, UploadTaskSnapshot } from './types';
1
3
  import { UploadOptions } from '../types/index';
2
- export declare class UploadTask {
4
+ import { NexaStorageError } from './NexaStorageError';
5
+ export declare class UploadTask implements PromiseLike<UploadTaskSnapshot> {
3
6
  private file;
4
7
  private path;
5
- private options?;
6
- constructor(path: string, file: File | Blob, options?: UploadOptions);
7
- getFile(): File | Blob;
8
- getPath(): string;
9
- notifyProgress(percent: number): void;
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
- getFile() {
11
- return this.file;
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
- getPath() {
14
- return this.path;
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
- notifyProgress(percent) {
17
- if (this.options && this.options.onProgress) {
18
- this.options.onProgress(percent);
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,55 @@
1
+ export type StorageErrorCode = 'storage/unauthenticated' | 'storage/unauthorized' | 'storage/object-not-found' | 'storage/object-already-exists' | 'storage/invalid-path' | 'storage/invalid-file' | 'storage/invalid-format' | '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
+ downloadTokens?: string;
19
+ }
20
+ export interface UploadMetadata {
21
+ md5Hash?: string;
22
+ cacheControl?: string;
23
+ contentDisposition?: string;
24
+ contentEncoding?: string;
25
+ contentLanguage?: string;
26
+ contentType?: string;
27
+ customMetadata?: Record<string, string>;
28
+ }
29
+ export type SettableMetadata = UploadMetadata;
30
+ export type StringFormat = 'raw' | 'base64' | 'base64url' | 'data_url';
31
+ export type TaskState = 'running' | 'paused' | 'success' | 'canceled' | 'error';
32
+ export interface UploadTaskSnapshot {
33
+ bytesTransferred: number;
34
+ totalBytes: number;
35
+ state: TaskState;
36
+ metadata?: StorageMetadata;
37
+ task: any;
38
+ ref: any;
39
+ }
40
+ export interface UploadResult {
41
+ bytesTransferred: number;
42
+ totalBytes: number;
43
+ state: TaskState;
44
+ metadata?: StorageMetadata;
45
+ ref: any;
46
+ }
47
+ export interface ListOptions {
48
+ maxResults?: number;
49
+ pageToken?: string;
50
+ }
51
+ export interface ListResult {
52
+ items: any[];
53
+ prefixes: any[];
54
+ nextPageToken?: string;
55
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexabase-console",
3
- "version": "2.0.4",
3
+ "version": "2.0.6",
4
4
  "description": "SDK Client resmi untuk NexaBase: Platform Sinkronisasi NoSQL, Realtime, File Storage, & Autentikasi Offline-First.",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",