nexabase-console 2.0.5 → 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.
@@ -1,5 +1,5 @@
1
1
  import { AxiosInstance } from 'axios';
2
- import { UploadMetadata, StorageMetadata } from './types';
2
+ import { UploadMetadata, StorageMetadata, SettableMetadata, ListOptions, ListResult } from './types';
3
3
  import { UploadOptions } from '../types/index';
4
4
  import { UploadTask } from './UploadTask';
5
5
  import { IStorageReference } from './StorageReference';
@@ -8,9 +8,9 @@ export declare class Storage {
8
8
  private client;
9
9
  emulatorUrl?: string;
10
10
  constructor(projectId: string, client: AxiosInstance);
11
- ref(path: string): IStorageReference;
11
+ ref(path?: string): IStorageReference;
12
12
  refFromURL(url: string): IStorageReference;
13
- createUploadTask(path: string, file: File | Blob, metadata?: UploadMetadata): UploadTask;
13
+ createUploadTask(path: string, file: Blob, metadata?: UploadMetadata): UploadTask;
14
14
  uploadFile(path: string, file: File | Blob, options?: UploadOptions): Promise<{
15
15
  url: string;
16
16
  path: string;
@@ -21,8 +21,8 @@ export declare class Storage {
21
21
  message?: string;
22
22
  }>;
23
23
  getMetadata(path: string): Promise<StorageMetadata>;
24
- updateMetadata(path: string, metadata: Partial<StorageMetadata>): Promise<StorageMetadata>;
25
- list(path: string, options?: any): Promise<any>;
24
+ updateMetadata(path: string, metadata: SettableMetadata): Promise<StorageMetadata>;
25
+ list(path: string, options?: ListOptions): Promise<ListResult>;
26
26
  }
27
- export declare const getStorage: (app: any) => Storage;
27
+ export declare const getStorage: (app?: any) => Storage;
28
28
  export declare const connectStorageEmulator: (storage: Storage, host: string, port: number) => void;
@@ -9,15 +9,16 @@ class Storage {
9
9
  this.projectId = projectId;
10
10
  this.client = client;
11
11
  }
12
- ref(path) {
12
+ ref(path = '') {
13
13
  return new StorageReference_1.StorageReferenceImpl(path, this);
14
14
  }
15
15
  refFromURL(url) {
16
- const match = url.match(/storage\/([^?]*)/);
16
+ // Handle both NexaBase and standard URL paths
17
+ const match = url.match(/[?&]path=([^&]+)/) || url.match(/storage\/file\/([^?]+)/) || url.match(/storage\/([^?]+)/);
17
18
  if (match && match[1]) {
18
19
  return this.ref(decodeURIComponent(match[1]));
19
20
  }
20
- throw new NexaStorageError_1.NexaStorageError('storage/invalid-path', 'Invalid storage URL');
21
+ throw new NexaStorageError_1.NexaStorageError('storage/invalid-path', 'Invalid storage URL format');
21
22
  }
22
23
  createUploadTask(path, file, metadata) {
23
24
  const task = new UploadTask_1.UploadTask(this.projectId, this.client, path, file, metadata);
@@ -35,11 +36,24 @@ class Storage {
35
36
  async getDownloadURL(path) {
36
37
  try {
37
38
  const res = await this.client.get(`/api/project/${this.projectId}/storage/url?path=${encodeURIComponent(path)}`);
38
- return res.data.url;
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}`;
50
+ }
51
+ }
52
+ return url;
39
53
  }
40
54
  catch (err) {
41
55
  if (err.response) {
42
- throw new NexaStorageError_1.NexaStorageError(err.response.data?.error?.code || 'storage/server-error', err.response.data?.error?.message || err.message);
56
+ throw new NexaStorageError_1.NexaStorageError(err.response.data?.error?.code || 'storage/server-error', err.response.data?.error?.message || err.message, err.response.status);
43
57
  }
44
58
  throw new NexaStorageError_1.NexaStorageError('storage/network-error', err.message);
45
59
  }
@@ -51,7 +65,7 @@ class Storage {
51
65
  }
52
66
  catch (err) {
53
67
  if (err.response) {
54
- throw new NexaStorageError_1.NexaStorageError(err.response.data?.error?.code || 'storage/server-error', err.response.data?.error?.message || err.message);
68
+ throw new NexaStorageError_1.NexaStorageError(err.response.data?.error?.code || 'storage/server-error', err.response.data?.error?.message || err.message, err.response.status);
55
69
  }
56
70
  throw new NexaStorageError_1.NexaStorageError('storage/network-error', err.message);
57
71
  }
@@ -62,8 +76,9 @@ class Storage {
62
76
  return res.data;
63
77
  }
64
78
  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);
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
+ }
67
82
  throw new NexaStorageError_1.NexaStorageError('storage/network-error', err.message);
68
83
  }
69
84
  }
@@ -73,19 +88,26 @@ class Storage {
73
88
  return res.data;
74
89
  }
75
90
  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);
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
+ }
78
94
  throw new NexaStorageError_1.NexaStorageError('storage/network-error', err.message);
79
95
  }
80
96
  }
81
97
  async list(path, options) {
82
98
  try {
83
- const res = await this.client.get(`/api/project/${this.projectId}/storage/list?path=${encodeURIComponent(path)}`);
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()}`);
84
105
  return res.data;
85
106
  }
86
107
  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);
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
+ }
89
111
  throw new NexaStorageError_1.NexaStorageError('storage/network-error', err.message);
90
112
  }
