nexabase-console 1.0.7 → 1.1.1

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.
Files changed (3) hide show
  1. package/README.md +113 -9
  2. package/dist/index.js +23 -9
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -7,8 +7,9 @@ SDK Client Resmi untuk **NexaBase**: Platform sinkronisasi database modern yang
7
7
  ## 🚀 Fitur Utama
8
8
 
9
9
  - **⚡ Real-Time Synchronization**: Sinkronisasi data real-time dengan latency rendah menggunakan Server-Sent Events (SSE).
10
+ - **🔒 Concurrency Control & Transactions (`runTransaction`)**: Transaksi dua arah (Read-Before-Write) atomik dengan kontrol konkurensi (Optimistic Locking & Automatic Retry) untuk mencegah race conditions pada pemotongan stok, kuota, atau transaksi finansial.
10
11
  - **📦 Offline-First & Auto-Sync**: Sinkronisasi state lokal secara otomatis menggunakan **IndexedDB**. Data tetap tersimpan di browser jika koneksi terputus dan akan disinkronkan ke server secara otomatis saat kembali online.
11
- - **🔥 Modular Firestore API**: Desain API modular modern mirip Firebase SDK (v9+) sehingga sangat mudah dipelajari.
12
+ - **🔥 Modular Firestore API & TypeScript Generics**: Desain API modular modern mirip Firebase SDK (v9+) dengan dukungan penuh parameter tipe generik (TypeScript Generics) untuk menjamin tipe data yang aman (type-safe) secara end-to-end.
12
13
  - **🔑 Secure Auth & Passwordless OTP**: Dukungan penuh registrasi, login email/sandi, serta OTP instan via SMTP yang aman.
13
14
  - **📂 File Storage Integration**: Unggah aset media dan file blob ke cloud storage dengan satu perintah.
14
15
 
@@ -109,24 +110,82 @@ const listenLive = () => {
109
110
  };
110
111
  ```
111
112
 
112
- #### Menggunakan Atomic Write Batch
113
+ #### Menggunakan Atomic Write Batch (Untuk Upload File / Excel Massal)
114
+ Jika Anda mengunggah data sekaligus dalam jumlah besar dari file (misal: import 500 resi dari Excel), hindari memanggil `setDoc` satu per satu di dalam _looping_. Gunakan fitur `writeBatch` yang membundel seluruh operasi Anda dalam satu pengiriman jaringan (hingga 500 operasi per eksekusi).
115
+
116
+ **Catatan:** Jika alur kerjanya adalah **kasir/admin melakukan scan barcode satu-per-satu lalu menekan enter**, maka Anda **TIDAK PERLU** menggunakan `writeBatch`. Menyimpan data satu-per-satu (`setDoc` / `addDoc`) setiap kali dienter adalah **cara yang paling benar dan normal** untuk kebutuhan _realtime_. Database dapat dengan mudah menangani 500 scan yang terjadi secara bertahap sepanjang hari.
117
+
113
118
  ```javascript
114
119
  import { doc, writeBatch } from 'nexabase-console';
115
120
 
