@a-cube-io/ereceipts-js-sdk 2.0.2 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/dist/index.cjs.js +828 -301
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +88 -31
- package/dist/index.esm.js +828 -301
- package/dist/index.esm.js.map +1 -1
- package/dist/index.native.js +828 -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;
|
|
@@ -7170,30 +7630,30 @@ class AxiosHttpAdapter {
|
|
|
7170
7630
|
config.headers.Authorization = `Bearer ${this.authToken}`;
|
|
7171
7631
|
}
|
|
7172
7632
|
const method = config.method?.toUpperCase() ?? 'UNKNOWN';
|
|
7173
|
-
log$
|
|
7633
|
+
log$2.debug(`→ ${method} ${config.url}`);
|
|
7174
7634
|
if (config.params && Object.keys(config.params).length > 0) {
|
|
7175
|
-
log$
|
|
7635
|
+
log$2.debug('Request params:', config.params);
|
|
7176
7636
|
}
|
|
7177
7637
|
if (config.data) {
|
|
7178
|
-
log$
|
|
7638
|
+
log$2.debug('Request body:', config.data);
|
|
7179
7639
|
}
|
|
7180
7640
|
return config;
|
|
7181
7641
|
}, (error) => {
|
|
7182
|
-
log$
|
|
7642
|
+
log$2.error('Request error:', error);
|
|
7183
7643
|
return Promise.reject(error);
|
|
7184
7644
|
});
|
|
7185
7645
|
this.client.interceptors.response.use((response) => {
|
|
7186
7646
|
const method = response.config.method?.toUpperCase() ?? 'UNKNOWN';
|
|
7187
|
-
log$
|
|
7647
|
+
log$2.debug(`← ${method} ${response.status} ${response.config.url}`);
|
|
7188
7648
|
if (response.data) {
|
|
7189
|
-
log$
|
|
7649
|
+
log$2.debug('Response body:', response.data);
|
|
7190
7650
|
}
|
|
7191
7651
|
return response;
|
|
7192
7652
|
}, (error) => {
|
|
7193
7653
|
const method = error.config?.method?.toUpperCase() ?? 'UNKNOWN';
|
|
7194
|
-
log$
|
|
7654
|
+
log$2.error(`← ${method} ${error.response?.status ?? 'ERR'} ${error.config?.url ?? 'unknown'}`);
|
|
7195
7655
|
if (error.response?.data) {
|
|
7196
|
-
log$
|
|
7656
|
+
log$2.error('Response body:', error.response.data);
|
|
7197
7657
|
}
|
|
7198
7658
|
return Promise.reject(error);
|
|
7199
7659
|
});
|
|
@@ -7316,6 +7776,7 @@ class SDKFactory {
|
|
|
7316
7776
|
baseUrl: config.baseUrl,
|
|
7317
7777
|
timeout: config.timeout,
|
|
7318
7778
|
});
|
|
7779
|
+
container.register(DI_TOKENS.BASE_HTTP_PORT, httpAdapter);
|
|
7319
7780
|
container.register(DI_TOKENS.HTTP_PORT, httpAdapter);
|
|
7320
7781
|
container.registerFactory(DI_TOKENS.RECEIPT_REPOSITORY, () => {
|
|
7321
7782
|
const http = container.get(DI_TOKENS.HTTP_PORT);
|
|
@@ -7363,6 +7824,23 @@ class SDKFactory {
|
|
|
7363
7824
|
});
|
|
7364
7825
|
return container;
|
|
7365
7826
|
}
|
|
7827
|
+
static registerCacheServices(container, cache, network) {
|
|
7828
|
+
container.register(DI_TOKENS.CACHE_PORT, cache);
|
|
7829
|
+
if (network) {
|
|
7830
|
+
container.register(DI_TOKENS.NETWORK_PORT, network);
|
|
7831
|
+
}
|
|
7832
|
+
const keyGenerator = new CacheKeyGenerator();
|
|
7833
|
+
container.register(DI_TOKENS.CACHE_KEY_GENERATOR, keyGenerator);
|
|
7834
|
+
const baseHttp = container.get(DI_TOKENS.BASE_HTTP_PORT);
|
|
7835
|
+
const cachingHttp = new CachingHttpDecorator(baseHttp, cache, keyGenerator, network);
|
|
7836
|
+
container.register(DI_TOKENS.HTTP_PORT, cachingHttp);
|
|
7837
|
+
}
|
|
7838
|
+
static getCacheKeyGenerator(container) {
|
|
7839
|
+
if (container.has(DI_TOKENS.CACHE_KEY_GENERATOR)) {
|
|
7840
|
+
return container.get(DI_TOKENS.CACHE_KEY_GENERATOR);
|
|
7841
|
+
}
|
|
7842
|
+
return undefined;
|
|
7843
|
+
}
|
|
7366
7844
|
static registerAuthServices(container, secureStorage, config) {
|
|
7367
7845
|
const tokenStorage = new TokenStorageAdapter(secureStorage);
|
|
7368
7846
|
container.register(DI_TOKENS.TOKEN_STORAGE_PORT, tokenStorage);
|
|
@@ -7400,6 +7878,7 @@ class SDKFactory {
|
|
|
7400
7878
|
}
|
|
7401
7879
|
}
|
|
7402
7880
|
|
|
7881
|
+
const log$1 = createPrefixedLogger('SDK');
|
|
7403
7882
|
class ACubeSDK {
|
|
7404
7883
|
constructor(config, customAdapters, events = {}) {
|
|
7405
7884
|
this.events = events;
|
|
@@ -7413,14 +7892,27 @@ class ACubeSDK {
|
|
|
7413
7892
|
}
|
|
7414
7893
|
async initialize() {
|
|
7415
7894
|
if (this.isInitialized) {
|
|
7895
|
+
log$1.debug('SDK already initialized, skipping');
|
|
7416
7896
|
return;
|
|
7417
7897
|
}
|
|
7898
|
+
log$1.info('Initializing SDK', {
|
|
7899
|
+
apiUrl: this.config.getApiUrl(),
|
|
7900
|
+
authUrl: this.config.getAuthUrl(),
|
|
7901
|
+
debugEnabled: this.config.isDebugEnabled(),
|
|
7902
|
+
});
|
|
7418
7903
|
try {
|
|
7419
7904
|
if (!this.adapters) {
|
|
7905
|
+
log$1.debug('Loading platform adapters');
|
|
7420
7906
|
const mtlsConfig = createACubeMTLSConfig(this.config.getApiUrl(), this.config.getTimeout(), true);
|
|
7421
7907
|
this.adapters = loadPlatformAdapters({
|
|
7422
7908
|
mtlsConfig,
|
|
7423
7909
|
});
|
|
7910
|
+
log$1.info('Platform adapters loaded', {
|
|
7911
|
+
hasCache: !!this.adapters.cache,
|
|
7912
|
+
hasNetworkMonitor: !!this.adapters.networkMonitor,
|
|
7913
|
+
hasMtls: !!this.adapters.mtls,
|
|
7914
|
+
hasSecureStorage: !!this.adapters.secureStorage,
|
|
7915
|
+
});
|
|
7424
7916
|
}
|
|
7425
7917
|
const factoryConfig = {
|
|
7426
7918
|
baseUrl: this.config.getApiUrl(),
|
|
@@ -7428,11 +7920,25 @@ class ACubeSDK {
|
|
|
7428
7920
|
timeout: this.config.getTimeout(),
|
|
7429
7921
|
debugEnabled: this.config.isDebugEnabled(),
|
|
7430
7922
|
};
|
|
7923
|
+
log$1.debug('Creating DI container');
|
|
7431
7924
|
this.container = SDKFactory.createContainer(factoryConfig);
|
|
7925
|
+
log$1.debug('Registering auth services');
|
|
7432
7926
|
SDKFactory.registerAuthServices(this.container, this.adapters.secureStorage, factoryConfig);
|
|
7927
|
+
if (this.adapters.cache) {
|
|
7928
|
+
log$1.info('Registering cache services', {
|
|
7929
|
+
hasNetworkMonitor: !!this.adapters.networkMonitor,
|
|
7930
|
+
});
|
|
7931
|
+
SDKFactory.registerCacheServices(this.container, this.adapters.cache, this.adapters.networkMonitor);
|
|
7932
|
+
}
|
|
7933
|
+
else {
|
|
7934
|
+
log$1.debug('No cache adapter available, caching disabled');
|
|
7935
|
+
}
|
|
7936
|
+
log$1.debug('Initializing certificate service');
|
|
7433
7937
|
this.certificateService = new CertificateService(this.adapters.secureStorage);
|
|
7434
7938
|
const tokenStorage = this.container.get(DI_TOKENS.TOKEN_STORAGE_PORT);
|
|
7435
7939
|
const httpPort = this.container.get(DI_TOKENS.HTTP_PORT);
|
|
7940
|
+
const baseHttpPort = this.container.get(DI_TOKENS.BASE_HTTP_PORT);
|
|
7941
|
+
log$1.debug('Initializing authentication service');
|
|
7436
7942
|
this.authService = new AuthenticationService(httpPort, tokenStorage, {
|
|
7437
7943
|
authUrl: this.config.getAuthUrl(),
|
|
7438
7944
|
timeout: this.config.getTimeout(),
|
|
@@ -7442,6 +7948,7 @@ class ACubeSDK {
|
|
|
7442
7948
|
this.events.onAuthError?.(new ACubeSDKError('AUTH_ERROR', error.message, error));
|
|
7443
7949
|
},
|
|
7444
7950
|
});
|
|
7951
|
+
log$1.debug('Initializing offline manager');
|
|
7445
7952
|
const queueEvents = {
|
|
7446
7953
|
onOperationAdded: (operation) => {
|
|
7447
7954
|
this.events.onOfflineOperationAdded?.(operation.id);
|
|
@@ -7460,9 +7967,7 @@ class ACubeSDK {
|
|
|
7460
7967
|
this.currentOnlineState = online;
|
|
7461
7968
|
this.events.onNetworkStatusChanged?.(online);
|
|
7462
7969
|
if (online && this.offlineManager) {
|
|
7463
|
-
this.offlineManager.sync().catch(() => {
|
|
7464
|
-
// Sync errors are handled internally by OfflineManager
|
|
7465
|
-
});
|
|
7970
|
+
this.offlineManager.sync().catch(() => { });
|
|
7466
7971
|
}
|
|
7467
7972
|
});
|
|
7468
7973
|
if (await this.authService.isAuthenticated()) {
|
|
@@ -7471,13 +7976,13 @@ class ACubeSDK {
|
|
|
7471
7976
|
httpPort.setAuthToken(token);
|
|
7472
7977
|
}
|
|
7473
7978
|
}
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
const httpWithMtls =
|
|
7979
|
+
if (this.adapters?.mtls && 'setMTLSAdapter' in baseHttpPort) {
|
|
7980
|
+
log$1.debug('Connecting mTLS adapter to HTTP port');
|
|
7981
|
+
const httpWithMtls = baseHttpPort;
|
|
7477
7982
|
httpWithMtls.setMTLSAdapter(this.adapters.mtls);
|
|
7478
7983
|
}
|
|
7479
|
-
|
|
7480
|
-
|
|
7984
|
+
if ('setAuthStrategy' in baseHttpPort) {
|
|
7985
|
+
log$1.debug('Configuring auth strategy');
|
|
7481
7986
|
const jwtHandler = new JwtAuthHandler(tokenStorage);
|
|
7482
7987
|
const certificatePort = this.certificateService
|
|
7483
7988
|
? {
|
|
@@ -7508,7 +8013,7 @@ class ACubeSDK {
|
|
|
7508
8013
|
},
|
|
7509
8014
|
};
|
|
7510
8015
|
const authStrategy = new AuthStrategy(jwtHandler, mtlsHandler, userProvider, this.adapters?.mtls || null);
|
|
7511
|
-
const httpWithStrategy =
|
|
8016
|
+
const httpWithStrategy = baseHttpPort;
|
|
7512
8017
|
httpWithStrategy.setAuthStrategy(authStrategy);
|
|
7513
8018
|
}
|
|
7514
8019
|
if (this.adapters?.mtls && this.certificateService) {
|
|
@@ -7525,13 +8030,22 @@ class ACubeSDK {
|
|
|
7525
8030
|
}
|
|
7526
8031
|
}
|
|
7527
8032
|
}
|
|
7528
|
-
catch {
|
|
7529
|
-
|
|
8033
|
+
catch (certError) {
|
|
8034
|
+
log$1.warn('Certificate auto-configuration failed, will retry on demand', {
|
|
8035
|
+
error: certError instanceof Error ? certError.message : certError,
|
|
8036
|
+
});
|
|
7530
8037
|
}
|
|
7531
8038
|
}
|
|
7532
8039
|
this.isInitialized = true;
|
|
8040
|
+
log$1.info('SDK initialized successfully', {
|
|
8041
|
+
hasCache: !!this.adapters.cache,
|
|
8042
|
+
hasMtls: !!this.adapters.mtls,
|
|
8043
|
+
});
|
|
7533
8044
|
}
|
|
7534
8045
|
catch (error) {
|
|
8046
|
+
log$1.error('SDK initialization failed', {
|
|
8047
|
+
error: error instanceof Error ? error.message : error,
|
|
8048
|
+
});
|
|
7535
8049
|
throw new ACubeSDKError('SDK_INITIALIZATION_ERROR', `Failed to initialize SDK: ${error instanceof Error ? error.message : 'Unknown error'}`, error);
|
|
7536
8050
|
}
|
|
7537
8051
|
}
|
|
@@ -7585,15 +8099,19 @@ class ACubeSDK {
|
|
|
7585
8099
|
}
|
|
7586
8100
|
async login(credentials) {
|
|
7587
8101
|
this.ensureInitialized();
|
|
8102
|
+
log$1.info('Login attempt', { email: credentials.email });
|
|
7588
8103
|
const user = await this.authService.login(credentials);
|
|
8104
|
+
log$1.info('Login successful', { roles: user.roles });
|
|
7589
8105
|
const token = await this.authService.getAccessToken();
|
|
7590
8106
|
if (token) {
|
|
7591
8107
|
this.httpPort.setAuthToken(token);
|
|
8108
|
+
log$1.debug('Auth token set on HTTP port');
|
|
7592
8109
|
}
|
|
7593
8110
|
return user;
|
|
7594
8111
|
}
|
|
7595
8112
|
async logout() {
|
|
7596
8113
|
this.ensureInitialized();
|
|
8114
|
+
log$1.info('Logout');
|
|
7597
8115
|
await this.authService.logout();
|
|
7598
8116
|
this.httpPort.setAuthToken(null);
|
|
7599
8117
|
}
|
|
@@ -7674,12 +8192,7 @@ class ACubeSDK {
|
|
|
7674
8192
|
async clearCertificate() {
|
|
7675
8193
|
this.ensureInitialized();
|
|
7676
8194
|
if (this.adapters?.mtls) {
|
|
7677
|
-
|
|
7678
|
-
await this.adapters.mtls.removeCertificate();
|
|
7679
|
-
}
|
|
7680
|
-
catch {
|
|
7681
|
-
// No certificate to remove
|
|
7682
|
-
}
|
|
8195
|
+
await this.adapters.mtls.removeCertificate().catch(() => { });
|
|
7683
8196
|
}
|
|
7684
8197
|
if (this.certificateService) {
|
|
7685
8198
|
await this.certificateService.clearCertificate();
|
|
@@ -8014,17 +8527,16 @@ class NotificationService {
|
|
|
8014
8527
|
}
|
|
8015
8528
|
|
|
8016
8529
|
const DEFAULT_CONFIG = {
|
|
8017
|
-
|
|
8018
|
-
cacheTtlMs: 300000,
|
|
8530
|
+
pollIntervalMs: 60000, // 1 minute default for telemetry
|
|
8019
8531
|
};
|
|
8020
8532
|
class TelemetryService {
|
|
8021
8533
|
get state$() {
|
|
8022
8534
|
return this.stateSubject.asObservable();
|
|
8023
8535
|
}
|
|
8024
|
-
constructor(repository,
|
|
8536
|
+
constructor(repository, networkPort, config, events) {
|
|
8025
8537
|
this.repository = repository;
|
|
8026
|
-
this.storagePort = storagePort;
|
|
8027
8538
|
this.networkPort = networkPort;
|
|
8539
|
+
this.events = events;
|
|
8028
8540
|
this.stateSubject = new BehaviorSubject({
|
|
8029
8541
|
data: null,
|
|
8030
8542
|
isCached: false,
|
|
@@ -8032,29 +8544,69 @@ class TelemetryService {
|
|
|
8032
8544
|
lastFetchedAt: null,
|
|
8033
8545
|
});
|
|
8034
8546
|
this.destroy$ = new Subject();
|
|
8035
|
-
this.isOnline = true;
|
|
8036
8547
|
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
8037
8548
|
this.setupNetworkMonitoring();
|
|
8038
8549
|
}
|
|
8039
8550
|
setupNetworkMonitoring() {
|
|
8040
|
-
this.networkPort.online
|
|
8041
|
-
|
|
8551
|
+
this.networkSubscription = this.networkPort.online$
|
|
8552
|
+
.pipe(startWith(true), pairwise(), filter(([wasOnline, isNowOnline]) => !wasOnline && isNowOnline), takeUntil(this.destroy$))
|
|
8553
|
+
.subscribe(() => {
|
|
8554
|
+
this.triggerSync();
|
|
8042
8555
|
});
|
|
8043
8556
|
}
|
|
8557
|
+
startPolling(pemId) {
|
|
8558
|
+
if (this.pollingSubscription) {
|
|
8559
|
+
// If already polling for same pemId, do nothing
|
|
8560
|
+
if (this.currentPemId === pemId) {
|
|
8561
|
+
return;
|
|
8562
|
+
}
|
|
8563
|
+
// If polling for different pemId, stop and restart
|
|
8564
|
+
this.stopPolling();
|
|
8565
|
+
}
|
|
8566
|
+
this.currentPemId = pemId;
|
|
8567
|
+
this.pollingSubscription = interval(this.config.pollIntervalMs)
|
|
8568
|
+
.pipe(startWith(0), switchMap(() => this.fetchTelemetry()), takeUntil(this.destroy$))
|
|
8569
|
+
.subscribe();
|
|
8570
|
+
}
|
|
8571
|
+
stopPolling() {
|
|
8572
|
+
this.pollingSubscription?.unsubscribe();
|
|
8573
|
+
this.pollingSubscription = undefined;
|
|
8574
|
+
this.currentPemId = undefined;
|
|
8575
|
+
}
|
|
8576
|
+
async triggerSync() {
|
|
8577
|
+
if (!this.currentPemId) {
|
|
8578
|
+
return this.stateSubject.value;
|
|
8579
|
+
}
|
|
8580
|
+
return this.fetchTelemetry();
|
|
8581
|
+
}
|
|
8044
8582
|
async getTelemetry(pemId) {
|
|
8045
|
-
|
|
8583
|
+
// Start polling if not already polling for this pemId
|
|
8584
|
+
if (this.currentPemId !== pemId) {
|
|
8585
|
+
this.startPolling(pemId);
|
|
8586
|
+
}
|
|
8587
|
+
return this.stateSubject.value;
|
|
8046
8588
|
}
|
|
8047
8589
|
async refreshTelemetry(pemId) {
|
|
8048
|
-
|
|
8590
|
+
// Update pemId and fetch immediately
|
|
8591
|
+
if (this.currentPemId !== pemId) {
|
|
8592
|
+
this.startPolling(pemId);
|
|
8593
|
+
}
|
|
8594
|
+
else {
|
|
8595
|
+
return this.fetchTelemetry();
|
|
8596
|
+
}
|
|
8597
|
+
return this.stateSubject.value;
|
|
8598
|
+
}
|
|
8599
|
+
async fetchTelemetry() {
|
|
8600
|
+
if (!this.currentPemId) {
|
|
8049
8601
|
return this.stateSubject.value;
|
|
8050
8602
|
}
|
|
8051
8603
|
this.stateSubject.next({
|
|
8052
8604
|
...this.stateSubject.value,
|
|
8053
8605
|
isLoading: true,
|
|
8606
|
+
error: undefined,
|
|
8054
8607
|
});
|
|
8055
8608
|
try {
|
|
8056
|
-
const data = await this.repository.getTelemetry(
|
|
8057
|
-
await this.cacheData(pemId, data);
|
|
8609
|
+
const data = await this.repository.getTelemetry(this.currentPemId);
|
|
8058
8610
|
const newState = {
|
|
8059
8611
|
data,
|
|
8060
8612
|
isCached: false,
|
|
@@ -8062,6 +8614,7 @@ class TelemetryService {
|
|
|
8062
8614
|
lastFetchedAt: Date.now(),
|
|
8063
8615
|
};
|
|
8064
8616
|
this.stateSubject.next(newState);
|
|
8617
|
+
this.events?.onTelemetryUpdate?.(data);
|
|
8065
8618
|
return newState;
|
|
8066
8619
|
}
|
|
8067
8620
|
catch (error) {
|
|
@@ -8072,89 +8625,25 @@ class TelemetryService {
|
|
|
8072
8625
|
error: errorMessage,
|
|
8073
8626
|
};
|
|
8074
8627
|
this.stateSubject.next(newState);
|
|
8628
|
+
if (error instanceof Error) {
|
|
8629
|
+
this.events?.onSyncError?.(error);
|
|
8630
|
+
}
|
|
8075
8631
|
return newState;
|
|
8076
8632
|
}
|
|
8077
8633
|
}
|
|
8078
|
-
|
|
8634
|
+
clearTelemetry() {
|
|
8079
8635
|
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
8636
|
data: null,
|
|
8117
8637
|
isCached: false,
|
|
8118
8638
|
isLoading: false,
|
|
8119
8639
|
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));
|
|
8640
|
+
});
|
|
8154
8641
|
}
|
|
8155
8642
|
destroy() {
|
|
8156
8643
|
this.destroy$.next();
|
|
8157
8644
|
this.destroy$.complete();
|
|
8645
|
+
this.pollingSubscription?.unsubscribe();
|
|
8646
|
+
this.networkSubscription?.unsubscribe();
|
|
8158
8647
|
}
|
|
8159
8648
|
}
|
|
8160
8649
|
|
|
@@ -8164,7 +8653,7 @@ class TelemetryService {
|
|
|
8164
8653
|
* Provides:
|
|
8165
8654
|
* - Single initialization point
|
|
8166
8655
|
* - Observable app state (NORMAL, WARNING, BLOCKED, OFFLINE)
|
|
8167
|
-
* - Observable telemetry state
|
|
8656
|
+
* - Observable telemetry state with polling
|
|
8168
8657
|
* - Simplified services for product use
|
|
8169
8658
|
*
|
|
8170
8659
|
* @example
|
|
@@ -8173,6 +8662,7 @@ class TelemetryService {
|
|
|
8173
8662
|
* SDKManager.configure({
|
|
8174
8663
|
* environment: 'sandbox',
|
|
8175
8664
|
* notificationPollIntervalMs: 30000,
|
|
8665
|
+
* telemetryPollIntervalMs: 60000,
|
|
8176
8666
|
* });
|
|
8177
8667
|
*
|
|
8178
8668
|
* // Initialize
|
|
@@ -8184,6 +8674,14 @@ class TelemetryService {
|
|
|
8184
8674
|
* console.log('App mode:', state.mode);
|
|
8185
8675
|
* });
|
|
8186
8676
|
*
|
|
8677
|
+
* // Start telemetry polling for a specific PEM
|
|
8678
|
+
* manager.startTelemetryPolling('PEM-123');
|
|
8679
|
+
*
|
|
8680
|
+
* // Subscribe to telemetry updates
|
|
8681
|
+
* manager.telemetry$.subscribe(telemetry => {
|
|
8682
|
+
* console.log('Telemetry:', telemetry);
|
|
8683
|
+
* });
|
|
8684
|
+
*
|
|
8187
8685
|
* // Cleanup
|
|
8188
8686
|
* SDKManager.destroy();
|
|
8189
8687
|
* ```
|
|
@@ -8239,43 +8737,34 @@ class SDKManager {
|
|
|
8239
8737
|
* Must be called after configure()
|
|
8240
8738
|
*/
|
|
8241
8739
|
async initialize() {
|
|
8242
|
-
if (this.isInitialized)
|
|
8740
|
+
if (this.isInitialized)
|
|
8243
8741
|
return;
|
|
8244
|
-
}
|
|
8245
|
-
// Create and initialize SDK
|
|
8246
8742
|
this.sdk = new ACubeSDK(this.config, this.adapters, this.events);
|
|
8247
8743
|
await this.sdk.initialize();
|
|
8248
|
-
// Get required adapters
|
|
8249
8744
|
const adaptersRef = this.sdk.getAdapters();
|
|
8250
8745
|
if (!adaptersRef) {
|
|
8251
8746
|
throw new ACubeSDKError('ADAPTERS_NOT_AVAILABLE', 'Platform adapters not available');
|
|
8252
8747
|
}
|
|
8253
8748
|
const networkPort = adaptersRef.networkMonitor;
|
|
8254
|
-
const storagePort = adaptersRef.storage;
|
|
8255
|
-
// Get repositories from SDK
|
|
8256
8749
|
const notificationRepo = this.sdk.notifications;
|
|
8257
8750
|
const telemetryRepo = this.sdk.telemetry;
|
|
8258
|
-
// Create NotificationService
|
|
8259
8751
|
this.notificationService = new NotificationService(notificationRepo, networkPort, {
|
|
8260
8752
|
pollIntervalMs: this.config.notificationPollIntervalMs ?? 30000,
|
|
8261
8753
|
defaultPageSize: this.config.notificationPageSize ?? 30,
|
|
8262
8754
|
});
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
cacheTtlMs: this.config.telemetryCacheTtlMs ?? 300000,
|
|
8755
|
+
this.telemetryService = new TelemetryService(telemetryRepo, networkPort, {
|
|
8756
|
+
pollIntervalMs: this.config.telemetryPollIntervalMs ?? 60000,
|
|
8266
8757
|
});
|
|
8267
|
-
// Create AppStateService
|
|
8268
8758
|
this.appStateService = new AppStateService(this.notificationService.notifications$, networkPort);
|
|
8269
|
-
// Subscribe to state changes for events
|
|
8270
8759
|
if (this.events?.onAppStateChanged) {
|
|
8271
8760
|
this.appStateService.state$.subscribe(this.events.onAppStateChanged);
|
|
8272
8761
|
}
|
|
8273
8762
|
if (this.events?.onTelemetryStateChanged) {
|
|
8274
8763
|
this.telemetryService.state$.subscribe(this.events.onTelemetryStateChanged);
|
|
8275
8764
|
}
|
|
8276
|
-
// Start notification polling
|
|
8277
|
-
this.notificationService.startPolling();
|
|
8278
8765
|
this.isInitialized = true;
|
|
8766
|
+
this.notificationService.startPolling();
|
|
8767
|
+
this.startTelemetryPollingAuto();
|
|
8279
8768
|
}
|
|
8280
8769
|
/**
|
|
8281
8770
|
* Observable stream of app state
|
|
@@ -8308,12 +8797,51 @@ class SDKManager {
|
|
|
8308
8797
|
return this.appStateService.warning$;
|
|
8309
8798
|
}
|
|
8310
8799
|
/**
|
|
8311
|
-
* Observable stream of telemetry state
|
|
8800
|
+
* Observable stream of telemetry state (data, isLoading, isCached, error)
|
|
8312
8801
|
*/
|
|
8313
8802
|
get telemetryState$() {
|
|
8314
8803
|
this.ensureInitialized();
|
|
8315
8804
|
return this.telemetryService.state$;
|
|
8316
8805
|
}
|
|
8806
|
+
/**
|
|
8807
|
+
* Get the pemId from the installed certificate
|
|
8808
|
+
*/
|
|
8809
|
+
async getPemId() {
|
|
8810
|
+
this.ensureInitialized();
|
|
8811
|
+
try {
|
|
8812
|
+
const certInfo = await this.sdk.getCertificatesInfo();
|
|
8813
|
+
return certInfo?.pemId ?? null;
|
|
8814
|
+
}
|
|
8815
|
+
catch {
|
|
8816
|
+
return null;
|
|
8817
|
+
}
|
|
8818
|
+
}
|
|
8819
|
+
/**
|
|
8820
|
+
* Start polling telemetry using the pemId from installed certificate
|
|
8821
|
+
* Returns the pemId if successful, null if no certificate is installed
|
|
8822
|
+
*/
|
|
8823
|
+
async startTelemetryPollingAuto() {
|
|
8824
|
+
this.ensureInitialized();
|
|
8825
|
+
const pemId = await this.getPemId();
|
|
8826
|
+
if (pemId) {
|
|
8827
|
+
this.telemetryService.startPolling(pemId);
|
|
8828
|
+
}
|
|
8829
|
+
return pemId;
|
|
8830
|
+
}
|
|
8831
|
+
/**
|
|
8832
|
+
* Start polling telemetry for a specific PEM
|
|
8833
|
+
*/
|
|
8834
|
+
startTelemetryPolling(pemId) {
|
|
8835
|
+
this.ensureInitialized();
|
|
8836
|
+
this.telemetryService.startPolling(pemId);
|
|
8837
|
+
}
|
|
8838
|
+
/**
|
|
8839
|
+
* Stop telemetry polling
|
|
8840
|
+
*/
|
|
8841
|
+
stopTelemetryPolling() {
|
|
8842
|
+
this.ensureInitialized();
|
|
8843
|
+
this.telemetryService.stopPolling();
|
|
8844
|
+
}
|
|
8317
8845
|
/**
|
|
8318
8846
|
* Get simplified services for product use
|
|
8319
8847
|
*/
|
|
@@ -8322,7 +8850,6 @@ class SDKManager {
|
|
|
8322
8850
|
const sdk = this.sdk;
|
|
8323
8851
|
const telemetryService = this.telemetryService;
|
|
8324
8852
|
return {
|
|
8325
|
-
// Business repositories
|
|
8326
8853
|
receipts: sdk.receipts,
|
|
8327
8854
|
merchants: sdk.merchants,
|
|
8328
8855
|
cashiers: sdk.cashiers,
|
|
@@ -8332,22 +8859,23 @@ class SDKManager {
|
|
|
8332
8859
|
pems: sdk.pems,
|
|
8333
8860
|
dailyReports: sdk.dailyReports,
|
|
8334
8861
|
journals: sdk.journals,
|
|
8335
|
-
// Simplified telemetry operations
|
|
8336
8862
|
telemetry: {
|
|
8863
|
+
startPollingAuto: () => this.startTelemetryPollingAuto(),
|
|
8864
|
+
startPolling: (pemId) => telemetryService.startPolling(pemId),
|
|
8865
|
+
stopPolling: () => telemetryService.stopPolling(),
|
|
8337
8866
|
getTelemetry: (pemId) => telemetryService.getTelemetry(pemId),
|
|
8338
8867
|
refreshTelemetry: (pemId) => telemetryService.refreshTelemetry(pemId),
|
|
8339
|
-
|
|
8868
|
+
triggerSync: () => telemetryService.triggerSync(),
|
|
8869
|
+
clearTelemetry: () => telemetryService.clearTelemetry(),
|
|
8870
|
+
getPemId: () => this.getPemId(),
|
|
8340
8871
|
},
|
|
8341
|
-
// Auth operations
|
|
8342
8872
|
login: (credentials) => sdk.login(credentials),
|
|
8343
8873
|
logout: () => sdk.logout(),
|
|
8344
8874
|
getCurrentUser: () => sdk.getCurrentUser(),
|
|
8345
8875
|
isAuthenticated: () => sdk.isAuthenticated(),
|
|
8346
|
-
// Certificate operations
|
|
8347
8876
|
storeCertificate: (certificate, privateKey, options) => sdk.storeCertificate(certificate, privateKey, options),
|
|
8348
8877
|
hasCertificate: () => sdk.hasCertificate(),
|
|
8349
8878
|
clearCertificate: () => sdk.clearCertificate(),
|
|
8350
|
-
// Network status
|
|
8351
8879
|
isOnline: () => sdk.isOnline(),
|
|
8352
8880
|
};
|
|
8353
8881
|
}
|
|
@@ -8359,12 +8887,11 @@ class SDKManager {
|
|
|
8359
8887
|
await this.notificationService.triggerSync();
|
|
8360
8888
|
}
|
|
8361
8889
|
/**
|
|
8362
|
-
*
|
|
8890
|
+
* Manually trigger a telemetry sync
|
|
8363
8891
|
*/
|
|
8364
|
-
async
|
|
8892
|
+
async syncTelemetry() {
|
|
8365
8893
|
this.ensureInitialized();
|
|
8366
|
-
|
|
8367
|
-
return state.data;
|
|
8894
|
+
return this.telemetryService.triggerSync();
|
|
8368
8895
|
}
|
|
8369
8896
|
/**
|
|
8370
8897
|
* Check if the manager is initialized
|