nexabase-console 2.0.0 → 2.0.2

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/auth/Auth.js CHANGED
@@ -2,71 +2,384 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AuthService = exports.Auth = void 0;
4
4
  const persistence_1 = require("./persistence");
5
- const NexaError_1 = require("../errors/NexaError");
5
+ const NexaAuthError_1 = require("./NexaAuthError");
6
6
  class Auth {
7
7
  constructor(projectId, client, initialToken) {
8
- this.token = null;
8
+ this.emulatorUrl = null;
9
+ this.currentSession = null;
10
+ this.listeners = new Set();
11
+ // Single concurrent token refresh mutex / request deduplication
12
+ this.refreshPromise = null;
13
+ this.refreshTimer = null;
9
14
  this.projectId = projectId;
10
15
  this.client = client;
11
16
  this.persistence = new persistence_1.AuthPersistence(projectId);
12
- this.token = initialToken || this.persistence.getSavedToken();
17
+ // Initial session restoration
18
+ this.initSession().catch((err) => {
19
+ console.warn('⚡ Session restore failed:', err);
20
+ });
21
+ // Multi-tab auth state synchronization via StorageEvent
22
+ if (typeof window !== 'undefined' && window.addEventListener) {
23
+ window.addEventListener('storage', (event) => {
24
+ if (event.key === `nexa_session_${this.projectId}`) {
25
+ this.handleStorageChange(event.newValue);
26
+ }
27
+ });
28
+ }
29
+ }
30
+ // --- Emulator Integration ---
31
+ connectAuthEmulator(url) {
32
+ this.emulatorUrl = url;
33
+ console.log(`🔌 Auth connected to emulator at ${url}`);
34
+ }
35
+ get emulator() {
36
+ return this.emulatorUrl;
37
+ }
38
+ // --- Initial Session Restore ---
39
+ async initSession() {
40
+ const saved = await this.persistence.getSession();
41
+ if (!saved) {
42
+ this.notifyListeners(null);
43
+ return;
44
+ }
45
+ const now = Date.now();
46
+ // Check if access token is valid or close to expiration (buffer 60 seconds)
47
+ if (saved.accessTokenExpiresAt > now + 60000) {
48
+ this.currentSession = saved;
49
+ this.scheduleAutoRefresh();
50
+ this.notifyListeners(saved.user);
51
+ }
52
+ else if (saved.refreshTokenExpiresAt > now) {
53
+ // Access token expired, attempt refresh
54
+ try {
55
+ await this.refreshAccessToken(saved.refreshToken);
56
+ }
57
+ catch {
58
+ await this.signOut();
59
+ }
60
+ }
61
+ else {
62
+ // Refresh token expired
63
+ await this.signOut();
64
+ }
65
+ }
66
+ // --- Auth State Listeners ---
67
+ onAuthStateChanged(callback) {
68
+ this.listeners.add(callback);
69
+ // Fire callback immediately with current state
70
+ callback(this.currentUser);
71
+ return () => {
72
+ this.listeners.delete(callback);
73
+ };
74
+ }
75
+ notifyListeners(user) {
76
+ this.listeners.forEach((cb) => {
77
+ try {
78
+ cb(user);
79
+ }
80
+ catch (e) {
81
+ console.error('Error in auth listener:', e);
82
+ }
83
+ });
84
+ }
85
+ async handleStorageChange(newValue) {
86
+ if (!newValue) {
87
+ this.currentSession = null;
88
+ this.clearAutoRefresh();
89
+ this.notifyListeners(null);
90
+ return;
91
+ }
92
+ try {
93
+ const parsed = JSON.parse(newValue);
94
+ this.currentSession = parsed;
95
+ this.scheduleAutoRefresh();
96
+ this.notifyListeners(parsed.user);
97
+ }
98
+ catch {
99
+ await this.signOut();
100
+ }
101
+ }
102
+ // --- User Getters ---
103
+ get currentUser() {
104
+ return this.currentSession?.user || null;
13
105
  }
14
106
  getToken() {
15
- return this.token;
107
+ return this.currentSession?.accessToken || null;
16
108
  }
17
109
  setToken(token) {
18
- this.token = token;
110
+ if (this.currentSession) {
111
+ this.currentSession.accessToken = token || '';
112
+ }
113
+ }
114
+ // --- Single Flight Token Refresh (Mutex / Request Deduplication) ---
115
+ async getIdToken(forceRefresh = false) {
116
+ if (!this.currentSession) {
117
+ throw new NexaAuthError_1.NexaAuthError('auth/unauthenticated', 'User belum login.');
118
+ }
119
+ const now = Date.now();
120
+ const isExpired = this.currentSession.accessTokenExpiresAt <= now + 60000;
121
+ if (forceRefresh || isExpired) {
122
+ const session = await this.refreshAccessTokenDeduplicated();
123
+ return session.accessToken;
124
+ }
125
+ return this.currentSession.accessToken;
126
+ }
127
+ async getIdTokenResult(forceRefresh = false) {
128
+ const token = await this.getIdToken(forceRefresh);
129
+ const claims = this.parseJwtClaims(token);
130
+ return {
131
+ token,
132
+ expirationTime: new Date(claims.exp * 1000).toISOString(),
133
+ authTime: new Date(claims.auth_time * 1000).toISOString(),
134
+ issuedAtTime: new Date(claims.iat * 1000).toISOString(),
135
+ signInProvider: claims.provider_id || 'password',
136
+ claims
137
+ };
138
+ }
139
+ parseJwtClaims(token) {
140
+ try {
141
+ const base64Url = token.split('.')[1];
142
+ const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
143
+ const jsonPayload = decodeURIComponent(atob(base64)
144
+ .split('')
145
+ .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
146
+ .join(''));
147
+ return JSON.parse(jsonPayload);
148
+ }
149
+ catch {
150
+ return {};
151
+ }
152
+ }
153
+ // Deduplicated token refresh (Concurrent Request Safety)
154
+ refreshAccessTokenDeduplicated() {
155
+ if (this.refreshPromise) {
156
+ return this.refreshPromise; // Return active in-flight request
157
+ }
158
+ if (!this.currentSession?.refreshToken) {
159
+ return Promise.reject(new NexaAuthError_1.NexaAuthError('auth/invalid-refresh-token', 'Refresh token tidak ditemukan.'));
160
+ }
161
+ this.refreshPromise = this.refreshAccessToken(this.currentSession.refreshToken)
162
+ .finally(() => {
163
+ this.refreshPromise = null;
164
+ });
165
+ return this.refreshPromise;
166
+ }
167
+ async refreshAccessToken(refreshToken) {
168
+ const endpoint = this.emulatorUrl || `/api/project/${this.projectId}/auth/token/refresh`;
169
+ try {
170
+ const res = await this.client.post(endpoint, { refreshToken });
171
+ const session = res.data;
172
+ this.currentSession = session;
173
+ await this.persistence.saveSession(session);
174
+ this.scheduleAutoRefresh();
175
+ this.notifyListeners(session.user);
176
+ return session;
177
+ }
178
+ catch (err) {
179
+ await this.signOut();
180
+ throw new NexaAuthError_1.NexaAuthError(err.response?.data?.code || 'auth/session-expired', err.response?.data?.message || 'Sesi telah berakhir, silakan login kembali.', err.response?.status || 401);
181
+ }
182
+ }
183
+ scheduleAutoRefresh() {
184
+ this.clearAutoRefresh();
185
+ if (!this.currentSession)
186
+ return;
187
+ const expiresAt = this.currentSession.accessTokenExpiresAt;
188
+ const now = Date.now();
189
+ // Refresh 2 minutes before expiry
190
+ const delay = Math.max(1000, expiresAt - now - 120000);
191
+ this.refreshTimer = setTimeout(() => {
192
+ this.refreshAccessTokenDeduplicated().catch((err) => {
193
+ console.warn('⚠️ Auto token refresh failed:', err);
194
+ });
195
+ }, delay);
196
+ }
197
+ clearAutoRefresh() {
198
+ if (this.refreshTimer) {
199
+ clearTimeout(this.refreshTimer);
200
+ this.refreshTimer = null;
201
+ }
202
+ }
203
+ // --- Email & Password Auth ---
204
+ async createUserWithEmailAndPassword(email, password, name) {
205
+ try {
206
+ const endpoint = this.emulatorUrl || `/api/project/${this.projectId}/auth/register`;
207
+ const res = await this.client.post(endpoint, { email, password, name });
208
+ const session = res.data;
209
+ this.currentSession = session;
210
+ await this.persistence.saveSession(session);
211
+ this.scheduleAutoRefresh();
212
+ this.notifyListeners(session.user);
213
+ return session;
214
+ }
215
+ catch (err) {
216
+ throw new NexaAuthError_1.NexaAuthError(err.response?.data?.code || 'auth/email-already-in-use', err.response?.data?.message || 'Gagal mendaftar pengguna baru.', err.response?.status || 400);
217
+ }
19
218
  }
20
219
  async signInWithEmailAndPassword(email, password) {
21
220
  try {
22
- const res = await this.client.post(`/api/project/${this.projectId}/auth/login`, { email, password });
23
- if (res.data.token) {
24
- this.token = res.data.token;
25
- this.persistence.saveSession(res.data.token, res.data.user);
26
- }
221
+ const endpoint = this.emulatorUrl || `/api/project/${this.projectId}/auth/login`;
222
+ const res = await this.client.post(endpoint, { email, password });
223
+ const session = res.data;
224
+ this.currentSession = session;
225
+ await this.persistence.saveSession(session);
226
+ this.scheduleAutoRefresh();
227
+ this.notifyListeners(session.user);
228
+ return session;
229
+ }
230
+ catch (err) {
231
+ throw new NexaAuthError_1.NexaAuthError(err.response?.data?.code || 'auth/invalid-credential', err.response?.data?.message || 'Email atau password salah.', err.response?.status || 401);
232
+ }
233
+ }
234
+ // --- OTP Auth ---
235
+ async sendOtp(email) {
236
+ try {
237
+ const endpoint = this.emulatorUrl || `/api/project/${this.projectId}/auth/otp/send`;
238
+ const res = await this.client.post(endpoint, { email });
27
239
  return res.data;
28
240
  }
29
241
  catch (err) {
30
- throw (0, NexaError_1.toNexaError)(err);
242
+ throw new NexaAuthError_1.NexaAuthError(err.response?.data?.code || 'auth/otp-rate-limit', err.response?.data?.message || 'Gagal mengirim OTP.', err.response?.status || 429);
31
243
  }
32
244
  }
33
- async createUserWithEmailAndPassword(email, password, name) {
245
+ async signInWithOtp(email, otp) {
34
246
  try {
35
- const res = await this.client.post(`/api/project/${this.projectId}/users`, { email, password, name });
247
+ const endpoint = this.emulatorUrl || `/api/project/${this.projectId}/auth/otp/login`;
248
+ const res = await this.client.post(endpoint, { email, otp });
249
+ const session = res.data;
250
+ this.currentSession = session;
251
+ await this.persistence.saveSession(session);
252
+ this.scheduleAutoRefresh();
253
+ this.notifyListeners(session.user);
254
+ return session;
255
+ }
256
+ catch (err) {
257
+ throw new NexaAuthError_1.NexaAuthError(err.response?.data?.code || 'auth/invalid-otp', err.response?.data?.message || 'Kode OTP tidak valid atau telah kedaluwarsa.', err.response?.status || 400);
258
+ }
259
+ }
260
+ // --- Password Reset & Verification ---
261
+ async sendPasswordResetEmail(email) {
262
+ try {
263
+ const endpoint = this.emulatorUrl || `/api/project/${this.projectId}/auth/password-reset/send`;
264
+ const res = await this.client.post(endpoint, { email });
36
265
  return res.data;
37
266
  }
38
267
  catch (err) {
39
- throw (0, NexaError_1.toNexaError)(err);
268
+ throw new NexaAuthError_1.NexaAuthError('auth/user-not-found', 'Permintaan reset password gagal.');
40
269
  }
41
270
  }
42
- async sendOtp(email) {
271
+ async confirmPasswordReset(code, newPassword) {
43
272
  try {
44
- const res = await this.client.post(`/api/project/${this.projectId}/auth/otp/send`, { email });
273
+ const endpoint = this.emulatorUrl || `/api/project/${this.projectId}/auth/password-reset/confirm`;
274
+ const res = await this.client.post(endpoint, { code, newPassword });
45
275
  return res.data;
46
276
  }
47
277
  catch (err) {
48
- throw (0, NexaError_1.toNexaError)(err);
278
+ throw new NexaAuthError_1.NexaAuthError('auth/invalid-credential', 'Kode reset password tidak valid.');
49
279
  }
50
280
  }
51
- async signInWithOtp(email, otp, name) {
281
+ async sendEmailVerification() {
282
+ const token = await this.getIdToken();
52
283
  try {
53
- const res = await this.client.post(`/api/project/${this.projectId}/auth/otp/login`, { email, otp, name });
54
- if (res.data.token) {
55
- this.token = res.data.token;
56
- this.persistence.saveSession(res.data.token, res.data.user);
57
- }
284
+ const endpoint = this.emulatorUrl || `/api/project/${this.projectId}/auth/email/send-verification`;
285
+ const res = await this.client.post(endpoint, {}, {
286
+ headers: { Authorization: `Bearer ${token}` }
287
+ });
58
288
  return res.data;
59
289
  }
60
290
  catch (err) {
61
- throw (0, NexaError_1.toNexaError)(err);
291
+ throw new NexaAuthError_1.NexaAuthError('auth/unauthenticated', 'Gagal mengirim email verifikasi.');
62
292
  }
63
293
  }
64
- signOut() {
65
- this.token = null;
66
- this.persistence.clearSession();
294
+ // --- User Profile & Account Management ---
295
+ async updateProfile(update) {
296
+ const token = await this.getIdToken();
297
+ try {
298
+ const endpoint = this.emulatorUrl || `/api/project/${this.projectId}/auth/profile`;
299
+ const res = await this.client.patch(endpoint, update, {
300
+ headers: { Authorization: `Bearer ${token}` }
301
+ });
302
+ const updatedUser = res.data;
303
+ if (this.currentSession) {
304
+ this.currentSession.user = updatedUser;
305
+ await this.persistence.saveSession(this.currentSession);
306
+ this.notifyListeners(updatedUser);
307
+ }
308
+ return updatedUser;
309
+ }
310
+ catch (err) {
311
+ throw new NexaAuthError_1.NexaAuthError('auth/invalid-argument', 'Gagal memperbarui profil.');
312
+ }
313
+ }
314
+ async updatePassword(newPassword) {
315
+ const token = await this.getIdToken();
316
+ try {
317
+ const endpoint = this.emulatorUrl || `/api/project/${this.projectId}/auth/password/update`;
318
+ await this.client.post(endpoint, { newPassword }, {
319
+ headers: { Authorization: `Bearer ${token}` }
320
+ });
321
+ }
322
+ catch (err) {
323
+ throw new NexaAuthError_1.NexaAuthError(err.response?.data?.code || 'auth/requires-recent-login', 'Gagal memperbarui password. Silakan re-autentikasi.', err.response?.status || 401);
324
+ }
325
+ }
326
+ async deleteUser() {
327
+ const token = await this.getIdToken();
328
+ try {
329
+ const endpoint = this.emulatorUrl || `/api/project/${this.projectId}/auth/user/delete`;
330
+ await this.client.delete(endpoint, {
331
+ headers: { Authorization: `Bearer ${token}` }
332
+ });
333
+ await this.signOut();
334
+ }
335
+ catch (err) {
336
+ throw new NexaAuthError_1.NexaAuthError('auth/requires-recent-login', 'Gagal menghapus akun.');
337
+ }
338
+ }
339
+ // --- Session Revocation & Sign Out ---
340
+ async revokeSession(sessionId) {
341
+ const token = await this.getIdToken();
342
+ try {
343
+ const endpoint = this.emulatorUrl || `/api/project/${this.projectId}/auth/session/revoke`;
344
+ await this.client.post(endpoint, { sessionId }, {
345
+ headers: { Authorization: `Bearer ${token}` }
346
+ });
347
+ }
348
+ catch (err) {
349
+ throw new NexaAuthError_1.NexaAuthError('auth/invalid-argument', 'Gagal mereduksi sesi.');
350
+ }
351
+ }
352
+ async revokeAllSessions() {
353
+ const token = await this.getIdToken();
354
+ try {
355
+ const endpoint = this.emulatorUrl || `/api/project/${this.projectId}/auth/session/revoke-all`;
356
+ await this.client.post(endpoint, {}, {
357
+ headers: { Authorization: `Bearer ${token}` }
358
+ });
359
+ await this.signOut();
360
+ }
361
+ catch (err) {
362
+ throw new NexaAuthError_1.NexaAuthError('auth/invalid-argument', 'Gagal mereduksi seluruh sesi.');
363
+ }
364
+ }
365
+ async signOut() {
366
+ this.clearAutoRefresh();
367
+ const oldSession = this.currentSession;
368
+ this.currentSession = null;
369
+ if (oldSession) {
370
+ try {
371
+ const endpoint = this.emulatorUrl || `/api/project/${this.projectId}/auth/logout`;
372
+ await this.client.post(endpoint, { refreshToken: oldSession.refreshToken });
373
+ }
374
+ catch {
375
+ // Non-blocking logout network failure
376
+ }
377
+ }
378
+ await this.persistence.clearSession();
379
+ this.notifyListeners(null);
67
380
  }
68
381
  getCurrentUser() {
69
- return this.persistence.getSavedUser();
382
+ return this.currentUser;
70
383
  }
71
384
  }
72
385
  exports.Auth = Auth;
@@ -0,0 +1,5 @@
1
+ export declare class NexaAuthError extends Error {
2
+ readonly code: string;
3
+ readonly status?: number;
4
+ constructor(code: string, message: string, status?: number);
5
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NexaAuthError = void 0;
4
+ class NexaAuthError extends Error {
5
+ constructor(code, message, status) {
6
+ super(message);
7
+ this.name = 'NexaAuthError';
8
+ this.code = code;
9
+ this.status = status;
10
+ if (typeof Error.captureStackTrace === 'function') {
11
+ Error.captureStackTrace(this, NexaAuthError);
12
+ }
13
+ }
14
+ }
15
+ exports.NexaAuthError = NexaAuthError;
@@ -1 +1,38 @@
1
- export type { User, AuthResponse } from '../types/index';
1
+ import { Timestamp } from '../firestore/Timestamp';
2
+ export interface User {
3
+ uid: string;
4
+ email: string | null;
5
+ emailVerified: boolean;
6
+ name: string | null;
7
+ photoURL: string | null;
8
+ phoneNumber: string | null;
9
+ disabled: boolean;
10
+ createdAt: Timestamp | string;
11
+ updatedAt: Timestamp | string;
12
+ lastLoginAt: Timestamp | string;
13
+ }
14
+ export interface AuthSession {
15
+ accessToken: string;
16
+ refreshToken: string;
17
+ accessTokenExpiresAt: number;
18
+ refreshTokenExpiresAt: number;
19
+ user: User;
20
+ }
21
+ export interface IdTokenResult {
22
+ token: string;
23
+ expirationTime: string;
24
+ authTime: string;
25
+ issuedAtTime: string;
26
+ signInProvider: string | null;
27
+ claims: Record<string, unknown>;
28
+ }
29
+ export interface UserProfileUpdate {
30
+ name?: string | null;
31
+ photoURL?: string | null;
32
+ }
33
+ export interface IAuthPersistence {
34
+ getSession(): Promise<AuthSession | null>;
35
+ saveSession(session: AuthSession): Promise<void>;
36
+ clearSession(): Promise<void>;
37
+ }
38
+ export type AuthStateCallback = (user: User | null) => void;
@@ -1,9 +1,11 @@
1
- import { User } from './authTypes';
2
- export declare class AuthPersistence {
1
+ import { AuthSession, IAuthPersistence } from './authTypes';
2
+ export declare class AuthPersistence implements IAuthPersistence {
3
3
  private projectId;
4
+ private storageKey;
4
5
  constructor(projectId: string);
5
- saveSession(token: string, user: User): void;
6
- clearSession(): void;
6
+ getSession(): Promise<AuthSession | null>;
7
+ saveSession(session: AuthSession): Promise<void>;
8
+ clearSession(): Promise<void>;
7
9
  getSavedToken(): string | null;
8
- getSavedUser(): User | null;
10
+ getSavedUser(): AuthSession['user'] | null;
9
11
  }
@@ -4,31 +4,60 @@ exports.AuthPersistence = void 0;
4
4
  class AuthPersistence {
5
5
  constructor(projectId) {
6
6
  this.projectId = projectId;
7
+ this.storageKey = `nexa_session_${projectId}`;
7
8
  }
8
- saveSession(token, user) {
9
+ async getSession() {
10
+ if (typeof window === 'undefined' || !window.localStorage) {
11
+ return null;
12
+ }
13
+ try {
14
+ const item = localStorage.getItem(this.storageKey);
15
+ if (!item)
16
+ return null;
17
+ return JSON.parse(item);
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ }
23
+ async saveSession(session) {
9
24
  if (typeof window !== 'undefined' && window.localStorage) {
10
- localStorage.setItem(`nexa_token_${this.projectId}`, token);
11
- localStorage.setItem(`nexa_user_${this.projectId}`, JSON.stringify(user));
25
+ localStorage.setItem(this.storageKey, JSON.stringify(session));
12
26
  }
13
27
  }
14
- clearSession() {
28
+ async clearSession() {
15
29
  if (typeof window !== 'undefined' && window.localStorage) {
16
- localStorage.removeItem(`nexa_token_${this.projectId}`);
17
- localStorage.removeItem(`nexa_user_${this.projectId}`);
30
+ localStorage.removeItem(this.storageKey);
18
31
  }
19
32
  }
33
+ // Helper backward compatibility
20
34
  getSavedToken() {
21
- if (typeof window !== 'undefined' && window.localStorage) {
22
- return localStorage.getItem(`nexa_token_${this.projectId}`);
35
+ if (typeof window === 'undefined' || !window.localStorage)
36
+ return null;
37
+ try {
38
+ const session = localStorage.getItem(this.storageKey);
39
+ if (!session)
40
+ return null;
41
+ const parsed = JSON.parse(session);
42
+ return parsed.accessToken || null;
43
+ }
44
+ catch {
45
+ return null;
23
46
  }
24
- return null;
25
47
  }
26
48
  getSavedUser() {
27
- if (typeof window !== 'undefined' && window.localStorage) {
28
- const userStr = localStorage.getItem(`nexa_user_${this.projectId}`);
29
- return userStr ? JSON.parse(userStr) : null;
49
+ if (typeof window === 'undefined' || !window.localStorage)
50
+ return null;
51
+ try {
52
+ const session = localStorage.getItem(this.storageKey);
53
+ if (!session)
54
+ return null;
55
+ const parsed = JSON.parse(session);
56
+ return parsed.user || null;
57
+ }
58
+ catch {
59
+ return null;
30
60
  }
31
- return null;
32
61
  }
33
62
  }
34
63
  exports.AuthPersistence = AuthPersistence;
@@ -0,0 +1,93 @@
1
+ import { NexaApp } from '../app/initializeApp';
2
+ export interface NexaSDKOptions {
3
+ app?: NexaApp;
4
+ baseURL?: string;
5
+ }
6
+ export interface AdjustStockParams {
7
+ productId: string;
8
+ delta: number;
9
+ reason?: string;
10
+ referenceId?: string;
11
+ }
12
+ export interface ScanResiParams {
13
+ noResi: string;
14
+ orderId?: string;
15
+ courier?: string;
16
+ }
17
+ export interface CancelOrderParams {
18
+ orderId: string;
19
+ reason?: string;
20
+ }
21
+ export interface TransferParams {
22
+ fromUserId: string;
23
+ toUserId: string;
24
+ amount: number;
25
+ currency?: string;
26
+ note?: string;
27
+ }
28
+ export interface IncrementCounterParams {
29
+ docPath: string;
30
+ field: string;
31
+ amount?: number;
32
+ }
33
+ /**
34
+ * Universal NexaBase Client SDK Facade (Domain & Server-Authoritative Logic)
35
+ * Decouples client components & stores from raw primitives like getDocs/runTransaction.
36
+ */
37
+ export declare class NexaSDKFacade {
38
+ private appInstance;
39
+ private customBaseURL;
40
+ constructor(options?: NexaSDKOptions);
41
+ private getApp;
42
+ private getBaseURL;
43
+ /**
44
+ * Universal Server-Authoritative Cloud Function Caller
45
+ */
46
+ call<T = any>(functionName: string, data?: any): Promise<T>;
47
+ products: {
48
+ /**
49
+ * Atomically adjust product stock in server with audit log trail
50
+ */
51
+ adjustStock: (params: AdjustStockParams) => Promise<any>;
52
+ /**
53
+ * Quick stock deduction helper
54
+ */
55
+ deductStock: (productId: string, qty: number, reason?: string) => Promise<any>;
56
+ /**
57
+ * Quick stock addition helper
58
+ */
59
+ addStock: (productId: string, qty: number, reason?: string) => Promise<any>;
60
+ };
61
+ orders: {
62
+ /**
63
+ * Scan tracking number (resi), update shipping status & auto-deduct stock atomically
64
+ */
65
+ scanResi: (params: ScanResiParams) => Promise<any>;
66
+ /**
67
+ * Cancel an order & automatically return deducted stock to inventory
68
+ */
69
+ cancelOrder: (params: CancelOrderParams) => Promise<any>;
70
+ };
71
+ finance: {
72
+ /**
73
+ * Atomic balance transfer between users with ledger record
74
+ */
75
+ transferBalance: (params: TransferParams) => Promise<any>;
76
+ };
77
+ utils: {
78
+ /**
79
+ * Atomic field incrementer on any Firestore document
80
+ */
81
+ increment: (params: IncrementCounterParams) => Promise<any>;
82
+ };
83
+ }
84
+ /**
85
+ * Singleton Default Facade Export
86
+ */
87
+ export declare const nexaSDK: NexaSDKFacade;
88
+ /**
89
+ * Factory function to create custom facade instance with specific NexaApp
90
+ */
91
+ export declare function createNexaSDK(app?: NexaApp, options?: {
92
+ baseURL?: string;
93
+ }): NexaSDKFacade;