116
- const executeBatch = async () => {
121
+ const executeBulkUpload = async (listResi) => {
117
122
  const batch = writeBatch(db);
118
123
 
119
- const ref1 = doc(db, 'products', 'prod_a');
120
- const ref2 = doc(db, 'products', 'prod_b');
121
-
122
- batch.set(ref1, { name: 'Sandal Kulit', price: 75000 });
123
- batch.set(ref2, { name: 'Sepatu Kulit', price: 250000 });
124
+ // ListResi adalah array berisi objek resi. Maksimal 500 per batch.
125
+ listResi.forEach((resi) => {
126
+ // Referensi dokumen
127
+ const docRef = doc(db, 'resi', resi.nomor_resi);
128
+ batch.set(docRef, resi);
129
+ });
124
130
 
131
+ // Eksekusi semua secara atomik (bersamaan dalam 1 request)
125
132
  const result = await batch.commit();
126
- console.log("Semua operasi batch berhasil di-commit secara atomik.");
133
+ console.log(`Berhasil mengunggah ${listResi.length} resi secara massal.`);
127
134
  };
128
135
  ```
129
136
 
137
+ #### Menggunakan Transactions (`runTransaction`) — Concurrency Control
138
+ ```javascript
139
+ import { doc, runTransaction } from 'nexabase-console';
140
+
141
+ async function potongStokAman(productId, variantId, qtyBeli) {
142
+ const productRef = doc(db, 'products', productId);
143
+
144
+ try {
145
+ const hasil = await runTransaction(db, async (transaction) => {
146
+ // 1. Baca dokumen di dalam transaksi (Read)
147
+ const productSnap = await transaction.get(productRef);
148
+ if (!productSnap.exists()) {
149
+ throw new Error("Produk tidak ditemukan!");
150
+ }
151
+
152
+ const productData = productSnap.data();
153
+ const variantIndex = productData.variants?.findIndex((v) => v.id === variantId);
154
+ if (variantIndex === -1 || variantIndex === undefined) {
155
+ throw new Error("Varian produk tidak ditemukan!");
156
+ }
157
+
158
+ const variant = productData.variants[variantIndex];
159
+ const stokSaatIni = variant.stock;
160
+
161
+ // 2. Validasi stok sebelum dipotong
162
+ if (stokSaatIni < qtyBeli) {
163
+ throw new Error(`Stok tidak mencukupi! Tersedia: ${stokSaatIni}, Diminta: ${qtyBeli}`);
164
+ }
165
+
166
+ // 3. Kalkulasi data baru
167
+ const updatedVariants = [...productData.variants];
168
+ updatedVariants[variantIndex] = {
169
+ ...variant,
170
+ stock: stokSaatIni - qtyBeli
171
+ };
172
+
173
+ // 4. Tulis perubahan di dalam transaksi (Write)
174
+ transaction.update(productRef, {
175
+ variants: updatedVariants,
176
+ updatedAt: new Date().toISOString()
177
+ });
178
+
179
+ return { success: true, sisaStok: stokSaatIni - qtyBeli };
180
+ });
181
+
182
+ console.log("Transaksi Berhasil!", hasil);
183
+ } catch (error) {
184
+ console.error("Transaksi Gagal / Conflict:", error.message);
185
+ }
186
+ }
187
+ ```
188
+
130
189
  ---
131
190
 
132
191
  ### 3. Autentikasi Pengguna & OTP Tanpa Sandi
@@ -185,6 +244,51 @@ const uploadImage = async (fileBlob) => {
185
244
 
186
245
  ---
187
246
 
247
+ ### 5. Keamanan Tipe TypeScript (Generics) & Kueri Lanjutan
248
+
249
+ #### Dukungan TypeScript Generics
250
+ Kini Anda bisa mendefinisikan tipe data struktur dokumen Anda agar penulisan dan pembacaan data sepenuhnya aman (Type-safe).
251
+
252
+ ```typescript
253
+ import { collection, doc, getDoc, getDocs } from 'nexabase-console';
254
+
255
+ interface Product {
256
+ name: string;
257
+ price: number;
258
+ stock: number;
259
+ tags: string[];
260
+ }
261
+
262
+ // 1. Definisikan tipe koleksi
263
+ const productsCol = collection<Product>(db, 'products');
264
+
265
+ // 2. Definisikan tipe referensi dokumen
266
+ const productRef = doc<Product>(db, 'products', 'prod_a');
267
+
268
+ // 3. Baca data dengan Autocomplete & Type Checking penuh
269
+ const snapshot = await getDoc(productRef);
270
+ if (snapshot.exists()) {
271
+ const data = snapshot.data(); // Tipe data otomatis terdeteksi sebagai 'Product'
272
+ console.log(data.name); // Aman! Autocomplete tersedia
273
+ console.log(data.price); // Aman!
274
+ }
275
+ ```
276
+
277
+ #### Kompatibilitas QuerySnapshot (`docChanges()`)
278
+ Bagi Anda yang bermigrasi dari Firebase, objek `QuerySnapshot` yang dikembalikan oleh `getDocs` atau `onSnapshot` kini mendukung metode `.docChanges()` secara native:
279
+
280
+ ```typescript
281
+ const snapshot = await getDocs(productsCol);
282
+ const changes = snapshot.docChanges(); // Mengembalikan array perubahan dokumen
283
+ ```
284
+
285
+ #### Filter Offline Lengkap (Advanced Querying Operators)
286
+ Mekanisme kueri offline pada SDK NexaBase kini mendukung operator Firestore tingkat lanjut secara penuh demi keandalan luar biasa bahkan saat koneksi terputus:
287
+ - Operator dasar: `==`, `!=`, `>`, `<`, `>=`, `<=`
288
+ - Operator array & keanggotaan: `in`, `not-in`, `array-contains`, `array-contains-any`
289
+
290
+ ---
291
+
188
292
  ## 🗄️ Dukungan Sinkronisasi Offline (IndexedDB)
189
293
 
190
294
  NexaBase JS SDK dikembangkan dengan arsitektur **Offline-First**.
package/dist/index.js CHANGED
@@ -696,15 +696,29 @@ const onSnapshot = (ref, callback) => {
696
696
  callback(cached);
697
697
  }
698
698
  }
699
- const triggerCallback = async () => {
700
- if (ref.type === 'collection' || ref.type === 'query') {
701
- const docs = await (0, exports.getDocs)(ref);
702
- callback(docs);
703
- }
704
- else {
705
- const docData = await (0, exports.getDoc)(ref);
706
- callback(docData);
707
- }
699
+ let debounceTimer = null;
700
+ const triggerCallback = () => {
701
+ if (debounceTimer)
702
+ clearTimeout(debounceTimer);
703
+ debounceTimer = setTimeout(async () => {
704
+ if (ref.type === 'collection' || ref.type === 'query') {
705
+ // Immediately serve from cache for UI responsiveness
706
+ callback((0, exports.getCachedDocs)(ref));
707
+ try {
708
+ const docs = await (0, exports.getDocs)(ref);
709
+ callback(docs);
710
+ }
711
+ catch (e) { }
712
+ }
713
+ else {
714
+ callback((0, exports.getCachedDoc)(ref));
715
+ try {
716
+ const docData = await (0, exports.getDoc)(ref);
717
+ callback(docData);
718
+ }
719
+ catch (e) { }
720
+ }
721
+ }, 50); // Debounce to batch rapid changes (fast enough for manual scans)
708
722
  };
709
723
  // 2. Fetch the latest live server data and update cache + trigger the callback again
710
724
  triggerCallback();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexabase-console",
3
- "version": "1.0.7",
3
+ "version": "1.1.1",
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",