@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.cjs.js
CHANGED
|
@@ -2482,7 +2482,7 @@ function formatDecimal(value, decimals = 2) {
|
|
|
2482
2482
|
return num.toFixed(decimals);
|
|
2483
2483
|
}
|
|
2484
2484
|
|
|
2485
|
-
const log$
|
|
2485
|
+
const log$f = createPrefixedLogger('MTLS-HANDLER');
|
|
2486
2486
|
class MtlsAuthHandler {
|
|
2487
2487
|
constructor(mtlsAdapter, certificatePort) {
|
|
2488
2488
|
this.mtlsAdapter = mtlsAdapter;
|
|
@@ -2524,7 +2524,7 @@ class MtlsAuthHandler {
|
|
|
2524
2524
|
async makeRequest(url, config, jwtToken) {
|
|
2525
2525
|
const requestKey = this.generateRequestKey(url, config, jwtToken);
|
|
2526
2526
|
if (this.pendingRequests.has(requestKey)) {
|
|
2527
|
-
log$
|
|
2527
|
+
log$f.debug('Deduplicating concurrent request:', url);
|
|
2528
2528
|
return this.pendingRequests.get(requestKey);
|
|
2529
2529
|
}
|
|
2530
2530
|
const requestPromise = this.executeRequest(url, config, jwtToken, false);
|
|
@@ -2548,10 +2548,10 @@ class MtlsAuthHandler {
|
|
|
2548
2548
|
};
|
|
2549
2549
|
if (jwtToken) {
|
|
2550
2550
|
headers['Authorization'] = `Bearer ${jwtToken}`;
|
|
2551
|
-
log$
|
|
2551
|
+
log$f.debug('JWT token present:', jwtToken.substring(0, 20) + '...');
|
|
2552
2552
|
}
|
|
2553
2553
|
else {
|
|
2554
|
-
log$
|
|
2554
|
+
log$f.warn('No JWT token provided for mTLS request');
|
|
2555
2555
|
}
|
|
2556
2556
|
const fullUrl = this.constructMtlsUrl(url);
|
|
2557
2557
|
const mtlsConfig = {
|
|
@@ -2562,25 +2562,25 @@ class MtlsAuthHandler {
|
|
|
2562
2562
|
timeout: config.timeout,
|
|
2563
2563
|
responseType: config.responseType,
|
|
2564
2564
|
};
|
|
2565
|
-
log$
|
|
2566
|
-
log$
|
|
2565
|
+
log$f.debug('header-mtls', headers);
|
|
2566
|
+
log$f.debug(`${config.method} ${fullUrl}`);
|
|
2567
2567
|
if (config.data) {
|
|
2568
|
-
log$
|
|
2568
|
+
log$f.debug('Request body:', config.data);
|
|
2569
2569
|
}
|
|
2570
2570
|
try {
|
|
2571
2571
|
const response = await this.mtlsAdapter.request(mtlsConfig);
|
|
2572
|
-
log$
|
|
2572
|
+
log$f.debug(`Response ${response.status} from ${fullUrl}`);
|
|
2573
2573
|
if (response.data) {
|
|
2574
|
-
log$
|
|
2574
|
+
log$f.debug('Response body:', response.data);
|
|
2575
2575
|
}
|
|
2576
2576
|
return response.data;
|
|
2577
2577
|
}
|
|
2578
2578
|
catch (error) {
|
|
2579
|
-
log$
|
|
2579
|
+
log$f.error(`Response error from ${fullUrl}:`, error);
|
|
2580
2580
|
if (error && typeof error === 'object' && 'response' in error) {
|
|
2581
2581
|
const axiosError = error;
|
|
2582
2582
|
if (axiosError.response?.data) {
|
|
2583
|
-
log$
|
|
2583
|
+
log$f.error('Response body:', axiosError.response.data);
|
|
2584
2584
|
}
|
|
2585
2585
|
}
|
|
2586
2586
|
if (isRetryAttempt) {
|
|
@@ -2590,7 +2590,7 @@ class MtlsAuthHandler {
|
|
|
2590
2590
|
if (!shouldRetry) {
|
|
2591
2591
|
throw error;
|
|
2592
2592
|
}
|
|
2593
|
-
log$
|
|
2593
|
+
log$f.debug('Request failed, reconfiguring certificate and retrying...');
|
|
2594
2594
|
try {
|
|
2595
2595
|
await this.configureCertificate(certificate);
|
|
2596
2596
|
return await this.executeRequest(url, config, jwtToken, true);
|
|
@@ -3317,7 +3317,7 @@ function decompressData(data, compressed) {
|
|
|
3317
3317
|
return new CompressionAdapter().decompress(data, compressed);
|
|
3318
3318
|
}
|
|
3319
3319
|
|
|
3320
|
-
const log$
|
|
3320
|
+
const log$e = createPrefixedLogger('CACHE-RN');
|
|
3321
3321
|
/**
|
|
3322
3322
|
* React Native cache adapter using SQLite (Expo or react-native-sqlite-storage)
|
|
3323
3323
|
* Cache never expires - data persists until explicitly invalidated
|
|
@@ -3401,26 +3401,26 @@ class ReactNativeCacheAdapter {
|
|
|
3401
3401
|
await this.runMigrations();
|
|
3402
3402
|
}
|
|
3403
3403
|
async runMigrations() {
|
|
3404
|
-
log$
|
|
3404
|
+
log$e.debug('Running database migrations...');
|
|
3405
3405
|
try {
|
|
3406
3406
|
// Check if compressed column exists
|
|
3407
3407
|
this.hasCompressedColumn = await this.checkColumnExists('compressed');
|
|
3408
3408
|
if (!this.hasCompressedColumn) {
|
|
3409
|
-
log$
|
|
3409
|
+
log$e.debug('Adding compressed column to cache table');
|
|
3410
3410
|
const addColumnSQL = `ALTER TABLE ${ReactNativeCacheAdapter.TABLE_NAME} ADD COLUMN compressed INTEGER DEFAULT 0`;
|
|
3411
3411
|
await this.executeSql(addColumnSQL);
|
|
3412
3412
|
this.hasCompressedColumn = true;
|
|
3413
|
-
log$
|
|
3413
|
+
log$e.debug('Successfully added compressed column');
|
|
3414
3414
|
}
|
|
3415
3415
|
else {
|
|
3416
|
-
log$
|
|
3416
|
+
log$e.debug('Compressed column already exists');
|
|
3417
3417
|
}
|
|
3418
|
-
log$
|
|
3418
|
+
log$e.debug('Database migrations completed', {
|
|
3419
3419
|
hasCompressedColumn: this.hasCompressedColumn,
|
|
3420
3420
|
});
|
|
3421
3421
|
}
|
|
3422
3422
|
catch (error) {
|
|
3423
|
-
log$
|
|
3423
|
+
log$e.debug('Migration failed, disabling compression features', error);
|
|
3424
3424
|
this.hasCompressedColumn = false;
|
|
3425
3425
|
// Don't throw - allow the app to continue even if migration fails
|
|
3426
3426
|
// The compressed feature will just be disabled
|
|
@@ -3431,20 +3431,20 @@ class ReactNativeCacheAdapter {
|
|
|
3431
3431
|
const pragmaSQL = `PRAGMA table_info(${ReactNativeCacheAdapter.TABLE_NAME})`;
|
|
3432
3432
|
const results = await this.executeSql(pragmaSQL);
|
|
3433
3433
|
const columns = this.normalizeResults(results);
|
|
3434
|
-
log$
|
|
3434
|
+
log$e.debug('Table columns found', { columns: columns.map((c) => c.name) });
|
|
3435
3435
|
return columns.some((column) => column.name === columnName);
|
|
3436
3436
|
}
|
|
3437
3437
|
catch (error) {
|
|
3438
|
-
log$
|
|
3438
|
+
log$e.debug('Error checking column existence', error);
|
|
3439
3439
|
return false;
|
|
3440
3440
|
}
|
|
3441
3441
|
}
|
|
3442
3442
|
async get(key) {
|
|
3443
3443
|
await this.ensureInitialized();
|
|
3444
3444
|
const sql = `SELECT * FROM ${ReactNativeCacheAdapter.TABLE_NAME} WHERE cache_key = ?`;
|
|
3445
|
-
log$
|
|
3445
|
+
log$e.debug('Executing get query', { sql, key });
|
|
3446
3446
|
const results = await this.executeSql(sql, [key]);
|
|
3447
|
-
log$
|
|
3447
|
+
log$e.debug('Get query results', { key, hasResults: !!results });
|
|
3448
3448
|
// Normalize results from different SQLite implementations
|
|
3449
3449
|
const rows = this.normalizeResults(results);
|
|
3450
3450
|
if (!rows || rows.length === 0) {
|
|
@@ -3467,7 +3467,7 @@ class ReactNativeCacheAdapter {
|
|
|
3467
3467
|
data,
|
|
3468
3468
|
timestamp: Date.now(),
|
|
3469
3469
|
};
|
|
3470
|
-
log$
|
|
3470
|
+
log$e.debug('Setting cache item', { key });
|
|
3471
3471
|
return this.setItem(key, item);
|
|
3472
3472
|
}
|
|
3473
3473
|
async setItem(key, item) {
|
|
@@ -3480,7 +3480,7 @@ class ReactNativeCacheAdapter {
|
|
|
3480
3480
|
const compressionResult = compressData(serializedData, this.options.compressionThreshold);
|
|
3481
3481
|
finalData = compressionResult.data;
|
|
3482
3482
|
isCompressed = compressionResult.compressed;
|
|
3483
|
-
log$
|
|
3483
|
+
log$e.debug('Compression result', {
|
|
3484
3484
|
key,
|
|
3485
3485
|
originalSize: compressionResult.originalSize,
|
|
3486
3486
|
compressedSize: compressionResult.compressedSize,
|
|
@@ -3488,7 +3488,7 @@ class ReactNativeCacheAdapter {
|
|
|
3488
3488
|
savings: compressionResult.originalSize - compressionResult.compressedSize,
|
|
3489
3489
|
});
|
|
3490
3490
|
}
|
|
3491
|
-
log$
|
|
3491
|
+
log$e.debug('Setting item with metadata', {
|
|
3492
3492
|
key,
|
|
3493
3493
|
timestamp: item.timestamp,
|
|
3494
3494
|
compressed: isCompressed,
|
|
@@ -3514,14 +3514,14 @@ class ReactNativeCacheAdapter {
|
|
|
3514
3514
|
`;
|
|
3515
3515
|
params = [key, finalData, item.timestamp];
|
|
3516
3516
|
}
|
|
3517
|
-
log$
|
|
3517
|
+
log$e.debug('Executing setItem SQL', { key, paramsCount: params.length });
|
|
3518
3518
|
await this.executeSql(sql, params);
|
|
3519
3519
|
}
|
|
3520
3520
|
async setBatch(items) {
|
|
3521
3521
|
if (items.length === 0)
|
|
3522
3522
|
return;
|
|
3523
3523
|
await this.ensureInitialized();
|
|
3524
|
-
log$
|
|
3524
|
+
log$e.debug('Batch setting items', { count: items.length });
|
|
3525
3525
|
if (this.isExpo) {
|
|
3526
3526
|
await this.db.withTransactionAsync(async () => {
|
|
3527
3527
|
for (const [key, item] of items) {
|
|
@@ -3539,7 +3539,7 @@ class ReactNativeCacheAdapter {
|
|
|
3539
3539
|
}, reject, () => resolve());
|
|
3540
3540
|
});
|
|
3541
3541
|
}
|
|
3542
|
-
log$
|
|
3542
|
+
log$e.debug('Batch operation completed', { count: items.length });
|
|
3543
3543
|
}
|
|
3544
3544
|
async setBatchItem(key, item) {
|
|
3545
3545
|
// Handle compression if enabled and compressed column is available
|
|
@@ -3713,10 +3713,10 @@ class MemoryCacheAdapter {
|
|
|
3713
3713
|
return keySize + itemSize;
|
|
3714
3714
|
}
|
|
3715
3715
|
async get(key) {
|
|
3716
|
-
log$
|
|
3716
|
+
log$e.debug('Getting cache item', { key });
|
|
3717
3717
|
const item = this.cache.get(key);
|
|
3718
3718
|
if (!item) {
|
|
3719
|
-
log$
|
|
3719
|
+
log$e.debug('Cache miss', { key });
|
|
3720
3720
|
return null;
|
|
3721
3721
|
}
|
|
3722
3722
|
// Handle decompression if needed
|
|
@@ -3726,7 +3726,7 @@ class MemoryCacheAdapter {
|
|
|
3726
3726
|
const decompressed = decompressData(item.data, true);
|
|
3727
3727
|
finalData = JSON.parse(decompressed.data);
|
|
3728
3728
|
}
|
|
3729
|
-
log$
|
|
3729
|
+
log$e.debug('Cache hit', { key, compressed: isCompressed });
|
|
3730
3730
|
return {
|
|
3731
3731
|
...item,
|
|
3732
3732
|
data: finalData,
|
|
@@ -3734,7 +3734,7 @@ class MemoryCacheAdapter {
|
|
|
3734
3734
|
};
|
|
3735
3735
|
}
|
|
3736
3736
|
async set(key, data) {
|
|
3737
|
-
log$
|
|
3737
|
+
log$e.debug('Setting cache item', { key });
|
|
3738
3738
|
// Handle compression if enabled
|
|
3739
3739
|
let finalData = data;
|
|
3740
3740
|
let isCompressed = false;
|
|
@@ -3744,7 +3744,7 @@ class MemoryCacheAdapter {
|
|
|
3744
3744
|
if (compressionResult.compressed) {
|
|
3745
3745
|
finalData = compressionResult.data;
|
|
3746
3746
|
isCompressed = true;
|
|
3747
|
-
log$
|
|
3747
|
+
log$e.debug('Compression result', {
|
|
3748
3748
|
key,
|
|
3749
3749
|
originalSize: compressionResult.originalSize,
|
|
3750
3750
|
compressedSize: compressionResult.compressedSize,
|
|
@@ -3776,13 +3776,13 @@ class MemoryCacheAdapter {
|
|
|
3776
3776
|
const oldestItemSize = this.calculateItemSize(oldestKey, oldestItem);
|
|
3777
3777
|
this.totalBytes -= oldestItemSize;
|
|
3778
3778
|
this.cache.delete(oldestKey);
|
|
3779
|
-
log$
|
|
3779
|
+
log$e.debug('Removed oldest item for capacity', { oldestKey, freedBytes: oldestItemSize });
|
|
3780
3780
|
}
|
|
3781
3781
|
}
|
|
3782
3782
|
// Set new item and update total size
|
|
3783
3783
|
this.cache.set(key, item);
|
|
3784
3784
|
this.totalBytes += newItemSize;
|
|
3785
|
-
log$
|
|
3785
|
+
log$e.debug('Updated cache size', {
|
|
3786
3786
|
entries: this.cache.size,
|
|
3787
3787
|
totalBytes: this.totalBytes,
|
|
3788
3788
|
newItemSize,
|
|
@@ -3791,7 +3791,7 @@ class MemoryCacheAdapter {
|
|
|
3791
3791
|
async setBatch(items) {
|
|
3792
3792
|
if (items.length === 0)
|
|
3793
3793
|
return;
|
|
3794
|
-
log$
|
|
3794
|
+
log$e.debug('Batch setting items', { count: items.length });
|
|
3795
3795
|
let totalNewBytes = 0;
|
|
3796
3796
|
let totalOldBytes = 0;
|
|
3797
3797
|
const itemsToRemove = [];
|
|
@@ -3820,7 +3820,7 @@ class MemoryCacheAdapter {
|
|
|
3820
3820
|
itemsToRemove.push(oldKey);
|
|
3821
3821
|
}
|
|
3822
3822
|
if (itemsToRemove.length > 0) {
|
|
3823
|
-
log$
|
|
3823
|
+
log$e.debug('Removed items for batch capacity', {
|
|
3824
3824
|
removedCount: itemsToRemove.length,
|
|
3825
3825
|
removedKeys: itemsToRemove,
|
|
3826
3826
|
});
|
|
@@ -3832,7 +3832,7 @@ class MemoryCacheAdapter {
|
|
|
3832
3832
|
for (const [key, item] of items) {
|
|
3833
3833
|
this.cache.set(key, item);
|
|
3834
3834
|
}
|
|
3835
|
-
log$
|
|
3835
|
+
log$e.debug('Batch operation completed', {
|
|
3836
3836
|
count: items.length,
|
|
3837
3837
|
totalBytes: this.totalBytes,
|
|
3838
3838
|
entries: this.cache.size,
|
|
@@ -3854,7 +3854,7 @@ class MemoryCacheAdapter {
|
|
|
3854
3854
|
}
|
|
3855
3855
|
}
|
|
3856
3856
|
if (removed > 0) {
|
|
3857
|
-
log$
|
|
3857
|
+
log$e.debug('Invalidation completed', {
|
|
3858
3858
|
pattern,
|
|
3859
3859
|
entriesRemoved: removed,
|
|
3860
3860
|
bytesFreed,
|
|
@@ -3866,7 +3866,7 @@ class MemoryCacheAdapter {
|
|
|
3866
3866
|
async clear() {
|
|
3867
3867
|
this.cache.clear();
|
|
3868
3868
|
this.totalBytes = 0;
|
|
3869
|
-
log$
|
|
3869
|
+
log$e.debug('Cache cleared', { entries: 0, bytes: 0 });
|
|
3870
3870
|
}
|
|
3871
3871
|
async getSize() {
|
|
3872
3872
|
return {
|
|
@@ -4197,7 +4197,7 @@ replaceTraps((oldTraps) => ({
|
|
|
4197
4197
|
},
|
|
4198
4198
|
}));
|
|
4199
4199
|
|
|
4200
|
-
const log$
|
|
4200
|
+
const log$d = createPrefixedLogger('CACHE-WEB');
|
|
4201
4201
|
/**
|
|
4202
4202
|
* Web cache adapter using IndexedDB with automatic error recovery
|
|
4203
4203
|
* Cache never expires - data persists until explicitly invalidated
|
|
@@ -4220,18 +4220,18 @@ class WebCacheAdapter {
|
|
|
4220
4220
|
async initialize() {
|
|
4221
4221
|
if (this.db)
|
|
4222
4222
|
return;
|
|
4223
|
-
log$
|
|
4223
|
+
log$d.debug('Initializing IndexedDB cache', {
|
|
4224
4224
|
dbName: WebCacheAdapter.DB_NAME,
|
|
4225
4225
|
version: WebCacheAdapter.DB_VERSION,
|
|
4226
4226
|
});
|
|
4227
4227
|
try {
|
|
4228
4228
|
this.db = await this.openDatabase();
|
|
4229
|
-
log$
|
|
4229
|
+
log$d.debug('IndexedDB cache initialized successfully');
|
|
4230
4230
|
this.retryCount = 0; // Reset retry count on success
|
|
4231
4231
|
}
|
|
4232
4232
|
catch (error) {
|
|
4233
4233
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
4234
|
-
log$
|
|
4234
|
+
log$d.debug('Failed to initialize IndexedDB', { error: errorMessage });
|
|
4235
4235
|
// Check if this is a version conflict error
|
|
4236
4236
|
if (this.isVersionConflictError(errorMessage)) {
|
|
4237
4237
|
await this.handleVersionConflict();
|
|
@@ -4244,32 +4244,32 @@ class WebCacheAdapter {
|
|
|
4244
4244
|
async openDatabase() {
|
|
4245
4245
|
return await openDB(WebCacheAdapter.DB_NAME, WebCacheAdapter.DB_VERSION, {
|
|
4246
4246
|
upgrade: (db, oldVersion, newVersion, transaction) => {
|
|
4247
|
-
log$
|
|
4247
|
+
log$d.debug('Database upgrade needed', { oldVersion, newVersion });
|
|
4248
4248
|
this.handleUpgrade(db, oldVersion, newVersion, transaction);
|
|
4249
4249
|
},
|
|
4250
4250
|
blocked: () => {
|
|
4251
|
-
log$
|
|
4251
|
+
log$d.debug('Database blocked - another tab may be open');
|
|
4252
4252
|
},
|
|
4253
4253
|
blocking: () => {
|
|
4254
|
-
log$
|
|
4254
|
+
log$d.debug('Database blocking - will close connection');
|
|
4255
4255
|
if (this.db) {
|
|
4256
4256
|
this.db.close();
|
|
4257
4257
|
this.db = null;
|
|
4258
4258
|
}
|
|
4259
4259
|
},
|
|
4260
4260
|
terminated: () => {
|
|
4261
|
-
log$
|
|
4261
|
+
log$d.debug('Database connection terminated unexpectedly');
|
|
4262
4262
|
this.db = null;
|
|
4263
4263
|
},
|
|
4264
4264
|
});
|
|
4265
4265
|
}
|
|
4266
4266
|
handleUpgrade(db, oldVersion, newVersion, transaction) {
|
|
4267
|
-
log$
|
|
4267
|
+
log$d.debug('Handling database upgrade', { oldVersion, newVersion });
|
|
4268
4268
|
// Create cache store if it doesn't exist (initial setup)
|
|
4269
4269
|
if (!db.objectStoreNames.contains(WebCacheAdapter.STORE_NAME)) {
|
|
4270
4270
|
const store = db.createObjectStore(WebCacheAdapter.STORE_NAME, { keyPath: 'key' });
|
|
4271
4271
|
store.createIndex('timestamp', 'timestamp', { unique: false });
|
|
4272
|
-
log$
|
|
4272
|
+
log$d.debug('Created cache store and timestamp index');
|
|
4273
4273
|
}
|
|
4274
4274
|
// Handle migration from version 1 to 2
|
|
4275
4275
|
if (oldVersion < 2) {
|
|
@@ -4280,16 +4280,16 @@ class WebCacheAdapter {
|
|
|
4280
4280
|
try {
|
|
4281
4281
|
if (store.indexNames.contains(indexName)) {
|
|
4282
4282
|
store.deleteIndex(indexName);
|
|
4283
|
-
log$
|
|
4283
|
+
log$d.debug(`Removed unused index: ${indexName}`);
|
|
4284
4284
|
}
|
|
4285
4285
|
}
|
|
4286
4286
|
catch (error) {
|
|
4287
4287
|
// Ignore errors if indexes don't exist
|
|
4288
|
-
log$
|
|
4288
|
+
log$d.debug(`Warning: Could not remove index ${indexName}`, error);
|
|
4289
4289
|
}
|
|
4290
4290
|
});
|
|
4291
4291
|
}
|
|
4292
|
-
log$
|
|
4292
|
+
log$d.debug('Database upgrade completed');
|
|
4293
4293
|
}
|
|
4294
4294
|
isVersionConflictError(errorMessage) {
|
|
4295
4295
|
return (errorMessage.includes('less than the existing version') ||
|
|
@@ -4297,7 +4297,7 @@ class WebCacheAdapter {
|
|
|
4297
4297
|
errorMessage.includes('VersionError'));
|
|
4298
4298
|
}
|
|
4299
4299
|
async handleVersionConflict() {
|
|
4300
|
-
log$
|
|
4300
|
+
log$d.debug('Handling version conflict, attempting recovery...');
|
|
4301
4301
|
if (this.retryCount >= this.maxRetries) {
|
|
4302
4302
|
throw new Error('Failed to resolve IndexedDB version conflict after multiple attempts');
|
|
4303
4303
|
}
|
|
@@ -4309,19 +4309,19 @@ class WebCacheAdapter {
|
|
|
4309
4309
|
this.db = null;
|
|
4310
4310
|
}
|
|
4311
4311
|
// Delete the problematic database
|
|
4312
|
-
log$
|
|
4312
|
+
log$d.debug('Deleting problematic database to resolve version conflict');
|
|
4313
4313
|
await deleteDB(WebCacheAdapter.DB_NAME);
|
|
4314
4314
|
// Wait a bit for the deletion to complete
|
|
4315
4315
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
4316
4316
|
// Try to open the database again
|
|
4317
|
-
log$
|
|
4317
|
+
log$d.debug(`Retrying database initialization (attempt ${this.retryCount}/${this.maxRetries})`);
|
|
4318
4318
|
this.db = await this.openDatabase();
|
|
4319
|
-
log$
|
|
4319
|
+
log$d.debug('Successfully recovered from version conflict');
|
|
4320
4320
|
this.retryCount = 0; // Reset retry count on success
|
|
4321
4321
|
}
|
|
4322
4322
|
catch (retryError) {
|
|
4323
4323
|
const retryErrorMessage = retryError instanceof Error ? retryError.message : 'Unknown error';
|
|
4324
|
-
log$
|
|
4324
|
+
log$d.debug('Recovery attempt failed', { attempt: this.retryCount, error: retryErrorMessage });
|
|
4325
4325
|
if (this.retryCount < this.maxRetries) {
|
|
4326
4326
|
// Try again
|
|
4327
4327
|
await this.handleVersionConflict();
|
|
@@ -4333,7 +4333,7 @@ class WebCacheAdapter {
|
|
|
4333
4333
|
}
|
|
4334
4334
|
async get(key) {
|
|
4335
4335
|
await this.ensureInitialized();
|
|
4336
|
-
log$
|
|
4336
|
+
log$d.debug('Getting cache item', { key });
|
|
4337
4337
|
try {
|
|
4338
4338
|
const transaction = this.db.transaction([WebCacheAdapter.STORE_NAME], 'readonly');
|
|
4339
4339
|
const store = transaction.objectStore(WebCacheAdapter.STORE_NAME);
|
|
@@ -4359,7 +4359,7 @@ class WebCacheAdapter {
|
|
|
4359
4359
|
};
|
|
4360
4360
|
}
|
|
4361
4361
|
catch (error) {
|
|
4362
|
-
log$
|
|
4362
|
+
log$d.debug('Error getting cache item', { key, error });
|
|
4363
4363
|
return null; // Return null on error instead of throwing
|
|
4364
4364
|
}
|
|
4365
4365
|
}
|
|
@@ -4381,7 +4381,7 @@ class WebCacheAdapter {
|
|
|
4381
4381
|
if (compressionResult.compressed) {
|
|
4382
4382
|
finalData = compressionResult.data;
|
|
4383
4383
|
isCompressed = true;
|
|
4384
|
-
log$
|
|
4384
|
+
log$d.debug('Compression result', {
|
|
4385
4385
|
key,
|
|
4386
4386
|
originalSize: compressionResult.originalSize,
|
|
4387
4387
|
compressedSize: compressionResult.compressedSize,
|
|
@@ -4390,7 +4390,7 @@ class WebCacheAdapter {
|
|
|
4390
4390
|
});
|
|
4391
4391
|
}
|
|
4392
4392
|
}
|
|
4393
|
-
log$
|
|
4393
|
+
log$d.debug('Setting cache item', { key, timestamp: item.timestamp, compressed: isCompressed });
|
|
4394
4394
|
const storedItem = {
|
|
4395
4395
|
key,
|
|
4396
4396
|
data: finalData,
|
|
@@ -4403,7 +4403,7 @@ class WebCacheAdapter {
|
|
|
4403
4403
|
await store.put(storedItem);
|
|
4404
4404
|
}
|
|
4405
4405
|
catch (error) {
|
|
4406
|
-
log$
|
|
4406
|
+
log$d.debug('Error setting cache item', { key, error });
|
|
4407
4407
|
// Silently fail for cache writes
|
|
4408
4408
|
}
|
|
4409
4409
|
}
|
|
@@ -4411,7 +4411,7 @@ class WebCacheAdapter {
|
|
|
4411
4411
|
if (items.length === 0)
|
|
4412
4412
|
return;
|
|
4413
4413
|
await this.ensureInitialized();
|
|
4414
|
-
log$
|
|
4414
|
+
log$d.debug('Batch setting items', { count: items.length });
|
|
4415
4415
|
try {
|
|
4416
4416
|
const transaction = this.db.transaction([WebCacheAdapter.STORE_NAME], 'readwrite');
|
|
4417
4417
|
const store = transaction.objectStore(WebCacheAdapter.STORE_NAME);
|
|
@@ -4421,10 +4421,10 @@ class WebCacheAdapter {
|
|
|
4421
4421
|
return store.put(storedItem);
|
|
4422
4422
|
});
|
|
4423
4423
|
await Promise.all(promises);
|
|
4424
|
-
log$
|
|
4424
|
+
log$d.debug('Batch operation completed', { count: items.length });
|
|
4425
4425
|
}
|
|
4426
4426
|
catch (error) {
|
|
4427
|
-
log$
|
|
4427
|
+
log$d.debug('Error in batch operation', { count: items.length, error });
|
|
4428
4428
|
// Silently fail for batch writes
|
|
4429
4429
|
}
|
|
4430
4430
|
}
|
|
@@ -4459,10 +4459,10 @@ class WebCacheAdapter {
|
|
|
4459
4459
|
const transaction = this.db.transaction([WebCacheAdapter.STORE_NAME], 'readwrite');
|
|
4460
4460
|
const store = transaction.objectStore(WebCacheAdapter.STORE_NAME);
|
|
4461
4461
|
await store.clear();
|
|
4462
|
-
log$
|
|
4462
|
+
log$d.debug('Cache cleared successfully');
|
|
4463
4463
|
}
|
|
4464
4464
|
catch (error) {
|
|
4465
|
-
log$
|
|
4465
|
+
log$d.debug('Error clearing cache', error);
|
|
4466
4466
|
// Silently fail for cache clear
|
|
4467
4467
|
}
|
|
4468
4468
|
}
|
|
@@ -4488,7 +4488,7 @@ class WebCacheAdapter {
|
|
|
4488
4488
|
};
|
|
4489
4489
|
}
|
|
4490
4490
|
catch (error) {
|
|
4491
|
-
log$
|
|
4491
|
+
log$d.debug('Error getting cache size', error);
|
|
4492
4492
|
return {
|
|
4493
4493
|
entries: 0,
|
|
4494
4494
|
bytes: 0,
|
|
@@ -4513,7 +4513,7 @@ class WebCacheAdapter {
|
|
|
4513
4513
|
return allKeys.filter((key) => regex.test(key));
|
|
4514
4514
|
}
|
|
4515
4515
|
catch (error) {
|
|
4516
|
-
log$
|
|
4516
|
+
log$d.debug('Error getting cache keys', error);
|
|
4517
4517
|
return [];
|
|
4518
4518
|
}
|
|
4519
4519
|
}
|
|
@@ -4526,7 +4526,7 @@ class WebCacheAdapter {
|
|
|
4526
4526
|
return true;
|
|
4527
4527
|
}
|
|
4528
4528
|
catch (error) {
|
|
4529
|
-
log$
|
|
4529
|
+
log$d.debug('Error deleting cache item', { key, error });
|
|
4530
4530
|
return false;
|
|
4531
4531
|
}
|
|
4532
4532
|
}
|
|
@@ -4538,7 +4538,7 @@ class WebCacheAdapter {
|
|
|
4538
4538
|
await this.initPromise;
|
|
4539
4539
|
}
|
|
4540
4540
|
catch (error) {
|
|
4541
|
-
log$
|
|
4541
|
+
log$d.debug('Failed to ensure initialization', error);
|
|
4542
4542
|
// Reset and try once more
|
|
4543
4543
|
this.initPromise = null;
|
|
4544
4544
|
this.db = null;
|
|
@@ -4557,7 +4557,7 @@ WebCacheAdapter.DB_NAME = 'acube_cache';
|
|
|
4557
4557
|
WebCacheAdapter.DB_VERSION = 2;
|
|
4558
4558
|
WebCacheAdapter.STORE_NAME = 'cache_entries';
|
|
4559
4559
|
|
|
4560
|
-
const log$
|
|
4560
|
+
const log$c = createPrefixedLogger('CACHE-LOADER');
|
|
4561
4561
|
function loadCacheAdapter(platform) {
|
|
4562
4562
|
try {
|
|
4563
4563
|
switch (platform) {
|
|
@@ -4589,7 +4589,7 @@ function loadCacheAdapter(platform) {
|
|
|
4589
4589
|
}
|
|
4590
4590
|
}
|
|
4591
4591
|
catch (error) {
|
|
4592
|
-
log$
|
|
4592
|
+
log$c.warn(`Cache adapter not available for platform ${platform}:`, error);
|
|
4593
4593
|
return undefined;
|
|
4594
4594
|
}
|
|
4595
4595
|
}
|
|
@@ -4646,7 +4646,7 @@ class BaseSecureStorageAdapter {
|
|
|
4646
4646
|
}
|
|
4647
4647
|
}
|
|
4648
4648
|
|
|
4649
|
-
const log$
|
|
4649
|
+
const log$b = createPrefixedLogger('NETWORK-BASE');
|
|
4650
4650
|
class NetworkBase {
|
|
4651
4651
|
constructor(initialOnline = true, debounceMs = 300) {
|
|
4652
4652
|
this.destroy$ = new Subject();
|
|
@@ -4666,14 +4666,14 @@ class NetworkBase {
|
|
|
4666
4666
|
const current = this.statusSubject.getValue();
|
|
4667
4667
|
if (current.online !== online) {
|
|
4668
4668
|
this.statusSubject.next({ online, timestamp: Date.now() });
|
|
4669
|
-
log$
|
|
4669
|
+
log$b.debug(`Network status changed: ${online ? 'online' : 'offline'}`);
|
|
4670
4670
|
}
|
|
4671
4671
|
}
|
|
4672
4672
|
destroy() {
|
|
4673
4673
|
this.destroy$.next();
|
|
4674
4674
|
this.destroy$.complete();
|
|
4675
4675
|
this.statusSubject.complete();
|
|
4676
|
-
log$
|
|
4676
|
+
log$b.debug('Network monitor destroyed');
|
|
4677
4677
|
}
|
|
4678
4678
|
}
|
|
4679
4679
|
|
|
@@ -4733,7 +4733,7 @@ class NodeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
4733
4733
|
}
|
|
4734
4734
|
}
|
|
4735
4735
|
|
|
4736
|
-
const log$
|
|
4736
|
+
const log$a = createPrefixedLogger('RN-STORAGE');
|
|
4737
4737
|
/**
|
|
4738
4738
|
* React Native storage adapter using AsyncStorage
|
|
4739
4739
|
* Note: Uses native batch operations for better performance (not base class)
|
|
@@ -4765,7 +4765,7 @@ class ReactNativeStorageAdapter {
|
|
|
4765
4765
|
return await this.AsyncStorage.getItem(key);
|
|
4766
4766
|
}
|
|
4767
4767
|
catch (error) {
|
|
4768
|
-
log$
|
|
4768
|
+
log$a.error('Failed to get item from AsyncStorage:', error);
|
|
4769
4769
|
return null;
|
|
4770
4770
|
}
|
|
4771
4771
|
}
|
|
@@ -4806,7 +4806,7 @@ class ReactNativeStorageAdapter {
|
|
|
4806
4806
|
return await this.AsyncStorage.getAllKeys();
|
|
4807
4807
|
}
|
|
4808
4808
|
catch (error) {
|
|
4809
|
-
log$
|
|
4809
|
+
log$a.error('Failed to get all keys:', error);
|
|
4810
4810
|
return [];
|
|
4811
4811
|
}
|
|
4812
4812
|
}
|
|
@@ -4823,7 +4823,7 @@ class ReactNativeStorageAdapter {
|
|
|
4823
4823
|
return result;
|
|
4824
4824
|
}
|
|
4825
4825
|
catch (error) {
|
|
4826
|
-
log$
|
|
4826
|
+
log$a.error('Failed to get multiple items:', error);
|
|
4827
4827
|
const result = {};
|
|
4828
4828
|
keys.forEach((key) => {
|
|
4829
4829
|
result[key] = null;
|
|
@@ -4873,7 +4873,7 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
4873
4873
|
return;
|
|
4874
4874
|
}
|
|
4875
4875
|
catch {
|
|
4876
|
-
log$
|
|
4876
|
+
log$a.debug('expo-secure-store not available, trying react-native-keychain');
|
|
4877
4877
|
}
|
|
4878
4878
|
try {
|
|
4879
4879
|
const Keychain = require('react-native-keychain');
|
|
@@ -4882,7 +4882,7 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
4882
4882
|
return;
|
|
4883
4883
|
}
|
|
4884
4884
|
catch {
|
|
4885
|
-
log$
|
|
4885
|
+
log$a.error('react-native-keychain not available');
|
|
4886
4886
|
}
|
|
4887
4887
|
throw new Error('No secure storage available. Please install expo-secure-store or react-native-keychain');
|
|
4888
4888
|
}
|
|
@@ -4900,7 +4900,7 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
4900
4900
|
}
|
|
4901
4901
|
}
|
|
4902
4902
|
catch (error) {
|
|
4903
|
-
log$
|
|
4903
|
+
log$a.error('Failed to get secure item:', error);
|
|
4904
4904
|
}
|
|
4905
4905
|
return null;
|
|
4906
4906
|
}
|
|
@@ -4937,10 +4937,10 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
4937
4937
|
}
|
|
4938
4938
|
}
|
|
4939
4939
|
async clear() {
|
|
4940
|
-
log$
|
|
4940
|
+
log$a.warn('Clear all secure items not fully implemented for React Native');
|
|
4941
4941
|
}
|
|
4942
4942
|
async getAllKeys() {
|
|
4943
|
-
log$
|
|
4943
|
+
log$a.warn('Get all secure keys not implemented for React Native');
|
|
4944
4944
|
return [];
|
|
4945
4945
|
}
|
|
4946
4946
|
async isAvailable() {
|
|
@@ -5158,7 +5158,7 @@ class NodeNetworkMonitor extends NetworkBase {
|
|
|
5158
5158
|
}
|
|
5159
5159
|
}
|
|
5160
5160
|
|
|
5161
|
-
const log$
|
|
5161
|
+
const log$9 = createPrefixedLogger('RN-NETWORK');
|
|
5162
5162
|
/**
|
|
5163
5163
|
* React Native network monitor using RxJS
|
|
5164
5164
|
* Supports both @react-native-community/netinfo and expo-network
|
|
@@ -5171,7 +5171,7 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5171
5171
|
this.moduleReady$ = new Subject();
|
|
5172
5172
|
this.isExpo = detectPlatform().isExpo;
|
|
5173
5173
|
this.init().catch((error) => {
|
|
5174
|
-
log$
|
|
5174
|
+
log$9.error('Network monitor initialization failed:', error);
|
|
5175
5175
|
});
|
|
5176
5176
|
}
|
|
5177
5177
|
async init() {
|
|
@@ -5190,10 +5190,10 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5190
5190
|
try {
|
|
5191
5191
|
const module = require('@react-native-community/netinfo');
|
|
5192
5192
|
this.netInfoModule = module.default || module;
|
|
5193
|
-
log$
|
|
5193
|
+
log$9.debug('Loaded @react-native-community/netinfo module');
|
|
5194
5194
|
}
|
|
5195
5195
|
catch (error) {
|
|
5196
|
-
log$
|
|
5196
|
+
log$9.error('Failed to load React Native NetInfo module:', error);
|
|
5197
5197
|
this.netInfoModule = null;
|
|
5198
5198
|
}
|
|
5199
5199
|
}
|
|
@@ -5201,10 +5201,10 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5201
5201
|
try {
|
|
5202
5202
|
const module = require('expo-network');
|
|
5203
5203
|
this.netInfoModule = module.default || module;
|
|
5204
|
-
log$
|
|
5204
|
+
log$9.debug('Loaded expo-network module');
|
|
5205
5205
|
}
|
|
5206
5206
|
catch (error) {
|
|
5207
|
-
log$
|
|
5207
|
+
log$9.error('Failed to load Expo Network module:', error);
|
|
5208
5208
|
this.netInfoModule = null;
|
|
5209
5209
|
}
|
|
5210
5210
|
}
|
|
@@ -5221,16 +5221,16 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5221
5221
|
}
|
|
5222
5222
|
const online = !!(state.isConnected && state.isInternetReachable !== false);
|
|
5223
5223
|
this.updateStatus(online);
|
|
5224
|
-
log$
|
|
5224
|
+
log$9.debug('Initial network state:', online ? 'online' : 'offline');
|
|
5225
5225
|
}
|
|
5226
5226
|
catch (error) {
|
|
5227
|
-
log$
|
|
5227
|
+
log$9.warn('Could not fetch initial network state:', error);
|
|
5228
5228
|
}
|
|
5229
5229
|
}
|
|
5230
5230
|
subscribeToStateChanges() {
|
|
5231
5231
|
if (!this.netInfoModule)
|
|
5232
5232
|
return;
|
|
5233
|
-
log$
|
|
5233
|
+
log$9.debug('Subscribing to network state changes');
|
|
5234
5234
|
const handleState = (state) => {
|
|
5235
5235
|
const online = !!(state.isConnected && (state.isInternetReachable ?? true));
|
|
5236
5236
|
this.updateStatus(online);
|
|
@@ -5272,7 +5272,7 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5272
5272
|
};
|
|
5273
5273
|
}
|
|
5274
5274
|
catch (error) {
|
|
5275
|
-
log$
|
|
5275
|
+
log$9.error('Failed to fetch detailed network info:', error);
|
|
5276
5276
|
return null;
|
|
5277
5277
|
}
|
|
5278
5278
|
}
|
|
@@ -5408,7 +5408,7 @@ class CertificateValidator {
|
|
|
5408
5408
|
}
|
|
5409
5409
|
}
|
|
5410
5410
|
|
|
5411
|
-
const log$
|
|
5411
|
+
const log$8 = createPrefixedLogger('RN-MTLS');
|
|
5412
5412
|
/**
|
|
5413
5413
|
* React Native mTLS Adapter using @a-cube-io/expo-mutual-tls
|
|
5414
5414
|
*/
|
|
@@ -5430,7 +5430,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5430
5430
|
this.expoMTLS = ExpoMutualTls;
|
|
5431
5431
|
// Set up debug logging with the correct event signature
|
|
5432
5432
|
const debugListener = ExpoMutualTls.onDebugLog((event) => {
|
|
5433
|
-
log$
|
|
5433
|
+
log$8.debug(`${event.type}: ${event.message}`, {
|
|
5434
5434
|
method: event.method,
|
|
5435
5435
|
url: event.url,
|
|
5436
5436
|
statusCode: event.statusCode,
|
|
@@ -5440,28 +5440,28 @@ class ReactNativeMTLSAdapter {
|
|
|
5440
5440
|
this.eventListeners.push(debugListener);
|
|
5441
5441
|
// Set up error logging with the correct event signature
|
|
5442
5442
|
const errorListener = ExpoMutualTls.onError((event) => {
|
|
5443
|
-
log$
|
|
5443
|
+
log$8.error(event.message, {
|
|
5444
5444
|
code: event.code,
|
|
5445
5445
|
});
|
|
5446
5446
|
});
|
|
5447
5447
|
this.eventListeners.push(errorListener);
|
|
5448
5448
|
// Set up certificate expiry monitoring with the correct event signature
|
|
5449
5449
|
const expiryListener = ExpoMutualTls.onCertificateExpiry((event) => {
|
|
5450
|
-
log$
|
|
5450
|
+
log$8.warn(`Certificate ${event.subject} expires at ${new Date(event.expiry)}`, {
|
|
5451
5451
|
alias: event.alias,
|
|
5452
5452
|
warning: event.warning,
|
|
5453
5453
|
});
|
|
5454
5454
|
});
|
|
5455
5455
|
this.eventListeners.push(expiryListener);
|
|
5456
|
-
log$
|
|
5456
|
+
log$8.debug('Expo mTLS module loaded successfully');
|
|
5457
5457
|
}
|
|
5458
5458
|
catch (error) {
|
|
5459
|
-
log$
|
|
5459
|
+
log$8.warn('@a-cube-io/expo-mutual-tls not available:', error);
|
|
5460
5460
|
}
|
|
5461
5461
|
}
|
|
5462
5462
|
async isMTLSSupported() {
|
|
5463
5463
|
const supported = this.expoMTLS !== null;
|
|
5464
|
-
log$
|
|
5464
|
+
log$8.debug('mTLS support check:', {
|
|
5465
5465
|
supported,
|
|
5466
5466
|
platform: this.getPlatformInfo().platform,
|
|
5467
5467
|
moduleAvailable: !!this.expoMTLS,
|
|
@@ -5473,7 +5473,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5473
5473
|
throw new MTLSError(exports.MTLSErrorType.NOT_SUPPORTED, 'Expo mTLS module not available');
|
|
5474
5474
|
}
|
|
5475
5475
|
this.config = config;
|
|
5476
|
-
log$
|
|
5476
|
+
log$8.debug('Initialized with config:', {
|
|
5477
5477
|
baseUrl: config.baseUrl,
|
|
5478
5478
|
port: config.port,
|
|
5479
5479
|
timeout: config.timeout,
|
|
@@ -5487,7 +5487,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5487
5487
|
if (!this.config) {
|
|
5488
5488
|
throw new MTLSError(exports.MTLSErrorType.CONFIGURATION_ERROR, 'Adapter not initialized. Call initialize() first.');
|
|
5489
5489
|
}
|
|
5490
|
-
log$
|
|
5490
|
+
log$8.debug('Configuring certificate:', {
|
|
5491
5491
|
format: certificateData.format,
|
|
5492
5492
|
hasPassword: !!certificateData.password,
|
|
5493
5493
|
certificateLength: certificateData.certificate.length,
|
|
@@ -5504,14 +5504,14 @@ class ReactNativeMTLSAdapter {
|
|
|
5504
5504
|
'client-key-service', // keyService
|
|
5505
5505
|
true // enableLogging - let the native module handle its own debug logging
|
|
5506
5506
|
);
|
|
5507
|
-
log$
|
|
5507
|
+
log$8.debug('PEM services configured:', configResult);
|
|
5508
5508
|
if (!configResult.success) {
|
|
5509
5509
|
throw new MTLSError(exports.MTLSErrorType.CONFIGURATION_ERROR, `PEM configuration failed: ${configResult.state}`);
|
|
5510
5510
|
}
|
|
5511
5511
|
// Step 2: Store the actual PEM certificate and private key
|
|
5512
5512
|
const storeResult = await this.expoMTLS.storePEM(certificateData.certificate, certificateData.privateKey, certificateData.password // passphrase (optional)
|
|
5513
5513
|
);
|
|
5514
|
-
log$
|
|
5514
|
+
log$8.debug('PEM certificate store result:', storeResult);
|
|
5515
5515
|
if (!storeResult) {
|
|
5516
5516
|
throw new MTLSError(exports.MTLSErrorType.CERTIFICATE_INVALID, 'Failed to store PEM certificate');
|
|
5517
5517
|
}
|
|
@@ -5524,14 +5524,14 @@ class ReactNativeMTLSAdapter {
|
|
|
5524
5524
|
const configResult = await this.expoMTLS.configureP12('client-p12-service', // keychainService
|
|
5525
5525
|
true // enableLogging - let the native module handle its own debug logging
|
|
5526
5526
|
);
|
|
5527
|
-
log$
|
|
5527
|
+
log$8.debug('P12 service configured:', configResult);
|
|
5528
5528
|
if (!configResult.success) {
|
|
5529
5529
|
throw new MTLSError(exports.MTLSErrorType.CONFIGURATION_ERROR, `P12 configuration failed: ${configResult.state}`);
|
|
5530
5530
|
}
|
|
5531
5531
|
// Step 2: Store the P12 certificate data
|
|
5532
5532
|
const storeResult = await this.expoMTLS.storeP12(certificateData.certificate, // P12 data in certificate field
|
|
5533
5533
|
certificateData.password);
|
|
5534
|
-
log$
|
|
5534
|
+
log$8.debug('P12 certificate store result:', storeResult);
|
|
5535
5535
|
if (!storeResult) {
|
|
5536
5536
|
throw new MTLSError(exports.MTLSErrorType.CERTIFICATE_INVALID, 'Failed to store P12 certificate');
|
|
5537
5537
|
}
|
|
@@ -5545,7 +5545,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5545
5545
|
if (error instanceof MTLSError) {
|
|
5546
5546
|
throw error;
|
|
5547
5547
|
}
|
|
5548
|
-
log$
|
|
5548
|
+
log$8.error('Certificate configuration failed:', error);
|
|
5549
5549
|
throw new MTLSError(exports.MTLSErrorType.CONFIGURATION_ERROR, 'Failed to configure certificate', error);
|
|
5550
5550
|
}
|
|
5551
5551
|
}
|
|
@@ -5556,38 +5556,38 @@ class ReactNativeMTLSAdapter {
|
|
|
5556
5556
|
try {
|
|
5557
5557
|
// Use static method call
|
|
5558
5558
|
const hasCert = await this.expoMTLS.hasCertificate();
|
|
5559
|
-
log$
|
|
5559
|
+
log$8.debug('Certificate availability check:', hasCert);
|
|
5560
5560
|
return hasCert;
|
|
5561
5561
|
}
|
|
5562
5562
|
catch (error) {
|
|
5563
|
-
log$
|
|
5563
|
+
log$8.error('Certificate check failed:', error);
|
|
5564
5564
|
return false;
|
|
5565
5565
|
}
|
|
5566
5566
|
}
|
|
5567
5567
|
async getCertificateInfo() {
|
|
5568
5568
|
if (!this.expoMTLS) {
|
|
5569
|
-
log$
|
|
5569
|
+
log$8.debug('Certificate info requested but module not available');
|
|
5570
5570
|
return null;
|
|
5571
5571
|
}
|
|
5572
5572
|
try {
|
|
5573
5573
|
const hasCert = await this.hasCertificate();
|
|
5574
5574
|
if (!hasCert) {
|
|
5575
|
-
log$
|
|
5575
|
+
log$8.debug('No certificate stored');
|
|
5576
5576
|
return null;
|
|
5577
5577
|
}
|
|
5578
5578
|
// Use getCertificatesInfo to retrieve information about stored certificates
|
|
5579
5579
|
const result = await this.expoMTLS.getCertificatesInfo();
|
|
5580
5580
|
if (!result || !result.certificates || result.certificates.length === 0) {
|
|
5581
|
-
log$
|
|
5581
|
+
log$8.debug('No certificate information available');
|
|
5582
5582
|
return null;
|
|
5583
5583
|
}
|
|
5584
5584
|
// Get the first certificate (primary client certificate)
|
|
5585
5585
|
const cert = result.certificates[0];
|
|
5586
5586
|
if (!cert) {
|
|
5587
|
-
log$
|
|
5587
|
+
log$8.debug('Certificate data is empty');
|
|
5588
5588
|
return null;
|
|
5589
5589
|
}
|
|
5590
|
-
log$
|
|
5590
|
+
log$8.debug('Retrieved certificate info:', {
|
|
5591
5591
|
subject: cert.subject.commonName,
|
|
5592
5592
|
issuer: cert.issuer.commonName,
|
|
5593
5593
|
validFrom: new Date(cert.validFrom),
|
|
@@ -5606,7 +5606,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5606
5606
|
};
|
|
5607
5607
|
}
|
|
5608
5608
|
catch (error) {
|
|
5609
|
-
log$
|
|
5609
|
+
log$8.error('Failed to get certificate info:', error);
|
|
5610
5610
|
return null;
|
|
5611
5611
|
}
|
|
5612
5612
|
}
|
|
@@ -5617,7 +5617,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5617
5617
|
*/
|
|
5618
5618
|
async parseCertificateData(certificateData) {
|
|
5619
5619
|
if (!this.expoMTLS) {
|
|
5620
|
-
log$
|
|
5620
|
+
log$8.debug('Parse certificate: Module not available');
|
|
5621
5621
|
return null;
|
|
5622
5622
|
}
|
|
5623
5623
|
try {
|
|
@@ -5634,14 +5634,14 @@ class ReactNativeMTLSAdapter {
|
|
|
5634
5634
|
else {
|
|
5635
5635
|
throw new MTLSError(exports.MTLSErrorType.CERTIFICATE_INVALID, `Unsupported certificate format: ${certificateData.format}`);
|
|
5636
5636
|
}
|
|
5637
|
-
log$
|
|
5637
|
+
log$8.debug('Certificate parsed successfully:', {
|
|
5638
5638
|
certificateCount: result.certificates.length,
|
|
5639
5639
|
subjects: result.certificates.map((cert) => cert.subject.commonName),
|
|
5640
5640
|
});
|
|
5641
5641
|
return result;
|
|
5642
5642
|
}
|
|
5643
5643
|
catch (error) {
|
|
5644
|
-
log$
|
|
5644
|
+
log$8.error('Failed to parse certificate:', error);
|
|
5645
5645
|
if (error instanceof MTLSError) {
|
|
5646
5646
|
throw error;
|
|
5647
5647
|
}
|
|
@@ -5656,7 +5656,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5656
5656
|
if (!hasCert) {
|
|
5657
5657
|
throw new MTLSError(exports.MTLSErrorType.CERTIFICATE_NOT_FOUND, 'No certificate configured');
|
|
5658
5658
|
}
|
|
5659
|
-
log$
|
|
5659
|
+
log$8.debug('Making mTLS request:', {
|
|
5660
5660
|
method: requestConfig.method || 'GET',
|
|
5661
5661
|
url: requestConfig.url,
|
|
5662
5662
|
headers: requestConfig.headers,
|
|
@@ -5664,7 +5664,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5664
5664
|
responseType: requestConfig.responseType,
|
|
5665
5665
|
});
|
|
5666
5666
|
if (requestConfig.data) {
|
|
5667
|
-
log$
|
|
5667
|
+
log$8.debug('mTLS request body:', requestConfig.data);
|
|
5668
5668
|
}
|
|
5669
5669
|
try {
|
|
5670
5670
|
const response = await this.expoMTLS.request(requestConfig.url, {
|
|
@@ -5673,7 +5673,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5673
5673
|
body: requestConfig.data ? JSON.stringify(requestConfig.data) : undefined,
|
|
5674
5674
|
responseType: requestConfig.responseType,
|
|
5675
5675
|
});
|
|
5676
|
-
log$
|
|
5676
|
+
log$8.debug('mTLS request successful:', response);
|
|
5677
5677
|
if (!response.success) {
|
|
5678
5678
|
throw new MTLSError(exports.MTLSErrorType.CONNECTION_FAILED, `mTLS request failed: ${response.statusMessage} (${response.statusCode})`, undefined, response.statusCode);
|
|
5679
5679
|
}
|
|
@@ -5685,7 +5685,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5685
5685
|
data = JSON.parse(response.body);
|
|
5686
5686
|
}
|
|
5687
5687
|
catch (parseError) {
|
|
5688
|
-
log$
|
|
5688
|
+
log$8.warn('Failed to parse JSON response:', parseError);
|
|
5689
5689
|
// If parsing fails, keep raw body
|
|
5690
5690
|
}
|
|
5691
5691
|
}
|
|
@@ -5702,7 +5702,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5702
5702
|
};
|
|
5703
5703
|
}
|
|
5704
5704
|
catch (error) {
|
|
5705
|
-
log$
|
|
5705
|
+
log$8.error('mTLS request failed:', error);
|
|
5706
5706
|
throw new MTLSError(exports.MTLSErrorType.CONNECTION_FAILED, 'mTLS request failed', error);
|
|
5707
5707
|
}
|
|
5708
5708
|
}
|
|
@@ -5715,18 +5715,18 @@ class ReactNativeMTLSAdapter {
|
|
|
5715
5715
|
*/
|
|
5716
5716
|
async testConnection() {
|
|
5717
5717
|
if (!this.expoMTLS || !this.config) {
|
|
5718
|
-
log$
|
|
5718
|
+
log$8.debug('Diagnostic test: No mTLS module or config available');
|
|
5719
5719
|
return false;
|
|
5720
5720
|
}
|
|
5721
5721
|
try {
|
|
5722
5722
|
const hasCert = await this.hasCertificate();
|
|
5723
5723
|
if (!hasCert) {
|
|
5724
|
-
log$
|
|
5724
|
+
log$8.debug('Diagnostic test: No certificate configured');
|
|
5725
5725
|
return false;
|
|
5726
5726
|
}
|
|
5727
|
-
log$
|
|
5727
|
+
log$8.debug('Running diagnostic test (may fail even if mTLS works):', this.config.baseUrl);
|
|
5728
5728
|
const result = await this.expoMTLS.testConnection(this.config.baseUrl);
|
|
5729
|
-
log$
|
|
5729
|
+
log$8.debug('Diagnostic test result (NOT validation):', {
|
|
5730
5730
|
success: result.success,
|
|
5731
5731
|
statusCode: result.statusCode,
|
|
5732
5732
|
statusMessage: result.statusMessage,
|
|
@@ -5737,13 +5737,13 @@ class ReactNativeMTLSAdapter {
|
|
|
5737
5737
|
return result.success;
|
|
5738
5738
|
}
|
|
5739
5739
|
catch (error) {
|
|
5740
|
-
log$
|
|
5740
|
+
log$8.warn('Diagnostic test failed (this is expected):', error);
|
|
5741
5741
|
return false;
|
|
5742
5742
|
}
|
|
5743
5743
|
}
|
|
5744
5744
|
async removeCertificate() {
|
|
5745
5745
|
if (!this.expoMTLS) {
|
|
5746
|
-
log$
|
|
5746
|
+
log$8.debug('Remove certificate: Module not available');
|
|
5747
5747
|
return;
|
|
5748
5748
|
}
|
|
5749
5749
|
try {
|
|
@@ -5752,10 +5752,10 @@ class ReactNativeMTLSAdapter {
|
|
|
5752
5752
|
this.isConfigured = false;
|
|
5753
5753
|
// Cleanup event listeners
|
|
5754
5754
|
this.cleanupEventListeners();
|
|
5755
|
-
log$
|
|
5755
|
+
log$8.debug('Certificate removed successfully');
|
|
5756
5756
|
}
|
|
5757
5757
|
catch (error) {
|
|
5758
|
-
log$
|
|
5758
|
+
log$8.error('Failed to remove certificate:', error);
|
|
5759
5759
|
throw new MTLSError(exports.MTLSErrorType.CONFIGURATION_ERROR, 'Failed to remove certificate', error);
|
|
5760
5760
|
}
|
|
5761
5761
|
}
|
|
@@ -5764,7 +5764,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5764
5764
|
*/
|
|
5765
5765
|
cleanupEventListeners() {
|
|
5766
5766
|
if (this.eventListeners.length > 0) {
|
|
5767
|
-
log$
|
|
5767
|
+
log$8.debug(`Cleaning up ${this.eventListeners.length} event listeners`);
|
|
5768
5768
|
}
|
|
5769
5769
|
// Remove individual listeners if they have remove methods
|
|
5770
5770
|
this.eventListeners.forEach((listener) => {
|
|
@@ -5801,7 +5801,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5801
5801
|
}
|
|
5802
5802
|
}
|
|
5803
5803
|
|
|
5804
|
-
const log$
|
|
5804
|
+
const log$7 = createPrefixedLogger('WEB-MTLS');
|
|
5805
5805
|
/**
|
|
5806
5806
|
* Web mTLS Adapter - Graceful fallback for web browsers
|
|
5807
5807
|
*
|
|
@@ -5815,13 +5815,13 @@ const log$4 = createPrefixedLogger('WEB-MTLS');
|
|
|
5815
5815
|
*/
|
|
5816
5816
|
class WebMTLSAdapter {
|
|
5817
5817
|
constructor() {
|
|
5818
|
-
log$
|
|
5819
|
-
log$
|
|
5818
|
+
log$7.warn('Web browsers do not support programmatic mTLS configuration');
|
|
5819
|
+
log$7.info('Use JWT authentication or configure client certificates in browser settings');
|
|
5820
5820
|
}
|
|
5821
5821
|
async isMTLSSupported() {
|
|
5822
5822
|
// mTLS is not supported programmatically in web browsers
|
|
5823
5823
|
const supported = false;
|
|
5824
|
-
log$
|
|
5824
|
+
log$7.debug('mTLS support check:', {
|
|
5825
5825
|
supported,
|
|
5826
5826
|
platform: this.getPlatformInfo().platform,
|
|
5827
5827
|
reason: 'Browser security model prevents programmatic certificate configuration',
|
|
@@ -5830,14 +5830,14 @@ class WebMTLSAdapter {
|
|
|
5830
5830
|
return supported;
|
|
5831
5831
|
}
|
|
5832
5832
|
async initialize(config) {
|
|
5833
|
-
log$
|
|
5833
|
+
log$7.warn('Initialized but mTLS not available in web browsers:', {
|
|
5834
5834
|
baseUrl: config.baseUrl,
|
|
5835
5835
|
port: config.port,
|
|
5836
5836
|
recommendation: 'Use standard HTTPS with JWT authentication',
|
|
5837
5837
|
});
|
|
5838
5838
|
}
|
|
5839
5839
|
async configureCertificate(certificateData) {
|
|
5840
|
-
log$
|
|
5840
|
+
log$7.error('Certificate configuration attempted:', {
|
|
5841
5841
|
format: certificateData.format,
|
|
5842
5842
|
reason: 'Not supported in web browsers',
|
|
5843
5843
|
alternatives: [
|
|
@@ -5852,15 +5852,15 @@ class WebMTLSAdapter {
|
|
|
5852
5852
|
}
|
|
5853
5853
|
async hasCertificate() {
|
|
5854
5854
|
// We cannot detect if the browser has certificates configured
|
|
5855
|
-
log$
|
|
5855
|
+
log$7.debug('Certificate availability check: Cannot detect browser certificates programmatically');
|
|
5856
5856
|
return false;
|
|
5857
5857
|
}
|
|
5858
5858
|
async getCertificateInfo() {
|
|
5859
|
-
log$
|
|
5859
|
+
log$7.debug('Certificate info requested: Not accessible in web browsers');
|
|
5860
5860
|
return null;
|
|
5861
5861
|
}
|
|
5862
5862
|
async request(requestConfig) {
|
|
5863
|
-
log$
|
|
5863
|
+
log$7.error('mTLS request attempted:', {
|
|
5864
5864
|
method: requestConfig.method,
|
|
5865
5865
|
url: requestConfig.url,
|
|
5866
5866
|
reason: 'Not supported in web browsers',
|
|
@@ -5875,11 +5875,11 @@ class WebMTLSAdapter {
|
|
|
5875
5875
|
'are properly configured in the browser certificate store.');
|
|
5876
5876
|
}
|
|
5877
5877
|
async testConnection() {
|
|
5878
|
-
log$
|
|
5878
|
+
log$7.debug('Connection test: mTLS not available in web browsers');
|
|
5879
5879
|
return false;
|
|
5880
5880
|
}
|
|
5881
5881
|
async removeCertificate() {
|
|
5882
|
-
log$
|
|
5882
|
+
log$7.debug('Remove certificate: No certificates to remove (not supported in web browsers)');
|
|
5883
5883
|
// No-op - cannot remove certificates programmatically in browsers
|
|
5884
5884
|
}
|
|
5885
5885
|
/**
|
|
@@ -5887,7 +5887,7 @@ class WebMTLSAdapter {
|
|
|
5887
5887
|
* Always returns null for web browsers as mTLS is not supported
|
|
5888
5888
|
*/
|
|
5889
5889
|
getBaseUrl() {
|
|
5890
|
-
log$
|
|
5890
|
+
log$7.debug('Base URL requested: Not supported in web browsers');
|
|
5891
5891
|
return null;
|
|
5892
5892
|
}
|
|
5893
5893
|
getPlatformInfo() {
|
|
@@ -5920,7 +5920,7 @@ class WebMTLSAdapter {
|
|
|
5920
5920
|
}
|
|
5921
5921
|
}
|
|
5922
5922
|
|
|
5923
|
-
const log$
|
|
5923
|
+
const log$6 = createPrefixedLogger('MTLS-LOADER');
|
|
5924
5924
|
function loadMTLSAdapter(platform, config) {
|
|
5925
5925
|
try {
|
|
5926
5926
|
let adapter;
|
|
@@ -5954,7 +5954,7 @@ function loadMTLSAdapter(platform, config) {
|
|
|
5954
5954
|
return adapter;
|
|
5955
5955
|
}
|
|
5956
5956
|
catch (error) {
|
|
5957
|
-
log$
|
|
5957
|
+
log$6.warn(`mTLS adapter not available for platform ${platform}:`, error);
|
|
5958
5958
|
return null;
|
|
5959
5959
|
}
|
|
5960
5960
|
}
|
|
@@ -5964,7 +5964,7 @@ async function initializeAdapterAsync(adapter, config) {
|
|
|
5964
5964
|
if (isSupported) {
|
|
5965
5965
|
await adapter.initialize(config);
|
|
5966
5966
|
const platformInfo = adapter.getPlatformInfo();
|
|
5967
|
-
log$
|
|
5967
|
+
log$6.debug('mTLS adapter initialized:', {
|
|
5968
5968
|
platform: platformInfo.platform,
|
|
5969
5969
|
mtlsSupported: platformInfo.mtlsSupported,
|
|
5970
5970
|
certificateStorage: platformInfo.certificateStorage,
|
|
@@ -5973,20 +5973,20 @@ async function initializeAdapterAsync(adapter, config) {
|
|
|
5973
5973
|
}
|
|
5974
5974
|
}
|
|
5975
5975
|
catch (error) {
|
|
5976
|
-
log$
|
|
5976
|
+
log$6.warn('Failed to initialize mTLS adapter:', error);
|
|
5977
5977
|
}
|
|
5978
5978
|
}
|
|
5979
5979
|
|
|
5980
|
-
const log$
|
|
5980
|
+
const log$5 = createPrefixedLogger('ADAPTER-LOADER');
|
|
5981
5981
|
function loadPlatformAdapters(options = {}) {
|
|
5982
5982
|
const { mtlsConfig } = options;
|
|
5983
5983
|
const { platform } = detectPlatform();
|
|
5984
|
-
log$
|
|
5984
|
+
log$5.debug('Loading adapters for platform:', platform);
|
|
5985
5985
|
const storageAdapters = loadStorageAdapters(platform);
|
|
5986
5986
|
const networkMonitor = loadNetworkMonitor(platform);
|
|
5987
5987
|
const cache = loadCacheAdapter(platform);
|
|
5988
5988
|
const mtls = loadMTLSAdapter(platform, mtlsConfig);
|
|
5989
|
-
log$
|
|
5989
|
+
log$5.debug('Adapters loaded:', {
|
|
5990
5990
|
platform,
|
|
5991
5991
|
hasStorage: !!storageAdapters.storage,
|
|
5992
5992
|
hasSecureStorage: !!storageAdapters.secureStorage,
|
|
@@ -6100,10 +6100,12 @@ class ACubeSDKError extends Error {
|
|
|
6100
6100
|
|
|
6101
6101
|
const DI_TOKENS = {
|
|
6102
6102
|
HTTP_PORT: Symbol('HTTP_PORT'),
|
|
6103
|
+
BASE_HTTP_PORT: Symbol('BASE_HTTP_PORT'),
|
|
6103
6104
|
STORAGE_PORT: Symbol('STORAGE_PORT'),
|
|
6104
6105
|
SECURE_STORAGE_PORT: Symbol('SECURE_STORAGE_PORT'),
|
|
6105
6106
|
NETWORK_PORT: Symbol('NETWORK_PORT'),
|
|
6106
6107
|
CACHE_PORT: Symbol('CACHE_PORT'),
|
|
6108
|
+
CACHE_KEY_GENERATOR: Symbol('CACHE_KEY_GENERATOR'),
|
|
6107
6109
|
MTLS_PORT: Symbol('MTLS_PORT'),
|
|
6108
6110
|
TOKEN_STORAGE_PORT: Symbol('TOKEN_STORAGE_PORT'),
|
|
6109
6111
|
RECEIPT_REPOSITORY: Symbol('RECEIPT_REPOSITORY'),
|
|
@@ -6333,7 +6335,7 @@ class ReceiptRepositoryImpl {
|
|
|
6333
6335
|
if (format === 'pdf') {
|
|
6334
6336
|
const response = await this.http.get(`/mf1/receipts/${receiptUuid}/details`, {
|
|
6335
6337
|
headers: { Accept: 'application/pdf' },
|
|
6336
|
-
responseType: '
|
|
6338
|
+
responseType: 'arraybuffer',
|
|
6337
6339
|
});
|
|
6338
6340
|
return response.data;
|
|
6339
6341
|
}
|
|
@@ -7096,7 +7098,465 @@ class TelemetryRepositoryImpl {
|
|
|
7096
7098
|
}
|
|
7097
7099
|
}
|
|
7098
7100
|
|
|
7099
|
-
const log$
|
|
7101
|
+
const log$4 = createPrefixedLogger('CACHE-KEY');
|
|
7102
|
+
const URL_PATTERNS = [
|
|
7103
|
+
// Receipt (mf1) - specific patterns first
|
|
7104
|
+
{
|
|
7105
|
+
pattern: /^\/mf1\/receipts\/([^/]+)\/returnable-items$/,
|
|
7106
|
+
resource: 'receipt',
|
|
7107
|
+
action: 'returnable',
|
|
7108
|
+
},
|
|
7109
|
+
{
|
|
7110
|
+
pattern: /^\/mf1\/receipts\/([^/]+)\/details$/,
|
|
7111
|
+
resource: 'receipt',
|
|
7112
|
+
action: 'details',
|
|
7113
|
+
},
|
|
7114
|
+
{ pattern: /^\/mf1\/receipts\/([^/]+)$/, resource: 'receipt' },
|
|
7115
|
+
{
|
|
7116
|
+
pattern: /^\/mf1\/point-of-sales\/([^/]+)\/receipts$/,
|
|
7117
|
+
resource: 'receipt',
|
|
7118
|
+
parent: 'point-of-sale',
|
|
7119
|
+
isList: true,
|
|
7120
|
+
},
|
|
7121
|
+
{ pattern: /^\/mf1\/receipts$/, resource: 'receipt', isList: true },
|
|
7122
|
+
// Merchant (mf2)
|
|
7123
|
+
{ pattern: /^\/mf2\/merchants\/([^/]+)$/, resource: 'merchant' },
|
|
7124
|
+
{ pattern: /^\/mf2\/merchants$/, resource: 'merchant', isList: true },
|
|
7125
|
+
// Cashier (mf1)
|
|
7126
|
+
{ pattern: /^\/mf1\/cashiers\/me$/, resource: 'cashier', action: 'me' },
|
|
7127
|
+
{ pattern: /^\/mf1\/cashiers\/([^/]+)$/, resource: 'cashier' },
|
|
7128
|
+
{ pattern: /^\/mf1\/cashiers$/, resource: 'cashier', isList: true },
|
|
7129
|
+
// Cash Register (mf1)
|
|
7130
|
+
{ pattern: /^\/mf1\/cash-registers\/([^/]+)$/, resource: 'cash-register' },
|
|
7131
|
+
{ pattern: /^\/mf1\/cash-registers$/, resource: 'cash-register', isList: true },
|
|
7132
|
+
// Point of Sale (mf1)
|
|
7133
|
+
{ pattern: /^\/mf1\/point-of-sales\/([^/]+)$/, resource: 'point-of-sale' },
|
|
7134
|
+
{ pattern: /^\/mf1\/point-of-sales$/, resource: 'point-of-sale', isList: true },
|
|
7135
|
+
// Nested resources under merchant (mf2)
|
|
7136
|
+
{
|
|
7137
|
+
pattern: /^\/mf2\/merchants\/([^/]+)\/suppliers\/([^/]+)$/,
|
|
7138
|
+
resource: 'supplier',
|
|
7139
|
+
parent: 'merchant',
|
|
7140
|
+
},
|
|
7141
|
+
{
|
|
7142
|
+
pattern: /^\/mf2\/merchants\/([^/]+)\/suppliers$/,
|
|
7143
|
+
resource: 'supplier',
|
|
7144
|
+
parent: 'merchant',
|
|
7145
|
+
isList: true,
|
|
7146
|
+
},
|
|
7147
|
+
{
|
|
7148
|
+
pattern: /^\/mf2\/merchants\/([^/]+)\/daily-reports/,
|
|
7149
|
+
resource: 'daily-report',
|
|
7150
|
+
parent: 'merchant',
|
|
7151
|
+
isList: true,
|
|
7152
|
+
},
|
|
7153
|
+
{
|
|
7154
|
+
pattern: /^\/mf2\/merchants\/([^/]+)\/journals/,
|
|
7155
|
+
resource: 'journal',
|
|
7156
|
+
parent: 'merchant',
|
|
7157
|
+
isList: true,
|
|
7158
|
+
},
|
|
7159
|
+
// PEM (mf2)
|
|
7160
|
+
{
|
|
7161
|
+
pattern: /^\/mf2\/point-of-sales\/([^/]+)\/certificates$/,
|
|
7162
|
+
resource: 'pem',
|
|
7163
|
+
action: 'certificates',
|
|
7164
|
+
},
|
|
7165
|
+
{ pattern: /^\/mf2\/point-of-sales\/([^/]+)$/, resource: 'pem' },
|
|
7166
|
+
// Others
|
|
7167
|
+
{ pattern: /^\/mf1\/notifications/, resource: 'notification', isList: true },
|
|
7168
|
+
{
|
|
7169
|
+
pattern: /^\/mf1\/point-of-sales\/([^/]+)\/telemetry$/,
|
|
7170
|
+
resource: 'telemetry',
|
|
7171
|
+
},
|
|
7172
|
+
];
|
|
7173
|
+
const DEFAULT_TTL_CONFIG = {
|
|
7174
|
+
// Data that rarely changes - 30 min TTL for items only
|
|
7175
|
+
merchant: { ttlMs: 30 * 60 * 1000, cacheList: false, cacheItem: true },
|
|
7176
|
+
'point-of-sale': { ttlMs: 30 * 60 * 1000, cacheList: false, cacheItem: true },
|
|
7177
|
+
'cash-register': { ttlMs: 30 * 60 * 1000, cacheList: false, cacheItem: true },
|
|
7178
|
+
pem: { ttlMs: 30 * 60 * 1000, cacheList: false, cacheItem: false },
|
|
7179
|
+
// Data that changes moderately - 10 min TTL for items only
|
|
7180
|
+
cashier: { ttlMs: 10 * 60 * 1000, cacheList: false, cacheItem: true },
|
|
7181
|
+
supplier: { ttlMs: 10 * 60 * 1000, cacheList: false, cacheItem: true },
|
|
7182
|
+
// Data that can change - 5 min TTL for items only
|
|
7183
|
+
receipt: { ttlMs: 5 * 60 * 1000, cacheList: false, cacheItem: true },
|
|
7184
|
+
'daily-report': { ttlMs: 5 * 60 * 1000, cacheList: false, cacheItem: true },
|
|
7185
|
+
journal: { ttlMs: 5 * 60 * 1000, cacheList: false, cacheItem: true },
|
|
7186
|
+
// Real-time data - 1 min TTL
|
|
7187
|
+
notification: { ttlMs: 1 * 60 * 1000, cacheList: false, cacheItem: false },
|
|
7188
|
+
telemetry: { ttlMs: 1 * 60 * 1000, cacheList: false, cacheItem: false },
|
|
7189
|
+
};
|
|
7190
|
+
const DEFAULT_TTL = 5 * 60 * 1000; // 5 minutes
|
|
7191
|
+
class CacheKeyGenerator {
|
|
7192
|
+
constructor(customConfig) {
|
|
7193
|
+
this.config = { ...DEFAULT_TTL_CONFIG, ...customConfig };
|
|
7194
|
+
log$4.info('CacheKeyGenerator initialized with config:', {
|
|
7195
|
+
resources: Object.keys(this.config),
|
|
7196
|
+
});
|
|
7197
|
+
}
|
|
7198
|
+
generate(url, params) {
|
|
7199
|
+
const parsed = this.parseUrl(url);
|
|
7200
|
+
if (!parsed) {
|
|
7201
|
+
// Fallback: use URL as key
|
|
7202
|
+
const paramStr = params ? this.serializeParams(params) : '';
|
|
7203
|
+
const key = paramStr ? `${url}?${paramStr}` : url;
|
|
7204
|
+
log$4.debug('URL not matched, using fallback key:', { url, key });
|
|
7205
|
+
return key;
|
|
7206
|
+
}
|
|
7207
|
+
const { resource, ids, action, isList, parent } = parsed;
|
|
7208
|
+
if (isList) {
|
|
7209
|
+
const paramStr = params ? this.serializeParams(params) : '';
|
|
7210
|
+
const parentPart = parent && ids.length > 0 ? `${parent}=${ids[0]}&` : '';
|
|
7211
|
+
const key = `${resource}:list:${parentPart}${paramStr}`;
|
|
7212
|
+
log$4.debug('Generated list cache key:', { url, key, resource });
|
|
7213
|
+
return key;
|
|
7214
|
+
}
|
|
7215
|
+
// Single item
|
|
7216
|
+
if (ids.length === 0 && action) {
|
|
7217
|
+
// Special case for endpoints like /cashiers/me
|
|
7218
|
+
const key = `${resource}:${action}`;
|
|
7219
|
+
log$4.debug('Generated special action cache key:', { url, key, resource, action });
|
|
7220
|
+
return key;
|
|
7221
|
+
}
|
|
7222
|
+
let key = `${resource}:${ids.join(':')}`;
|
|
7223
|
+
if (action) {
|
|
7224
|
+
key += `:${action}`;
|
|
7225
|
+
}
|
|
7226
|
+
log$4.debug('Generated item cache key:', { url, key, resource, ids, action });
|
|
7227
|
+
return key;
|
|
7228
|
+
}
|
|
7229
|
+
parseResource(url) {
|
|
7230
|
+
const parsed = this.parseUrl(url);
|
|
7231
|
+
return parsed?.resource;
|
|
7232
|
+
}
|
|
7233
|
+
getTTL(url) {
|
|
7234
|
+
const resource = this.parseResource(url);
|
|
7235
|
+
if (!resource) {
|
|
7236
|
+
log$4.debug('No resource found for URL, using default TTL:', { url, ttl: DEFAULT_TTL });
|
|
7237
|
+
return DEFAULT_TTL;
|
|
7238
|
+
}
|
|
7239
|
+
const ttl = this.config[resource].ttlMs;
|
|
7240
|
+
log$4.debug('TTL for resource:', { url, resource, ttlMs: ttl, ttlMin: ttl / 60000 });
|
|
7241
|
+
return ttl;
|
|
7242
|
+
}
|
|
7243
|
+
shouldCache(url) {
|
|
7244
|
+
const parsed = this.parseUrl(url);
|
|
7245
|
+
if (!parsed) {
|
|
7246
|
+
log$4.debug('URL not recognized, should not cache:', { url });
|
|
7247
|
+
return false;
|
|
7248
|
+
}
|
|
7249
|
+
const { resource, isList } = parsed;
|
|
7250
|
+
const config = this.config[resource];
|
|
7251
|
+
if (isList) {
|
|
7252
|
+
log$4.debug('List endpoint cache decision:', {
|
|
7253
|
+
url,
|
|
7254
|
+
resource,
|
|
7255
|
+
isList: true,
|
|
7256
|
+
shouldCache: config.cacheList,
|
|
7257
|
+
});
|
|
7258
|
+
return config.cacheList;
|
|
7259
|
+
}
|
|
7260
|
+
log$4.debug('Item endpoint cache decision:', {
|
|
7261
|
+
url,
|
|
7262
|
+
resource,
|
|
7263
|
+
isList: false,
|
|
7264
|
+
shouldCache: config.cacheItem,
|
|
7265
|
+
});
|
|
7266
|
+
return config.cacheItem;
|
|
7267
|
+
}
|
|
7268
|
+
getInvalidationPatterns(url, method) {
|
|
7269
|
+
const parsed = this.parseUrl(url);
|
|
7270
|
+
if (!parsed) {
|
|
7271
|
+
log$4.debug('No patterns to invalidate for URL:', { url, method });
|
|
7272
|
+
return [];
|
|
7273
|
+
}
|
|
7274
|
+
const { resource, ids, parent } = parsed;
|
|
7275
|
+
const patterns = [];
|
|
7276
|
+
// Always invalidate list on mutations
|
|
7277
|
+
if (method === 'POST' || method === 'PUT' || method === 'PATCH' || method === 'DELETE') {
|
|
7278
|
+
if (parent && ids.length > 0) {
|
|
7279
|
+
patterns.push(`${resource}:list:${parent}=${ids[0]}*`);
|
|
7280
|
+
}
|
|
7281
|
+
patterns.push(`${resource}:list:*`);
|
|
7282
|
+
}
|
|
7283
|
+
// Invalidate specific item on PUT/PATCH/DELETE
|
|
7284
|
+
if (method === 'PUT' || method === 'PATCH' || method === 'DELETE') {
|
|
7285
|
+
if (ids.length > 0) {
|
|
7286
|
+
patterns.push(`${resource}:${ids.join(':')}*`);
|
|
7287
|
+
}
|
|
7288
|
+
}
|
|
7289
|
+
// Special cases
|
|
7290
|
+
if (resource === 'cashier' && (method === 'PUT' || method === 'DELETE')) {
|
|
7291
|
+
patterns.push('cashier:me');
|
|
7292
|
+
}
|
|
7293
|
+
log$4.debug('Invalidation patterns:', { url, method, patterns });
|
|
7294
|
+
return patterns;
|
|
7295
|
+
}
|
|
7296
|
+
parseUrl(url) {
|
|
7297
|
+
// Remove query string for pattern matching
|
|
7298
|
+
const urlPath = url.split('?')[0];
|
|
7299
|
+
for (const pattern of URL_PATTERNS) {
|
|
7300
|
+
const match = urlPath?.match(pattern.pattern);
|
|
7301
|
+
if (match) {
|
|
7302
|
+
// Extract IDs from capture groups
|
|
7303
|
+
const ids = match.slice(1).filter(Boolean);
|
|
7304
|
+
return {
|
|
7305
|
+
resource: pattern.resource,
|
|
7306
|
+
ids,
|
|
7307
|
+
action: pattern.action,
|
|
7308
|
+
isList: pattern.isList,
|
|
7309
|
+
parent: pattern.parent,
|
|
7310
|
+
};
|
|
7311
|
+
}
|
|
7312
|
+
}
|
|
7313
|
+
return null;
|
|
7314
|
+
}
|
|
7315
|
+
serializeParams(params) {
|
|
7316
|
+
const sortedKeys = Object.keys(params).sort();
|
|
7317
|
+
const parts = [];
|
|
7318
|
+
for (const key of sortedKeys) {
|
|
7319
|
+
const value = params[key];
|
|
7320
|
+
if (value !== undefined && value !== null) {
|
|
7321
|
+
parts.push(`${key}=${String(value)}`);
|
|
7322
|
+
}
|
|
7323
|
+
}
|
|
7324
|
+
return parts.join('&');
|
|
7325
|
+
}
|
|
7326
|
+
}
|
|
7327
|
+
|
|
7328
|
+
const log$3 = createPrefixedLogger('CACHE');
|
|
7329
|
+
class CachingHttpDecorator {
|
|
7330
|
+
constructor(http, cache, keyGenerator, networkMonitor, config = {}) {
|
|
7331
|
+
this.http = http;
|
|
7332
|
+
this.cache = cache;
|
|
7333
|
+
this.keyGenerator = keyGenerator;
|
|
7334
|
+
this.networkMonitor = networkMonitor;
|
|
7335
|
+
this.config = config;
|
|
7336
|
+
this.currentOnlineState = true;
|
|
7337
|
+
this.authToken = null;
|
|
7338
|
+
log$3.info('CachingHttpDecorator initialized', {
|
|
7339
|
+
enabled: config.enabled !== false,
|
|
7340
|
+
hasNetworkMonitor: !!networkMonitor,
|
|
7341
|
+
});
|
|
7342
|
+
this.setupNetworkMonitoring();
|
|
7343
|
+
}
|
|
7344
|
+
setupNetworkMonitoring() {
|
|
7345
|
+
if (this.networkMonitor) {
|
|
7346
|
+
this.networkSubscription = this.networkMonitor.online$.subscribe((online) => {
|
|
7347
|
+
if (this.currentOnlineState !== online) {
|
|
7348
|
+
log$3.info('Network state changed:', { online });
|
|
7349
|
+
}
|
|
7350
|
+
this.currentOnlineState = online;
|
|
7351
|
+
});
|
|
7352
|
+
}
|
|
7353
|
+
}
|
|
7354
|
+
isOnline() {
|
|
7355
|
+
if (this.networkMonitor) {
|
|
7356
|
+
return this.currentOnlineState;
|
|
7357
|
+
}
|
|
7358
|
+
if (typeof navigator !== 'undefined' && 'onLine' in navigator) {
|
|
7359
|
+
return navigator.onLine;
|
|
7360
|
+
}
|
|
7361
|
+
return true;
|
|
7362
|
+
}
|
|
7363
|
+
async get(url, config) {
|
|
7364
|
+
const startTime = Date.now();
|
|
7365
|
+
// Check if caching is disabled globally
|
|
7366
|
+
if (this.config.enabled === false) {
|
|
7367
|
+
log$3.debug('GET (cache disabled globally):', { url });
|
|
7368
|
+
return this.http.get(url, config);
|
|
7369
|
+
}
|
|
7370
|
+
// Check if this URL should be cached
|
|
7371
|
+
const shouldCache = this.keyGenerator.shouldCache(url);
|
|
7372
|
+
if (!shouldCache) {
|
|
7373
|
+
log$3.debug('GET (not cacheable - likely a list endpoint):', { url });
|
|
7374
|
+
return this.http.get(url, config);
|
|
7375
|
+
}
|
|
7376
|
+
const cacheKey = this.keyGenerator.generate(url, config?.params);
|
|
7377
|
+
const ttl = this.keyGenerator.getTTL(url);
|
|
7378
|
+
const resource = this.keyGenerator.parseResource(url);
|
|
7379
|
+
log$3.info('GET request starting:', {
|
|
7380
|
+
url,
|
|
7381
|
+
resource,
|
|
7382
|
+
cacheKey,
|
|
7383
|
+
ttlMs: ttl,
|
|
7384
|
+
ttlMin: Math.round(ttl / 60000),
|
|
7385
|
+
params: config?.params,
|
|
7386
|
+
});
|
|
7387
|
+
// Check cache
|
|
7388
|
+
let cached = null;
|
|
7389
|
+
try {
|
|
7390
|
+
cached = await this.cache.get(cacheKey);
|
|
7391
|
+
if (cached) {
|
|
7392
|
+
log$3.debug('Cache entry found:', {
|
|
7393
|
+
cacheKey,
|
|
7394
|
+
timestamp: new Date(cached.timestamp).toISOString(),
|
|
7395
|
+
ageMs: Date.now() - cached.timestamp,
|
|
7396
|
+
});
|
|
7397
|
+
}
|
|
7398
|
+
else {
|
|
7399
|
+
log$3.debug('Cache entry not found:', { cacheKey });
|
|
7400
|
+
}
|
|
7401
|
+
}
|
|
7402
|
+
catch (error) {
|
|
7403
|
+
log$3.warn('Cache lookup failed:', {
|
|
7404
|
+
cacheKey,
|
|
7405
|
+
error: error instanceof Error ? error.message : error,
|
|
7406
|
+
});
|
|
7407
|
+
}
|
|
7408
|
+
if (cached) {
|
|
7409
|
+
const age = Date.now() - cached.timestamp;
|
|
7410
|
+
const isExpired = age >= ttl;
|
|
7411
|
+
log$3.debug('Cache analysis:', {
|
|
7412
|
+
cacheKey,
|
|
7413
|
+
ageMs: age,
|
|
7414
|
+
ageSec: Math.round(age / 1000),
|
|
7415
|
+
ttlMs: ttl,
|
|
7416
|
+
isExpired,
|
|
7417
|
+
isOnline: this.isOnline(),
|
|
7418
|
+
});
|
|
7419
|
+
// If within TTL, return cached data
|
|
7420
|
+
if (!isExpired) {
|
|
7421
|
+
const duration = Date.now() - startTime;
|
|
7422
|
+
log$3.info('CACHE HIT:', {
|
|
7423
|
+
url,
|
|
7424
|
+
cacheKey,
|
|
7425
|
+
ageMs: age,
|
|
7426
|
+
durationMs: duration,
|
|
7427
|
+
});
|
|
7428
|
+
return {
|
|
7429
|
+
data: cached.data,
|
|
7430
|
+
status: 200,
|
|
7431
|
+
headers: { 'x-cache': 'HIT' },
|
|
7432
|
+
};
|
|
7433
|
+
}
|
|
7434
|
+
// If offline and cache is stale, return stale data
|
|
7435
|
+
if (!this.isOnline()) {
|
|
7436
|
+
const duration = Date.now() - startTime;
|
|
7437
|
+
log$3.info('CACHE STALE (offline):', {
|
|
7438
|
+
url,
|
|
7439
|
+
cacheKey,
|
|
7440
|
+
ageMs: age,
|
|
7441
|
+
durationMs: duration,
|
|
7442
|
+
});
|
|
7443
|
+
return {
|
|
7444
|
+
data: cached.data,
|
|
7445
|
+
status: 200,
|
|
7446
|
+
headers: { 'x-cache': 'STALE' },
|
|
7447
|
+
};
|
|
7448
|
+
}
|
|
7449
|
+
log$3.debug('Cache expired, fetching fresh data:', { cacheKey, ageMs: age, ttlMs: ttl });
|
|
7450
|
+
}
|
|
7451
|
+
// Fetch fresh data
|
|
7452
|
+
try {
|
|
7453
|
+
log$3.debug('Fetching from network:', { url });
|
|
7454
|
+
const response = await this.http.get(url, config);
|
|
7455
|
+
// Cache the response
|
|
7456
|
+
try {
|
|
7457
|
+
await this.cache.set(cacheKey, response.data);
|
|
7458
|
+
log$3.debug('Response cached successfully:', { cacheKey });
|
|
7459
|
+
}
|
|
7460
|
+
catch (error) {
|
|
7461
|
+
log$3.error('Failed to cache response:', {
|
|
7462
|
+
cacheKey,
|
|
7463
|
+
error: error instanceof Error ? error.message : error,
|
|
7464
|
+
});
|
|
7465
|
+
}
|
|
7466
|
+
const duration = Date.now() - startTime;
|
|
7467
|
+
log$3.info('CACHE MISS (fetched fresh):', {
|
|
7468
|
+
url,
|
|
7469
|
+
cacheKey,
|
|
7470
|
+
status: response.status,
|
|
7471
|
+
durationMs: duration,
|
|
7472
|
+
});
|
|
7473
|
+
return {
|
|
7474
|
+
...response,
|
|
7475
|
+
headers: { ...response.headers, 'x-cache': 'MISS' },
|
|
7476
|
+
};
|
|
7477
|
+
}
|
|
7478
|
+
catch (error) {
|
|
7479
|
+
// On error, return stale cache if available
|
|
7480
|
+
if (cached) {
|
|
7481
|
+
const duration = Date.now() - startTime;
|
|
7482
|
+
log$3.warn('CACHE STALE (network error):', {
|
|
7483
|
+
url,
|
|
7484
|
+
cacheKey,
|
|
7485
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
7486
|
+
durationMs: duration,
|
|
7487
|
+
});
|
|
7488
|
+
return {
|
|
7489
|
+
data: cached.data,
|
|
7490
|
+
status: 200,
|
|
7491
|
+
headers: { 'x-cache': 'STALE' },
|
|
7492
|
+
};
|
|
7493
|
+
}
|
|
7494
|
+
log$3.error('Network error with no cache fallback:', {
|
|
7495
|
+
url,
|
|
7496
|
+
error: error instanceof Error ? error.message : error,
|
|
7497
|
+
});
|
|
7498
|
+
throw error;
|
|
7499
|
+
}
|
|
7500
|
+
}
|
|
7501
|
+
async post(url, data, config) {
|
|
7502
|
+
log$3.info('POST request:', { url });
|
|
7503
|
+
const response = await this.http.post(url, data, config);
|
|
7504
|
+
await this.invalidateRelated(url, 'POST');
|
|
7505
|
+
return response;
|
|
7506
|
+
}
|
|
7507
|
+
async put(url, data, config) {
|
|
7508
|
+
log$3.info('PUT request:', { url });
|
|
7509
|
+
const response = await this.http.put(url, data, config);
|
|
7510
|
+
await this.invalidateRelated(url, 'PUT');
|
|
7511
|
+
return response;
|
|
7512
|
+
}
|
|
7513
|
+
async patch(url, data, config) {
|
|
7514
|
+
log$3.info('PATCH request:', { url });
|
|
7515
|
+
const response = await this.http.patch(url, data, config);
|
|
7516
|
+
await this.invalidateRelated(url, 'PATCH');
|
|
7517
|
+
return response;
|
|
7518
|
+
}
|
|
7519
|
+
async delete(url, config) {
|
|
7520
|
+
log$3.info('DELETE request:', { url });
|
|
7521
|
+
const response = await this.http.delete(url, config);
|
|
7522
|
+
await this.invalidateRelated(url, 'DELETE');
|
|
7523
|
+
return response;
|
|
7524
|
+
}
|
|
7525
|
+
setAuthToken(token) {
|
|
7526
|
+
log$3.debug('Auth token updated:', { hasToken: !!token });
|
|
7527
|
+
this.authToken = token;
|
|
7528
|
+
this.http.setAuthToken(token);
|
|
7529
|
+
}
|
|
7530
|
+
getAuthToken() {
|
|
7531
|
+
return this.authToken;
|
|
7532
|
+
}
|
|
7533
|
+
async invalidateRelated(url, method) {
|
|
7534
|
+
const patterns = this.keyGenerator.getInvalidationPatterns(url, method);
|
|
7535
|
+
if (patterns.length === 0) {
|
|
7536
|
+
log$3.debug('No cache patterns to invalidate:', { url, method });
|
|
7537
|
+
return;
|
|
7538
|
+
}
|
|
7539
|
+
log$3.info('Invalidating cache patterns:', { url, method, patterns });
|
|
7540
|
+
for (const pattern of patterns) {
|
|
7541
|
+
try {
|
|
7542
|
+
await this.cache.invalidate(pattern);
|
|
7543
|
+
log$3.debug('Cache pattern invalidated:', { pattern });
|
|
7544
|
+
}
|
|
7545
|
+
catch (error) {
|
|
7546
|
+
log$3.error('Failed to invalidate pattern:', {
|
|
7547
|
+
pattern,
|
|
7548
|
+
error: error instanceof Error ? error.message : error,
|
|
7549
|
+
});
|
|
7550
|
+
}
|
|
7551
|
+
}
|
|
7552
|
+
}
|
|
7553
|
+
destroy() {
|
|
7554
|
+
log$3.debug('CachingHttpDecorator destroyed');
|
|
7555
|
+
this.networkSubscription?.unsubscribe();
|
|
7556
|
+
}
|
|
7557
|
+
}
|
|
7558
|
+
|
|
7559
|
+
const log$2 = createPrefixedLogger('HTTP-MTLS');
|
|
7100
7560
|
class AxiosHttpAdapter {
|
|
7101
7561
|
constructor(config) {
|
|
7102
7562
|
this.authToken = null;
|
|
@@ -7114,11 +7574,11 @@ class AxiosHttpAdapter {
|
|
|
7114
7574
|
}
|
|
7115
7575
|
setMTLSAdapter(adapter) {
|
|
7116
7576
|
this.mtlsAdapter = adapter;
|
|
7117
|
-
log$
|
|
7577
|
+
log$2.debug('mTLS adapter configured:', !!adapter);
|
|
7118
7578
|
}
|
|
7119
7579
|
setAuthStrategy(strategy) {
|
|
7120
7580
|
this.authStrategy = strategy;
|
|
7121
|
-
log$
|
|
7581
|
+
log$2.debug('Auth strategy configured:', !!strategy);
|
|
7122
7582
|
}
|
|
7123
7583
|
async shouldUseMTLS(url, method) {
|
|
7124
7584
|
if (!this.mtlsAdapter) {
|
|
@@ -7126,7 +7586,7 @@ class AxiosHttpAdapter {
|
|
|
7126
7586
|
}
|
|
7127
7587
|
if (this.authStrategy) {
|
|
7128
7588
|
const config = await this.authStrategy.determineAuthConfig(url, method);
|
|
7129
|
-
log$
|
|
7589
|
+
log$2.debug(`Auth config for ${method} ${url}:`, config);
|
|
7130
7590
|
return config.mode === 'mtls';
|
|
7131
7591
|
}
|
|
7132
7592
|
// Fallback: use mTLS for mf1/mf2 endpoints if no strategy
|
|
@@ -7143,10 +7603,10 @@ class AxiosHttpAdapter {
|
|
|
7143
7603
|
};
|
|
7144
7604
|
if (this.authToken) {
|
|
7145
7605
|
headers['Authorization'] = `Bearer ${this.authToken}`;
|
|
7146
|
-
log$
|
|
7606
|
+
log$2.debug('JWT token present for mTLS request');
|
|
7147
7607
|
}
|
|
7148
7608
|
else {
|
|
7149
|
-
log$
|
|
7609
|
+
log$2.warn('No JWT token for mTLS request');
|
|
7150
7610
|
}
|
|
7151
7611
|
const mtlsConfig = {
|
|
7152
7612
|
url: fullUrl,
|
|
@@ -7155,15 +7615,15 @@ class AxiosHttpAdapter {
|
|
|
7155
7615
|
data,
|
|
7156
7616
|
timeout: config?.timeout,
|
|
7157
7617
|
};
|
|
7158
|
-
log$
|
|
7618
|
+
log$2.debug(`mTLS ${method} ${fullUrl}`);
|
|
7159
7619
|
if (data) {
|
|
7160
|
-
log$
|
|
7620
|
+
log$2.debug('Request body:', data);
|
|
7161
7621
|
}
|
|
7162
7622
|
try {
|
|
7163
7623
|
const response = await this.mtlsAdapter.request(mtlsConfig);
|
|
7164
|
-
log$
|
|
7624
|
+
log$2.debug(`mTLS Response ${response.status} from ${fullUrl}`);
|
|
7165
7625
|
if (response.data) {
|
|
7166
|
-
log$
|
|
7626
|
+
log$2.debug('Response body:', response.data);
|
|
7167
7627
|
}
|
|
7168
7628
|
return {
|
|
7169
7629
|
data: response.data,
|
|
@@ -7172,11 +7632,11 @@ class AxiosHttpAdapter {
|
|
|
7172
7632
|
};
|
|
7173
7633
|
}
|
|
7174
7634
|
catch (error) {
|
|
7175
|
-
log$
|
|
7635
|
+
log$2.error(`mTLS Response error from ${fullUrl}:`, error);
|
|
7176
7636
|
if (error && typeof error === 'object' && 'response' in error) {
|
|
7177
7637
|
const axiosError = error;
|
|
7178
7638
|
if (axiosError.response?.data) {
|
|
7179
|
-
log$
|
|
7639
|
+
log$2.error('Response body:', axiosError.response.data);
|
|
7180
7640
|
}
|
|
7181
7641
|
}
|
|
7182
7642
|
throw error;
|
|
@@ -7193,30 +7653,30 @@ class AxiosHttpAdapter {
|
|
|
7193
7653
|
config.headers.Authorization = `Bearer ${this.authToken}`;
|
|
7194
7654
|
}
|
|
7195
7655
|
const method = config.method?.toUpperCase() ?? 'UNKNOWN';
|
|
7196
|
-
log$
|
|
7656
|
+
log$2.debug(`→ ${method} ${config.url}`);
|
|
7197
7657
|
if (config.params && Object.keys(config.params).length > 0) {
|
|
7198
|
-
log$
|
|
7658
|
+
log$2.debug('Request params:', config.params);
|
|
7199
7659
|
}
|
|
7200
7660
|
if (config.data) {
|
|
7201
|
-
log$
|
|
7661
|
+
log$2.debug('Request body:', config.data);
|
|
7202
7662
|
}
|
|
7203
7663
|
return config;
|
|
7204
7664
|
}, (error) => {
|
|
7205
|
-
log$
|
|
7665
|
+
log$2.error('Request error:', error);
|
|
7206
7666
|
return Promise.reject(error);
|
|
7207
7667
|
});
|
|
7208
7668
|
this.client.interceptors.response.use((response) => {
|
|
7209
7669
|
const method = response.config.method?.toUpperCase() ?? 'UNKNOWN';
|
|
7210
|
-
log$
|
|
7670
|
+
log$2.debug(`← ${method} ${response.status} ${response.config.url}`);
|
|
7211
7671
|
if (response.data) {
|
|
7212
|
-
log$
|
|
7672
|
+
log$2.debug('Response body:', response.data);
|
|
7213
7673
|
}
|
|
7214
7674
|
return response;
|
|
7215
7675
|
}, (error) => {
|
|
7216
7676
|
const method = error.config?.method?.toUpperCase() ?? 'UNKNOWN';
|
|
7217
|
-
log$
|
|
7677
|
+
log$2.error(`← ${method} ${error.response?.status ?? 'ERR'} ${error.config?.url ?? 'unknown'}`);
|
|
7218
7678
|
if (error.response?.data) {
|
|
7219
|
-
log$
|
|
7679
|
+
log$2.error('Response body:', error.response.data);
|
|
7220
7680
|
}
|
|
7221
7681
|
return Promise.reject(error);
|
|
7222
7682
|
});
|
|
@@ -7339,6 +7799,7 @@ class SDKFactory {
|
|
|
7339
7799
|
baseUrl: config.baseUrl,
|
|
7340
7800
|
timeout: config.timeout,
|
|
7341
7801
|
});
|
|
7802
|
+
container.register(DI_TOKENS.BASE_HTTP_PORT, httpAdapter);
|
|
7342
7803
|
container.register(DI_TOKENS.HTTP_PORT, httpAdapter);
|
|
7343
7804
|
container.registerFactory(DI_TOKENS.RECEIPT_REPOSITORY, () => {
|
|
7344
7805
|
const http = container.get(DI_TOKENS.HTTP_PORT);
|
|
@@ -7386,6 +7847,23 @@ class SDKFactory {
|
|
|
7386
7847
|
});
|
|
7387
7848
|
return container;
|
|
7388
7849
|
}
|
|
7850
|
+
static registerCacheServices(container, cache, network) {
|
|
7851
|
+
container.register(DI_TOKENS.CACHE_PORT, cache);
|
|
7852
|
+
if (network) {
|
|
7853
|
+
container.register(DI_TOKENS.NETWORK_PORT, network);
|
|
7854
|
+
}
|
|
7855
|
+
const keyGenerator = new CacheKeyGenerator();
|
|
7856
|
+
container.register(DI_TOKENS.CACHE_KEY_GENERATOR, keyGenerator);
|
|
7857
|
+
const baseHttp = container.get(DI_TOKENS.BASE_HTTP_PORT);
|
|
7858
|
+
const cachingHttp = new CachingHttpDecorator(baseHttp, cache, keyGenerator, network);
|
|
7859
|
+
container.register(DI_TOKENS.HTTP_PORT, cachingHttp);
|
|
7860
|
+
}
|
|
7861
|
+
static getCacheKeyGenerator(container) {
|
|
7862
|
+
if (container.has(DI_TOKENS.CACHE_KEY_GENERATOR)) {
|
|
7863
|
+
return container.get(DI_TOKENS.CACHE_KEY_GENERATOR);
|
|
7864
|
+
}
|
|
7865
|
+
return undefined;
|
|
7866
|
+
}
|
|
7389
7867
|
static registerAuthServices(container, secureStorage, config) {
|
|
7390
7868
|
const tokenStorage = new TokenStorageAdapter(secureStorage);
|
|
7391
7869
|
container.register(DI_TOKENS.TOKEN_STORAGE_PORT, tokenStorage);
|
|
@@ -7423,6 +7901,7 @@ class SDKFactory {
|
|
|
7423
7901
|
}
|
|
7424
7902
|
}
|
|
7425
7903
|
|
|
7904
|
+
const log$1 = createPrefixedLogger('SDK');
|
|
7426
7905
|
class ACubeSDK {
|
|
7427
7906
|
constructor(config, customAdapters, events = {}) {
|
|
7428
7907
|
this.events = events;
|
|
@@ -7436,14 +7915,27 @@ class ACubeSDK {
|
|
|
7436
7915
|
}
|
|
7437
7916
|
async initialize() {
|
|
7438
7917
|
if (this.isInitialized) {
|
|
7918
|
+
log$1.debug('SDK already initialized, skipping');
|
|
7439
7919
|
return;
|
|
7440
7920
|
}
|
|
7921
|
+
log$1.info('Initializing SDK', {
|
|
7922
|
+
apiUrl: this.config.getApiUrl(),
|
|
7923
|
+
authUrl: this.config.getAuthUrl(),
|
|
7924
|
+
debugEnabled: this.config.isDebugEnabled(),
|
|
7925
|
+
});
|
|
7441
7926
|
try {
|
|
7442
7927
|
if (!this.adapters) {
|
|
7928
|
+
log$1.debug('Loading platform adapters');
|
|
7443
7929
|
const mtlsConfig = createACubeMTLSConfig(this.config.getApiUrl(), this.config.getTimeout(), true);
|
|
7444
7930
|
this.adapters = loadPlatformAdapters({
|
|
7445
7931
|
mtlsConfig,
|
|
7446
7932
|
});
|
|
7933
|
+
log$1.info('Platform adapters loaded', {
|
|
7934
|
+
hasCache: !!this.adapters.cache,
|
|
7935
|
+
hasNetworkMonitor: !!this.adapters.networkMonitor,
|
|
7936
|
+
hasMtls: !!this.adapters.mtls,
|
|
7937
|
+
hasSecureStorage: !!this.adapters.secureStorage,
|
|
7938
|
+
});
|
|
7447
7939
|
}
|
|
7448
7940
|
const factoryConfig = {
|
|
7449
7941
|
baseUrl: this.config.getApiUrl(),
|
|
@@ -7451,11 +7943,25 @@ class ACubeSDK {
|
|
|
7451
7943
|
timeout: this.config.getTimeout(),
|
|
7452
7944
|
debugEnabled: this.config.isDebugEnabled(),
|
|
7453
7945
|
};
|
|
7946
|
+
log$1.debug('Creating DI container');
|
|
7454
7947
|
this.container = SDKFactory.createContainer(factoryConfig);
|
|
7948
|
+
log$1.debug('Registering auth services');
|
|
7455
7949
|
SDKFactory.registerAuthServices(this.container, this.adapters.secureStorage, factoryConfig);
|
|
7950
|
+
if (this.adapters.cache) {
|
|
7951
|
+
log$1.info('Registering cache services', {
|
|
7952
|
+
hasNetworkMonitor: !!this.adapters.networkMonitor,
|
|
7953
|
+
});
|
|
7954
|
+
SDKFactory.registerCacheServices(this.container, this.adapters.cache, this.adapters.networkMonitor);
|
|
7955
|
+
}
|
|
7956
|
+
else {
|
|
7957
|
+
log$1.debug('No cache adapter available, caching disabled');
|
|
7958
|
+
}
|
|
7959
|
+
log$1.debug('Initializing certificate service');
|
|
7456
7960
|
this.certificateService = new CertificateService(this.adapters.secureStorage);
|
|
7457
7961
|
const tokenStorage = this.container.get(DI_TOKENS.TOKEN_STORAGE_PORT);
|
|
7458
7962
|
const httpPort = this.container.get(DI_TOKENS.HTTP_PORT);
|
|
7963
|
+
const baseHttpPort = this.container.get(DI_TOKENS.BASE_HTTP_PORT);
|
|
7964
|
+
log$1.debug('Initializing authentication service');
|
|
7459
7965
|
this.authService = new AuthenticationService(httpPort, tokenStorage, {
|
|
7460
7966
|
authUrl: this.config.getAuthUrl(),
|
|
7461
7967
|
timeout: this.config.getTimeout(),
|
|
@@ -7465,6 +7971,7 @@ class ACubeSDK {
|
|
|
7465
7971
|
this.events.onAuthError?.(new ACubeSDKError('AUTH_ERROR', error.message, error));
|
|
7466
7972
|
},
|
|
7467
7973
|
});
|
|
7974
|
+
log$1.debug('Initializing offline manager');
|
|
7468
7975
|
const queueEvents = {
|
|
7469
7976
|
onOperationAdded: (operation) => {
|
|
7470
7977
|
this.events.onOfflineOperationAdded?.(operation.id);
|
|
@@ -7483,9 +7990,7 @@ class ACubeSDK {
|
|
|
7483
7990
|
this.currentOnlineState = online;
|
|
7484
7991
|
this.events.onNetworkStatusChanged?.(online);
|
|
7485
7992
|
if (online && this.offlineManager) {
|
|
7486
|
-
this.offlineManager.sync().catch(() => {
|
|
7487
|
-
// Sync errors are handled internally by OfflineManager
|
|
7488
|
-
});
|
|
7993
|
+
this.offlineManager.sync().catch(() => { });
|
|
7489
7994
|
}
|
|
7490
7995
|
});
|
|
7491
7996
|
if (await this.authService.isAuthenticated()) {
|
|
@@ -7494,13 +7999,13 @@ class ACubeSDK {
|
|
|
7494
7999
|
httpPort.setAuthToken(token);
|
|
7495
8000
|
}
|
|
7496
8001
|
}
|
|
7497
|
-
|
|
7498
|
-
|
|
7499
|
-
const httpWithMtls =
|
|
8002
|
+
if (this.adapters?.mtls && 'setMTLSAdapter' in baseHttpPort) {
|
|
8003
|
+
log$1.debug('Connecting mTLS adapter to HTTP port');
|
|
8004
|
+
const httpWithMtls = baseHttpPort;
|
|
7500
8005
|
httpWithMtls.setMTLSAdapter(this.adapters.mtls);
|
|
7501
8006
|
}
|
|
7502
|
-
|
|
7503
|
-
|
|
8007
|
+
if ('setAuthStrategy' in baseHttpPort) {
|
|
8008
|
+
log$1.debug('Configuring auth strategy');
|
|
7504
8009
|
const jwtHandler = new JwtAuthHandler(tokenStorage);
|
|
7505
8010
|
const certificatePort = this.certificateService
|
|
7506
8011
|
? {
|
|
@@ -7531,7 +8036,7 @@ class ACubeSDK {
|
|
|
7531
8036
|
},
|
|
7532
8037
|
};
|
|
7533
8038
|
const authStrategy = new AuthStrategy(jwtHandler, mtlsHandler, userProvider, this.adapters?.mtls || null);
|
|
7534
|
-
const httpWithStrategy =
|
|
8039
|
+
const httpWithStrategy = baseHttpPort;
|
|
7535
8040
|
httpWithStrategy.setAuthStrategy(authStrategy);
|
|
7536
8041
|
}
|
|
7537
8042
|
if (this.adapters?.mtls && this.certificateService) {
|
|
@@ -7548,13 +8053,22 @@ class ACubeSDK {
|
|
|
7548
8053
|
}
|
|
7549
8054
|
}
|
|
7550
8055
|
}
|
|
7551
|
-
catch {
|
|
7552
|
-
|
|
8056
|
+
catch (certError) {
|
|
8057
|
+
log$1.warn('Certificate auto-configuration failed, will retry on demand', {
|
|
8058
|
+
error: certError instanceof Error ? certError.message : certError,
|
|
8059
|
+
});
|
|
7553
8060
|
}
|
|
7554
8061
|
}
|
|
7555
8062
|
this.isInitialized = true;
|
|
8063
|
+
log$1.info('SDK initialized successfully', {
|
|
8064
|
+
hasCache: !!this.adapters.cache,
|
|
8065
|
+
hasMtls: !!this.adapters.mtls,
|
|
8066
|
+
});
|
|
7556
8067
|
}
|
|
7557
8068
|
catch (error) {
|
|
8069
|
+
log$1.error('SDK initialization failed', {
|
|
8070
|
+
error: error instanceof Error ? error.message : error,
|
|
8071
|
+
});
|
|
7558
8072
|
throw new ACubeSDKError('SDK_INITIALIZATION_ERROR', `Failed to initialize SDK: ${error instanceof Error ? error.message : 'Unknown error'}`, error);
|
|
7559
8073
|
}
|
|
7560
8074
|
}
|
|
@@ -7608,15 +8122,19 @@ class ACubeSDK {
|
|
|
7608
8122
|
}
|
|
7609
8123
|
async login(credentials) {
|
|
7610
8124
|
this.ensureInitialized();
|
|
8125
|
+
log$1.info('Login attempt', { email: credentials.email });
|
|
7611
8126
|
const user = await this.authService.login(credentials);
|
|
8127
|
+
log$1.info('Login successful', { roles: user.roles });
|
|
7612
8128
|
const token = await this.authService.getAccessToken();
|
|
7613
8129
|
if (token) {
|
|
7614
8130
|
this.httpPort.setAuthToken(token);
|
|
8131
|
+
log$1.debug('Auth token set on HTTP port');
|
|
7615
8132
|
}
|
|
7616
8133
|
return user;
|
|
7617
8134
|
}
|
|
7618
8135
|
async logout() {
|
|
7619
8136
|
this.ensureInitialized();
|
|
8137
|
+
log$1.info('Logout');
|
|
7620
8138
|
await this.authService.logout();
|
|
7621
8139
|
this.httpPort.setAuthToken(null);
|
|
7622
8140
|
}
|
|
@@ -7697,12 +8215,7 @@ class ACubeSDK {
|
|
|
7697
8215
|
async clearCertificate() {
|
|
7698
8216
|
this.ensureInitialized();
|
|
7699
8217
|
if (this.adapters?.mtls) {
|
|
7700
|
-
|
|
7701
|
-
await this.adapters.mtls.removeCertificate();
|
|
7702
|
-
}
|
|
7703
|
-
catch {
|
|
7704
|
-
// No certificate to remove
|
|
7705
|
-
}
|
|
8218
|
+
await this.adapters.mtls.removeCertificate().catch(() => { });
|
|
7706
8219
|
}
|
|
7707
8220
|
if (this.certificateService) {
|
|
7708
8221
|
await this.certificateService.clearCertificate();
|
|
@@ -8037,17 +8550,16 @@ class NotificationService {
|
|
|
8037
8550
|
}
|
|
8038
8551
|
|
|
8039
8552
|
const DEFAULT_CONFIG = {
|
|
8040
|
-
|
|
8041
|
-
cacheTtlMs: 300000,
|
|
8553
|
+
pollIntervalMs: 60000, // 1 minute default for telemetry
|
|
8042
8554
|
};
|
|
8043
8555
|
class TelemetryService {
|
|
8044
8556
|
get state$() {
|
|
8045
8557
|
return this.stateSubject.asObservable();
|
|
8046
8558
|
}
|
|
8047
|
-
constructor(repository,
|
|
8559
|
+
constructor(repository, networkPort, config, events) {
|
|
8048
8560
|
this.repository = repository;
|
|
8049
|
-
this.storagePort = storagePort;
|
|
8050
8561
|
this.networkPort = networkPort;
|
|
8562
|
+
this.events = events;
|
|
8051
8563
|
this.stateSubject = new BehaviorSubject({
|
|
8052
8564
|
data: null,
|
|
8053
8565
|
isCached: false,
|
|
@@ -8055,29 +8567,69 @@ class TelemetryService {
|
|
|
8055
8567
|
lastFetchedAt: null,
|
|
8056
8568
|
});
|
|
8057
8569
|
this.destroy$ = new Subject();
|
|
8058
|
-
this.isOnline = true;
|
|
8059
8570
|
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
8060
8571
|
this.setupNetworkMonitoring();
|
|
8061
8572
|
}
|
|
8062
8573
|
setupNetworkMonitoring() {
|
|
8063
|
-
this.networkPort.online
|
|
8064
|
-
|
|
8574
|
+
this.networkSubscription = this.networkPort.online$
|
|
8575
|
+
.pipe(startWith(true), pairwise(), filter(([wasOnline, isNowOnline]) => !wasOnline && isNowOnline), takeUntil(this.destroy$))
|
|
8576
|
+
.subscribe(() => {
|
|
8577
|
+
this.triggerSync();
|
|
8065
8578
|
});
|
|
8066
8579
|
}
|
|
8580
|
+
startPolling(pemId) {
|
|
8581
|
+
if (this.pollingSubscription) {
|
|
8582
|
+
// If already polling for same pemId, do nothing
|
|
8583
|
+
if (this.currentPemId === pemId) {
|
|
8584
|
+
return;
|
|
8585
|
+
}
|
|
8586
|
+
// If polling for different pemId, stop and restart
|
|
8587
|
+
this.stopPolling();
|
|
8588
|
+
}
|
|
8589
|
+
this.currentPemId = pemId;
|
|
8590
|
+
this.pollingSubscription = interval(this.config.pollIntervalMs)
|
|
8591
|
+
.pipe(startWith(0), switchMap(() => this.fetchTelemetry()), takeUntil(this.destroy$))
|
|
8592
|
+
.subscribe();
|
|
8593
|
+
}
|
|
8594
|
+
stopPolling() {
|
|
8595
|
+
this.pollingSubscription?.unsubscribe();
|
|
8596
|
+
this.pollingSubscription = undefined;
|
|
8597
|
+
this.currentPemId = undefined;
|
|
8598
|
+
}
|
|
8599
|
+
async triggerSync() {
|
|
8600
|
+
if (!this.currentPemId) {
|
|
8601
|
+
return this.stateSubject.value;
|
|
8602
|
+
}
|
|
8603
|
+
return this.fetchTelemetry();
|
|
8604
|
+
}
|
|
8067
8605
|
async getTelemetry(pemId) {
|
|
8068
|
-
|
|
8606
|
+
// Start polling if not already polling for this pemId
|
|
8607
|
+
if (this.currentPemId !== pemId) {
|
|
8608
|
+
this.startPolling(pemId);
|
|
8609
|
+
}
|
|
8610
|
+
return this.stateSubject.value;
|
|
8069
8611
|
}
|
|
8070
8612
|
async refreshTelemetry(pemId) {
|
|
8071
|
-
|
|
8613
|
+
// Update pemId and fetch immediately
|
|
8614
|
+
if (this.currentPemId !== pemId) {
|
|
8615
|
+
this.startPolling(pemId);
|
|
8616
|
+
}
|
|
8617
|
+
else {
|
|
8618
|
+
return this.fetchTelemetry();
|
|
8619
|
+
}
|
|
8620
|
+
return this.stateSubject.value;
|
|
8621
|
+
}
|
|
8622
|
+
async fetchTelemetry() {
|
|
8623
|
+
if (!this.currentPemId) {
|
|
8072
8624
|
return this.stateSubject.value;
|
|
8073
8625
|
}
|
|
8074
8626
|
this.stateSubject.next({
|
|
8075
8627
|
...this.stateSubject.value,
|
|
8076
8628
|
isLoading: true,
|
|
8629
|
+
error: undefined,
|
|
8077
8630
|
});
|
|
8078
8631
|
try {
|
|
8079
|
-
const data = await this.repository.getTelemetry(
|
|
8080
|
-
await this.cacheData(pemId, data);
|
|
8632
|
+
const data = await this.repository.getTelemetry(this.currentPemId);
|
|
8081
8633
|
const newState = {
|
|
8082
8634
|
data,
|
|
8083
8635
|
isCached: false,
|
|
@@ -8085,6 +8637,7 @@ class TelemetryService {
|
|
|
8085
8637
|
lastFetchedAt: Date.now(),
|
|
8086
8638
|
};
|
|
8087
8639
|
this.stateSubject.next(newState);
|
|
8640
|
+
this.events?.onTelemetryUpdate?.(data);
|
|
8088
8641
|
return newState;
|
|
8089
8642
|
}
|
|
8090
8643
|
catch (error) {
|
|
@@ -8095,89 +8648,25 @@ class TelemetryService {
|
|
|
8095
8648
|
error: errorMessage,
|
|
8096
8649
|
};
|
|
8097
8650
|
this.stateSubject.next(newState);
|
|
8651
|
+
if (error instanceof Error) {
|
|
8652
|
+
this.events?.onSyncError?.(error);
|
|
8653
|
+
}
|
|
8098
8654
|
return newState;
|
|
8099
8655
|
}
|
|
8100
8656
|
}
|
|
8101
|
-
|
|
8657
|
+
clearTelemetry() {
|
|
8102
8658
|
this.stateSubject.next({
|
|
8103
|
-
...this.stateSubject.value,
|
|
8104
|
-
isLoading: true,
|
|
8105
|
-
});
|
|
8106
|
-
if (this.isOnline) {
|
|
8107
|
-
try {
|
|
8108
|
-
const data = await this.repository.getTelemetry(pemId);
|
|
8109
|
-
await this.cacheData(pemId, data);
|
|
8110
|
-
const newState = {
|
|
8111
|
-
data,
|
|
8112
|
-
isCached: false,
|
|
8113
|
-
isLoading: false,
|
|
8114
|
-
lastFetchedAt: Date.now(),
|
|
8115
|
-
};
|
|
8116
|
-
this.stateSubject.next(newState);
|
|
8117
|
-
return newState;
|
|
8118
|
-
}
|
|
8119
|
-
catch (error) {
|
|
8120
|
-
return this.loadFromCache(pemId, error);
|
|
8121
|
-
}
|
|
8122
|
-
}
|
|
8123
|
-
return this.loadFromCache(pemId);
|
|
8124
|
-
}
|
|
8125
|
-
async loadFromCache(pemId, originalError) {
|
|
8126
|
-
const cached = await this.getCachedData(pemId);
|
|
8127
|
-
if (cached && this.isCacheValid(cached.timestamp)) {
|
|
8128
|
-
const newState = {
|
|
8129
|
-
data: cached.data,
|
|
8130
|
-
isCached: true,
|
|
8131
|
-
isLoading: false,
|
|
8132
|
-
lastFetchedAt: cached.timestamp,
|
|
8133
|
-
};
|
|
8134
|
-
this.stateSubject.next(newState);
|
|
8135
|
-
return newState;
|
|
8136
|
-
}
|
|
8137
|
-
const errorMessage = originalError instanceof Error ? originalError.message : 'No cached data available';
|
|
8138
|
-
const newState = {
|
|
8139
8659
|
data: null,
|
|
8140
8660
|
isCached: false,
|
|
8141
8661
|
isLoading: false,
|
|
8142
8662
|
lastFetchedAt: null,
|
|
8143
|
-
|
|
8144
|
-
};
|
|
8145
|
-
this.stateSubject.next(newState);
|
|
8146
|
-
return newState;
|
|
8147
|
-
}
|
|
8148
|
-
async cacheData(pemId, data) {
|
|
8149
|
-
const cacheKey = this.getCacheKey(pemId);
|
|
8150
|
-
const cached = {
|
|
8151
|
-
data,
|
|
8152
|
-
timestamp: Date.now(),
|
|
8153
|
-
};
|
|
8154
|
-
await this.storagePort.set(cacheKey, JSON.stringify(cached));
|
|
8155
|
-
}
|
|
8156
|
-
async getCachedData(pemId) {
|
|
8157
|
-
const cacheKey = this.getCacheKey(pemId);
|
|
8158
|
-
const stored = await this.storagePort.get(cacheKey);
|
|
8159
|
-
if (!stored) {
|
|
8160
|
-
return null;
|
|
8161
|
-
}
|
|
8162
|
-
try {
|
|
8163
|
-
return JSON.parse(stored);
|
|
8164
|
-
}
|
|
8165
|
-
catch {
|
|
8166
|
-
return null;
|
|
8167
|
-
}
|
|
8168
|
-
}
|
|
8169
|
-
isCacheValid(timestamp) {
|
|
8170
|
-
return Date.now() - timestamp < this.config.cacheTtlMs;
|
|
8171
|
-
}
|
|
8172
|
-
getCacheKey(pemId) {
|
|
8173
|
-
return `${this.config.cacheKeyPrefix}${pemId}`;
|
|
8174
|
-
}
|
|
8175
|
-
clearCache(pemId) {
|
|
8176
|
-
return this.storagePort.remove(this.getCacheKey(pemId));
|
|
8663
|
+
});
|
|
8177
8664
|
}
|
|
8178
8665
|
destroy() {
|
|
8179
8666
|
this.destroy$.next();
|
|
8180
8667
|
this.destroy$.complete();
|
|
8668
|
+
this.pollingSubscription?.unsubscribe();
|
|
8669
|
+
this.networkSubscription?.unsubscribe();
|
|
8181
8670
|
}
|
|
8182
8671
|
}
|
|
8183
8672
|
|
|
@@ -8187,7 +8676,7 @@ class TelemetryService {
|
|
|
8187
8676
|
* Provides:
|
|
8188
8677
|
* - Single initialization point
|
|
8189
8678
|
* - Observable app state (NORMAL, WARNING, BLOCKED, OFFLINE)
|
|
8190
|
-
* - Observable telemetry state
|
|
8679
|
+
* - Observable telemetry state with polling
|
|
8191
8680
|
* - Simplified services for product use
|
|
8192
8681
|
*
|
|
8193
8682
|
* @example
|
|
@@ -8196,6 +8685,7 @@ class TelemetryService {
|
|
|
8196
8685
|
* SDKManager.configure({
|
|
8197
8686
|
* environment: 'sandbox',
|
|
8198
8687
|
* notificationPollIntervalMs: 30000,
|
|
8688
|
+
* telemetryPollIntervalMs: 60000,
|
|
8199
8689
|
* });
|
|
8200
8690
|
*
|
|
8201
8691
|
* // Initialize
|
|
@@ -8207,6 +8697,14 @@ class TelemetryService {
|
|
|
8207
8697
|
* console.log('App mode:', state.mode);
|
|
8208
8698
|
* });
|
|
8209
8699
|
*
|
|
8700
|
+
* // Start telemetry polling for a specific PEM
|
|
8701
|
+
* manager.startTelemetryPolling('PEM-123');
|
|
8702
|
+
*
|
|
8703
|
+
* // Subscribe to telemetry updates
|
|
8704
|
+
* manager.telemetry$.subscribe(telemetry => {
|
|
8705
|
+
* console.log('Telemetry:', telemetry);
|
|
8706
|
+
* });
|
|
8707
|
+
*
|
|
8210
8708
|
* // Cleanup
|
|
8211
8709
|
* SDKManager.destroy();
|
|
8212
8710
|
* ```
|
|
@@ -8262,43 +8760,34 @@ class SDKManager {
|
|
|
8262
8760
|
* Must be called after configure()
|
|
8263
8761
|
*/
|
|
8264
8762
|
async initialize() {
|
|
8265
|
-
if (this.isInitialized)
|
|
8763
|
+
if (this.isInitialized)
|
|
8266
8764
|
return;
|
|
8267
|
-
}
|
|
8268
|
-
// Create and initialize SDK
|
|
8269
8765
|
this.sdk = new ACubeSDK(this.config, this.adapters, this.events);
|
|
8270
8766
|
await this.sdk.initialize();
|
|
8271
|
-
// Get required adapters
|
|
8272
8767
|
const adaptersRef = this.sdk.getAdapters();
|
|
8273
8768
|
if (!adaptersRef) {
|
|
8274
8769
|
throw new ACubeSDKError('ADAPTERS_NOT_AVAILABLE', 'Platform adapters not available');
|
|
8275
8770
|
}
|
|
8276
8771
|
const networkPort = adaptersRef.networkMonitor;
|
|
8277
|
-
const storagePort = adaptersRef.storage;
|
|
8278
|
-
// Get repositories from SDK
|
|
8279
8772
|
const notificationRepo = this.sdk.notifications;
|
|
8280
8773
|
const telemetryRepo = this.sdk.telemetry;
|
|
8281
|
-
// Create NotificationService
|
|
8282
8774
|
this.notificationService = new NotificationService(notificationRepo, networkPort, {
|
|
8283
8775
|
pollIntervalMs: this.config.notificationPollIntervalMs ?? 30000,
|
|
8284
8776
|
defaultPageSize: this.config.notificationPageSize ?? 30,
|
|
8285
8777
|
});
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
cacheTtlMs: this.config.telemetryCacheTtlMs ?? 300000,
|
|
8778
|
+
this.telemetryService = new TelemetryService(telemetryRepo, networkPort, {
|
|
8779
|
+
pollIntervalMs: this.config.telemetryPollIntervalMs ?? 60000,
|
|
8289
8780
|
});
|
|
8290
|
-
// Create AppStateService
|
|
8291
8781
|
this.appStateService = new AppStateService(this.notificationService.notifications$, networkPort);
|
|
8292
|
-
// Subscribe to state changes for events
|
|
8293
8782
|
if (this.events?.onAppStateChanged) {
|
|
8294
8783
|
this.appStateService.state$.subscribe(this.events.onAppStateChanged);
|
|
8295
8784
|
}
|
|
8296
8785
|
if (this.events?.onTelemetryStateChanged) {
|
|
8297
8786
|
this.telemetryService.state$.subscribe(this.events.onTelemetryStateChanged);
|
|
8298
8787
|
}
|
|
8299
|
-
// Start notification polling
|
|
8300
|
-
this.notificationService.startPolling();
|
|
8301
8788
|
this.isInitialized = true;
|
|
8789
|
+
this.notificationService.startPolling();
|
|
8790
|
+
this.startTelemetryPollingAuto();
|
|
8302
8791
|
}
|
|
8303
8792
|
/**
|
|
8304
8793
|
* Observable stream of app state
|
|
@@ -8331,12 +8820,51 @@ class SDKManager {
|
|
|
8331
8820
|
return this.appStateService.warning$;
|
|
8332
8821
|
}
|
|
8333
8822
|
/**
|
|
8334
|
-
* Observable stream of telemetry state
|
|
8823
|
+
* Observable stream of telemetry state (data, isLoading, isCached, error)
|
|
8335
8824
|
*/
|
|
8336
8825
|
get telemetryState$() {
|
|
8337
8826
|
this.ensureInitialized();
|
|
8338
8827
|
return this.telemetryService.state$;
|
|
8339
8828
|
}
|
|
8829
|
+
/**
|
|
8830
|
+
* Get the pemId from the installed certificate
|
|
8831
|
+
*/
|
|
8832
|
+
async getPemId() {
|
|
8833
|
+
this.ensureInitialized();
|
|
8834
|
+
try {
|
|
8835
|
+
const certInfo = await this.sdk.getCertificatesInfo();
|
|
8836
|
+
return certInfo?.pemId ?? null;
|
|
8837
|
+
}
|
|
8838
|
+
catch {
|
|
8839
|
+
return null;
|
|
8840
|
+
}
|
|
8841
|
+
}
|
|
8842
|
+
/**
|
|
8843
|
+
* Start polling telemetry using the pemId from installed certificate
|
|
8844
|
+
* Returns the pemId if successful, null if no certificate is installed
|
|
8845
|
+
*/
|
|
8846
|
+
async startTelemetryPollingAuto() {
|
|
8847
|
+
this.ensureInitialized();
|
|
8848
|
+
const pemId = await this.getPemId();
|
|
8849
|
+
if (pemId) {
|
|
8850
|
+
this.telemetryService.startPolling(pemId);
|
|
8851
|
+
}
|
|
8852
|
+
return pemId;
|
|
8853
|
+
}
|
|
8854
|
+
/**
|
|
8855
|
+
* Start polling telemetry for a specific PEM
|
|
8856
|
+
*/
|
|
8857
|
+
startTelemetryPolling(pemId) {
|
|
8858
|
+
this.ensureInitialized();
|
|
8859
|
+
this.telemetryService.startPolling(pemId);
|
|
8860
|
+
}
|
|
8861
|
+
/**
|
|
8862
|
+
* Stop telemetry polling
|
|
8863
|
+
*/
|
|
8864
|
+
stopTelemetryPolling() {
|
|
8865
|
+
this.ensureInitialized();
|
|
8866
|
+
this.telemetryService.stopPolling();
|
|
8867
|
+
}
|
|
8340
8868
|
/**
|
|
8341
8869
|
* Get simplified services for product use
|
|
8342
8870
|
*/
|
|
@@ -8345,7 +8873,6 @@ class SDKManager {
|
|
|
8345
8873
|
const sdk = this.sdk;
|
|
8346
8874
|
const telemetryService = this.telemetryService;
|
|
8347
8875
|
return {
|
|
8348
|
-
// Business repositories
|
|
8349
8876
|
receipts: sdk.receipts,
|
|
8350
8877
|
merchants: sdk.merchants,
|
|
8351
8878
|
cashiers: sdk.cashiers,
|
|
@@ -8355,22 +8882,23 @@ class SDKManager {
|
|
|
8355
8882
|
pems: sdk.pems,
|
|
8356
8883
|
dailyReports: sdk.dailyReports,
|
|
8357
8884
|
journals: sdk.journals,
|
|
8358
|
-
// Simplified telemetry operations
|
|
8359
8885
|
telemetry: {
|
|
8886
|
+
startPollingAuto: () => this.startTelemetryPollingAuto(),
|
|
8887
|
+
startPolling: (pemId) => telemetryService.startPolling(pemId),
|
|
8888
|
+
stopPolling: () => telemetryService.stopPolling(),
|
|
8360
8889
|
getTelemetry: (pemId) => telemetryService.getTelemetry(pemId),
|
|
8361
8890
|
refreshTelemetry: (pemId) => telemetryService.refreshTelemetry(pemId),
|
|
8362
|
-
|
|
8891
|
+
triggerSync: () => telemetryService.triggerSync(),
|
|
8892
|
+
clearTelemetry: () => telemetryService.clearTelemetry(),
|
|
8893
|
+
getPemId: () => this.getPemId(),
|
|
8363
8894
|
},
|
|
8364
|
-
// Auth operations
|
|
8365
8895
|
login: (credentials) => sdk.login(credentials),
|
|
8366
8896
|
logout: () => sdk.logout(),
|
|
8367
8897
|
getCurrentUser: () => sdk.getCurrentUser(),
|
|
8368
8898
|
isAuthenticated: () => sdk.isAuthenticated(),
|
|
8369
|
-
// Certificate operations
|
|
8370
8899
|
storeCertificate: (certificate, privateKey, options) => sdk.storeCertificate(certificate, privateKey, options),
|
|
8371
8900
|
hasCertificate: () => sdk.hasCertificate(),
|
|
8372
8901
|
clearCertificate: () => sdk.clearCertificate(),
|
|
8373
|
-
// Network status
|
|
8374
8902
|
isOnline: () => sdk.isOnline(),
|
|
8375
8903
|
};
|
|
8376
8904
|
}
|
|
@@ -8382,12 +8910,11 @@ class SDKManager {
|
|
|
8382
8910
|
await this.notificationService.triggerSync();
|
|
8383
8911
|
}
|
|
8384
8912
|
/**
|
|
8385
|
-
*
|
|
8913
|
+
* Manually trigger a telemetry sync
|
|
8386
8914
|
*/
|
|
8387
|
-
async
|
|
8915
|
+
async syncTelemetry() {
|
|
8388
8916
|
this.ensureInitialized();
|
|
8389
|
-
|
|
8390
|
-
return state.data;
|
|
8917
|
+
return this.telemetryService.triggerSync();
|
|
8391
8918
|
}
|
|
8392
8919
|
/**
|
|
8393
8920
|
* Check if the manager is initialized
|