91
113
  }
@@ -95,7 +117,13 @@ const getStorage = (app) => {
95
117
  if (app && typeof app.storage === 'function') {
96
118
  return app.storage();
97
119
  }
98
- 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)');
99
127
  };
100
128
  exports.getStorage = getStorage;
101
129
  const connectStorageEmulator = (storage, host, port) => {
@@ -103,7 +131,6 @@ const connectStorageEmulator = (storage, host, port) => {
103
131
  throw new Error('Emulator already connected.');
104
132
  }
105
133
  storage.emulatorUrl = `http://${host}:${port}`;
106
- // Modify the axios client inside storage to point to emulator
107
134
  const client = storage.client;
108
135
  if (client) {
109
136
  client.defaults.baseURL = storage.emulatorUrl;
@@ -1,20 +1,26 @@
1
1
  import { Storage } from './Storage';
2
2
  import { UploadTask } from './UploadTask';
3
- import { UploadMetadata, StorageMetadata } from './types';
3
+ import { UploadMetadata, StorageMetadata, SettableMetadata, StringFormat, UploadResult, ListOptions, ListResult } from './types';
4
4
  import { UploadOptions } from '../types/index';
5
5
  export interface IStorageReference {
6
+ name: string;
7
+ bucket: string;
8
+ fullPath: string;
6
9
  path: string;
7
- put(file: File | Blob, metadata?: UploadMetadata): UploadTask;
8
- putString(data: string, format?: string, metadata?: UploadMetadata): UploadTask;
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;
9
15
  getDownloadURL(): Promise<string>;
10
16
  getMetadata(): Promise<StorageMetadata>;
11
- updateMetadata(metadata: Partial<StorageMetadata>): Promise<StorageMetadata>;
17
+ updateMetadata(metadata: SettableMetadata): Promise<StorageMetadata>;
12
18
  delete(): Promise<{
13
19
  success: boolean;
14
20
  message?: string;
15
21
  }>;
16
- listAll(): Promise<any>;
17
- list(options?: any): Promise<any>;
22
+ listAll(): Promise<ListResult>;
23
+ list(options?: ListOptions): Promise<ListResult>;
18
24
  upload?: (file: File | Blob, options?: UploadOptions) => Promise<{
19
25
  url: string;
20
26
  path: string;
@@ -22,28 +28,39 @@ export interface IStorageReference {
22
28
  }
23
29
  export declare class StorageReferenceImpl implements IStorageReference {
24
30
  path: string;
25
- private storage;
31
+ fullPath: string;
32
+ name: string;
33
+ bucket: string;
34
+ storage: Storage;
26
35
  constructor(path: string, storage: Storage);
27
- put(file: File | Blob, metadata?: UploadMetadata): UploadTask;
28
- putString(data: string, format?: string, metadata?: UploadMetadata): UploadTask;
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;
29
40
  getDownloadURL(): Promise<string>;
30
41
  getMetadata(): Promise<StorageMetadata>;
31
- updateMetadata(metadata: Partial<StorageMetadata>): Promise<StorageMetadata>;
42
+ updateMetadata(metadata: SettableMetadata): Promise<StorageMetadata>;
32
43
  delete(): Promise<{
33
44
  success: boolean;
34
45
  message?: string;
35
46
  }>;
36
- listAll(): Promise<any>;
37
- list(options?: any): Promise<any>;
47
+ listAll(): Promise<ListResult>;
48
+ list(options?: ListOptions): Promise<ListResult>;
38
49
  upload(file: File | Blob, options?: UploadOptions): Promise<{
39
50
  url: string;
40
51
  path: string;
41
52
  }>;
42
53
  }
43
- export declare const ref: (storage: Storage, path: string) => IStorageReference;
44
- export declare const uploadBytes: (storageRef: IStorageReference, file: File | Blob, metadata?: UploadMetadata) => Promise<any>;
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>;
45
61
  export declare const getDownloadURL: (storageRef: IStorageReference) => Promise<string>;
46
- export declare const deleteObject: (storageRef: IStorageReference) => Promise<{
47
- success: boolean;
48
- message?: string;
49
- }>;
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,28 +1,81 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.deleteObject = exports.getDownloadURL = exports.uploadBytes = exports.ref = 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
- put(file, metadata) {
10
- return this.storage.createUploadTask(this.path, file, metadata);
14
+ get root() {
15
+ return new StorageReferenceImpl('', this.storage);
11
16
  }
12
- putString(data, format = 'raw', metadata) {
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) {
13
25
  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]);
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' });
22
31
  }
23
32
  else {
24
33
  blob = new Blob([data]);
25
34
  }
35
+ return this.storage.createUploadTask(this.path, blob, metadata);
36
+ }
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
+ }
26
79
  return this.put(blob, metadata);
27
80
  }
