@a-cube-io/ereceipts-js-sdk 2.0.2 → 2.0.4
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 +16 -0
- package/dist/index.cjs.js +839 -301
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +88 -31
- package/dist/index.esm.js +839 -301
- package/dist/index.esm.js.map +1 -1
- package/dist/index.native.js +839 -301
- package/dist/index.native.js.map +1 -1
- package/package.json +1 -1
package/dist/index.native.js
CHANGED
|
@@ -2459,7 +2459,7 @@ function formatDecimal(value, decimals = 2) {
|
|
|
2459
2459
|
return num.toFixed(decimals);
|
|
2460
2460
|
}
|
|
2461
2461
|
|
|
2462
|
-
const log$
|
|
2462
|
+
const log$f = createPrefixedLogger('MTLS-HANDLER');
|
|
2463
2463
|
class MtlsAuthHandler {
|
|
2464
2464
|
constructor(mtlsAdapter, certificatePort) {
|
|
2465
2465
|
this.mtlsAdapter = mtlsAdapter;
|
|
@@ -2501,7 +2501,7 @@ class MtlsAuthHandler {
|
|
|
2501
2501
|
async makeRequest(url, config, jwtToken) {
|
|
2502
2502
|
const requestKey = this.generateRequestKey(url, config, jwtToken);
|
|
2503
2503
|
if (this.pendingRequests.has(requestKey)) {
|
|
2504
|
-
log$
|
|
2504
|
+
log$f.debug('Deduplicating concurrent request:', url);
|
|
2505
2505
|
return this.pendingRequests.get(requestKey);
|
|
2506
2506
|
}
|
|
2507
2507
|
const requestPromise = this.executeRequest(url, config, jwtToken, false);
|
|
@@ -2525,10 +2525,10 @@ class MtlsAuthHandler {
|
|
|
2525
2525
|
};
|
|
2526
2526
|
if (jwtToken) {
|
|
2527
2527
|
headers['Authorization'] = `Bearer ${jwtToken}`;
|
|
2528
|
-
log$
|
|
2528
|
+
log$f.debug('JWT token present:', jwtToken.substring(0, 20) + '...');
|
|
2529
2529
|
}
|
|
2530
2530
|
else {
|
|
2531
|
-
log$
|
|
2531
|
+
log$f.warn('No JWT token provided for mTLS request');
|
|
2532
2532
|
}
|
|
2533
2533
|
const fullUrl = this.constructMtlsUrl(url);
|
|
2534
2534
|
const mtlsConfig = {
|
|
@@ -2539,25 +2539,25 @@ class MtlsAuthHandler {
|
|
|
2539
2539
|
timeout: config.timeout,
|
|
2540
2540
|
responseType: config.responseType,
|
|
2541
2541
|
};
|
|
2542
|
-
log$
|
|
2543
|
-
log$
|
|
2542
|
+
log$f.debug('header-mtls', headers);
|
|
2543
|
+
log$f.debug(`${config.method} ${fullUrl}`);
|
|
2544
2544
|
if (config.data) {
|
|
2545
|
-
log$
|
|
2545
|
+
log$f.debug('Request body:', config.data);
|
|
2546
2546
|
}
|
|
2547
2547
|
try {
|
|
2548
2548
|
const response = await this.mtlsAdapter.request(mtlsConfig);
|
|
2549
|
-
log$
|
|
2549
|
+
log$f.debug(`Response ${response.status} from ${fullUrl}`);
|
|
2550
2550
|
if (response.data) {
|
|
2551
|
-
log$
|
|
2551
|
+
log$f.debug('Response body:', response.data);
|
|
2552
2552
|
}
|
|
2553
2553
|
return response.data;
|
|
2554
2554
|
}
|
|
2555
2555
|
catch (error) {
|
|
2556
|
-
log$
|
|
2556
|
+
log$f.error(`Response error from ${fullUrl}:`, error);
|
|
2557
2557
|
if (error && typeof error === 'object' && 'response' in error) {
|
|
2558
2558
|
const axiosError = error;
|
|
2559
2559
|
if (axiosError.response?.data) {
|
|
2560
|
-
log$
|
|
2560
|
+
log$f.error('Response body:', axiosError.response.data);
|
|
2561
2561
|
}
|
|
2562
2562
|
}
|
|
2563
2563
|
if (isRetryAttempt) {
|
|
@@ -2567,7 +2567,7 @@ class MtlsAuthHandler {
|
|
|
2567
2567
|
if (!shouldRetry) {
|
|
2568
2568
|
throw error;
|
|
2569
2569
|
}
|
|
2570
|
-
log$
|
|
2570
|
+
log$f.debug('Request failed, reconfiguring certificate and retrying...');
|
|
2571
2571
|
try {
|
|
2572
2572
|
await this.configureCertificate(certificate);
|
|
2573
2573
|
return await this.executeRequest(url, config, jwtToken, true);
|
|
@@ -3294,7 +3294,7 @@ function decompressData(data, compressed) {
|
|
|
3294
3294
|
return new CompressionAdapter().decompress(data, compressed);
|
|
3295
3295
|
}
|
|
3296
3296
|
|
|
3297
|
-
const log$
|
|
3297
|
+
const log$e = createPrefixedLogger('CACHE-RN');
|
|
3298
3298
|
/**
|
|
3299
3299
|
* React Native cache adapter using SQLite (Expo or react-native-sqlite-storage)
|
|
3300
3300
|
* Cache never expires - data persists until explicitly invalidated
|
|
@@ -3378,26 +3378,26 @@ class ReactNativeCacheAdapter {
|
|
|
3378
3378
|
await this.runMigrations();
|
|
3379
3379
|
}
|
|
3380
3380
|
async runMigrations() {
|
|
3381
|
-
log$
|
|
3381
|
+
log$e.debug('Running database migrations...');
|
|
3382
3382
|
try {
|
|
3383
3383
|
// Check if compressed column exists
|
|
3384
3384
|
this.hasCompressedColumn = await this.checkColumnExists('compressed');
|
|
3385
3385
|
if (!this.hasCompressedColumn) {
|
|
3386
|
-
log$
|
|
3386
|
+
log$e.debug('Adding compressed column to cache table');
|
|
3387
3387
|
const addColumnSQL = `ALTER TABLE ${ReactNativeCacheAdapter.TABLE_NAME} ADD COLUMN compressed INTEGER DEFAULT 0`;
|
|
3388
3388
|
await this.executeSql(addColumnSQL);
|
|
3389
3389
|
this.hasCompressedColumn = true;
|
|
3390
|
-
log$
|
|
3390
|
+
log$e.debug('Successfully added compressed column');
|
|
3391
3391
|
}
|
|
3392
3392
|
else {
|
|
3393
|
-
log$
|
|
3393
|
+
log$e.debug('Compressed column already exists');
|
|
3394
3394
|
}
|
|
3395
|
-
log$
|
|
3395
|
+
log$e.debug('Database migrations completed', {
|
|
3396
3396
|
hasCompressedColumn: this.hasCompressedColumn,
|
|
3397
3397
|
});
|
|
3398
3398
|
}
|
|
3399
3399
|
catch (error) {
|
|
3400
|
-
log$
|
|
3400
|
+
log$e.debug('Migration failed, disabling compression features', error);
|
|
3401
3401
|
this.hasCompressedColumn = false;
|
|
3402
3402
|
// Don't throw - allow the app to continue even if migration fails
|
|
3403
3403
|
// The compressed feature will just be disabled
|
|
@@ -3408,20 +3408,20 @@ class ReactNativeCacheAdapter {
|
|
|
3408
3408
|
const pragmaSQL = `PRAGMA table_info(${ReactNativeCacheAdapter.TABLE_NAME})`;
|
|
3409
3409
|
const results = await this.executeSql(pragmaSQL);
|
|
3410
3410
|
const columns = this.normalizeResults(results);
|
|
3411
|
-
log$
|
|
3411
|
+
log$e.debug('Table columns found', { columns: columns.map((c) => c.name) });
|
|
3412
3412
|
return columns.some((column) => column.name === columnName);
|
|
3413
3413
|
}
|
|
3414
3414
|
catch (error) {
|
|
3415
|
-
log$
|
|
3415
|
+
log$e.debug('Error checking column existence', error);
|
|
3416
3416
|
return false;
|
|
3417
3417
|
}
|
|
3418
3418
|
}
|
|
3419
3419
|
async get(key) {
|
|
3420
3420
|
await this.ensureInitialized();
|
|
3421
3421
|
const sql = `SELECT * FROM ${ReactNativeCacheAdapter.TABLE_NAME} WHERE cache_key = ?`;
|
|
3422
|
-
log$
|
|
3422
|
+
log$e.debug('Executing get query', { sql, key });
|
|
3423
3423
|
const results = await this.executeSql(sql, [key]);
|
|
3424
|
-
log$
|
|
3424
|
+
log$e.debug('Get query results', { key, hasResults: !!results });
|
|
3425
3425
|
// Normalize results from different SQLite implementations
|
|
3426
3426
|
const rows = this.normalizeResults(results);
|
|
3427
3427
|
if (!rows || rows.length === 0) {
|
|
@@ -3444,7 +3444,7 @@ class ReactNativeCacheAdapter {
|
|
|
3444
3444
|
data,
|
|
3445
3445
|
timestamp: Date.now(),
|
|
3446
3446
|
};
|
|
3447
|
-
log$
|
|
3447
|
+
log$e.debug('Setting cache item', { key });
|
|
3448
3448
|
return this.setItem(key, item);
|
|
3449
3449
|
}
|
|
3450
3450
|
async setItem(key, item) {
|
|
@@ -3457,7 +3457,7 @@ class ReactNativeCacheAdapter {
|
|
|
3457
3457
|
const compressionResult = compressData(serializedData, this.options.compressionThreshold);
|
|
3458
3458
|
finalData = compressionResult.data;
|
|
3459
3459
|
isCompressed = compressionResult.compressed;
|
|
3460
|
-
log$
|
|
3460
|
+
log$e.debug('Compression result', {
|
|
3461
3461
|
key,
|
|
3462
3462
|
originalSize: compressionResult.originalSize,
|
|
3463
3463
|
compressedSize: compressionResult.compressedSize,
|
|
@@ -3465,7 +3465,7 @@ class ReactNativeCacheAdapter {
|
|
|
3465
3465
|
savings: compressionResult.originalSize - compressionResult.compressedSize,
|
|
3466
3466
|
});
|
|
3467
3467
|
}
|
|
3468
|
-
log$
|
|
3468
|
+
log$e.debug('Setting item with metadata', {
|
|
3469
3469
|
key,
|
|
3470
3470
|
timestamp: item.timestamp,
|
|
3471
3471
|
compressed: isCompressed,
|
|
@@ -3491,14 +3491,14 @@ class ReactNativeCacheAdapter {
|
|
|
3491
3491
|
`;
|
|
3492
3492
|
params = [key, finalData, item.timestamp];
|
|
3493
3493
|
}
|
|
3494
|
-
log$
|
|
3494
|
+
log$e.debug('Executing setItem SQL', { key, paramsCount: params.length });
|
|
3495
3495
|
await this.executeSql(sql, params);
|
|
3496
3496
|
}
|
|
3497
3497
|
async setBatch(items) {
|
|
3498
3498
|
if (items.length === 0)
|
|
3499
3499
|
return;
|
|
3500
3500
|
await this.ensureInitialized();
|
|
3501
|
-
log$
|
|
3501
|
+
log$e.debug('Batch setting items', { count: items.length });
|
|
3502
3502
|
if (this.isExpo) {
|
|
3503
3503
|
await this.db.withTransactionAsync(async () => {
|
|
3504
3504
|
for (const [key, item] of items) {
|
|
@@ -3516,7 +3516,7 @@ class ReactNativeCacheAdapter {
|
|
|
3516
3516
|
}, reject, () => resolve());
|
|
3517
3517
|
});
|
|
3518
3518
|
}
|
|
3519
|
-
log$
|
|
3519
|
+
log$e.debug('Batch operation completed', { count: items.length });
|
|
3520
3520
|
}
|
|
3521
3521
|
async setBatchItem(key, item) {
|
|
3522
3522
|
// Handle compression if enabled and compressed column is available
|
|
@@ -3690,10 +3690,10 @@ class MemoryCacheAdapter {
|
|
|
3690
3690
|
return keySize + itemSize;
|
|
3691
3691
|
}
|
|
3692
3692
|
async get(key) {
|
|
3693
|
-
log$
|
|
3693
|
+
log$e.debug('Getting cache item', { key });
|
|
3694
3694
|
const item = this.cache.get(key);
|
|
3695
3695
|
if (!item) {
|
|
3696
|
-
log$
|
|
3696
|
+
log$e.debug('Cache miss', { key });
|
|
3697
3697
|
return null;
|
|
3698
3698
|
}
|
|
3699
3699
|
// Handle decompression if needed
|
|
@@ -3703,7 +3703,7 @@ class MemoryCacheAdapter {
|
|
|
3703
3703
|
const decompressed = decompressData(item.data, true);
|
|
3704
3704
|
finalData = JSON.parse(decompressed.data);
|
|
3705
3705
|
}
|
|
3706
|
-
log$
|
|
3706
|
+
log$e.debug('Cache hit', { key, compressed: isCompressed });
|
|
3707
3707
|
return {
|
|
3708
3708
|
...item,
|
|
3709
3709
|
data: finalData,
|
|
@@ -3711,7 +3711,7 @@ class MemoryCacheAdapter {
|
|
|
3711
3711
|
};
|
|
3712
3712
|
}
|
|
3713
3713
|
async set(key, data) {
|
|
3714
|
-
log$
|
|
3714
|
+
log$e.debug('Setting cache item', { key });
|
|
3715
3715
|
// Handle compression if enabled
|
|
3716
3716
|
let finalData = data;
|
|
3717
3717
|
let isCompressed = false;
|
|
@@ -3721,7 +3721,7 @@ class MemoryCacheAdapter {
|
|
|
3721
3721
|
if (compressionResult.compressed) {
|
|
3722
3722
|
finalData = compressionResult.data;
|
|
3723
3723
|
isCompressed = true;
|
|
3724
|
-
log$
|
|
3724
|
+
log$e.debug('Compression result', {
|
|
3725
3725
|
key,
|
|
3726
3726
|
originalSize: compressionResult.originalSize,
|
|
3727
3727
|
compressedSize: compressionResult.compressedSize,
|
|
@@ -3753,13 +3753,13 @@ class MemoryCacheAdapter {
|
|
|
3753
3753
|
const oldestItemSize = this.calculateItemSize(oldestKey, oldestItem);
|
|
3754
3754
|
this.totalBytes -= oldestItemSize;
|
|
3755
3755
|
this.cache.delete(oldestKey);
|
|
3756
|
-
log$
|
|
3756
|
+
log$e.debug('Removed oldest item for capacity', { oldestKey, freedBytes: oldestItemSize });
|
|
3757
3757
|
}
|
|
3758
3758
|
}
|
|
3759
3759
|
// Set new item and update total size
|
|
3760
3760
|
this.cache.set(key, item);
|
|
3761
3761
|
this.totalBytes += newItemSize;
|
|
3762
|
-
log$
|
|
3762
|
+
log$e.debug('Updated cache size', {
|
|
3763
3763
|
entries: this.cache.size,
|
|
3764
3764
|
totalBytes: this.totalBytes,
|
|
3765
3765
|
newItemSize,
|
|
@@ -3768,7 +3768,7 @@ class MemoryCacheAdapter {
|
|
|
3768
3768
|
async setBatch(items) {
|
|
3769
3769
|
if (items.length === 0)
|
|
3770
3770
|
return;
|
|
3771
|
-
log$
|
|
3771
|
+
log$e.debug('Batch setting items', { count: items.length });
|
|
3772
3772
|
let totalNewBytes = 0;
|
|
3773
3773
|
let totalOldBytes = 0;
|
|
3774
3774
|
const itemsToRemove = [];
|
|
@@ -3797,7 +3797,7 @@ class MemoryCacheAdapter {
|
|
|
3797
3797
|
itemsToRemove.push(oldKey);
|
|
3798
3798
|
}
|
|
3799
3799
|
if (itemsToRemove.length > 0) {
|
|
3800
|
-
log$
|
|
3800
|
+
log$e.debug('Removed items for batch capacity', {
|
|
3801
3801
|
removedCount: itemsToRemove.length,
|
|
3802
3802
|
removedKeys: itemsToRemove,
|
|
3803
3803
|
});
|
|
@@ -3809,7 +3809,7 @@ class MemoryCacheAdapter {
|
|
|
3809
3809
|
for (const [key, item] of items) {
|
|
3810
3810
|
this.cache.set(key, item);
|
|
3811
3811
|
}
|
|
3812
|
-
log$
|
|
3812
|
+
log$e.debug('Batch operation completed', {
|
|
3813
3813
|
count: items.length,
|
|
3814
3814
|
totalBytes: this.totalBytes,
|
|
3815
3815
|
entries: this.cache.size,
|
|
@@ -3831,7 +3831,7 @@ class MemoryCacheAdapter {
|
|
|
3831
3831
|
}
|
|
3832
3832
|
}
|
|
3833
3833
|
if (removed > 0) {
|
|
3834
|
-
log$
|
|
3834
|
+
log$e.debug('Invalidation completed', {
|
|
3835
3835
|
pattern,
|
|
3836
3836
|
entriesRemoved: removed,
|
|
3837
3837
|
bytesFreed,
|
|
@@ -3843,7 +3843,7 @@ class MemoryCacheAdapter {
|
|
|
3843
3843
|
async clear() {
|
|
3844
3844
|
this.cache.clear();
|
|
3845
3845
|
this.totalBytes = 0;
|
|
3846
|
-
log$
|
|
3846
|
+
log$e.debug('Cache cleared', { entries: 0, bytes: 0 });
|
|
3847
3847
|
}
|
|
3848
3848
|
async getSize() {
|
|
3849
3849
|
return {
|
|
@@ -4174,7 +4174,7 @@ replaceTraps((oldTraps) => ({
|
|
|
4174
4174
|
},
|
|
4175
4175
|
}));
|
|
4176
4176
|
|
|
4177
|
-
const log$
|
|
4177
|
+
const log$d = createPrefixedLogger('CACHE-WEB');
|
|
4178
4178
|
/**
|
|
4179
4179
|
* Web cache adapter using IndexedDB with automatic error recovery
|
|
4180
4180
|
* Cache never expires - data persists until explicitly invalidated
|
|
@@ -4197,18 +4197,18 @@ class WebCacheAdapter {
|
|
|
4197
4197
|
async initialize() {
|
|
4198
4198
|
if (this.db)
|
|
4199
4199
|
return;
|
|
4200
|
-
log$
|
|
4200
|
+
log$d.debug('Initializing IndexedDB cache', {
|
|
4201
4201
|
dbName: WebCacheAdapter.DB_NAME,
|
|
4202
4202
|
version: WebCacheAdapter.DB_VERSION,
|
|
4203
4203
|
});
|
|
4204
4204
|
try {
|
|
4205
4205
|
this.db = await this.openDatabase();
|
|
4206
|
-
log$
|
|
4206
|
+
log$d.debug('IndexedDB cache initialized successfully');
|
|
4207
4207
|
this.retryCount = 0; // Reset retry count on success
|
|
4208
4208
|
}
|
|
4209
4209
|
catch (error) {
|
|
4210
4210
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
4211
|
-
log$
|
|
4211
|
+
log$d.debug('Failed to initialize IndexedDB', { error: errorMessage });
|
|
4212
4212
|
// Check if this is a version conflict error
|
|
4213
4213
|
if (this.isVersionConflictError(errorMessage)) {
|
|
4214
4214
|
await this.handleVersionConflict();
|
|
@@ -4221,32 +4221,32 @@ class WebCacheAdapter {
|
|
|
4221
4221
|
async openDatabase() {
|
|
4222
4222
|
return await openDB(WebCacheAdapter.DB_NAME, WebCacheAdapter.DB_VERSION, {
|
|
4223
4223
|
upgrade: (db, oldVersion, newVersion, transaction) => {
|
|
4224
|
-
log$
|
|
4224
|
+
log$d.debug('Database upgrade needed', { oldVersion, newVersion });
|
|
4225
4225
|
this.handleUpgrade(db, oldVersion, newVersion, transaction);
|
|
4226
4226
|
},
|
|
4227
4227
|
blocked: () => {
|
|
4228
|
-
log$
|
|
4228
|
+
log$d.debug('Database blocked - another tab may be open');
|
|
4229
4229
|
},
|
|
4230
4230
|
blocking: () => {
|
|
4231
|
-
log$
|
|
4231
|
+
log$d.debug('Database blocking - will close connection');
|
|
4232
4232
|
if (this.db) {
|
|
4233
4233
|
this.db.close();
|
|
4234
4234
|
this.db = null;
|
|
4235
4235
|
}
|
|
4236
4236
|
},
|
|
4237
4237
|
terminated: () => {
|
|
4238
|
-
log$
|
|
4238
|
+
log$d.debug('Database connection terminated unexpectedly');
|
|
4239
4239
|
this.db = null;
|
|
4240
4240
|
},
|
|
4241
4241
|
});
|
|
4242
4242
|
}
|
|
4243
4243
|
handleUpgrade(db, oldVersion, newVersion, transaction) {
|
|
4244
|
-
log$
|
|
4244
|
+
log$d.debug('Handling database upgrade', { oldVersion, newVersion });
|
|
4245
4245
|
// Create cache store if it doesn't exist (initial setup)
|
|
4246
4246
|
if (!db.objectStoreNames.contains(WebCacheAdapter.STORE_NAME)) {
|
|
4247
4247
|
const store = db.createObjectStore(WebCacheAdapter.STORE_NAME, { keyPath: 'key' });
|
|
4248
4248
|
store.createIndex('timestamp', 'timestamp', { unique: false });
|
|
4249
|
-
log$
|
|
4249
|
+
log$d.debug('Created cache store and timestamp index');
|
|
4250
4250
|
}
|
|
4251
4251
|
// Handle migration from version 1 to 2
|
|
4252
4252
|
if (oldVersion < 2) {
|
|
@@ -4257,16 +4257,16 @@ class WebCacheAdapter {
|
|
|
4257
4257
|
try {
|
|
4258
4258
|
if (store.indexNames.contains(indexName)) {
|
|
4259
4259
|
store.deleteIndex(indexName);
|
|
4260
|
-
log$
|
|
4260
|
+
log$d.debug(`Removed unused index: ${indexName}`);
|
|
4261
4261
|
}
|
|
4262
4262
|
}
|
|
4263
4263
|
catch (error) {
|
|
4264
4264
|
// Ignore errors if indexes don't exist
|
|
4265
|
-
log$
|
|
4265
|
+
log$d.debug(`Warning: Could not remove index ${indexName}`, error);
|
|
4266
4266
|
}
|
|
4267
4267
|
});
|
|
4268
4268
|
}
|
|
4269
|
-
log$
|
|
4269
|
+
log$d.debug('Database upgrade completed');
|
|
4270
4270
|
}
|
|
4271
4271
|
isVersionConflictError(errorMessage) {
|
|
4272
4272
|
return (errorMessage.includes('less than the existing version') ||
|
|
@@ -4274,7 +4274,7 @@ class WebCacheAdapter {
|
|
|
4274
4274
|
errorMessage.includes('VersionError'));
|
|
4275
4275
|
}
|
|
4276
4276
|
async handleVersionConflict() {
|
|
4277
|
-
log$
|
|
4277
|
+
log$d.debug('Handling version conflict, attempting recovery...');
|
|
4278
4278
|
if (this.retryCount >= this.maxRetries) {
|
|
4279
4279
|
throw new Error('Failed to resolve IndexedDB version conflict after multiple attempts');
|
|
4280
4280
|
}
|
|
@@ -4286,19 +4286,19 @@ class WebCacheAdapter {
|
|
|
4286
4286
|
this.db = null;
|
|
4287
4287
|
}
|
|
4288
4288
|
// Delete the problematic database
|
|
4289
|
-
log$
|
|
4289
|
+
log$d.debug('Deleting problematic database to resolve version conflict');
|
|
4290
4290
|
await deleteDB(WebCacheAdapter.DB_NAME);
|
|
4291
4291
|
// Wait a bit for the deletion to complete
|
|
4292
4292
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
4293
4293
|
// Try to open the database again
|
|
4294
|
-
log$
|
|
4294
|
+
log$d.debug(`Retrying database initialization (attempt ${this.retryCount}/${this.maxRetries})`);
|
|
4295
4295
|
this.db = await this.openDatabase();
|
|
4296
|
-
log$
|
|
4296
|
+
log$d.debug('Successfully recovered from version conflict');
|
|
4297
4297
|
this.retryCount = 0; // Reset retry count on success
|
|
4298
4298
|
}
|
|
4299
4299
|
catch (retryError) {
|
|
4300
4300
|
const retryErrorMessage = retryError instanceof Error ? retryError.message : 'Unknown error';
|
|
4301
|
-
log$
|
|
4301
|
+
log$d.debug('Recovery attempt failed', { attempt: this.retryCount, error: retryErrorMessage });
|
|
4302
4302
|
if (this.retryCount < this.maxRetries) {
|
|
4303
4303
|
// Try again
|
|
4304
4304
|
await this.handleVersionConflict();
|
|
@@ -4310,7 +4310,7 @@ class WebCacheAdapter {
|
|
|
4310
4310
|
}
|
|
4311
4311
|
async get(key) {
|
|
4312
4312
|
await this.ensureInitialized();
|
|
4313
|
-
log$
|
|
4313
|
+
log$d.debug('Getting cache item', { key });
|
|
4314
4314
|
try {
|
|
4315
4315
|
const transaction = this.db.transaction([WebCacheAdapter.STORE_NAME], 'readonly');
|
|
4316
4316
|
const store = transaction.objectStore(WebCacheAdapter.STORE_NAME);
|
|
@@ -4336,7 +4336,7 @@ class WebCacheAdapter {
|
|
|
4336
4336
|
};
|
|
4337
4337
|
}
|
|
4338
4338
|
catch (error) {
|
|
4339
|
-
log$
|
|
4339
|
+
log$d.debug('Error getting cache item', { key, error });
|
|
4340
4340
|
return null; // Return null on error instead of throwing
|
|
4341
4341
|
}
|
|
4342
4342
|
}
|
|
@@ -4358,7 +4358,7 @@ class WebCacheAdapter {
|
|
|
4358
4358
|
if (compressionResult.compressed) {
|
|
4359
4359
|
finalData = compressionResult.data;
|
|
4360
4360
|
isCompressed = true;
|
|
4361
|
-
log$
|
|
4361
|
+
log$d.debug('Compression result', {
|
|
4362
4362
|
key,
|
|
4363
4363
|
originalSize: compressionResult.originalSize,
|
|
4364
4364
|
compressedSize: compressionResult.compressedSize,
|
|
@@ -4367,7 +4367,7 @@ class WebCacheAdapter {
|
|
|
4367
4367
|
});
|
|
4368
4368
|
}
|
|
4369
4369
|
}
|
|
4370
|
-
log$
|
|
4370
|
+
log$d.debug('Setting cache item', { key, timestamp: item.timestamp, compressed: isCompressed });
|
|
4371
4371
|
const storedItem = {
|
|
4372
4372
|
key,
|
|
4373
4373
|
data: finalData,
|
|
@@ -4380,7 +4380,7 @@ class WebCacheAdapter {
|
|
|
4380
4380
|
await store.put(storedItem);
|
|
4381
4381
|
}
|
|
4382
4382
|
catch (error) {
|
|
4383
|
-
log$
|
|
4383
|
+
log$d.debug('Error setting cache item', { key, error });
|
|
4384
4384
|
// Silently fail for cache writes
|
|
4385
4385
|
}
|
|
4386
4386
|
}
|
|
@@ -4388,7 +4388,7 @@ class WebCacheAdapter {
|
|
|
4388
4388
|
if (items.length === 0)
|
|
4389
4389
|
return;
|
|
4390
4390
|
await this.ensureInitialized();
|
|
4391
|
-
log$
|
|
4391
|
+
log$d.debug('Batch setting items', { count: items.length });
|
|
4392
4392
|
try {
|
|
4393
4393
|
const transaction = this.db.transaction([WebCacheAdapter.STORE_NAME], 'readwrite');
|
|
4394
4394
|
const store = transaction.objectStore(WebCacheAdapter.STORE_NAME);
|
|
@@ -4398,10 +4398,10 @@ class WebCacheAdapter {
|
|
|
4398
4398
|
return store.put(storedItem);
|
|
4399
4399
|
});
|
|
4400
4400
|
await Promise.all(promises);
|
|
4401
|
-
log$
|
|
4401
|
+
log$d.debug('Batch operation completed', { count: items.length });
|
|
4402
4402
|
}
|
|
4403
4403
|
catch (error) {
|
|
4404
|
-
log$
|
|
4404
|
+
log$d.debug('Error in batch operation', { count: items.length, error });
|
|
4405
4405
|
// Silently fail for batch writes
|
|
4406
4406
|
}
|
|
4407
4407
|
}
|
|
@@ -4436,10 +4436,10 @@ class WebCacheAdapter {
|
|
|
4436
4436
|
const transaction = this.db.transaction([WebCacheAdapter.STORE_NAME], 'readwrite');
|
|
4437
4437
|
const store = transaction.objectStore(WebCacheAdapter.STORE_NAME);
|
|
4438
4438
|
await store.clear();
|
|
4439
|
-
log$
|
|
4439
|
+
log$d.debug('Cache cleared successfully');
|
|
4440
4440
|
}
|
|
4441
4441
|
catch (error) {
|
|
4442
|
-
log$
|
|
4442
|
+
log$d.debug('Error clearing cache', error);
|
|
4443
4443
|
// Silently fail for cache clear
|
|
4444
4444
|
}
|
|
4445
4445
|
}
|
|
@@ -4465,7 +4465,7 @@ class WebCacheAdapter {
|
|
|
4465
4465
|
};
|
|
4466
4466
|
}
|
|
4467
4467
|
catch (error) {
|
|
4468
|
-
log$
|
|
4468
|
+
log$d.debug('Error getting cache size', error);
|
|
4469
4469
|
return {
|
|
4470
4470
|
entries: 0,
|
|
4471
4471
|
bytes: 0,
|
|
@@ -4490,7 +4490,7 @@ class WebCacheAdapter {
|
|
|
4490
4490
|
return allKeys.filter((key) => regex.test(key));
|
|
4491
4491
|
}
|
|
4492
4492
|
catch (error) {
|
|
4493
|
-
log$
|
|
4493
|
+
log$d.debug('Error getting cache keys', error);
|
|
4494
4494
|
return [];
|
|
4495
4495
|
}
|
|
4496
4496
|
}
|
|
@@ -4503,7 +4503,7 @@ class WebCacheAdapter {
|
|
|
4503
4503
|
return true;
|
|
4504
4504
|
}
|
|
4505
4505
|
catch (error) {
|
|
4506
|
-
log$
|
|
4506
|
+
log$d.debug('Error deleting cache item', { key, error });
|
|
4507
4507
|
return false;
|
|
4508
4508
|
}
|
|
4509
4509
|
}
|
|
@@ -4515,7 +4515,7 @@ class WebCacheAdapter {
|
|
|
4515
4515
|
await this.initPromise;
|
|
4516
4516
|
}
|
|
4517
4517
|
catch (error) {
|
|
4518
|
-
log$
|
|
4518
|
+
log$d.debug('Failed to ensure initialization', error);
|
|
4519
4519
|
// Reset and try once more
|
|
4520
4520
|
this.initPromise = null;
|
|
4521
4521
|
this.db = null;
|
|
@@ -4534,7 +4534,7 @@ WebCacheAdapter.DB_NAME = 'acube_cache';
|
|
|
4534
4534
|
WebCacheAdapter.DB_VERSION = 2;
|
|
4535
4535
|
WebCacheAdapter.STORE_NAME = 'cache_entries';
|
|
4536
4536
|
|
|
4537
|
-
const log$
|
|
4537
|
+
const log$c = createPrefixedLogger('CACHE-LOADER');
|
|
4538
4538
|
function loadCacheAdapter(platform) {
|
|
4539
4539
|
try {
|
|
4540
4540
|
switch (platform) {
|
|
@@ -4566,7 +4566,7 @@ function loadCacheAdapter(platform) {
|
|
|
4566
4566
|
}
|
|
4567
4567
|
}
|
|
4568
4568
|
catch (error) {
|
|
4569
|
-
log$
|
|
4569
|
+
log$c.warn(`Cache adapter not available for platform ${platform}:`, error);
|
|
4570
4570
|
return undefined;
|
|
4571
4571
|
}
|
|
4572
4572
|
}
|
|
@@ -4623,7 +4623,7 @@ class BaseSecureStorageAdapter {
|
|
|
4623
4623
|
}
|
|
4624
4624
|
}
|
|
4625
4625
|
|
|
4626
|
-
const log$
|
|
4626
|
+
const log$b = createPrefixedLogger('NETWORK-BASE');
|
|
4627
4627
|
class NetworkBase {
|
|
4628
4628
|
constructor(initialOnline = true, debounceMs = 300) {
|
|
4629
4629
|
this.destroy$ = new Subject();
|
|
@@ -4643,14 +4643,14 @@ class NetworkBase {
|
|
|
4643
4643
|
const current = this.statusSubject.getValue();
|
|
4644
4644
|
if (current.online !== online) {
|
|
4645
4645
|
this.statusSubject.next({ online, timestamp: Date.now() });
|
|
4646
|
-
log$
|
|
4646
|
+
log$b.debug(`Network status changed: ${online ? 'online' : 'offline'}`);
|
|
4647
4647
|
}
|
|
4648
4648
|
}
|
|
4649
4649
|
destroy() {
|
|
4650
4650
|
this.destroy$.next();
|
|
4651
4651
|
this.destroy$.complete();
|
|
4652
4652
|
this.statusSubject.complete();
|
|
4653
|
-
log$
|
|
4653
|
+
log$b.debug('Network monitor destroyed');
|
|
4654
4654
|
}
|
|
4655
4655
|
}
|
|
4656
4656
|
|
|
@@ -4710,7 +4710,7 @@ class NodeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
4710
4710
|
}
|
|
4711
4711
|
}
|
|
4712
4712
|
|
|
4713
|
-
const log$
|
|
4713
|
+
const log$a = createPrefixedLogger('RN-STORAGE');
|
|
4714
4714
|
/**
|
|
4715
4715
|
* React Native storage adapter using AsyncStorage
|
|
4716
4716
|
* Note: Uses native batch operations for better performance (not base class)
|
|
@@ -4742,7 +4742,7 @@ class ReactNativeStorageAdapter {
|
|
|
4742
4742
|
return await this.AsyncStorage.getItem(key);
|
|
4743
4743
|
}
|
|
4744
4744
|
catch (error) {
|
|
4745
|
-
log$
|
|
4745
|
+
log$a.error('Failed to get item from AsyncStorage:', error);
|
|
4746
4746
|
return null;
|
|
4747
4747
|
}
|
|
4748
4748
|
}
|
|
@@ -4783,7 +4783,7 @@ class ReactNativeStorageAdapter {
|
|
|
4783
4783
|
return await this.AsyncStorage.getAllKeys();
|
|
4784
4784
|
}
|
|
4785
4785
|
catch (error) {
|
|
4786
|
-
log$
|
|
4786
|
+
log$a.error('Failed to get all keys:', error);
|
|
4787
4787
|
return [];
|
|
4788
4788
|
}
|
|
4789
4789
|
}
|
|
@@ -4800,7 +4800,7 @@ class ReactNativeStorageAdapter {
|
|
|
4800
4800
|
return result;
|
|
4801
4801
|
}
|
|
4802
4802
|
catch (error) {
|
|
4803
|
-
log$
|
|
4803
|
+
log$a.error('Failed to get multiple items:', error);
|
|
4804
4804
|
const result = {};
|
|
4805
4805
|
keys.forEach((key) => {
|
|
4806
4806
|
result[key] = null;
|
|
@@ -4850,7 +4850,7 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
4850
4850
|
return;
|
|
4851
4851
|
}
|
|
4852
4852
|
catch {
|
|
4853
|
-
log$
|
|
4853
|
+
log$a.debug('expo-secure-store not available, trying react-native-keychain');
|
|
4854
4854
|
}
|
|
4855
4855
|
try {
|
|
4856
4856
|
const Keychain = require('react-native-keychain');
|
|
@@ -4859,7 +4859,7 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
4859
4859
|
return;
|
|
4860
4860
|
}
|
|
4861
4861
|
catch {
|
|
4862
|
-
log$
|
|
4862
|
+
log$a.error('react-native-keychain not available');
|
|
4863
4863
|
}
|
|
4864
4864
|
throw new Error('No secure storage available. Please install expo-secure-store or react-native-keychain');
|
|
4865
4865
|
}
|
|
@@ -4877,7 +4877,7 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
4877
4877
|
}
|
|
4878
4878
|
}
|
|
4879
4879
|
catch (error) {
|
|
4880
|
-
log$
|
|
4880
|
+
log$a.error('Failed to get secure item:', error);
|
|
4881
4881
|
}
|
|
4882
4882
|
return null;
|
|
4883
4883
|
}
|
|
@@ -4914,10 +4914,10 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
4914
4914
|
}
|
|
4915
4915
|
}
|
|
4916
4916
|
async clear() {
|
|
4917
|
-
log$
|
|
4917
|
+
log$a.warn('Clear all secure items not fully implemented for React Native');
|
|
4918
4918
|
}
|
|
4919
4919
|
async getAllKeys() {
|
|
4920
|
-
log$
|
|
4920
|
+
log$a.warn('Get all secure keys not implemented for React Native');
|
|
4921
4921
|
return [];
|
|
4922
4922
|
}
|
|
4923
4923
|
async isAvailable() {
|
|
@@ -5135,7 +5135,7 @@ class NodeNetworkMonitor extends NetworkBase {
|
|
|
5135
5135
|
}
|
|
5136
5136
|
}
|
|
5137
5137
|
|
|
5138
|
-
const log$
|
|
5138
|
+
const log$9 = createPrefixedLogger('RN-NETWORK');
|
|
5139
5139
|
/**
|
|
5140
5140
|
* React Native network monitor using RxJS
|
|
5141
5141
|
* Supports both @react-native-community/netinfo and expo-network
|
|
@@ -5148,7 +5148,7 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5148
5148
|
this.moduleReady$ = new Subject();
|
|
5149
5149
|
this.isExpo = detectPlatform().isExpo;
|
|
5150
5150
|
this.init().catch((error) => {
|
|
5151
|
-
log$
|
|
5151
|
+
log$9.error('Network monitor initialization failed:', error);
|
|
5152
5152
|
});
|
|
5153
5153
|
}
|
|
5154
5154
|
async init() {
|
|
@@ -5167,10 +5167,10 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5167
5167
|
try {
|
|
5168
5168
|
const module = require('@react-native-community/netinfo');
|
|
5169
5169
|
this.netInfoModule = module.default || module;
|
|
5170
|
-
log$
|
|
5170
|
+
log$9.debug('Loaded @react-native-community/netinfo module');
|
|
5171
5171
|
}
|
|
5172
5172
|
catch (error) {
|
|
5173
|
-
log$
|
|
5173
|
+
log$9.error('Failed to load React Native NetInfo module:', error);
|
|
5174
5174
|
this.netInfoModule = null;
|
|
5175
5175
|
}
|
|
5176
5176
|
}
|
|
@@ -5178,10 +5178,10 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5178
5178
|
try {
|
|
5179
5179
|
const module = require('expo-network');
|
|
5180
5180
|
this.netInfoModule = module.default || module;
|
|
5181
|
-
log$
|
|
5181
|
+
log$9.debug('Loaded expo-network module');
|
|
5182
5182
|
}
|
|
5183
5183
|
catch (error) {
|
|
5184
|
-
log$
|
|
5184
|
+
log$9.error('Failed to load Expo Network module:', error);
|
|
5185
5185
|
this.netInfoModule = null;
|
|
5186
5186
|
}
|
|
5187
5187
|
}
|
|
@@ -5198,16 +5198,16 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5198
5198
|
}
|
|
5199
5199
|
const online = !!(state.isConnected && state.isInternetReachable !== false);
|
|
5200
5200
|
this.updateStatus(online);
|
|
5201
|
-
log$
|
|
5201
|
+
log$9.debug('Initial network state:', online ? 'online' : 'offline');
|
|
5202
5202
|
}
|
|
5203
5203
|
catch (error) {
|
|
5204
|
-
log$
|
|
5204
|
+
log$9.warn('Could not fetch initial network state:', error);
|
|
5205
5205
|
}
|
|
5206
5206
|
}
|
|
5207
5207
|
subscribeToStateChanges() {
|
|
5208
5208
|
if (!this.netInfoModule)
|
|
5209
5209
|
return;
|
|
5210
|
-
log$
|
|
5210
|
+
log$9.debug('Subscribing to network state changes');
|
|
5211
5211
|
const handleState = (state) => {
|
|
5212
5212
|
const online = !!(state.isConnected && (state.isInternetReachable ?? true));
|
|
5213
5213
|
this.updateStatus(online);
|
|
@@ -5249,7 +5249,7 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5249
5249
|
};
|
|
5250
5250
|
}
|
|
5251
5251
|
catch (error) {
|
|
5252
|
-
log$
|
|
5252
|
+
log$9.error('Failed to fetch detailed network info:', error);
|
|
5253
5253
|
return null;
|
|
5254
5254
|
}
|
|
5255
5255
|
}
|
|
@@ -5385,7 +5385,7 @@ class CertificateValidator {
|
|
|
5385
5385
|
}
|
|
5386
5386
|
}
|
|
5387
5387
|
|
|
5388
|
-
const log$
|
|
5388
|
+
const log$8 = createPrefixedLogger('RN-MTLS');
|
|
5389
5389
|
/**
|
|
5390
5390
|
* React Native mTLS Adapter using @a-cube-io/expo-mutual-tls
|
|
5391
5391
|
*/
|
|
@@ -5407,7 +5407,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5407
5407
|
this.expoMTLS = ExpoMutualTls;
|
|
5408
5408
|
// Set up debug logging with the correct event signature
|
|
5409
5409
|
const debugListener = ExpoMutualTls.onDebugLog((event) => {
|
|
5410
|
-
log$
|
|
5410
|
+
log$8.debug(`${event.type}: ${event.message}`, {
|
|
5411
5411
|
method: event.method,
|
|
5412
5412
|
url: event.url,
|
|
5413
5413
|
statusCode: event.statusCode,
|
|
@@ -5417,28 +5417,28 @@ class ReactNativeMTLSAdapter {
|
|
|
5417
5417
|
this.eventListeners.push(debugListener);
|
|
5418
5418
|
// Set up error logging with the correct event signature
|
|
5419
5419
|
const errorListener = ExpoMutualTls.onError((event) => {
|
|
5420
|
-
log$
|
|
5420
|
+
log$8.error(event.message, {
|
|
5421
5421
|
code: event.code,
|
|
5422
5422
|
});
|
|
5423
5423
|
});
|
|
5424
5424
|
this.eventListeners.push(errorListener);
|
|
5425
5425
|
// Set up certificate expiry monitoring with the correct event signature
|
|
5426
5426
|
const expiryListener = ExpoMutualTls.onCertificateExpiry((event) => {
|
|
5427
|
-
log$
|
|
5427
|
+
log$8.warn(`Certificate ${event.subject} expires at ${new Date(event.expiry)}`, {
|
|
5428
5428
|
alias: event.alias,
|
|
5429
5429
|
warning: event.warning,
|
|
5430
5430
|
});
|
|
5431
5431
|
});
|
|
5432
5432
|
this.eventListeners.push(expiryListener);
|
|
5433
|
-
log$
|
|
5433
|
+
log$8.debug('Expo mTLS module loaded successfully');
|
|
5434
5434
|
}
|
|
5435
5435
|
catch (error) {
|
|
5436
|
-
log$
|
|
5436
|
+
log$8.warn('@a-cube-io/expo-mutual-tls not available:', error);
|
|
5437
5437
|
}
|
|
5438
5438
|
}
|
|
5439
5439
|
async isMTLSSupported() {
|
|
5440
5440
|
const supported = this.expoMTLS !== null;
|
|
5441
|
-
log$
|
|
5441
|
+
log$8.debug('mTLS support check:', {
|
|
5442
5442
|
supported,
|
|
5443
5443
|
platform: this.getPlatformInfo().platform,
|
|
5444
5444
|
moduleAvailable: !!this.expoMTLS,
|
|
@@ -5450,7 +5450,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5450
5450
|
throw new MTLSError(MTLSErrorType$1.NOT_SUPPORTED, 'Expo mTLS module not available');
|
|
5451
5451
|
}
|
|
5452
5452
|
this.config = config;
|
|
5453
|
-
log$
|
|
5453
|
+
log$8.debug('Initialized with config:', {
|
|
5454
5454
|
baseUrl: config.baseUrl,
|
|
5455
5455
|
port: config.port,
|
|
5456
5456
|
timeout: config.timeout,
|
|
@@ -5464,7 +5464,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5464
5464
|
if (!this.config) {
|
|
5465
5465
|
throw new MTLSError(MTLSErrorType$1.CONFIGURATION_ERROR, 'Adapter not initialized. Call initialize() first.');
|
|
5466
5466
|
}
|
|
5467
|
-
log$
|
|
5467
|
+
log$8.debug('Configuring certificate:', {
|
|
5468
5468
|
format: certificateData.format,
|
|
5469
5469
|
hasPassword: !!certificateData.password,
|
|
5470
5470
|
certificateLength: certificateData.certificate.length,
|
|
@@ -5481,14 +5481,14 @@ class ReactNativeMTLSAdapter {
|
|
|
5481
5481
|
'client-key-service', // keyService
|
|
5482
5482
|
true // enableLogging - let the native module handle its own debug logging
|
|
5483
5483
|
);
|
|
5484
|
-
log$
|
|
5484
|
+
log$8.debug('PEM services configured:', configResult);
|
|
5485
5485
|
if (!configResult.success) {
|
|
5486
5486
|
throw new MTLSError(MTLSErrorType$1.CONFIGURATION_ERROR, `PEM configuration failed: ${configResult.state}`);
|
|
5487
5487
|
}
|
|
5488
5488
|
// Step 2: Store the actual PEM certificate and private key
|
|
5489
5489
|
const storeResult = await this.expoMTLS.storePEM(certificateData.certificate, certificateData.privateKey, certificateData.password // passphrase (optional)
|
|
5490
5490
|
);
|
|
5491
|
-
log$
|
|
5491
|
+
log$8.debug('PEM certificate store result:', storeResult);
|
|
5492
5492
|
if (!storeResult) {
|
|
5493
5493
|
throw new MTLSError(MTLSErrorType$1.CERTIFICATE_INVALID, 'Failed to store PEM certificate');
|
|
5494
5494
|
}
|
|
@@ -5501,14 +5501,14 @@ class ReactNativeMTLSAdapter {
|
|
|
5501
5501
|
const configResult = await this.expoMTLS.configureP12('client-p12-service', // keychainService
|
|
5502
5502
|
true // enableLogging - let the native module handle its own debug logging
|
|
5503
5503
|
);
|
|
5504
|
-
log$
|
|
5504
|
+
log$8.debug('P12 service configured:', configResult);
|
|
5505
5505
|
if (!configResult.success) {
|
|
5506
5506
|
throw new MTLSError(MTLSErrorType$1.CONFIGURATION_ERROR, `P12 configuration failed: ${configResult.state}`);
|
|
5507
5507
|
}
|
|
5508
5508
|
// Step 2: Store the P12 certificate data
|
|
5509
5509
|
const storeResult = await this.expoMTLS.storeP12(certificateData.certificate, // P12 data in certificate field
|
|
5510
5510
|
certificateData.password);
|
|
5511
|
-
log$
|
|
5511
|
+
log$8.debug('P12 certificate store result:', storeResult);
|
|
5512
5512
|
if (!storeResult) {
|
|
5513
5513
|
throw new MTLSError(MTLSErrorType$1.CERTIFICATE_INVALID, 'Failed to store P12 certificate');
|
|
5514
5514
|
}
|
|
@@ -5522,7 +5522,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5522
5522
|
if (error instanceof MTLSError) {
|
|
5523
5523
|
throw error;
|
|
5524
5524
|
}
|
|
5525
|
-
log$
|
|
5525
|
+
log$8.error('Certificate configuration failed:', error);
|
|
5526
5526
|
throw new MTLSError(MTLSErrorType$1.CONFIGURATION_ERROR, 'Failed to configure certificate', error);
|
|
5527
5527
|
}
|
|
5528
5528
|
}
|
|
@@ -5533,38 +5533,38 @@ class ReactNativeMTLSAdapter {
|
|
|
5533
5533
|
try {
|
|
5534
5534
|
// Use static method call
|
|
5535
5535
|
const hasCert = await this.expoMTLS.hasCertificate();
|
|
5536
|
-
log$
|
|
5536
|
+
log$8.debug('Certificate availability check:', hasCert);
|
|
5537
5537
|
return hasCert;
|
|
5538
5538
|
}
|
|
5539
5539
|
catch (error) {
|
|
5540
|
-
log$
|
|
5540
|
+
log$8.error('Certificate check failed:', error);
|
|
5541
5541
|
return false;
|
|
5542
5542
|
}
|
|
5543
5543
|
}
|
|
5544
5544
|
async getCertificateInfo() {
|
|
5545
5545
|
if (!this.expoMTLS) {
|
|
5546
|
-
log$
|
|
5546
|
+
log$8.debug('Certificate info requested but module not available');
|
|
5547
5547
|
return null;
|
|
5548
5548
|
}
|
|
5549
5549
|
try {
|
|
5550
5550
|
const hasCert = await this.hasCertificate();
|
|
5551
5551
|
if (!hasCert) {
|
|
5552
|
-
log$
|
|
5552
|
+
log$8.debug('No certificate stored');
|
|
5553
5553
|
return null;
|
|
5554
5554
|
}
|
|
5555
5555
|
// Use getCertificatesInfo to retrieve information about stored certificates
|
|
5556
5556
|
const result = await this.expoMTLS.getCertificatesInfo();
|
|
5557
5557
|
if (!result || !result.certificates || result.certificates.length === 0) {
|
|
5558
|
-
log$
|
|
5558
|
+
log$8.debug('No certificate information available');
|
|
5559
5559
|
return null;
|
|
5560
5560
|
}
|
|
5561
5561
|
// Get the first certificate (primary client certificate)
|
|
5562
5562
|
const cert = result.certificates[0];
|
|
5563
5563
|
if (!cert) {
|
|
5564
|
-
log$
|
|
5564
|
+
log$8.debug('Certificate data is empty');
|
|
5565
5565
|
return null;
|
|
5566
5566
|
}
|
|
5567
|
-
log$
|
|
5567
|
+
log$8.debug('Retrieved certificate info:', {
|
|
5568
5568
|
subject: cert.subject.commonName,
|
|
5569
5569
|
issuer: cert.issuer.commonName,
|
|
5570
5570
|
validFrom: new Date(cert.validFrom),
|
|
@@ -5583,7 +5583,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5583
5583
|
};
|
|
5584
5584
|
}
|
|
5585
5585
|
catch (error) {
|
|
5586
|
-
log$
|
|
5586
|
+
log$8.error('Failed to get certificate info:', error);
|
|
5587
5587
|
return null;
|
|
5588
5588
|
}
|
|
5589
5589
|
}
|
|
@@ -5594,7 +5594,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5594
5594
|
*/
|
|
5595
5595
|
async parseCertificateData(certificateData) {
|
|
5596
5596
|
if (!this.expoMTLS) {
|
|
5597
|
-
log$
|
|
5597
|
+
log$8.debug('Parse certificate: Module not available');
|
|
5598
5598
|
return null;
|
|
5599
5599
|
}
|
|
5600
5600
|
try {
|
|
@@ -5611,14 +5611,14 @@ class ReactNativeMTLSAdapter {
|
|
|
5611
5611
|
else {
|
|
5612
5612
|
throw new MTLSError(MTLSErrorType$1.CERTIFICATE_INVALID, `Unsupported certificate format: ${certificateData.format}`);
|
|
5613
5613
|
}
|
|
5614
|
-
log$
|
|
5614
|
+
log$8.debug('Certificate parsed successfully:', {
|
|
5615
5615
|
certificateCount: result.certificates.length,
|
|
5616
5616
|
subjects: result.certificates.map((cert) => cert.subject.commonName),
|
|
5617
5617
|
});
|
|
5618
5618
|
return result;
|
|
5619
5619
|
}
|
|
5620
5620
|
catch (error) {
|
|
5621
|
-
log$
|
|
5621
|
+
log$8.error('Failed to parse certificate:', error);
|
|
5622
5622
|
if (error instanceof MTLSError) {
|
|
5623
5623
|
throw error;
|
|
5624
5624
|
}
|
|
@@ -5633,7 +5633,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5633
5633
|
if (!hasCert) {
|
|
5634
5634
|
throw new MTLSError(MTLSErrorType$1.CERTIFICATE_NOT_FOUND, 'No certificate configured');
|
|
5635
5635
|
}
|
|
5636
|
-
log$
|
|
5636
|
+
log$8.debug('Making mTLS request:', {
|
|
5637
5637
|
method: requestConfig.method || 'GET',
|
|
5638
5638
|
url: requestConfig.url,
|
|
5639
5639
|
headers: requestConfig.headers,
|
|
@@ -5641,7 +5641,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5641
5641
|
responseType: requestConfig.responseType,
|
|
5642
5642
|
});
|
|
5643
5643
|
if (requestConfig.data) {
|
|
5644
|
-
log$
|
|
5644
|
+
log$8.debug('mTLS request body:', requestConfig.data);
|
|
5645
5645
|
}
|
|
5646
5646
|
try {
|
|
5647
5647
|
const response = await this.expoMTLS.request(requestConfig.url, {
|
|
@@ -5650,7 +5650,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5650
5650
|
body: requestConfig.data ? JSON.stringify(requestConfig.data) : undefined,
|
|
5651
5651
|
responseType: requestConfig.responseType,
|
|
5652
5652
|
});
|
|
5653
|
-
log$
|
|
5653
|
+
log$8.debug('mTLS request successful:', response);
|
|
5654
5654
|
if (!response.success) {
|
|
5655
5655
|
throw new MTLSError(MTLSErrorType$1.CONNECTION_FAILED, `mTLS request failed: ${response.statusMessage} (${response.statusCode})`, undefined, response.statusCode);
|
|
5656
5656
|
}
|
|
@@ -5662,7 +5662,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5662
5662
|
data = JSON.parse(response.body);
|
|
5663
5663
|
}
|
|
5664
5664
|
catch (parseError) {
|
|
5665
|
-
log$
|
|
5665
|
+
log$8.warn('Failed to parse JSON response:', parseError);
|
|
5666
5666
|
// If parsing fails, keep raw body
|
|
5667
5667
|
}
|
|
5668
5668
|
}
|
|
@@ -5679,7 +5679,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5679
5679
|
};
|
|
5680
5680
|
}
|
|
5681
5681
|
catch (error) {
|
|
5682
|
-
log$
|
|
5682
|
+
log$8.error('mTLS request failed:', error);
|
|
5683
5683
|
throw new MTLSError(MTLSErrorType$1.CONNECTION_FAILED, 'mTLS request failed', error);
|
|
5684
5684
|
}
|
|
5685
5685
|
}
|
|
@@ -5692,18 +5692,18 @@ class ReactNativeMTLSAdapter {
|
|
|
5692
5692
|
*/
|
|
5693
5693
|
async testConnection() {
|
|
5694
5694
|
if (!this.expoMTLS || !this.config) {
|
|
5695
|
-
log$
|
|
5695
|
+
log$8.debug('Diagnostic test: No mTLS module or config available');
|
|
5696
5696
|
return false;
|
|
5697
5697
|
}
|
|
5698
5698
|
try {
|
|
5699
5699
|
const hasCert = await this.hasCertificate();
|
|
5700
5700
|
if (!hasCert) {
|
|
5701
|
-
log$
|
|
5701
|
+
log$8.debug('Diagnostic test: No certificate configured');
|
|
5702
5702
|
return false;
|
|
5703
5703
|
}
|
|
5704
|
-
log$
|
|
5704
|
+
log$8.debug('Running diagnostic test (may fail even if mTLS works):', this.config.baseUrl);
|
|
5705
5705
|
const result = await this.expoMTLS.testConnection(this.config.baseUrl);
|
|
5706
|
-
log$
|
|
5706
|
+
log$8.debug('Diagnostic test result (NOT validation):', {
|
|
5707
5707
|
success: result.success,
|
|
5708
5708
|
statusCode: result.statusCode,
|
|
5709
5709
|
statusMessage: result.statusMessage,
|
|
@@ -5714,13 +5714,13 @@ class ReactNativeMTLSAdapter {
|
|
|
5714
5714
|
return result.success;
|
|
5715
5715
|
}
|
|
5716
5716
|
catch (error) {
|
|
5717
|
-
log$
|
|
5717
|
+
log$8.warn('Diagnostic test failed (this is expected):', error);
|
|
5718
5718
|
return false;
|
|
5719
5719
|
}
|
|
5720
5720
|
}
|
|
5721
5721
|
async removeCertificate() {
|
|
5722
5722
|
if (!this.expoMTLS) {
|
|
5723
|
-
log$
|
|
5723
|
+
log$8.debug('Remove certificate: Module not available');
|
|
5724
5724
|
return;
|
|
5725
5725
|
}
|
|
5726
5726
|
try {
|
|
@@ -5729,10 +5729,10 @@ class ReactNativeMTLSAdapter {
|
|
|
5729
5729
|
this.isConfigured = false;
|
|
5730
5730
|
// Cleanup event listeners
|
|
5731
5731
|
this.cleanupEventListeners();
|
|
5732
|
-
log$
|
|
5732
|
+
log$8.debug('Certificate removed successfully');
|
|
5733
5733
|
}
|
|
5734
5734
|
catch (error) {
|
|
5735
|
-
log$
|
|
5735
|
+
log$8.error('Failed to remove certificate:', error);
|
|
5736
5736
|
throw new MTLSError(MTLSErrorType$1.CONFIGURATION_ERROR, 'Failed to remove certificate', error);
|
|
5737
5737
|
}
|
|
5738
5738
|
}
|
|
@@ -5741,7 +5741,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5741
5741
|
*/
|
|
5742
5742
|
cleanupEventListeners() {
|
|
5743
5743
|
if (this.eventListeners.length > 0) {
|
|
5744
|
-
log$
|
|
5744
|
+
log$8.debug(`Cleaning up ${this.eventListeners.length} event listeners`);
|
|
5745
5745
|
}
|
|
5746
5746
|
// Remove individual listeners if they have remove methods
|
|
5747
5747
|
this.eventListeners.forEach((listener) => {
|
|
@@ -5778,7 +5778,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5778
5778
|
}
|
|
5779
5779
|
}
|
|
5780
5780
|
|
|
5781
|
-
const log$
|
|
5781
|
+
const log$7 = createPrefixedLogger('WEB-MTLS');
|
|
5782
5782
|
/**
|
|
5783
5783
|
* Web mTLS Adapter - Graceful fallback for web browsers
|
|
5784
5784
|
*
|
|
@@ -5792,13 +5792,13 @@ const log$4 = createPrefixedLogger('WEB-MTLS');
|
|
|
5792
5792
|
*/
|
|
5793
5793
|
class WebMTLSAdapter {
|
|
5794
5794
|
constructor() {
|
|
5795
|
-
log$
|
|
5796
|
-
log$
|
|
5795
|
+
log$7.warn('Web browsers do not support programmatic mTLS configuration');
|
|
5796
|
+
log$7.info('Use JWT authentication or configure client certificates in browser settings');
|
|
5797
5797
|
}
|
|
5798
5798
|
async isMTLSSupported() {
|
|
5799
5799
|
// mTLS is not supported programmatically in web browsers
|
|
5800
5800
|
const supported = false;
|
|
5801
|
-
log$
|
|
5801
|
+
log$7.debug('mTLS support check:', {
|
|
5802
5802
|
supported,
|
|
5803
5803
|
platform: this.getPlatformInfo().platform,
|
|
5804
5804
|
reason: 'Browser security model prevents programmatic certificate configuration',
|
|
@@ -5807,14 +5807,14 @@ class WebMTLSAdapter {
|
|
|
5807
5807
|
return supported;
|
|
5808
5808
|
}
|
|
5809
5809
|
async initialize(config) {
|
|
5810
|
-
log$
|
|
5810
|
+
log$7.warn('Initialized but mTLS not available in web browsers:', {
|
|
5811
5811
|
baseUrl: config.baseUrl,
|
|
5812
5812
|
port: config.port,
|
|
5813
5813
|
recommendation: 'Use standard HTTPS with JWT authentication',
|
|
5814
5814
|
});
|
|
5815
5815
|
}
|
|
5816
5816
|
async configureCertificate(certificateData) {
|
|
5817
|
-
log$
|
|
5817
|
+
log$7.error('Certificate configuration attempted:', {
|
|
5818
5818
|
format: certificateData.format,
|
|
5819
5819
|
reason: 'Not supported in web browsers',
|
|
5820
5820
|
alternatives: [
|
|
@@ -5829,15 +5829,15 @@ class WebMTLSAdapter {
|
|
|
5829
5829
|
}
|
|
5830
5830
|
async hasCertificate() {
|
|
5831
5831
|
// We cannot detect if the browser has certificates configured
|
|
5832
|
-
log$
|
|
5832
|
+
log$7.debug('Certificate availability check: Cannot detect browser certificates programmatically');
|
|
5833
5833
|
return false;
|
|
5834
5834
|
}
|
|
5835
5835
|
async getCertificateInfo() {
|
|
5836
|
-
log$
|
|
5836
|
+
log$7.debug('Certificate info requested: Not accessible in web browsers');
|
|
5837
5837
|
return null;
|
|
5838
5838
|
}
|
|
5839
5839
|
async request(requestConfig) {
|
|
5840
|
-
log$
|
|
5840
|
+
log$7.error('mTLS request attempted:', {
|
|
5841
5841
|
method: requestConfig.method,
|
|
5842
5842
|
url: requestConfig.url,
|
|
5843
5843
|
reason: 'Not supported in web browsers',
|
|
@@ -5852,11 +5852,11 @@ class WebMTLSAdapter {
|
|
|
5852
5852
|
'are properly configured in the browser certificate store.');
|
|
5853
5853
|
}
|
|
5854
5854
|
async testConnection() {
|
|
5855
|
-
log$
|
|
5855
|
+
log$7.debug('Connection test: mTLS not available in web browsers');
|
|
5856
5856
|
return false;
|
|
5857
5857
|
}
|
|
5858
5858
|
async removeCertificate() {
|
|
5859
|
-
log$
|
|
5859
|
+
log$7.debug('Remove certificate: No certificates to remove (not supported in web browsers)');
|
|
5860
5860
|
// No-op - cannot remove certificates programmatically in browsers
|
|
5861
5861
|
}
|
|
5862
5862
|
/**
|
|
@@ -5864,7 +5864,7 @@ class WebMTLSAdapter {
|
|
|
5864
5864
|
* Always returns null for web browsers as mTLS is not supported
|
|
5865
5865
|
*/
|
|
5866
5866
|
getBaseUrl() {
|
|
5867
|
-
log$
|
|
5867
|
+
log$7.debug('Base URL requested: Not supported in web browsers');
|
|
5868
5868
|
return null;
|
|
5869
5869
|
}
|
|
5870
5870
|
getPlatformInfo() {
|
|
@@ -5897,7 +5897,7 @@ class WebMTLSAdapter {
|
|
|
5897
5897
|
}
|
|
5898
5898
|
}
|
|
5899
5899
|
|
|
5900
|
-
const log$
|
|
5900
|
+
const log$6 = createPrefixedLogger('MTLS-LOADER');
|
|
5901
5901
|
function loadMTLSAdapter(platform, config) {
|
|
5902
5902
|
try {
|
|
5903
5903
|
let adapter;
|
|
@@ -5931,7 +5931,7 @@ function loadMTLSAdapter(platform, config) {
|
|
|
5931
5931
|
return adapter;
|
|
5932
5932
|
}
|
|
5933
5933
|
catch (error) {
|
|
5934
|
-
log$
|
|
5934
|
+
log$6.warn(`mTLS adapter not available for platform ${platform}:`, error);
|
|
5935
5935
|
return null;
|
|
5936
5936
|
}
|
|
5937
5937
|
}
|
|
@@ -5941,7 +5941,7 @@ async function initializeAdapterAsync(adapter, config) {
|
|
|
5941
5941
|
if (isSupported) {
|
|
5942
5942
|
await adapter.initialize(config);
|
|
5943
5943
|
const platformInfo = adapter.getPlatformInfo();
|
|
5944
|
-
log$
|
|
5944
|
+
log$6.debug('mTLS adapter initialized:', {
|
|
5945
5945
|
platform: platformInfo.platform,
|
|
5946
5946
|
mtlsSupported: platformInfo.mtlsSupported,
|
|
5947
5947
|
certificateStorage: platformInfo.certificateStorage,
|
|
@@ -5950,20 +5950,20 @@ async function initializeAdapterAsync(adapter, config) {
|
|
|
5950
5950
|
}
|
|
5951
5951
|
}
|
|
5952
5952
|
catch (error) {
|
|
5953
|
-
log$
|
|
5953
|
+
log$6.warn('Failed to initialize mTLS adapter:', error);
|
|
5954
5954
|
}
|
|
5955
5955
|
}
|
|
5956
5956
|
|
|
5957
|
-
const log$
|
|
5957
|
+
const log$5 = createPrefixedLogger('ADAPTER-LOADER');
|
|
5958
5958
|
function loadPlatformAdapters(options = {}) {
|
|
5959
5959
|
const { mtlsConfig } = options;
|
|
5960
5960
|
const { platform } = detectPlatform();
|
|
5961
|
-
log$
|
|
5961
|
+
log$5.debug('Loading adapters for platform:', platform);
|
|
5962
5962
|
const storageAdapters = loadStorageAdapters(platform);
|
|
5963
5963
|
const networkMonitor = loadNetworkMonitor(platform);
|
|
5964
5964
|
const cache = loadCacheAdapter(platform);
|
|
5965
5965
|
const mtls = loadMTLSAdapter(platform, mtlsConfig);
|
|
5966
|
-
log$
|
|
5966
|
+
log$5.debug('Adapters loaded:', {
|
|
5967
5967
|
platform,
|
|
5968
5968
|
hasStorage: !!storageAdapters.storage,
|
|
5969
5969
|
hasSecureStorage: !!storageAdapters.secureStorage,
|
|
@@ -6077,10 +6077,12 @@ class ACubeSDKError extends Error {
|
|
|
6077
6077
|
|
|
6078
6078
|
const DI_TOKENS = {
|
|
6079
6079
|
HTTP_PORT: Symbol('HTTP_PORT'),
|
|
6080
|
+
BASE_HTTP_PORT: Symbol('BASE_HTTP_PORT'),
|
|
6080
6081
|
STORAGE_PORT: Symbol('STORAGE_PORT'),
|
|
6081
6082
|
SECURE_STORAGE_PORT: Symbol('SECURE_STORAGE_PORT'),
|
|
6082
6083
|
NETWORK_PORT: Symbol('NETWORK_PORT'),
|
|
6083
6084
|
CACHE_PORT: Symbol('CACHE_PORT'),
|
|
6085
|
+
CACHE_KEY_GENERATOR: Symbol('CACHE_KEY_GENERATOR'),
|
|
6084
6086
|
MTLS_PORT: Symbol('MTLS_PORT'),
|
|
6085
6087
|
TOKEN_STORAGE_PORT: Symbol('TOKEN_STORAGE_PORT'),
|
|
6086
6088
|
RECEIPT_REPOSITORY: Symbol('RECEIPT_REPOSITORY'),
|
|
@@ -6310,7 +6312,7 @@ class ReceiptRepositoryImpl {
|
|
|
6310
6312
|
if (format === 'pdf') {
|
|
6311
6313
|
const response = await this.http.get(`/mf1/receipts/${receiptUuid}/details`, {
|
|
6312
6314
|
headers: { Accept: 'application/pdf' },
|
|
6313
|
-
responseType: '
|
|
6315
|
+
responseType: 'arraybuffer',
|
|
6314
6316
|
});
|
|
6315
6317
|
return response.data;
|
|
6316
6318
|
}
|
|
@@ -7073,7 +7075,465 @@ class TelemetryRepositoryImpl {
|
|
|
7073
7075
|
}
|
|
7074
7076
|
}
|
|
7075
7077
|
|
|
7076
|
-
const log$
|
|
7078
|
+
const log$4 = createPrefixedLogger('CACHE-KEY');
|
|
7079
|
+
const URL_PATTERNS = [
|
|
7080
|
+
// Receipt (mf1) - specific patterns first
|
|
7081
|
+
{
|
|
7082
|
+
pattern: /^\/mf1\/receipts\/([^/]+)\/returnable-items$/,
|
|
7083
|
+
resource: 'receipt',
|
|
7084
|
+
action: 'returnable',
|
|
7085
|
+
},
|
|
7086
|
+
{
|
|
7087
|
+
pattern: /^\/mf1\/receipts\/([^/]+)\/details$/,
|
|
7088
|
+
resource: 'receipt',
|
|
7089
|
+
action: 'details',
|
|
7090
|
+
},
|
|
7091
|
+
{ pattern: /^\/mf1\/receipts\/([^/]+)$/, resource: 'receipt' },
|
|
7092
|
+
{
|
|
7093
|
+
pattern: /^\/mf1\/point-of-sales\/([^/]+)\/receipts$/,
|
|
7094
|
+
resource: 'receipt',
|
|
7095
|
+
parent: 'point-of-sale',
|
|
7096
|
+
isList: true,
|
|
7097
|
+
},
|
|
7098
|
+
{ pattern: /^\/mf1\/receipts$/, resource: 'receipt', isList: true },
|
|
7099
|
+
// Merchant (mf2)
|
|
7100
|
+
{ pattern: /^\/mf2\/merchants\/([^/]+)$/, resource: 'merchant' },
|
|
7101
|
+
{ pattern: /^\/mf2\/merchants$/, resource: 'merchant', isList: true },
|
|
7102
|
+
// Cashier (mf1)
|
|
7103
|
+
{ pattern: /^\/mf1\/cashiers\/me$/, resource: 'cashier', action: 'me' },
|
|
7104
|
+
{ pattern: /^\/mf1\/cashiers\/([^/]+)$/, resource: 'cashier' },
|
|
7105
|
+
{ pattern: /^\/mf1\/cashiers$/, resource: 'cashier', isList: true },
|
|
7106
|
+
// Cash Register (mf1)
|
|
7107
|
+
{ pattern: /^\/mf1\/cash-registers\/([^/]+)$/, resource: 'cash-register' },
|
|
7108
|
+
{ pattern: /^\/mf1\/cash-registers$/, resource: 'cash-register', isList: true },
|
|
7109
|
+
// Point of Sale (mf1)
|
|
7110
|
+
{ pattern: /^\/mf1\/point-of-sales\/([^/]+)$/, resource: 'point-of-sale' },
|
|
7111
|
+
{ pattern: /^\/mf1\/point-of-sales$/, resource: 'point-of-sale', isList: true },
|
|
7112
|
+
// Nested resources under merchant (mf2)
|
|
7113
|
+
{
|
|
7114
|
+
pattern: /^\/mf2\/merchants\/([^/]+)\/suppliers\/([^/]+)$/,
|
|
7115
|
+
resource: 'supplier',
|
|
7116
|
+
parent: 'merchant',
|
|
7117
|
+
},
|
|
7118
|
+
{
|
|
7119
|
+
pattern: /^\/mf2\/merchants\/([^/]+)\/suppliers$/,
|
|
7120
|
+
resource: 'supplier',
|
|
7121
|
+
parent: 'merchant',
|
|
7122
|
+
isList: true,
|
|
7123
|
+
},
|
|
7124
|
+
{
|
|
7125
|
+
pattern: /^\/mf2\/merchants\/([^/]+)\/daily-reports/,
|
|
7126
|
+
resource: 'daily-report',
|
|
7127
|
+
parent: 'merchant',
|
|
7128
|
+
isList: true,
|
|
7129
|
+
},
|
|
7130
|
+
{
|
|
7131
|
+
pattern: /^\/mf2\/merchants\/([^/]+)\/journals/,
|
|
7132
|
+
resource: 'journal',
|
|
7133
|
+
parent: 'merchant',
|
|
7134
|
+
isList: true,
|
|
7135
|
+
},
|
|
7136
|
+
// PEM (mf2)
|
|
7137
|
+
{
|
|
7138
|
+
pattern: /^\/mf2\/point-of-sales\/([^/]+)\/certificates$/,
|
|
7139
|
+
resource: 'pem',
|
|
7140
|
+
action: 'certificates',
|
|
7141
|
+
},
|
|
7142
|
+
{ pattern: /^\/mf2\/point-of-sales\/([^/]+)$/, resource: 'pem' },
|
|
7143
|
+
// Others
|
|
7144
|
+
{ pattern: /^\/mf1\/notifications/, resource: 'notification', isList: true },
|
|
7145
|
+
{
|
|
7146
|
+
pattern: /^\/mf1\/point-of-sales\/([^/]+)\/telemetry$/,
|
|
7147
|
+
resource: 'telemetry',
|
|
7148
|
+
},
|
|
7149
|
+
];
|
|
7150
|
+
const DEFAULT_TTL_CONFIG = {
|
|
7151
|
+
// Data that rarely changes - 30 min TTL for items only
|
|
7152
|
+
merchant: { ttlMs: 30 * 60 * 1000, cacheList: false, cacheItem: true },
|
|
7153
|
+
'point-of-sale': { ttlMs: 30 * 60 * 1000, cacheList: false, cacheItem: true },
|
|
7154
|
+
'cash-register': { ttlMs: 30 * 60 * 1000, cacheList: false, cacheItem: true },
|
|
7155
|
+
pem: { ttlMs: 30 * 60 * 1000, cacheList: false, cacheItem: false },
|
|
7156
|
+
// Data that changes moderately - 10 min TTL for items only
|
|
7157
|
+
cashier: { ttlMs: 10 * 60 * 1000, cacheList: false, cacheItem: true },
|
|
7158
|
+
supplier: { ttlMs: 10 * 60 * 1000, cacheList: false, cacheItem: true },
|
|
7159
|
+
// Data that can change - 5 min TTL for items only
|
|
7160
|
+
receipt: { ttlMs: 5 * 60 * 1000, cacheList: false, cacheItem: true },
|
|
7161
|
+
'daily-report': { ttlMs: 5 * 60 * 1000, cacheList: false, cacheItem: true },
|
|
7162
|
+
journal: { ttlMs: 5 * 60 * 1000, cacheList: false, cacheItem: true },
|
|
7163
|
+
// Real-time data - 1 min TTL
|
|
7164
|
+
notification: { ttlMs: 1 * 60 * 1000, cacheList: false, cacheItem: false },
|
|
7165
|
+
telemetry: { ttlMs: 1 * 60 * 1000, cacheList: false, cacheItem: false },
|
|
7166
|
+
};
|
|
7167
|
+
const DEFAULT_TTL = 5 * 60 * 1000; // 5 minutes
|
|
7168
|
+
class CacheKeyGenerator {
|
|
7169
|
+
constructor(customConfig) {
|
|
7170
|
+
this.config = { ...DEFAULT_TTL_CONFIG, ...customConfig };
|
|
7171
|
+
log$4.info('CacheKeyGenerator initialized with config:', {
|
|
7172
|
+
resources: Object.keys(this.config),
|
|
7173
|
+
});
|
|
7174
|
+
}
|
|
7175
|
+
generate(url, params) {
|
|
7176
|
+
const parsed = this.parseUrl(url);
|
|
7177
|
+
if (!parsed) {
|
|
7178
|
+
// Fallback: use URL as key
|
|
7179
|
+
const paramStr = params ? this.serializeParams(params) : '';
|
|
7180
|
+
const key = paramStr ? `${url}?${paramStr}` : url;
|
|
7181
|
+
log$4.debug('URL not matched, using fallback key:', { url, key });
|
|
7182
|
+
return key;
|
|
7183
|
+
}
|
|
7184
|
+
const { resource, ids, action, isList, parent } = parsed;
|
|
7185
|
+
if (isList) {
|
|
7186
|
+
const paramStr = params ? this.serializeParams(params) : '';
|
|
7187
|
+
const parentPart = parent && ids.length > 0 ? `${parent}=${ids[0]}&` : '';
|
|
7188
|
+
const key = `${resource}:list:${parentPart}${paramStr}`;
|
|
7189
|
+
log$4.debug('Generated list cache key:', { url, key, resource });
|
|
7190
|
+
return key;
|
|
7191
|
+
}
|
|
7192
|
+
// Single item
|
|
7193
|
+
if (ids.length === 0 && action) {
|
|
7194
|
+
// Special case for endpoints like /cashiers/me
|
|
7195
|
+
const key = `${resource}:${action}`;
|
|
7196
|
+
log$4.debug('Generated special action cache key:', { url, key, resource, action });
|
|
7197
|
+
return key;
|
|
7198
|
+
}
|
|
7199
|
+
let key = `${resource}:${ids.join(':')}`;
|
|
7200
|
+
if (action) {
|
|
7201
|
+
key += `:${action}`;
|
|
7202
|
+
}
|
|
7203
|
+
log$4.debug('Generated item cache key:', { url, key, resource, ids, action });
|
|
7204
|
+
return key;
|
|
7205
|
+
}
|
|
7206
|
+
parseResource(url) {
|
|
7207
|
+
const parsed = this.parseUrl(url);
|
|
7208
|
+
return parsed?.resource;
|
|
7209
|
+
}
|
|
7210
|
+
getTTL(url) {
|
|
7211
|
+
const resource = this.parseResource(url);
|
|
7212
|
+
if (!resource) {
|
|
7213
|
+
log$4.debug('No resource found for URL, using default TTL:', { url, ttl: DEFAULT_TTL });
|
|
7214
|
+
return DEFAULT_TTL;
|
|
7215
|
+
}
|
|
7216
|
+
const ttl = this.config[resource].ttlMs;
|
|
7217
|
+
log$4.debug('TTL for resource:', { url, resource, ttlMs: ttl, ttlMin: ttl / 60000 });
|
|
7218
|
+
return ttl;
|
|
7219
|
+
}
|
|
7220
|
+
shouldCache(url) {
|
|
7221
|
+
const parsed = this.parseUrl(url);
|
|
7222
|
+
if (!parsed) {
|
|
7223
|
+
log$4.debug('URL not recognized, should not cache:', { url });
|
|
7224
|
+
return false;
|
|
7225
|
+
}
|
|
7226
|
+
const { resource, isList } = parsed;
|
|
7227
|
+
const config = this.config[resource];
|
|
7228
|
+
if (isList) {
|
|
7229
|
+
log$4.debug('List endpoint cache decision:', {
|
|
7230
|
+
url,
|
|
7231
|
+
resource,
|
|
7232
|
+
isList: true,
|
|
7233
|
+
shouldCache: config.cacheList,
|
|
7234
|
+
});
|
|
7235
|
+
return config.cacheList;
|
|
7236
|
+
}
|
|
7237
|
+
log$4.debug('Item endpoint cache decision:', {
|
|
7238
|
+
url,
|
|
7239
|
+
resource,
|
|
7240
|
+
isList: false,
|
|
7241
|
+
shouldCache: config.cacheItem,
|
|
7242
|
+
});
|
|
7243
|
+
return config.cacheItem;
|
|
7244
|
+
}
|
|
7245
|
+
getInvalidationPatterns(url, method) {
|
|
7246
|
+
const parsed = this.parseUrl(url);
|
|
7247
|
+
if (!parsed) {
|
|
7248
|
+
log$4.debug('No patterns to invalidate for URL:', { url, method });
|
|
7249
|
+
return [];
|
|
7250
|
+
}
|
|
7251
|
+
const { resource, ids, parent } = parsed;
|
|
7252
|
+
const patterns = [];
|
|
7253
|
+
// Always invalidate list on mutations
|
|
7254
|
+
if (method === 'POST' || method === 'PUT' || method === 'PATCH' || method === 'DELETE') {
|
|
7255
|
+
if (parent && ids.length > 0) {
|
|
7256
|
+
patterns.push(`${resource}:list:${parent}=${ids[0]}*`);
|
|
7257
|
+
}
|
|
7258
|
+
patterns.push(`${resource}:list:*`);
|
|
7259
|
+
}
|
|
7260
|
+
// Invalidate specific item on PUT/PATCH/DELETE
|
|
7261
|
+
if (method === 'PUT' || method === 'PATCH' || method === 'DELETE') {
|
|
7262
|
+
if (ids.length > 0) {
|
|
7263
|
+
patterns.push(`${resource}:${ids.join(':')}*`);
|
|
7264
|
+
}
|
|
7265
|
+
}
|
|
7266
|
+
// Special cases
|
|
7267
|
+
if (resource === 'cashier' && (method === 'PUT' || method === 'DELETE')) {
|
|
7268
|
+
patterns.push('cashier:me');
|
|
7269
|
+
}
|
|
7270
|
+
log$4.debug('Invalidation patterns:', { url, method, patterns });
|
|
7271
|
+
return patterns;
|
|
7272
|
+
}
|
|
7273
|
+
parseUrl(url) {
|
|
7274
|
+
// Remove query string for pattern matching
|
|
7275
|
+
const urlPath = url.split('?')[0];
|
|
7276
|
+
for (const pattern of URL_PATTERNS) {
|
|
7277
|
+
const match = urlPath?.match(pattern.pattern);
|
|
7278
|
+
if (match) {
|
|
7279
|
+
// Extract IDs from capture groups
|
|
7280
|
+
const ids = match.slice(1).filter(Boolean);
|
|
7281
|
+
return {
|
|
7282
|
+
resource: pattern.resource,
|
|
7283
|
+
ids,
|
|
7284
|
+
action: pattern.action,
|
|
7285
|
+
isList: pattern.isList,
|
|
7286
|
+
parent: pattern.parent,
|
|
7287
|
+
};
|
|
7288
|
+
}
|
|
7289
|
+
}
|
|
7290
|
+
return null;
|
|
7291
|
+
}
|
|
7292
|
+
serializeParams(params) {
|
|
7293
|
+
const sortedKeys = Object.keys(params).sort();
|
|
7294
|
+
const parts = [];
|
|
7295
|
+
for (const key of sortedKeys) {
|
|
7296
|
+
const value = params[key];
|
|
7297
|
+
if (value !== undefined && value !== null) {
|
|
7298
|
+
parts.push(`${key}=${String(value)}`);
|
|
7299
|
+
}
|
|
7300
|
+
}
|
|
7301
|
+
return parts.join('&');
|
|
7302
|
+
}
|
|
7303
|
+
}
|
|
7304
|
+
|
|
7305
|
+
const log$3 = createPrefixedLogger('CACHE');
|
|
7306
|
+
class CachingHttpDecorator {
|
|
7307
|
+
constructor(http, cache, keyGenerator, networkMonitor, config = {}) {
|
|
7308
|
+
this.http = http;
|
|
7309
|
+
this.cache = cache;
|
|
7310
|
+
this.keyGenerator = keyGenerator;
|
|
7311
|
+
this.networkMonitor = networkMonitor;
|
|
7312
|
+
this.config = config;
|
|
7313
|
+
this.currentOnlineState = true;
|
|
7314
|
+
this.authToken = null;
|
|
7315
|
+
log$3.info('CachingHttpDecorator initialized', {
|
|
7316
|
+
enabled: config.enabled !== false,
|
|
7317
|
+
hasNetworkMonitor: !!networkMonitor,
|
|
7318
|
+
});
|
|
7319
|
+
this.setupNetworkMonitoring();
|
|
7320
|
+
}
|
|
7321
|
+
setupNetworkMonitoring() {
|
|
7322
|
+
if (this.networkMonitor) {
|
|
7323
|
+
this.networkSubscription = this.networkMonitor.online$.subscribe((online) => {
|
|
7324
|
+
if (this.currentOnlineState !== online) {
|
|
7325
|
+
log$3.info('Network state changed:', { online });
|
|
7326
|
+
}
|
|
7327
|
+
this.currentOnlineState = online;
|
|
7328
|
+
});
|
|
7329
|
+
}
|
|
7330
|
+
}
|
|
7331
|
+
isOnline() {
|
|
7332
|
+
if (this.networkMonitor) {
|
|
7333
|
+
return this.currentOnlineState;
|
|
7334
|
+
}
|
|
7335
|
+
if (typeof navigator !== 'undefined' && 'onLine' in navigator) {
|
|
7336
|
+
return navigator.onLine;
|
|
7337
|
+
}
|
|
7338
|
+
return true;
|
|
7339
|
+
}
|
|
7340
|
+
async get(url, config) {
|
|
7341
|
+
const startTime = Date.now();
|
|
7342
|
+
// Check if caching is disabled globally
|
|
7343
|
+
if (this.config.enabled === false) {
|
|
7344
|
+
log$3.debug('GET (cache disabled globally):', { url });
|
|
7345
|
+
return this.http.get(url, config);
|
|
7346
|
+
}
|
|
7347
|
+
// Check if this URL should be cached
|
|
7348
|
+
const shouldCache = this.keyGenerator.shouldCache(url);
|
|
7349
|
+
if (!shouldCache) {
|
|
7350
|
+
log$3.debug('GET (not cacheable - likely a list endpoint):', { url });
|
|
7351
|
+
return this.http.get(url, config);
|
|
7352
|
+
}
|
|
7353
|
+
const cacheKey = this.keyGenerator.generate(url, config?.params);
|
|
7354
|
+
const ttl = this.keyGenerator.getTTL(url);
|
|
7355
|
+
const resource = this.keyGenerator.parseResource(url);
|
|
7356
|
+
log$3.info('GET request starting:', {
|
|
7357
|
+
url,
|
|
7358
|
+
resource,
|
|
7359
|
+
cacheKey,
|
|
7360
|
+
ttlMs: ttl,
|
|
7361
|
+
ttlMin: Math.round(ttl / 60000),
|
|
7362
|
+
params: config?.params,
|
|
7363
|
+
});
|
|
7364
|
+
// Check cache
|
|
7365
|
+
let cached = null;
|
|
7366
|
+
try {
|
|
7367
|
+
cached = await this.cache.get(cacheKey);
|
|
7368
|
+
if (cached) {
|
|
7369
|
+
log$3.debug('Cache entry found:', {
|
|
7370
|
+
cacheKey,
|
|
7371
|
+
timestamp: new Date(cached.timestamp).toISOString(),
|
|
7372
|
+
ageMs: Date.now() - cached.timestamp,
|
|
7373
|
+
});
|
|
7374
|
+
}
|
|
7375
|
+
else {
|
|
7376
|
+
log$3.debug('Cache entry not found:', { cacheKey });
|
|
7377
|
+
}
|
|
7378
|
+
}
|
|
7379
|
+
catch (error) {
|
|
7380
|
+
log$3.warn('Cache lookup failed:', {
|
|
7381
|
+
cacheKey,
|
|
7382
|
+
error: error instanceof Error ? error.message : error,
|
|
7383
|
+
});
|
|
7384
|
+
}
|
|
7385
|
+
if (cached) {
|
|
7386
|
+
const age = Date.now() - cached.timestamp;
|
|
7387
|
+
const isExpired = age >= ttl;
|
|
7388
|
+
log$3.debug('Cache analysis:', {
|
|
7389
|
+
cacheKey,
|
|
7390
|
+
ageMs: age,
|
|
7391
|
+
ageSec: Math.round(age / 1000),
|
|
7392
|
+
ttlMs: ttl,
|
|
7393
|
+
isExpired,
|
|
7394
|
+
isOnline: this.isOnline(),
|
|
7395
|
+
});
|
|
7396
|
+
// If within TTL, return cached data
|
|
7397
|
+
if (!isExpired) {
|
|
7398
|
+
const duration = Date.now() - startTime;
|
|
7399
|
+
log$3.info('CACHE HIT:', {
|
|
7400
|
+
url,
|
|
7401
|
+
cacheKey,
|
|
7402
|
+
ageMs: age,
|
|
7403
|
+
durationMs: duration,
|
|
7404
|
+
});
|
|
7405
|
+
return {
|
|
7406
|
+
data: cached.data,
|
|
7407
|
+
status: 200,
|
|
7408
|
+
headers: { 'x-cache': 'HIT' },
|
|
7409
|
+
};
|
|
7410
|
+
}
|
|
7411
|
+
// If offline and cache is stale, return stale data
|
|
7412
|
+
if (!this.isOnline()) {
|
|
7413
|
+
const duration = Date.now() - startTime;
|
|
7414
|
+
log$3.info('CACHE STALE (offline):', {
|
|
7415
|
+
url,
|
|
7416
|
+
cacheKey,
|
|
7417
|
+
ageMs: age,
|
|
7418
|
+
durationMs: duration,
|
|
7419
|
+
});
|
|
7420
|
+
return {
|
|
7421
|
+
data: cached.data,
|
|
7422
|
+
status: 200,
|
|
7423
|
+
headers: { 'x-cache': 'STALE' },
|
|
7424
|
+
};
|
|
7425
|
+
}
|
|
7426
|
+
log$3.debug('Cache expired, fetching fresh data:', { cacheKey, ageMs: age, ttlMs: ttl });
|
|
7427
|
+
}
|
|
7428
|
+
// Fetch fresh data
|
|
7429
|
+
try {
|
|
7430
|
+
log$3.debug('Fetching from network:', { url });
|
|
7431
|
+
const response = await this.http.get(url, config);
|
|
7432
|
+
// Cache the response
|
|
7433
|
+
try {
|
|
7434
|
+
await this.cache.set(cacheKey, response.data);
|
|
7435
|
+
log$3.debug('Response cached successfully:', { cacheKey });
|
|
7436
|
+
}
|
|
7437
|
+
catch (error) {
|
|
7438
|
+
log$3.error('Failed to cache response:', {
|
|
7439
|
+
cacheKey,
|
|
7440
|
+
error: error instanceof Error ? error.message : error,
|
|
7441
|
+
});
|
|
7442
|
+
}
|
|
7443
|
+
const duration = Date.now() - startTime;
|
|
7444
|
+
log$3.info('CACHE MISS (fetched fresh):', {
|
|
7445
|
+
url,
|
|
7446
|
+
cacheKey,
|
|
7447
|
+
status: response.status,
|
|
7448
|
+
durationMs: duration,
|
|
7449
|
+
});
|
|
7450
|
+
return {
|
|
7451
|
+
...response,
|
|
7452
|
+
headers: { ...response.headers, 'x-cache': 'MISS' },
|
|
7453
|
+
};
|
|
7454
|
+
}
|
|
7455
|
+
catch (error) {
|
|
7456
|
+
// On error, return stale cache if available
|
|
7457
|
+
if (cached) {
|
|
7458
|
+
const duration = Date.now() - startTime;
|
|
7459
|
+
log$3.warn('CACHE STALE (network error):', {
|
|
7460
|
+
url,
|
|
7461
|
+
cacheKey,
|
|
7462
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
7463
|
+
durationMs: duration,
|
|
7464
|
+
});
|
|
7465
|
+
return {
|
|
7466
|
+
data: cached.data,
|
|
7467
|
+
status: 200,
|
|
7468
|
+
headers: { 'x-cache': 'STALE' },
|
|
7469
|
+
};
|
|
7470
|
+
}
|
|
7471
|
+
log$3.error('Network error with no cache fallback:', {
|
|
7472
|
+
url,
|
|
7473
|
+
error: error instanceof Error ? error.message : error,
|
|
7474
|
+
});
|
|
7475
|
+
throw error;
|
|
7476
|
+
}
|
|
7477
|
+
}
|
|
7478
|
+
async post(url, data, config) {
|
|
7479
|
+
log$3.info('POST request:', { url });
|
|
7480
|
+
const response = await this.http.post(url, data, config);
|
|
7481
|
+
await this.invalidateRelated(url, 'POST');
|
|
7482
|
+
return response;
|
|
7483
|
+
}
|
|
7484
|
+
async put(url, data, config) {
|
|
7485
|
+
log$3.info('PUT request:', { url });
|
|
7486
|
+
const response = await this.http.put(url, data, config);
|
|
7487
|
+
await this.invalidateRelated(url, 'PUT');
|
|
7488
|
+
return response;
|
|
7489
|
+
}
|
|
7490
|
+
async patch(url, data, config) {
|
|
7491
|
+
log$3.info('PATCH request:', { url });
|
|
7492
|
+
const response = await this.http.patch(url, data, config);
|
|
7493
|
+
await this.invalidateRelated(url, 'PATCH');
|
|
7494
|
+
return response;
|
|
7495
|
+
}
|
|
7496
|
+
async delete(url, config) {
|
|
7497
|
+
log$3.info('DELETE request:', { url });
|
|
7498
|
+
const response = await this.http.delete(url, config);
|
|
7499
|
+
await this.invalidateRelated(url, 'DELETE');
|
|
7500
|
+
return response;
|
|
7501
|
+
}
|
|
7502
|
+
setAuthToken(token) {
|
|
7503
|
+
log$3.debug('Auth token updated:', { hasToken: !!token });
|
|
7504
|
+
this.authToken = token;
|
|
7505
|
+
this.http.setAuthToken(token);
|
|
7506
|
+
}
|
|
7507
|
+
getAuthToken() {
|
|
7508
|
+
return this.authToken;
|
|
7509
|
+
}
|
|
7510
|
+
async invalidateRelated(url, method) {
|
|
7511
|
+
const patterns = this.keyGenerator.getInvalidationPatterns(url, method);
|
|
7512
|
+
if (patterns.length === 0) {
|
|
7513
|
+
log$3.debug('No cache patterns to invalidate:', { url, method });
|
|
7514
|
+
return;
|
|
7515
|
+
}
|
|
7516
|
+
log$3.info('Invalidating cache patterns:', { url, method, patterns });
|
|
7517
|
+
for (const pattern of patterns) {
|
|
7518
|
+
try {
|
|
7519
|
+
await this.cache.invalidate(pattern);
|
|
7520
|
+
log$3.debug('Cache pattern invalidated:', { pattern });
|
|
7521
|
+
}
|
|
7522
|
+
catch (error) {
|
|
7523
|
+
log$3.error('Failed to invalidate pattern:', {
|
|
7524
|
+
pattern,
|
|
7525
|
+
error: error instanceof Error ? error.message : error,
|
|
7526
|
+
});
|
|
7527
|
+
}
|
|
7528
|
+
}
|
|
7529
|
+
}
|
|
7530
|
+
destroy() {
|
|
7531
|
+
log$3.debug('CachingHttpDecorator destroyed');
|
|
7532
|
+
this.networkSubscription?.unsubscribe();
|
|
7533
|
+
}
|
|
7534
|
+
}
|
|
7535
|
+
|
|
7536
|
+
const log$2 = createPrefixedLogger('HTTP-MTLS');
|
|
7077
7537
|
class AxiosHttpAdapter {
|
|
7078
7538
|
constructor(config) {
|
|
7079
7539
|
this.authToken = null;
|
|
@@ -7091,11 +7551,11 @@ class AxiosHttpAdapter {
|
|
|
7091
7551
|
}
|
|
7092
7552
|
setMTLSAdapter(adapter) {
|
|
7093
7553
|
this.mtlsAdapter = adapter;
|
|
7094
|
-
log$
|
|
7554
|
+
log$2.debug('mTLS adapter configured:', !!adapter);
|
|
7095
7555
|
}
|
|
7096
7556
|
setAuthStrategy(strategy) {
|
|
7097
7557
|
this.authStrategy = strategy;
|
|
7098
|
-
log$
|
|
7558
|
+
log$2.debug('Auth strategy configured:', !!strategy);
|
|
7099
7559
|
}
|
|
7100
7560
|
async shouldUseMTLS(url, method) {
|
|
7101
7561
|
if (!this.mtlsAdapter) {
|
|
@@ -7103,7 +7563,7 @@ class AxiosHttpAdapter {
|
|
|
7103
7563
|
}
|
|
7104
7564
|
if (this.authStrategy) {
|
|
7105
7565
|
const config = await this.authStrategy.determineAuthConfig(url, method);
|
|
7106
|
-
log$
|
|
7566
|
+
log$2.debug(`Auth config for ${method} ${url}:`, config);
|
|
7107
7567
|
return config.mode === 'mtls';
|
|
7108
7568
|
}
|
|
7109
7569
|
// Fallback: use mTLS for mf1/mf2 endpoints if no strategy
|
|
@@ -7120,10 +7580,10 @@ class AxiosHttpAdapter {
|
|
|
7120
7580
|
};
|
|
7121
7581
|
if (this.authToken) {
|
|
7122
7582
|
headers['Authorization'] = `Bearer ${this.authToken}`;
|
|
7123
|
-
log$
|
|
7583
|
+
log$2.debug('JWT token present for mTLS request');
|
|
7124
7584
|
}
|
|
7125
7585
|
else {
|
|
7126
|
-
log$
|
|
7586
|
+
log$2.warn('No JWT token for mTLS request');
|
|
7127
7587
|
}
|
|
7128
7588
|
const mtlsConfig = {
|
|
7129
7589
|
url: fullUrl,
|
|
@@ -7132,15 +7592,15 @@ class AxiosHttpAdapter {
|
|
|
7132
7592
|
data,
|
|
7133
7593
|
timeout: config?.timeout,
|
|
7134
7594
|
};
|
|
7135
|
-
log$
|
|
7595
|
+
log$2.debug(`mTLS ${method} ${fullUrl}`);
|
|
7136
7596
|
if (data) {
|
|
7137
|
-
log$
|
|
7597
|
+
log$2.debug('Request body:', data);
|
|
7138
7598
|
}
|
|
7139
7599
|
try {
|
|
7140
7600
|
const response = await this.mtlsAdapter.request(mtlsConfig);
|
|
7141
|
-
log$
|
|
7601
|
+
log$2.debug(`mTLS Response ${response.status} from ${fullUrl}`);
|
|
7142
7602
|
if (response.data) {
|
|
7143
|
-
log$
|
|
7603
|
+
log$2.debug('Response body:', response.data);
|
|
7144
7604
|
}
|
|
7145
7605
|
return {
|
|
7146
7606
|
data: response.data,
|
|
@@ -7149,11 +7609,11 @@ class AxiosHttpAdapter {
|
|
|
7149
7609
|
};
|
|
7150
7610
|
}
|
|
7151
7611
|
catch (error) {
|
|
7152
|
-
log$
|
|
7612
|
+
log$2.error(`mTLS Response error from ${fullUrl}:`, error);
|
|
7153
7613
|
if (error && typeof error === 'object' && 'response' in error) {
|
|
7154
7614
|
const axiosError = error;
|
|
7155
7615
|
if (axiosError.response?.data) {
|
|
7156
|
-
log$
|
|
7616
|
+
log$2.error('Response body:', axiosError.response.data);
|
|
7157
7617
|
}
|
|
7158
7618
|
}
|
|
7159
7619
|
throw error;
|
|
@@ -7168,32 +7628,36 @@ class AxiosHttpAdapter {
|
|
|
7168
7628
|
this.client.interceptors.request.use((config) => {
|
|
7169
7629
|
if (this.authToken) {
|
|
7170
7630
|
config.headers.Authorization = `Bearer ${this.authToken}`;
|
|
7631
|
+
log$2.debug('Adding JWT token to request');
|
|
7632
|
+
}
|
|
7633
|
+
else {
|
|
7634
|
+
log$2.warn('No JWT token available for request:', { url: config.url });
|
|
7171
7635
|
}
|
|
7172
7636
|
const method = config.method?.toUpperCase() ?? 'UNKNOWN';
|
|
7173
|
-
log$
|
|
7637
|
+
log$2.debug(`→ ${method} ${config.url}`);
|
|
7174
7638
|
if (config.params && Object.keys(config.params).length > 0) {
|
|
7175
|
-
log$
|
|
7639
|
+
log$2.debug('Request params:', config.params);
|
|
7176
7640
|
}
|
|
7177
7641
|
if (config.data) {
|
|
7178
|
-
log$
|
|
7642
|
+
log$2.debug('Request body:', config.data);
|
|
7179
7643
|
}
|
|
7180
7644
|
return config;
|
|
7181
7645
|
}, (error) => {
|
|
7182
|
-
log$
|
|
7646
|
+
log$2.error('Request error:', error);
|
|
7183
7647
|
return Promise.reject(error);
|
|
7184
7648
|
});
|
|
7185
7649
|
this.client.interceptors.response.use((response) => {
|
|
7186
7650
|
const method = response.config.method?.toUpperCase() ?? 'UNKNOWN';
|
|
7187
|
-
log$
|
|
7651
|
+
log$2.debug(`← ${method} ${response.status} ${response.config.url}`);
|
|
7188
7652
|
if (response.data) {
|
|
7189
|
-
log$
|
|
7653
|
+
log$2.debug('Response body:', response.data);
|
|
7190
7654
|
}
|
|
7191
7655
|
return response;
|
|
7192
7656
|
}, (error) => {
|
|
7193
7657
|
const method = error.config?.method?.toUpperCase() ?? 'UNKNOWN';
|
|
7194
|
-
log$
|
|
7658
|
+
log$2.error(`← ${method} ${error.response?.status ?? 'ERR'} ${error.config?.url ?? 'unknown'}`);
|
|
7195
7659
|
if (error.response?.data) {
|
|
7196
|
-
log$
|
|
7660
|
+
log$2.error('Response body:', error.response.data);
|
|
7197
7661
|
}
|
|
7198
7662
|
return Promise.reject(error);
|
|
7199
7663
|
});
|
|
@@ -7263,6 +7727,7 @@ class AxiosHttpAdapter {
|
|
|
7263
7727
|
return this.mapResponse(response);
|
|
7264
7728
|
}
|
|
7265
7729
|
setAuthToken(token) {
|
|
7730
|
+
log$2.info('setAuthToken called:', { hasToken: !!token, tokenPrefix: token?.substring(0, 20) });
|
|
7266
7731
|
this.authToken = token;
|
|
7267
7732
|
}
|
|
7268
7733
|
getAuthToken() {
|
|
@@ -7316,6 +7781,7 @@ class SDKFactory {
|
|
|
7316
7781
|
baseUrl: config.baseUrl,
|
|
7317
7782
|
timeout: config.timeout,
|
|
7318
7783
|
});
|
|
7784
|
+
container.register(DI_TOKENS.BASE_HTTP_PORT, httpAdapter);
|
|
7319
7785
|
container.register(DI_TOKENS.HTTP_PORT, httpAdapter);
|
|
7320
7786
|
container.registerFactory(DI_TOKENS.RECEIPT_REPOSITORY, () => {
|
|
7321
7787
|
const http = container.get(DI_TOKENS.HTTP_PORT);
|
|
@@ -7363,6 +7829,23 @@ class SDKFactory {
|
|
|
7363
7829
|
});
|
|
7364
7830
|
return container;
|
|
7365
7831
|
}
|
|
7832
|
+
static registerCacheServices(container, cache, network) {
|
|
7833
|
+
container.register(DI_TOKENS.CACHE_PORT, cache);
|
|
7834
|
+
if (network) {
|
|
7835
|
+
container.register(DI_TOKENS.NETWORK_PORT, network);
|
|
7836
|
+
}
|
|
7837
|
+
const keyGenerator = new CacheKeyGenerator();
|
|
7838
|
+
container.register(DI_TOKENS.CACHE_KEY_GENERATOR, keyGenerator);
|
|
7839
|
+
const baseHttp = container.get(DI_TOKENS.BASE_HTTP_PORT);
|
|
7840
|
+
const cachingHttp = new CachingHttpDecorator(baseHttp, cache, keyGenerator, network);
|
|
7841
|
+
container.register(DI_TOKENS.HTTP_PORT, cachingHttp);
|
|
7842
|
+
}
|
|
7843
|
+
static getCacheKeyGenerator(container) {
|
|
7844
|
+
if (container.has(DI_TOKENS.CACHE_KEY_GENERATOR)) {
|
|
7845
|
+
return container.get(DI_TOKENS.CACHE_KEY_GENERATOR);
|
|
7846
|
+
}
|
|
7847
|
+
return undefined;
|
|
7848
|
+
}
|
|
7366
7849
|
static registerAuthServices(container, secureStorage, config) {
|
|
7367
7850
|
const tokenStorage = new TokenStorageAdapter(secureStorage);
|
|
7368
7851
|
container.register(DI_TOKENS.TOKEN_STORAGE_PORT, tokenStorage);
|
|
@@ -7400,6 +7883,7 @@ class SDKFactory {
|
|
|
7400
7883
|
}
|
|
7401
7884
|
}
|
|
7402
7885
|
|
|
7886
|
+
const log$1 = createPrefixedLogger('SDK');
|
|
7403
7887
|
class ACubeSDK {
|
|
7404
7888
|
constructor(config, customAdapters, events = {}) {
|
|
7405
7889
|
this.events = events;
|
|
@@ -7413,14 +7897,27 @@ class ACubeSDK {
|
|
|
7413
7897
|
}
|
|
7414
7898
|
async initialize() {
|
|
7415
7899
|
if (this.isInitialized) {
|
|
7900
|
+
log$1.debug('SDK already initialized, skipping');
|
|
7416
7901
|
return;
|
|
7417
7902
|
}
|
|
7903
|
+
log$1.info('Initializing SDK', {
|
|
7904
|
+
apiUrl: this.config.getApiUrl(),
|
|
7905
|
+
authUrl: this.config.getAuthUrl(),
|
|
7906
|
+
debugEnabled: this.config.isDebugEnabled(),
|
|
7907
|
+
});
|
|
7418
7908
|
try {
|
|
7419
7909
|
if (!this.adapters) {
|
|
7910
|
+
log$1.debug('Loading platform adapters');
|
|
7420
7911
|
const mtlsConfig = createACubeMTLSConfig(this.config.getApiUrl(), this.config.getTimeout(), true);
|
|
7421
7912
|
this.adapters = loadPlatformAdapters({
|
|
7422
7913
|
mtlsConfig,
|
|
7423
7914
|
});
|
|
7915
|
+
log$1.info('Platform adapters loaded', {
|
|
7916
|
+
hasCache: !!this.adapters.cache,
|
|
7917
|
+
hasNetworkMonitor: !!this.adapters.networkMonitor,
|
|
7918
|
+
hasMtls: !!this.adapters.mtls,
|
|
7919
|
+
hasSecureStorage: !!this.adapters.secureStorage,
|
|
7920
|
+
});
|
|
7424
7921
|
}
|
|
7425
7922
|
const factoryConfig = {
|
|
7426
7923
|
baseUrl: this.config.getApiUrl(),
|
|
@@ -7428,11 +7925,25 @@ class ACubeSDK {
|
|
|
7428
7925
|
timeout: this.config.getTimeout(),
|
|
7429
7926
|
debugEnabled: this.config.isDebugEnabled(),
|
|
7430
7927
|
};
|
|
7928
|
+
log$1.debug('Creating DI container');
|
|
7431
7929
|
this.container = SDKFactory.createContainer(factoryConfig);
|
|
7930
|
+
log$1.debug('Registering auth services');
|
|
7432
7931
|
SDKFactory.registerAuthServices(this.container, this.adapters.secureStorage, factoryConfig);
|
|
7932
|
+
if (this.adapters.cache) {
|
|
7933
|
+
log$1.info('Registering cache services', {
|
|
7934
|
+
hasNetworkMonitor: !!this.adapters.networkMonitor,
|
|
7935
|
+
});
|
|
7936
|
+
SDKFactory.registerCacheServices(this.container, this.adapters.cache, this.adapters.networkMonitor);
|
|
7937
|
+
}
|
|
7938
|
+
else {
|
|
7939
|
+
log$1.debug('No cache adapter available, caching disabled');
|
|
7940
|
+
}
|
|
7941
|
+
log$1.debug('Initializing certificate service');
|
|
7433
7942
|
this.certificateService = new CertificateService(this.adapters.secureStorage);
|
|
7434
7943
|
const tokenStorage = this.container.get(DI_TOKENS.TOKEN_STORAGE_PORT);
|
|
7435
7944
|
const httpPort = this.container.get(DI_TOKENS.HTTP_PORT);
|
|
7945
|
+
const baseHttpPort = this.container.get(DI_TOKENS.BASE_HTTP_PORT);
|
|
7946
|
+
log$1.debug('Initializing authentication service');
|
|
7436
7947
|
this.authService = new AuthenticationService(httpPort, tokenStorage, {
|
|
7437
7948
|
authUrl: this.config.getAuthUrl(),
|
|
7438
7949
|
timeout: this.config.getTimeout(),
|
|
@@ -7442,6 +7953,7 @@ class ACubeSDK {
|
|
|
7442
7953
|
this.events.onAuthError?.(new ACubeSDKError('AUTH_ERROR', error.message, error));
|
|
7443
7954
|
},
|
|
7444
7955
|
});
|
|
7956
|
+
log$1.debug('Initializing offline manager');
|
|
7445
7957
|
const queueEvents = {
|
|
7446
7958
|
onOperationAdded: (operation) => {
|
|
7447
7959
|
this.events.onOfflineOperationAdded?.(operation.id);
|
|
@@ -7460,9 +7972,7 @@ class ACubeSDK {
|
|
|
7460
7972
|
this.currentOnlineState = online;
|
|
7461
7973
|
this.events.onNetworkStatusChanged?.(online);
|
|
7462
7974
|
if (online && this.offlineManager) {
|
|
7463
|
-
this.offlineManager.sync().catch(() => {
|
|
7464
|
-
// Sync errors are handled internally by OfflineManager
|
|
7465
|
-
});
|
|
7975
|
+
this.offlineManager.sync().catch(() => { });
|
|
7466
7976
|
}
|
|
7467
7977
|
});
|
|
7468
7978
|
if (await this.authService.isAuthenticated()) {
|
|
@@ -7471,13 +7981,13 @@ class ACubeSDK {
|
|
|
7471
7981
|
httpPort.setAuthToken(token);
|
|
7472
7982
|
}
|
|
7473
7983
|
}
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
const httpWithMtls =
|
|
7984
|
+
if (this.adapters?.mtls && 'setMTLSAdapter' in baseHttpPort) {
|
|
7985
|
+
log$1.debug('Connecting mTLS adapter to HTTP port');
|
|
7986
|
+
const httpWithMtls = baseHttpPort;
|
|
7477
7987
|
httpWithMtls.setMTLSAdapter(this.adapters.mtls);
|
|
7478
7988
|
}
|
|
7479
|
-
|
|
7480
|
-
|
|
7989
|
+
if ('setAuthStrategy' in baseHttpPort) {
|
|
7990
|
+
log$1.debug('Configuring auth strategy');
|
|
7481
7991
|
const jwtHandler = new JwtAuthHandler(tokenStorage);
|
|
7482
7992
|
const certificatePort = this.certificateService
|
|
7483
7993
|
? {
|
|
@@ -7508,7 +8018,7 @@ class ACubeSDK {
|
|
|
7508
8018
|
},
|
|
7509
8019
|
};
|
|
7510
8020
|
const authStrategy = new AuthStrategy(jwtHandler, mtlsHandler, userProvider, this.adapters?.mtls || null);
|
|
7511
|
-
const httpWithStrategy =
|
|
8021
|
+
const httpWithStrategy = baseHttpPort;
|
|
7512
8022
|
httpWithStrategy.setAuthStrategy(authStrategy);
|
|
7513
8023
|
}
|
|
7514
8024
|
if (this.adapters?.mtls && this.certificateService) {
|
|
@@ -7525,13 +8035,22 @@ class ACubeSDK {
|
|
|
7525
8035
|
}
|
|
7526
8036
|
}
|
|
7527
8037
|
}
|
|
7528
|
-
catch {
|
|
7529
|
-
|
|
8038
|
+
catch (certError) {
|
|
8039
|
+
log$1.warn('Certificate auto-configuration failed, will retry on demand', {
|
|
8040
|
+
error: certError instanceof Error ? certError.message : certError,
|
|
8041
|
+
});
|
|
7530
8042
|
}
|
|
7531
8043
|
}
|
|
7532
8044
|
this.isInitialized = true;
|
|
8045
|
+
log$1.info('SDK initialized successfully', {
|
|
8046
|
+
hasCache: !!this.adapters.cache,
|
|
8047
|
+
hasMtls: !!this.adapters.mtls,
|
|
8048
|
+
});
|
|
7533
8049
|
}
|
|
7534
8050
|
catch (error) {
|
|
8051
|
+
log$1.error('SDK initialization failed', {
|
|
8052
|
+
error: error instanceof Error ? error.message : error,
|
|
8053
|
+
});
|
|
7535
8054
|
throw new ACubeSDKError('SDK_INITIALIZATION_ERROR', `Failed to initialize SDK: ${error instanceof Error ? error.message : 'Unknown error'}`, error);
|
|
7536
8055
|
}
|
|
7537
8056
|
}
|
|
@@ -7585,15 +8104,19 @@ class ACubeSDK {
|
|
|
7585
8104
|
}
|
|
7586
8105
|
async login(credentials) {
|
|
7587
8106
|
this.ensureInitialized();
|
|
8107
|
+
log$1.info('Login attempt', { email: credentials.email });
|
|
7588
8108
|
const user = await this.authService.login(credentials);
|
|
8109
|
+
log$1.info('Login successful', { roles: user.roles });
|
|
7589
8110
|
const token = await this.authService.getAccessToken();
|
|
7590
8111
|
if (token) {
|
|
7591
8112
|
this.httpPort.setAuthToken(token);
|
|
8113
|
+
log$1.debug('Auth token set on HTTP port');
|
|
7592
8114
|
}
|
|
7593
8115
|
return user;
|
|
7594
8116
|
}
|
|
7595
8117
|
async logout() {
|
|
7596
8118
|
this.ensureInitialized();
|
|
8119
|
+
log$1.info('Logout');
|
|
7597
8120
|
await this.authService.logout();
|
|
7598
8121
|
this.httpPort.setAuthToken(null);
|
|
7599
8122
|
}
|
|
@@ -7674,12 +8197,7 @@ class ACubeSDK {
|
|
|
7674
8197
|
async clearCertificate() {
|
|
7675
8198
|
this.ensureInitialized();
|
|
7676
8199
|
if (this.adapters?.mtls) {
|
|
7677
|
-
|
|
7678
|
-
await this.adapters.mtls.removeCertificate();
|
|
7679
|
-
}
|
|
7680
|
-
catch {
|
|
7681
|
-
// No certificate to remove
|
|
7682
|
-
}
|
|
8200
|
+
await this.adapters.mtls.removeCertificate().catch(() => { });
|
|
7683
8201
|
}
|
|
7684
8202
|
if (this.certificateService) {
|
|
7685
8203
|
await this.certificateService.clearCertificate();
|
|
@@ -8014,17 +8532,16 @@ class NotificationService {
|
|
|
8014
8532
|
}
|
|
8015
8533
|
|
|
8016
8534
|
const DEFAULT_CONFIG = {
|
|
8017
|
-
|
|
8018
|
-
cacheTtlMs: 300000,
|
|
8535
|
+
pollIntervalMs: 60000, // 1 minute default for telemetry
|
|
8019
8536
|
};
|
|
8020
8537
|
class TelemetryService {
|
|
8021
8538
|
get state$() {
|
|
8022
8539
|
return this.stateSubject.asObservable();
|
|
8023
8540
|
}
|
|
8024
|
-
constructor(repository,
|
|
8541
|
+
constructor(repository, networkPort, config, events) {
|
|
8025
8542
|
this.repository = repository;
|
|
8026
|
-
this.storagePort = storagePort;
|
|
8027
8543
|
this.networkPort = networkPort;
|
|
8544
|
+
this.events = events;
|
|
8028
8545
|
this.stateSubject = new BehaviorSubject({
|
|
8029
8546
|
data: null,
|
|
8030
8547
|
isCached: false,
|
|
@@ -8032,29 +8549,69 @@ class TelemetryService {
|
|
|
8032
8549
|
lastFetchedAt: null,
|
|
8033
8550
|
});
|
|
8034
8551
|
this.destroy$ = new Subject();
|
|
8035
|
-
this.isOnline = true;
|
|
8036
8552
|
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
8037
8553
|
this.setupNetworkMonitoring();
|
|
8038
8554
|
}
|
|
8039
8555
|
setupNetworkMonitoring() {
|
|
8040
|
-
this.networkPort.online
|
|
8041
|
-
|
|
8556
|
+
this.networkSubscription = this.networkPort.online$
|
|
8557
|
+
.pipe(startWith(true), pairwise(), filter(([wasOnline, isNowOnline]) => !wasOnline && isNowOnline), takeUntil(this.destroy$))
|
|
8558
|
+
.subscribe(() => {
|
|
8559
|
+
this.triggerSync();
|
|
8042
8560
|
});
|
|
8043
8561
|
}
|
|
8562
|
+
startPolling(pemId) {
|
|
8563
|
+
if (this.pollingSubscription) {
|
|
8564
|
+
// If already polling for same pemId, do nothing
|
|
8565
|
+
if (this.currentPemId === pemId) {
|
|
8566
|
+
return;
|
|
8567
|
+
}
|
|
8568
|
+
// If polling for different pemId, stop and restart
|
|
8569
|
+
this.stopPolling();
|
|
8570
|
+
}
|
|
8571
|
+
this.currentPemId = pemId;
|
|
8572
|
+
this.pollingSubscription = interval(this.config.pollIntervalMs)
|
|
8573
|
+
.pipe(startWith(0), switchMap(() => this.fetchTelemetry()), takeUntil(this.destroy$))
|
|
8574
|
+
.subscribe();
|
|
8575
|
+
}
|
|
8576
|
+
stopPolling() {
|
|
8577
|
+
this.pollingSubscription?.unsubscribe();
|
|
8578
|
+
this.pollingSubscription = undefined;
|
|
8579
|
+
this.currentPemId = undefined;
|
|
8580
|
+
}
|
|
8581
|
+
async triggerSync() {
|
|
8582
|
+
if (!this.currentPemId) {
|
|
8583
|
+
return this.stateSubject.value;
|
|
8584
|
+
}
|
|
8585
|
+
return this.fetchTelemetry();
|
|
8586
|
+
}
|
|
8044
8587
|
async getTelemetry(pemId) {
|
|
8045
|
-
|
|
8588
|
+
// Start polling if not already polling for this pemId
|
|
8589
|
+
if (this.currentPemId !== pemId) {
|
|
8590
|
+
this.startPolling(pemId);
|
|
8591
|
+
}
|
|
8592
|
+
return this.stateSubject.value;
|
|
8046
8593
|
}
|
|
8047
8594
|
async refreshTelemetry(pemId) {
|
|
8048
|
-
|
|
8595
|
+
// Update pemId and fetch immediately
|
|
8596
|
+
if (this.currentPemId !== pemId) {
|
|
8597
|
+
this.startPolling(pemId);
|
|
8598
|
+
}
|
|
8599
|
+
else {
|
|
8600
|
+
return this.fetchTelemetry();
|
|
8601
|
+
}
|
|
8602
|
+
return this.stateSubject.value;
|
|
8603
|
+
}
|
|
8604
|
+
async fetchTelemetry() {
|
|
8605
|
+
if (!this.currentPemId) {
|
|
8049
8606
|
return this.stateSubject.value;
|
|
8050
8607
|
}
|
|
8051
8608
|
this.stateSubject.next({
|
|
8052
8609
|
...this.stateSubject.value,
|
|
8053
8610
|
isLoading: true,
|
|
8611
|
+
error: undefined,
|
|
8054
8612
|
});
|
|
8055
8613
|
try {
|
|
8056
|
-
const data = await this.repository.getTelemetry(
|
|
8057
|
-
await this.cacheData(pemId, data);
|
|
8614
|
+
const data = await this.repository.getTelemetry(this.currentPemId);
|
|
8058
8615
|
const newState = {
|
|
8059
8616
|
data,
|
|
8060
8617
|
isCached: false,
|
|
@@ -8062,6 +8619,7 @@ class TelemetryService {
|
|
|
8062
8619
|
lastFetchedAt: Date.now(),
|
|
8063
8620
|
};
|
|
8064
8621
|
this.stateSubject.next(newState);
|
|
8622
|
+
this.events?.onTelemetryUpdate?.(data);
|
|
8065
8623
|
return newState;
|
|
8066
8624
|
}
|
|
8067
8625
|
catch (error) {
|
|
@@ -8072,89 +8630,25 @@ class TelemetryService {
|
|
|
8072
8630
|
error: errorMessage,
|
|
8073
8631
|
};
|
|
8074
8632
|
this.stateSubject.next(newState);
|
|
8633
|
+
if (error instanceof Error) {
|
|
8634
|
+
this.events?.onSyncError?.(error);
|
|
8635
|
+
}
|
|
8075
8636
|
return newState;
|
|
8076
8637
|
}
|
|
8077
8638
|
}
|
|
8078
|
-
|
|
8639
|
+
clearTelemetry() {
|
|
8079
8640
|
this.stateSubject.next({
|
|
8080
|
-
...this.stateSubject.value,
|
|
8081
|
-
isLoading: true,
|
|
8082
|
-
});
|
|
8083
|
-
if (this.isOnline) {
|
|
8084
|
-
try {
|
|
8085
|
-
const data = await this.repository.getTelemetry(pemId);
|
|
8086
|
-
await this.cacheData(pemId, data);
|
|
8087
|
-
const newState = {
|
|
8088
|
-
data,
|
|
8089
|
-
isCached: false,
|
|
8090
|
-
isLoading: false,
|
|
8091
|
-
lastFetchedAt: Date.now(),
|
|
8092
|
-
};
|
|
8093
|
-
this.stateSubject.next(newState);
|
|
8094
|
-
return newState;
|
|
8095
|
-
}
|
|
8096
|
-
catch (error) {
|
|
8097
|
-
return this.loadFromCache(pemId, error);
|
|
8098
|
-
}
|
|
8099
|
-
}
|
|
8100
|
-
return this.loadFromCache(pemId);
|
|
8101
|
-
}
|
|
8102
|
-
async loadFromCache(pemId, originalError) {
|
|
8103
|
-
const cached = await this.getCachedData(pemId);
|
|
8104
|
-
if (cached && this.isCacheValid(cached.timestamp)) {
|
|
8105
|
-
const newState = {
|
|
8106
|
-
data: cached.data,
|
|
8107
|
-
isCached: true,
|
|
8108
|
-
isLoading: false,
|
|
8109
|
-
lastFetchedAt: cached.timestamp,
|
|
8110
|
-
};
|
|
8111
|
-
this.stateSubject.next(newState);
|
|
8112
|
-
return newState;
|
|
8113
|
-
}
|
|
8114
|
-
const errorMessage = originalError instanceof Error ? originalError.message : 'No cached data available';
|
|
8115
|
-
const newState = {
|
|
8116
8641
|
data: null,
|
|
8117
8642
|
isCached: false,
|
|
8118
8643
|
isLoading: false,
|
|
8119
8644
|
lastFetchedAt: null,
|
|
8120
|
-
|
|
8121
|
-
};
|
|
8122
|
-
this.stateSubject.next(newState);
|
|
8123
|
-
return newState;
|
|
8124
|
-
}
|
|
8125
|
-
async cacheData(pemId, data) {
|
|
8126
|
-
const cacheKey = this.getCacheKey(pemId);
|
|
8127
|
-
const cached = {
|
|
8128
|
-
data,
|
|
8129
|
-
timestamp: Date.now(),
|
|
8130
|
-
};
|
|
8131
|
-
await this.storagePort.set(cacheKey, JSON.stringify(cached));
|
|
8132
|
-
}
|
|
8133
|
-
async getCachedData(pemId) {
|
|
8134
|
-
const cacheKey = this.getCacheKey(pemId);
|
|
8135
|
-
const stored = await this.storagePort.get(cacheKey);
|
|
8136
|
-
if (!stored) {
|
|
8137
|
-
return null;
|
|
8138
|
-
}
|
|
8139
|
-
try {
|
|
8140
|
-
return JSON.parse(stored);
|
|
8141
|
-
}
|
|
8142
|
-
catch {
|
|
8143
|
-
return null;
|
|
8144
|
-
}
|
|
8145
|
-
}
|
|
8146
|
-
isCacheValid(timestamp) {
|
|
8147
|
-
return Date.now() - timestamp < this.config.cacheTtlMs;
|
|
8148
|
-
}
|
|
8149
|
-
getCacheKey(pemId) {
|
|
8150
|
-
return `${this.config.cacheKeyPrefix}${pemId}`;
|
|
8151
|
-
}
|
|
8152
|
-
clearCache(pemId) {
|
|
8153
|
-
return this.storagePort.remove(this.getCacheKey(pemId));
|
|
8645
|
+
});
|
|
8154
8646
|
}
|
|
8155
8647
|
destroy() {
|
|
8156
8648
|
this.destroy$.next();
|
|
8157
8649
|
this.destroy$.complete();
|
|
8650
|
+
this.pollingSubscription?.unsubscribe();
|
|
8651
|
+
this.networkSubscription?.unsubscribe();
|
|
8158
8652
|
}
|
|
8159
8653
|
}
|
|
8160
8654
|
|
|
@@ -8164,7 +8658,7 @@ class TelemetryService {
|
|
|
8164
8658
|
* Provides:
|
|
8165
8659
|
* - Single initialization point
|
|
8166
8660
|
* - Observable app state (NORMAL, WARNING, BLOCKED, OFFLINE)
|
|
8167
|
-
* - Observable telemetry state
|
|
8661
|
+
* - Observable telemetry state with polling
|
|
8168
8662
|
* - Simplified services for product use
|
|
8169
8663
|
*
|
|
8170
8664
|
* @example
|
|
@@ -8173,6 +8667,7 @@ class TelemetryService {
|
|
|
8173
8667
|
* SDKManager.configure({
|
|
8174
8668
|
* environment: 'sandbox',
|
|
8175
8669
|
* notificationPollIntervalMs: 30000,
|
|
8670
|
+
* telemetryPollIntervalMs: 60000,
|
|
8176
8671
|
* });
|
|
8177
8672
|
*
|
|
8178
8673
|
* // Initialize
|
|
@@ -8184,6 +8679,14 @@ class TelemetryService {
|
|
|
8184
8679
|
* console.log('App mode:', state.mode);
|
|
8185
8680
|
* });
|
|
8186
8681
|
*
|
|
8682
|
+
* // Start telemetry polling for a specific PEM
|
|
8683
|
+
* manager.startTelemetryPolling('PEM-123');
|
|
8684
|
+
*
|
|
8685
|
+
* // Subscribe to telemetry updates
|
|
8686
|
+
* manager.telemetry$.subscribe(telemetry => {
|
|
8687
|
+
* console.log('Telemetry:', telemetry);
|
|
8688
|
+
* });
|
|
8689
|
+
*
|
|
8187
8690
|
* // Cleanup
|
|
8188
8691
|
* SDKManager.destroy();
|
|
8189
8692
|
* ```
|
|
@@ -8239,43 +8742,40 @@ class SDKManager {
|
|
|
8239
8742
|
* Must be called after configure()
|
|
8240
8743
|
*/
|
|
8241
8744
|
async initialize() {
|
|
8242
|
-
if (this.isInitialized)
|
|
8745
|
+
if (this.isInitialized)
|
|
8243
8746
|
return;
|
|
8244
|
-
}
|
|
8245
|
-
// Create and initialize SDK
|
|
8246
8747
|
this.sdk = new ACubeSDK(this.config, this.adapters, this.events);
|
|
8247
8748
|
await this.sdk.initialize();
|
|
8248
|
-
// Get required adapters
|
|
8249
8749
|
const adaptersRef = this.sdk.getAdapters();
|
|
8250
8750
|
if (!adaptersRef) {
|
|
8251
8751
|
throw new ACubeSDKError('ADAPTERS_NOT_AVAILABLE', 'Platform adapters not available');
|
|
8252
8752
|
}
|
|
8253
8753
|
const networkPort = adaptersRef.networkMonitor;
|
|
8254
|
-
const storagePort = adaptersRef.storage;
|
|
8255
|
-
// Get repositories from SDK
|
|
8256
8754
|
const notificationRepo = this.sdk.notifications;
|
|
8257
8755
|
const telemetryRepo = this.sdk.telemetry;
|
|
8258
|
-
// Create NotificationService
|
|
8259
8756
|
this.notificationService = new NotificationService(notificationRepo, networkPort, {
|
|
8260
8757
|
pollIntervalMs: this.config.notificationPollIntervalMs ?? 30000,
|
|
8261
8758
|
defaultPageSize: this.config.notificationPageSize ?? 30,
|
|
8262
8759
|
});
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
cacheTtlMs: this.config.telemetryCacheTtlMs ?? 300000,
|
|
8760
|
+
this.telemetryService = new TelemetryService(telemetryRepo, networkPort, {
|
|
8761
|
+
pollIntervalMs: this.config.telemetryPollIntervalMs ?? 60000,
|
|
8266
8762
|
});
|
|
8267
|
-
// Create AppStateService
|
|
8268
8763
|
this.appStateService = new AppStateService(this.notificationService.notifications$, networkPort);
|
|
8269
|
-
// Subscribe to state changes for events
|
|
8270
8764
|
if (this.events?.onAppStateChanged) {
|
|
8271
8765
|
this.appStateService.state$.subscribe(this.events.onAppStateChanged);
|
|
8272
8766
|
}
|
|
8273
8767
|
if (this.events?.onTelemetryStateChanged) {
|
|
8274
8768
|
this.telemetryService.state$.subscribe(this.events.onTelemetryStateChanged);
|
|
8275
8769
|
}
|
|
8276
|
-
// Start notification polling
|
|
8277
|
-
this.notificationService.startPolling();
|
|
8278
8770
|
this.isInitialized = true;
|
|
8771
|
+
// Only start polling for MERCHANT/CASHIER users (SUPPLIER gets 401 on these endpoints)
|
|
8772
|
+
const user = await this.sdk.getCurrentUser();
|
|
8773
|
+
const canPoll = user && hasAnyRole(user.roles, ['ROLE_MERCHANT', 'ROLE_CASHIER']);
|
|
8774
|
+
if (canPoll) {
|
|
8775
|
+
this.notificationService.startPolling();
|
|
8776
|
+
await this.startTelemetryPollingAuto();
|
|
8777
|
+
}
|
|
8778
|
+
// AppStateService remains active for all users (handles OFFLINE network state)
|
|
8279
8779
|
}
|
|
8280
8780
|
/**
|
|
8281
8781
|
* Observable stream of app state
|
|
@@ -8308,12 +8808,51 @@ class SDKManager {
|
|
|
8308
8808
|
return this.appStateService.warning$;
|
|
8309
8809
|
}
|
|
8310
8810
|
/**
|
|
8311
|
-
* Observable stream of telemetry state
|
|
8811
|
+
* Observable stream of telemetry state (data, isLoading, isCached, error)
|
|
8312
8812
|
*/
|
|
8313
8813
|
get telemetryState$() {
|
|
8314
8814
|
this.ensureInitialized();
|
|
8315
8815
|
return this.telemetryService.state$;
|
|
8316
8816
|
}
|
|
8817
|
+
/**
|
|
8818
|
+
* Get the pemId from the installed certificate
|
|
8819
|
+
*/
|
|
8820
|
+
async getPemId() {
|
|
8821
|
+
this.ensureInitialized();
|
|
8822
|
+
try {
|
|
8823
|
+
const certInfo = await this.sdk.getCertificatesInfo();
|
|
8824
|
+
return certInfo?.pemId ?? null;
|
|
8825
|
+
}
|
|
8826
|
+
catch {
|
|
8827
|
+
return null;
|
|
8828
|
+
}
|
|
8829
|
+
}
|
|
8830
|
+
/**
|
|
8831
|
+
* Start polling telemetry using the pemId from installed certificate
|
|
8832
|
+
* Returns the pemId if successful, null if no certificate is installed
|
|
8833
|
+
*/
|
|
8834
|
+
async startTelemetryPollingAuto() {
|
|
8835
|
+
this.ensureInitialized();
|
|
8836
|
+
const pemId = await this.getPemId();
|
|
8837
|
+
if (pemId) {
|
|
8838
|
+
this.telemetryService.startPolling(pemId);
|
|
8839
|
+
}
|
|
8840
|
+
return pemId;
|
|
8841
|
+
}
|
|
8842
|
+
/**
|
|
8843
|
+
* Start polling telemetry for a specific PEM
|
|
8844
|
+
*/
|
|
8845
|
+
startTelemetryPolling(pemId) {
|
|
8846
|
+
this.ensureInitialized();
|
|
8847
|
+
this.telemetryService.startPolling(pemId);
|
|
8848
|
+
}
|
|
8849
|
+
/**
|
|
8850
|
+
* Stop telemetry polling
|
|
8851
|
+
*/
|
|
8852
|
+
stopTelemetryPolling() {
|
|
8853
|
+
this.ensureInitialized();
|
|
8854
|
+
this.telemetryService.stopPolling();
|
|
8855
|
+
}
|
|
8317
8856
|
/**
|
|
8318
8857
|
* Get simplified services for product use
|
|
8319
8858
|
*/
|
|
@@ -8322,7 +8861,6 @@ class SDKManager {
|
|
|
8322
8861
|
const sdk = this.sdk;
|
|
8323
8862
|
const telemetryService = this.telemetryService;
|
|
8324
8863
|
return {
|
|
8325
|
-
// Business repositories
|
|
8326
8864
|
receipts: sdk.receipts,
|
|
8327
8865
|
merchants: sdk.merchants,
|
|
8328
8866
|
cashiers: sdk.cashiers,
|
|
@@ -8332,22 +8870,23 @@ class SDKManager {
|
|
|
8332
8870
|
pems: sdk.pems,
|
|
8333
8871
|
dailyReports: sdk.dailyReports,
|
|
8334
8872
|
journals: sdk.journals,
|
|
8335
|
-
// Simplified telemetry operations
|
|
8336
8873
|
telemetry: {
|
|
8874
|
+
startPollingAuto: () => this.startTelemetryPollingAuto(),
|
|
8875
|
+
startPolling: (pemId) => telemetryService.startPolling(pemId),
|
|
8876
|
+
stopPolling: () => telemetryService.stopPolling(),
|
|
8337
8877
|
getTelemetry: (pemId) => telemetryService.getTelemetry(pemId),
|
|
8338
8878
|
refreshTelemetry: (pemId) => telemetryService.refreshTelemetry(pemId),
|
|
8339
|
-
|
|
8879
|
+
triggerSync: () => telemetryService.triggerSync(),
|
|
8880
|
+
clearTelemetry: () => telemetryService.clearTelemetry(),
|
|
8881
|
+
getPemId: () => this.getPemId(),
|
|
8340
8882
|
},
|
|
8341
|
-
// Auth operations
|
|
8342
8883
|
login: (credentials) => sdk.login(credentials),
|
|
8343
8884
|
logout: () => sdk.logout(),
|
|
8344
8885
|
getCurrentUser: () => sdk.getCurrentUser(),
|
|
8345
8886
|
isAuthenticated: () => sdk.isAuthenticated(),
|
|
8346
|
-
// Certificate operations
|
|
8347
8887
|
storeCertificate: (certificate, privateKey, options) => sdk.storeCertificate(certificate, privateKey, options),
|
|
8348
8888
|
hasCertificate: () => sdk.hasCertificate(),
|
|
8349
8889
|
clearCertificate: () => sdk.clearCertificate(),
|
|
8350
|
-
// Network status
|
|
8351
8890
|
isOnline: () => sdk.isOnline(),
|
|
8352
8891
|
};
|
|
8353
8892
|
}
|
|
@@ -8359,12 +8898,11 @@ class SDKManager {
|
|
|
8359
8898
|
await this.notificationService.triggerSync();
|
|
8360
8899
|
}
|
|
8361
8900
|
/**
|
|
8362
|
-
*
|
|
8901
|
+
* Manually trigger a telemetry sync
|
|
8363
8902
|
*/
|
|
8364
|
-
async
|
|
8903
|
+
async syncTelemetry() {
|
|
8365
8904
|
this.ensureInitialized();
|
|
8366
|
-
|
|
8367
|
-
return state.data;
|
|
8905
|
+
return this.telemetryService.triggerSync();
|
|
8368
8906
|
}
|
|
8369
8907
|
/**
|
|
8370
8908
|
* Check if the manager is initialized
|