nexabase-console 2.0.1 → 2.0.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/README.md CHANGED
@@ -142,6 +142,40 @@ const listenLiveChats = () => {
142
142
  };
143
143
  ```
144
144
 
145
+ #### Agregasi Data & Hitung Dokumen (`getCountFromServer` / `getAggregateFromServer`)
146
+ Mendukung fungsi agregasi server 1:1 seperti Firebase SDK: `getCountFromServer()`, `getAggregateFromServer()`, `count()`, `sum()`, dan `average()`.
147
+
148
+ ```javascript
149
+ import {
150
+ collection,
151
+ query,
152
+ where,
153
+ getCountFromServer,
154
+ getAggregateFromServer,
155
+ count,
156
+ sum,
157
+ average
158
+ } from 'nexabase-console';
159
+
160
+ // 1. Menghitung total dokumen (getCountFromServer)
161
+ const productsRef = collection(db, 'products');
162
+ const qAvailable = query(productsRef, where('status', '==', 'active'));
163
+
164
+ const countSnapshot = await getCountFromServer(qAvailable);
165
+ console.log("Total produk aktif:", countSnapshot.data().count);
166
+
167
+ // 2. Agregasi multi-field (getAggregateFromServer)
168
+ const orderStats = await getAggregateFromServer(collection(db, 'orders'), {
169
+ totalOrders: count(),
170
+ totalRevenue: sum('totalAmount'),
171
+ avgOrderValue: average('totalAmount')
172
+ });
173
+
174
+ console.log("Jumlah order:", orderStats.data().totalOrders);
175
+ console.log("Total pendapatan:", orderStats.data().totalRevenue);
176
+ console.log("Rata-rata transaksi:", orderStats.data().avgOrderValue);
177
+ ```
178
+
145
179
  #### Menggunakan Atomic Write Batch (Untuk Upload File / Excel Massal)
146
180
  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).
147
181
 
@@ -220,47 +254,120 @@ async function potongStokAman(productId, variantId, qtyBeli) {
220
254
 
221
255
  ---
222
256
 
223
- ### 3. Autentikasi Pengguna & OTP Tanpa Sandi
257
+ ### 3. Autentikasi Pengguna & OTP Tanpa Sandi (SDK v2.0.0)
258
+
259
+ #### Inisialisasi Auth & Realtime State Listener
260
+ ```javascript
261
+ import { getAuth, NexaAuthError } from 'nexabase-console';
262
+
263
+ const auth = getAuth(app);
224
264
 
225
- #### Pendaftaran & Login Tradisional
265
+ // Realtime Listener & Sync Sesi Multi-Tab via StorageEvent
266
+ const unsubscribe = auth.onAuthStateChanged((user) => {
267
+ if (user) {
268
+ console.log("User terautentikasi:", user.uid, "| Email:", user.email);
269
+ } else {
270
+ console.log("User ter-logout / belum login");
271
+ }
272
+ });
273
+ ```
274
+
275
+ #### Pendaftaran & Login Email/Password
226
276
  ```javascript
227
- // Registrasi User Baru
277
+ // 1. Registrasi Akun Baru
228
278
  const register = async () => {
229
- const user = await app.auth().createUserWithEmailAndPassword(
230
- 'test@example.com',
231
- 'password123',
232
- 'Andi Wijaya'
233
- );
234
- console.log("Registrasi Berhasil:", user);
279
+ try {
280
+ const session = await auth.createUserWithEmailAndPassword(
281
+ 'budi@example.com',
282
+ 'Password123!',
283
+ 'Budi Santoso'
284
+ );
285
+ console.log("Registrasi Berhasil! UID:", session.user.uid);
286
+ } catch (err) {
287
+ if (err instanceof NexaAuthError && err.code === 'auth/email-already-in-use') {
288
+ console.error("Email sudah terdaftar.");
289
+ }
290
+ }
235
291
  };
236
292
 