28
81
  getDownloadURL() {
@@ -46,27 +99,79 @@ class StorageReferenceImpl {
46
99
  // Legacy for backward compatibility
47
100
  async upload(file, options) {
48
101
  const task = this.put(file, options);
49
- task.options = options; // Inject legacy options
102
+ task.options = options;
50
103
  await task;
51
104
  const url = await this.getDownloadURL();
52
105
  return { url, path: this.path };
53
106
  }
54
107
  }
55
108
  exports.StorageReferenceImpl = StorageReferenceImpl;
56
- const ref = (storage, path) => {
57
- 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);
58
124
  };
59
125
  exports.ref = ref;
60
- const uploadBytes = async (storageRef, file, metadata) => {
61
- const task = storageRef.put(file, metadata);
62
- return await task;
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
+ };
63
136
  };
64
137
  exports.uploadBytes = uploadBytes;
138
+ const uploadBytesResumable = (storageRef, data, metadata) => {
139
+ return storageRef.put(data, metadata);
140
+ };
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;
65
154
  const getDownloadURL = async (storageRef) => {
66
155
  return storageRef.getDownloadURL();
67
156
  };
68
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;
69
166
  const deleteObject = async (storageRef) => {
70
- return storageRef.delete();
167
+ await storageRef.delete();
71
168
  };
72
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,4 +1,4 @@
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';
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
2
  export interface StorageMetadata {
3
3
  name: string;
4
4
  bucket: string;
@@ -15,6 +15,7 @@ export interface StorageMetadata {
15
15
  contentLanguage?: string;
16
16
  contentType?: string;
17
17
  customMetadata?: Record<string, string>;
18
+ downloadTokens?: string;
18
19
  }
19
20
  export interface UploadMetadata {
20
21
  md5Hash?: string;
@@ -25,6 +26,8 @@ export interface UploadMetadata {
25
26
  contentType?: string;
26
27
  customMetadata?: Record<string, string>;
27
28
  }
29
+ export type SettableMetadata = UploadMetadata;
30
+ export type StringFormat = 'raw' | 'base64' | 'base64url' | 'data_url';
28
31
  export type TaskState = 'running' | 'paused' | 'success' | 'canceled' | 'error';
29
32
  export interface UploadTaskSnapshot {
30
33
  bytesTransferred: number;
@@ -34,3 +37,19 @@ export interface UploadTaskSnapshot {
34
37
  task: any;
35
38
  ref: any;
36
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexabase-console",
3
- "version": "2.0.5",
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",