237
- // Login User
293
+ // 2. Login User (Dukungan Automatic Single-Flight Token Refresh)
238
294
  const login = async () => {
239
- const authSession = await app.auth().signInWithEmailAndPassword(
240
- 'test@example.com',
241
- 'password123'
242
- );
243
- console.log("Token Sesi JWT:", authSession.token);
244
- console.log("Data Akun Pengguna:", authSession.user);
295
+ try {
296
+ const session = await auth.signInWithEmailAndPassword(
297
+ 'budi@example.com',
298
+ 'Password123!'
299
+ );
300
+ console.log("Access Token JWT:", session.accessToken);
301
+ console.log("Refresh Token:", session.refreshToken);
302
+ } catch (err) {
303
+ if (err instanceof NexaAuthError && err.code === 'auth/invalid-credential') {
304
+ console.error("Email atau password salah.");
305
+ }
306
+ }
245
307
  };
246
308
  ```
247
309
 
248
- #### Login Passwordless dengan OTP (Email)
310
+ #### Login Passwordless dengan OTP (Email 6-Digit)
249
311
  ```javascript
250
- // 1. Kirim OTP Ke Email Pengguna
312
+ // 1. Kirim OTP Ke Email (Berlaku 5 Menit, Hash Single-Use)
251
313
  const sendMyOtp = async () => {
252
- const res = await app.auth().sendOtp('test@example.com');
253
- console.log("Kode OTP berhasil terkirim ke email Anda.");
314
+ const res = await auth.sendOtp('budi@example.com');
315
+ console.log("Kode OTP berhasil terkirim:", res.message);
254
316
  };
255
317
 
256
318
  // 2. Verifikasi OTP dari Input Pengguna
257
319
  const verifyMyOtp = async () => {
258
- const otpCode = '123456'; // Kode 6 digit yang dikirim ke email
259
- const authSession = await app.auth().signInWithOtp('test@example.com', otpCode, 'Andi Wijaya');
260
- console.log("Login OTP Berhasil! Sesi JWT Aktif:", authSession.token);
320
+ try {
321
+ const otpCode = '123456'; // Kode 6 digit yang dikirim ke email
322
+ const session = await auth.signInWithOtp('budi@example.com', otpCode);
323
+ console.log("Login OTP Berhasil! Welcome:", session.user.email);
324
+ } catch (err) {
325
+ if (err instanceof NexaAuthError) {
326
+ if (err.code === 'auth/invalid-otp') console.error("OTP tidak valid.");
327
+ if (err.code === 'auth/expired-otp') console.error("OTP kedaluwarsa.");
328
+ if (err.code === 'auth/too-many-otp-attempts') console.error("Terlalu banyak percobaan.");
329
+ }
330
+ }
261
331
  };
262
332
  ```
263
333
 
334
+ #### Mengambil ID Token untuk Request API / Server Verification
335
+ ```javascript
336
+ // Mengambil token JWT (dengan auto-refresh jika hampir expired)
337
+ const idToken = await auth.getIdToken(/* forceRefresh */ false);
338
+
339
+ // Mengambil rincian klaim token (exp, authTime, claims)
340
+ const tokenResult = await auth.getIdTokenResult();
341
+ console.log("Token Kedaluwarsa:", tokenResult.expirationTime);
342
+ ```
343
+
344
+ #### Update Profil, Reset Password & Sign Out
345
+ ```javascript
346
+ // Update Profil
347
+ await auth.updateProfile({ name: 'Budi Santoso, M.T.', photoURL: 'https://example.com/photo.jpg' });
348
+
349
+ // Reset Password Email
350
+ await auth.sendPasswordResetEmail('budi@example.com');
351
+
352
+ // Revoke All Sessions
353
+ await auth.revokeAllSessions();
354
+
355
+ // === Opsi Logout Akun (Sign Out) ===
356
+ // 1. Method Instance (Rekomendasi)
357
+ await auth.signOut();
358
+
359
+ // 2. Fungsi Standar Modular
360
+ import { signOut } from 'nexabase-console';
361
+ await signOut(auth);
362
+
363
+ // 3. Namespace Helper
364
+ import { NexabaseAuth } from 'nexabase-console';
365
+ await NexabaseAuth.signOut(auth);
366
+
367
+ // 4. Shortcut dari objek App
368
+ await app.signOut();
369
+ ```
370
+
264
371
  ---
265
372
 
266
373
  ### 4. File Storage API
@@ -1,8 +1,9 @@
1
1
  import { AxiosInstance } from 'axios';
2
- import { NexaConfig, User, AuthResponse, DatabaseReference, StorageReference, UploadOptions, OfflineJob } from '../types/index';
2
+ import { NexaConfig, DatabaseReference, StorageReference, UploadOptions, OfflineJob } from '../types/index';
3
3
  import { HttpClient } from '../transport/HttpClient';
4
4
  import { SSEClient } from '../transport/SSEClient';
5
5
  import { Auth } from '../auth/Auth';
6
+ import { User as AuthUser, AuthSession } from '../auth/authTypes';
6
7
  import { IndexedDB } from '../persistence/IndexedDB';
7
8
  import { LocalStore } from '../persistence/LocalStore';
8
9
  import { MutationQueue } from '../persistence/MutationQueue';
@@ -44,15 +45,15 @@ export declare class NexaApp {
44
45
  _ensureFirestoreSSE(): void;
45
46
  _closeFirestoreSSEIfIdle(): void;
46
47
  _notifySnapshotCallbacks(path: string, actionType: string, payload: any): void;
47
- signInWithEmailAndPassword(email: string, password: string): Promise<AuthResponse>;
48
- createUserWithEmailAndPassword(email: string, password: string, name: string): Promise<User>;
48
+ signInWithEmailAndPassword(email: string, password: string): Promise<AuthSession>;
49
+ createUserWithEmailAndPassword(email: string, password: string, name?: string): Promise<AuthSession>;
49
50
  sendOtp(email: string): Promise<{
50
51
  success: boolean;
51
52
  message: string;
52
53
  }>;
53
- signInWithOtp(email: string, otp: string, name?: string): Promise<AuthResponse>;
54
+ signInWithOtp(email: string, otp: string): Promise<AuthSession>;
54
55
  signOut(): void;
55
- getCurrentUser(): User | null;
56
+ getCurrentUser(): AuthUser | null;
56
57
  ref(path: string): DatabaseReference;
57
58
  storageRef(path: string): StorageReference;
58
59
  uploadFile(path: string, file: File | Blob, options?: UploadOptions): Promise<{
@@ -150,8 +150,8 @@ class NexaApp {
150
150
  async sendOtp(email) {
151
151
  return this.authService.sendOtp(email);
152
152
  }
153
- async signInWithOtp(email, otp, name) {
154
- const res = await this.authService.signInWithOtp(email, otp, name);
153
+ async signInWithOtp(email, otp) {
154
+ const res = await this.authService.signInWithOtp(email, otp);
155
155
  this.token = this.authService.getToken();
156
156
  return res;
157
157
  }
@@ -1,8 +1,14 @@
1
1
  import { NexaApp } from './NexaApp';
2
2
  import { NexaConfig } from '../types/index';
3
+ import { Auth } from '../auth/Auth';
3
4
  export declare const NexaBase: {
4
5
  init: (config: NexaConfig) => NexaApp;
5
6
  initializeApp: (config: NexaConfig) => NexaApp;
6
7
  };
7
8
  export declare const initializeApp: (config: NexaConfig) => NexaApp;
8
9
  export declare const getFirestore: (app: NexaApp) => NexaApp;
10
+ export declare const getAuth: (app: NexaApp) => Auth;
11
+ export declare const signOut: (auth: Auth) => Promise<void>;
12
+ export declare const NexabaseAuth: {
13
+ signOut: (auth: Auth) => Promise<void>;
14
+ };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getFirestore = exports.initializeApp = exports.NexaBase = void 0;
3
+ exports.NexabaseAuth = exports.signOut = exports.getAuth = exports.getFirestore = exports.initializeApp = exports.NexaBase = void 0;
4
4
  const NexaApp_1 = require("./NexaApp");
5
5
  exports.NexaBase = {
6
6
  init: (config) => {
@@ -25,3 +25,16 @@ const getFirestore = (app) => {
25
25
  return app;
26
26
  };
27
27
  exports.getFirestore = getFirestore;
28
+ const getAuth = (app) => {
29
+ return app.authService;
30
+ };
31
+ exports.getAuth = getAuth;
32
+ const signOut = (auth) => {
33
+ return auth.signOut();
34
+ };
35
+ exports.signOut = signOut;
36
+ exports.NexabaseAuth = {
37
+ signOut: (auth) => {
38
+ return auth.signOut();
39
+ }
40
+ };
@@ -1,21 +1,53 @@
1
1
  import { AxiosInstance } from 'axios';
2
- import { User, AuthResponse } from './authTypes';
2
+ import { User, AuthSession, IdTokenResult, UserProfileUpdate, AuthStateCallback } from './authTypes';
3
3
  export declare class Auth {
4
4
  private projectId;
5
5
  private client;
6
- private token;
7
6
  private persistence;
7
+ private emulatorUrl;
8
+ private currentSession;
9
+ private listeners;
10
+ private refreshPromise;
11
+ private refreshTimer;
8
12
  constructor(projectId: string, client: AxiosInstance, initialToken: string | null);
13
+ connectAuthEmulator(url: string): void;
14
+ get emulator(): string | null;
15
+ private initSession;
16
+ onAuthStateChanged(callback: AuthStateCallback): () => void;
17
+ private notifyListeners;
18
+ private handleStorageChange;
19
+ get currentUser(): User | null;
9
20
  getToken(): string | null;
10
21
  setToken(token: string | null): void;
11
- signInWithEmailAndPassword(email: string, password: string): Promise<AuthResponse>;
12
- createUserWithEmailAndPassword(email: string, password: string, name: string): Promise<User>;
22
+ getIdToken(forceRefresh?: boolean): Promise<string>;
23
+ getIdTokenResult(forceRefresh?: boolean): Promise<IdTokenResult>;
24
+ private parseJwtClaims;
25
+ private refreshAccessTokenDeduplicated;
26
+ private refreshAccessToken;
27
+ private scheduleAutoRefresh;
28
+ private clearAutoRefresh;
29
+ createUserWithEmailAndPassword(email: string, password: string, name?: string): Promise<AuthSession>;
30
+ signInWithEmailAndPassword(email: string, password: string): Promise<AuthSession>;
13
31
  sendOtp(email: string): Promise<{
14
32
  success: boolean;
15
33
  message: string;
16
34
  }>;
17
- signInWithOtp(email: string, otp: string, name?: string): Promise<AuthResponse>;
18
- signOut(): void;
35
+ signInWithOtp(email: string, otp: string): Promise<AuthSession>;
36
+ sendPasswordResetEmail(email: string): Promise<{
37
+ success: boolean;
38
+ }>;
39
+ confirmPasswordReset(code: string, newPassword: string): Promise<{
40
+ success: boolean;
41
+ }>;
42
+ sendEmailVerification(): Promise<{
43
+ success: boolean;
44
+ }>;
45
+ updateProfile(update: UserProfileUpdate): Promise<User>;
46
+ updatePassword(newPassword: string): Promise<void>;
47
+ deleteUser(): Promise<void>;
48
+ revokeSession(sessionId?: string): Promise<void>;
49
+ revokeAllSessions(): Promise<void>;
50
+ signOut(): Promise<void>;
19
51
  getCurrentUser(): User | null;
20
52
  }
21
53
  export { Auth as AuthService };