@a-cube-io/ereceipts-js-sdk 2.0.8 → 2.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.js +326 -246
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +20 -3
- package/dist/index.esm.js +326 -246
- package/dist/index.esm.js.map +1 -1
- package/dist/index.native.js +326 -246
- package/dist/index.native.js.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -1392,6 +1392,15 @@ function from(input, scheduler) {
|
|
|
1392
1392
|
return scheduler ? scheduled(input, scheduler) : innerFrom(input);
|
|
1393
1393
|
}
|
|
1394
1394
|
|
|
1395
|
+
function of() {
|
|
1396
|
+
var args = [];
|
|
1397
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
1398
|
+
args[_i] = arguments[_i];
|
|
1399
|
+
}
|
|
1400
|
+
var scheduler = popScheduler(args);
|
|
1401
|
+
return from(args, scheduler);
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1395
1404
|
function isValidDate(value) {
|
|
1396
1405
|
return value instanceof Date && !isNaN(value);
|
|
1397
1406
|
}
|
|
@@ -2103,7 +2112,7 @@ function formatDecimal(value, decimals = 2) {
|
|
|
2103
2112
|
return num.toFixed(decimals);
|
|
2104
2113
|
}
|
|
2105
2114
|
|
|
2106
|
-
const log$
|
|
2115
|
+
const log$h = createPrefixedLogger('AUTH-SERVICE');
|
|
2107
2116
|
class AuthenticationService {
|
|
2108
2117
|
get user$() {
|
|
2109
2118
|
return this.userSubject.asObservable();
|
|
@@ -2125,7 +2134,7 @@ class AuthenticationService {
|
|
|
2125
2134
|
}
|
|
2126
2135
|
async login(credentials) {
|
|
2127
2136
|
this.authStateSubject.next('authenticating');
|
|
2128
|
-
log$
|
|
2137
|
+
log$h.info('Login attempt', {
|
|
2129
2138
|
authUrl: this.config.authUrl,
|
|
2130
2139
|
email: credentials.email,
|
|
2131
2140
|
});
|
|
@@ -2136,7 +2145,7 @@ class AuthenticationService {
|
|
|
2136
2145
|
});
|
|
2137
2146
|
const jwtPayload = parseJwt(response.data.token);
|
|
2138
2147
|
const expiresAt = jwtPayload.exp * 1000;
|
|
2139
|
-
log$
|
|
2148
|
+
log$h.info('Login successful', {
|
|
2140
2149
|
authUrl: this.config.authUrl,
|
|
2141
2150
|
tokenPrefix: response.data.token.substring(0, 30) + '...',
|
|
2142
2151
|
expiresAt: new Date(expiresAt).toISOString(),
|
|
@@ -2165,21 +2174,21 @@ class AuthenticationService {
|
|
|
2165
2174
|
const token = await this.tokenStorage.getAccessToken();
|
|
2166
2175
|
if (!token) {
|
|
2167
2176
|
// No token - clear any stale user state
|
|
2168
|
-
log$
|
|
2177
|
+
log$h.debug('getCurrentUser: No token in storage');
|
|
2169
2178
|
if (this.userSubject.value) {
|
|
2170
2179
|
this.userSubject.next(null);
|
|
2171
2180
|
this.authStateSubject.next('idle');
|
|
2172
2181
|
}
|
|
2173
2182
|
return null;
|
|
2174
2183
|
}
|
|
2175
|
-
log$
|
|
2184
|
+
log$h.debug('getCurrentUser: Token found', {
|
|
2176
2185
|
tokenPrefix: token.substring(0, 30) + '...',
|
|
2177
2186
|
tokenLength: token.length,
|
|
2178
2187
|
});
|
|
2179
2188
|
const jwtPayload = parseJwt(token);
|
|
2180
2189
|
if (isTokenExpired(jwtPayload)) {
|
|
2181
2190
|
// Token expired - clear everything
|
|
2182
|
-
log$
|
|
2191
|
+
log$h.warn('getCurrentUser: Token expired');
|
|
2183
2192
|
await this.tokenStorage.clearTokens();
|
|
2184
2193
|
this.userSubject.next(null);
|
|
2185
2194
|
this.authStateSubject.next('idle');
|
|
@@ -2189,7 +2198,7 @@ class AuthenticationService {
|
|
|
2189
2198
|
// Token is valid - return cached user if available
|
|
2190
2199
|
const currentUser = this.userSubject.value;
|
|
2191
2200
|
if (currentUser) {
|
|
2192
|
-
log$
|
|
2201
|
+
log$h.debug('getCurrentUser: Returning cached user', {
|
|
2193
2202
|
email: currentUser.email,
|
|
2194
2203
|
roles: currentUser.roles,
|
|
2195
2204
|
});
|
|
@@ -2212,12 +2221,12 @@ class AuthenticationService {
|
|
|
2212
2221
|
async isAuthenticated() {
|
|
2213
2222
|
const token = await this.tokenStorage.getAccessToken();
|
|
2214
2223
|
if (!token) {
|
|
2215
|
-
log$
|
|
2224
|
+
log$h.debug('isAuthenticated: No token in storage');
|
|
2216
2225
|
return false;
|
|
2217
2226
|
}
|
|
2218
2227
|
const jwtPayload = parseJwt(token);
|
|
2219
2228
|
const expired = isTokenExpired(jwtPayload);
|
|
2220
|
-
log$
|
|
2229
|
+
log$h.debug('isAuthenticated: Token check', {
|
|
2221
2230
|
hasToken: true,
|
|
2222
2231
|
expired,
|
|
2223
2232
|
expiresAt: new Date(jwtPayload.exp * 1000).toISOString(),
|
|
@@ -2889,7 +2898,7 @@ class ACubeSDKError extends Error {
|
|
|
2889
2898
|
}
|
|
2890
2899
|
}
|
|
2891
2900
|
|
|
2892
|
-
const log$
|
|
2901
|
+
const log$g = createPrefixedLogger('AUTH-STRATEGY');
|
|
2893
2902
|
class AuthStrategy {
|
|
2894
2903
|
constructor(jwtHandler, mtlsHandler, userProvider, mtlsAdapter) {
|
|
2895
2904
|
this.jwtHandler = jwtHandler;
|
|
@@ -2904,7 +2913,7 @@ class AuthStrategy {
|
|
|
2904
2913
|
const platform = this.detectPlatform();
|
|
2905
2914
|
const userRole = await this.getUserRole();
|
|
2906
2915
|
const isReceiptEndpoint = this.isReceiptEndpoint(url);
|
|
2907
|
-
log$
|
|
2916
|
+
log$g.debug('Determining auth config', {
|
|
2908
2917
|
url,
|
|
2909
2918
|
method,
|
|
2910
2919
|
platform,
|
|
@@ -3035,7 +3044,7 @@ class JwtAuthHandler {
|
|
|
3035
3044
|
}
|
|
3036
3045
|
}
|
|
3037
3046
|
|
|
3038
|
-
const log$
|
|
3047
|
+
const log$f = createPrefixedLogger('MTLS-HANDLER');
|
|
3039
3048
|
class MtlsAuthHandler {
|
|
3040
3049
|
constructor(mtlsAdapter, certificatePort) {
|
|
3041
3050
|
this.mtlsAdapter = mtlsAdapter;
|
|
@@ -3077,7 +3086,7 @@ class MtlsAuthHandler {
|
|
|
3077
3086
|
async makeRequest(url, config, jwtToken) {
|
|
3078
3087
|
const requestKey = this.generateRequestKey(url, config, jwtToken);
|
|
3079
3088
|
if (this.pendingRequests.has(requestKey)) {
|
|
3080
|
-
log$
|
|
3089
|
+
log$f.debug('Deduplicating concurrent request:', url);
|
|
3081
3090
|
return this.pendingRequests.get(requestKey);
|
|
3082
3091
|
}
|
|
3083
3092
|
const requestPromise = this.executeRequest(url, config, jwtToken, false);
|
|
@@ -3101,10 +3110,10 @@ class MtlsAuthHandler {
|
|
|
3101
3110
|
};
|
|
3102
3111
|
if (jwtToken) {
|
|
3103
3112
|
headers['Authorization'] = `Bearer ${jwtToken}`;
|
|
3104
|
-
log$
|
|
3113
|
+
log$f.debug('JWT token present:', jwtToken.substring(0, 20) + '...');
|
|
3105
3114
|
}
|
|
3106
3115
|
else {
|
|
3107
|
-
log$
|
|
3116
|
+
log$f.warn('No JWT token provided for mTLS request');
|
|
3108
3117
|
}
|
|
3109
3118
|
const fullUrl = this.constructMtlsUrl(url);
|
|
3110
3119
|
const mtlsConfig = {
|
|
@@ -3115,25 +3124,25 @@ class MtlsAuthHandler {
|
|
|
3115
3124
|
timeout: config.timeout,
|
|
3116
3125
|
responseType: config.responseType,
|
|
3117
3126
|
};
|
|
3118
|
-
log$
|
|
3119
|
-
log$
|
|
3127
|
+
log$f.debug('header-mtls', headers);
|
|
3128
|
+
log$f.debug(`${config.method} ${fullUrl}`);
|
|
3120
3129
|
if (config.data) {
|
|
3121
|
-
log$
|
|
3130
|
+
log$f.debug('Request body:', config.data);
|
|
3122
3131
|
}
|
|
3123
3132
|
try {
|
|
3124
3133
|
const response = await this.mtlsAdapter.request(mtlsConfig);
|
|
3125
|
-
log$
|
|
3134
|
+
log$f.debug(`Response ${response.status} from ${fullUrl}`);
|
|
3126
3135
|
if (response.data) {
|
|
3127
|
-
log$
|
|
3136
|
+
log$f.debug('Response body:', response.data);
|
|
3128
3137
|
}
|
|
3129
3138
|
return response.data;
|
|
3130
3139
|
}
|
|
3131
3140
|
catch (error) {
|
|
3132
|
-
log$
|
|
3141
|
+
log$f.error(`Response error from ${fullUrl}:`, error);
|
|
3133
3142
|
if (error && typeof error === 'object' && 'response' in error) {
|
|
3134
3143
|
const axiosError = error;
|
|
3135
3144
|
if (axiosError.response?.data) {
|
|
3136
|
-
log$
|
|
3145
|
+
log$f.error('Response body:', axiosError.response.data);
|
|
3137
3146
|
}
|
|
3138
3147
|
}
|
|
3139
3148
|
if (isRetryAttempt) {
|
|
@@ -3143,7 +3152,7 @@ class MtlsAuthHandler {
|
|
|
3143
3152
|
if (!shouldRetry) {
|
|
3144
3153
|
throw error;
|
|
3145
3154
|
}
|
|
3146
|
-
log$
|
|
3155
|
+
log$f.debug('Request failed, reconfiguring certificate and retrying...');
|
|
3147
3156
|
try {
|
|
3148
3157
|
await this.configureCertificate(certificate);
|
|
3149
3158
|
return await this.executeRequest(url, config, jwtToken, true);
|
|
@@ -3870,7 +3879,7 @@ function decompressData(data, compressed) {
|
|
|
3870
3879
|
return new CompressionAdapter().decompress(data, compressed);
|
|
3871
3880
|
}
|
|
3872
3881
|
|
|
3873
|
-
const log$
|
|
3882
|
+
const log$e = createPrefixedLogger('CACHE-RN');
|
|
3874
3883
|
/**
|
|
3875
3884
|
* React Native cache adapter using SQLite (Expo or react-native-sqlite-storage)
|
|
3876
3885
|
* Cache never expires - data persists until explicitly invalidated
|
|
@@ -3954,26 +3963,26 @@ class ReactNativeCacheAdapter {
|
|
|
3954
3963
|
await this.runMigrations();
|
|
3955
3964
|
}
|
|
3956
3965
|
async runMigrations() {
|
|
3957
|
-
log$
|
|
3966
|
+
log$e.debug('Running database migrations...');
|
|
3958
3967
|
try {
|
|
3959
3968
|
// Check if compressed column exists
|
|
3960
3969
|
this.hasCompressedColumn = await this.checkColumnExists('compressed');
|
|
3961
3970
|
if (!this.hasCompressedColumn) {
|
|
3962
|
-
log$
|
|
3971
|
+
log$e.debug('Adding compressed column to cache table');
|
|
3963
3972
|
const addColumnSQL = `ALTER TABLE ${ReactNativeCacheAdapter.TABLE_NAME} ADD COLUMN compressed INTEGER DEFAULT 0`;
|
|
3964
3973
|
await this.executeSql(addColumnSQL);
|
|
3965
3974
|
this.hasCompressedColumn = true;
|
|
3966
|
-
log$
|
|
3975
|
+
log$e.debug('Successfully added compressed column');
|
|
3967
3976
|
}
|
|
3968
3977
|
else {
|
|
3969
|
-
log$
|
|
3978
|
+
log$e.debug('Compressed column already exists');
|
|
3970
3979
|
}
|
|
3971
|
-
log$
|
|
3980
|
+
log$e.debug('Database migrations completed', {
|
|
3972
3981
|
hasCompressedColumn: this.hasCompressedColumn,
|
|
3973
3982
|
});
|
|
3974
3983
|
}
|
|
3975
3984
|
catch (error) {
|
|
3976
|
-
log$
|
|
3985
|
+
log$e.debug('Migration failed, disabling compression features', error);
|
|
3977
3986
|
this.hasCompressedColumn = false;
|
|
3978
3987
|
// Don't throw - allow the app to continue even if migration fails
|
|
3979
3988
|
// The compressed feature will just be disabled
|
|
@@ -3984,20 +3993,20 @@ class ReactNativeCacheAdapter {
|
|
|
3984
3993
|
const pragmaSQL = `PRAGMA table_info(${ReactNativeCacheAdapter.TABLE_NAME})`;
|
|
3985
3994
|
const results = await this.executeSql(pragmaSQL);
|
|
3986
3995
|
const columns = this.normalizeResults(results);
|
|
3987
|
-
log$
|
|
3996
|
+
log$e.debug('Table columns found', { columns: columns.map((c) => c.name) });
|
|
3988
3997
|
return columns.some((column) => column.name === columnName);
|
|
3989
3998
|
}
|
|
3990
3999
|
catch (error) {
|
|
3991
|
-
log$
|
|
4000
|
+
log$e.debug('Error checking column existence', error);
|
|
3992
4001
|
return false;
|
|
3993
4002
|
}
|
|
3994
4003
|
}
|
|
3995
4004
|
async get(key) {
|
|
3996
4005
|
await this.ensureInitialized();
|
|
3997
4006
|
const sql = `SELECT * FROM ${ReactNativeCacheAdapter.TABLE_NAME} WHERE cache_key = ?`;
|
|
3998
|
-
log$
|
|
4007
|
+
log$e.debug('Executing get query', { sql, key });
|
|
3999
4008
|
const results = await this.executeSql(sql, [key]);
|
|
4000
|
-
log$
|
|
4009
|
+
log$e.debug('Get query results', { key, hasResults: !!results });
|
|
4001
4010
|
// Normalize results from different SQLite implementations
|
|
4002
4011
|
const rows = this.normalizeResults(results);
|
|
4003
4012
|
if (!rows || rows.length === 0) {
|
|
@@ -4020,7 +4029,7 @@ class ReactNativeCacheAdapter {
|
|
|
4020
4029
|
data,
|
|
4021
4030
|
timestamp: Date.now(),
|
|
4022
4031
|
};
|
|
4023
|
-
log$
|
|
4032
|
+
log$e.debug('Setting cache item', { key });
|
|
4024
4033
|
return this.setItem(key, item);
|
|
4025
4034
|
}
|
|
4026
4035
|
async setItem(key, item) {
|
|
@@ -4033,7 +4042,7 @@ class ReactNativeCacheAdapter {
|
|
|
4033
4042
|
const compressionResult = compressData(serializedData, this.options.compressionThreshold);
|
|
4034
4043
|
finalData = compressionResult.data;
|
|
4035
4044
|
isCompressed = compressionResult.compressed;
|
|
4036
|
-
log$
|
|
4045
|
+
log$e.debug('Compression result', {
|
|
4037
4046
|
key,
|
|
4038
4047
|
originalSize: compressionResult.originalSize,
|
|
4039
4048
|
compressedSize: compressionResult.compressedSize,
|
|
@@ -4041,7 +4050,7 @@ class ReactNativeCacheAdapter {
|
|
|
4041
4050
|
savings: compressionResult.originalSize - compressionResult.compressedSize,
|
|
4042
4051
|
});
|
|
4043
4052
|
}
|
|
4044
|
-
log$
|
|
4053
|
+
log$e.debug('Setting item with metadata', {
|
|
4045
4054
|
key,
|
|
4046
4055
|
timestamp: item.timestamp,
|
|
4047
4056
|
compressed: isCompressed,
|
|
@@ -4067,14 +4076,14 @@ class ReactNativeCacheAdapter {
|
|
|
4067
4076
|
`;
|
|
4068
4077
|
params = [key, finalData, item.timestamp];
|
|
4069
4078
|
}
|
|
4070
|
-
log$
|
|
4079
|
+
log$e.debug('Executing setItem SQL', { key, paramsCount: params.length });
|
|
4071
4080
|
await this.executeSql(sql, params);
|
|
4072
4081
|
}
|
|
4073
4082
|
async setBatch(items) {
|
|
4074
4083
|
if (items.length === 0)
|
|
4075
4084
|
return;
|
|
4076
4085
|
await this.ensureInitialized();
|
|
4077
|
-
log$
|
|
4086
|
+
log$e.debug('Batch setting items', { count: items.length });
|
|
4078
4087
|
if (this.isExpo) {
|
|
4079
4088
|
await this.db.withTransactionAsync(async () => {
|
|
4080
4089
|
for (const [key, item] of items) {
|
|
@@ -4092,7 +4101,7 @@ class ReactNativeCacheAdapter {
|
|
|
4092
4101
|
}, reject, () => resolve());
|
|
4093
4102
|
});
|
|
4094
4103
|
}
|
|
4095
|
-
log$
|
|
4104
|
+
log$e.debug('Batch operation completed', { count: items.length });
|
|
4096
4105
|
}
|
|
4097
4106
|
async setBatchItem(key, item) {
|
|
4098
4107
|
// Handle compression if enabled and compressed column is available
|
|
@@ -4266,10 +4275,10 @@ class MemoryCacheAdapter {
|
|
|
4266
4275
|
return keySize + itemSize;
|
|
4267
4276
|
}
|
|
4268
4277
|
async get(key) {
|
|
4269
|
-
log$
|
|
4278
|
+
log$e.debug('Getting cache item', { key });
|
|
4270
4279
|
const item = this.cache.get(key);
|
|
4271
4280
|
if (!item) {
|
|
4272
|
-
log$
|
|
4281
|
+
log$e.debug('Cache miss', { key });
|
|
4273
4282
|
return null;
|
|
4274
4283
|
}
|
|
4275
4284
|
// Handle decompression if needed
|
|
@@ -4279,7 +4288,7 @@ class MemoryCacheAdapter {
|
|
|
4279
4288
|
const decompressed = decompressData(item.data, true);
|
|
4280
4289
|
finalData = JSON.parse(decompressed.data);
|
|
4281
4290
|
}
|
|
4282
|
-
log$
|
|
4291
|
+
log$e.debug('Cache hit', { key, compressed: isCompressed });
|
|
4283
4292
|
return {
|
|
4284
4293
|
...item,
|
|
4285
4294
|
data: finalData,
|
|
@@ -4287,7 +4296,7 @@ class MemoryCacheAdapter {
|
|
|
4287
4296
|
};
|
|
4288
4297
|
}
|
|
4289
4298
|
async set(key, data) {
|
|
4290
|
-
log$
|
|
4299
|
+
log$e.debug('Setting cache item', { key });
|
|
4291
4300
|
// Handle compression if enabled
|
|
4292
4301
|
let finalData = data;
|
|
4293
4302
|
let isCompressed = false;
|
|
@@ -4297,7 +4306,7 @@ class MemoryCacheAdapter {
|
|
|
4297
4306
|
if (compressionResult.compressed) {
|
|
4298
4307
|
finalData = compressionResult.data;
|
|
4299
4308
|
isCompressed = true;
|
|
4300
|
-
log$
|
|
4309
|
+
log$e.debug('Compression result', {
|
|
4301
4310
|
key,
|
|
4302
4311
|
originalSize: compressionResult.originalSize,
|
|
4303
4312
|
compressedSize: compressionResult.compressedSize,
|
|
@@ -4329,13 +4338,13 @@ class MemoryCacheAdapter {
|
|
|
4329
4338
|
const oldestItemSize = this.calculateItemSize(oldestKey, oldestItem);
|
|
4330
4339
|
this.totalBytes -= oldestItemSize;
|
|
4331
4340
|
this.cache.delete(oldestKey);
|
|
4332
|
-
log$
|
|
4341
|
+
log$e.debug('Removed oldest item for capacity', { oldestKey, freedBytes: oldestItemSize });
|
|
4333
4342
|
}
|
|
4334
4343
|
}
|
|
4335
4344
|
// Set new item and update total size
|
|
4336
4345
|
this.cache.set(key, item);
|
|
4337
4346
|
this.totalBytes += newItemSize;
|
|
4338
|
-
log$
|
|
4347
|
+
log$e.debug('Updated cache size', {
|
|
4339
4348
|
entries: this.cache.size,
|
|
4340
4349
|
totalBytes: this.totalBytes,
|
|
4341
4350
|
newItemSize,
|
|
@@ -4344,7 +4353,7 @@ class MemoryCacheAdapter {
|
|
|
4344
4353
|
async setBatch(items) {
|
|
4345
4354
|
if (items.length === 0)
|
|
4346
4355
|
return;
|
|
4347
|
-
log$
|
|
4356
|
+
log$e.debug('Batch setting items', { count: items.length });
|
|
4348
4357
|
let totalNewBytes = 0;
|
|
4349
4358
|
let totalOldBytes = 0;
|
|
4350
4359
|
const itemsToRemove = [];
|
|
@@ -4373,7 +4382,7 @@ class MemoryCacheAdapter {
|
|
|
4373
4382
|
itemsToRemove.push(oldKey);
|
|
4374
4383
|
}
|
|
4375
4384
|
if (itemsToRemove.length > 0) {
|
|
4376
|
-
log$
|
|
4385
|
+
log$e.debug('Removed items for batch capacity', {
|
|
4377
4386
|
removedCount: itemsToRemove.length,
|
|
4378
4387
|
removedKeys: itemsToRemove,
|
|
4379
4388
|
});
|
|
@@ -4385,7 +4394,7 @@ class MemoryCacheAdapter {
|
|
|
4385
4394
|
for (const [key, item] of items) {
|
|
4386
4395
|
this.cache.set(key, item);
|
|
4387
4396
|
}
|
|
4388
|
-
log$
|
|
4397
|
+
log$e.debug('Batch operation completed', {
|
|
4389
4398
|
count: items.length,
|
|
4390
4399
|
totalBytes: this.totalBytes,
|
|
4391
4400
|
entries: this.cache.size,
|
|
@@ -4407,7 +4416,7 @@ class MemoryCacheAdapter {
|
|
|
4407
4416
|
}
|
|
4408
4417
|
}
|
|
4409
4418
|
if (removed > 0) {
|
|
4410
|
-
log$
|
|
4419
|
+
log$e.debug('Invalidation completed', {
|
|
4411
4420
|
pattern,
|
|
4412
4421
|
entriesRemoved: removed,
|
|
4413
4422
|
bytesFreed,
|
|
@@ -4419,7 +4428,7 @@ class MemoryCacheAdapter {
|
|
|
4419
4428
|
async clear() {
|
|
4420
4429
|
this.cache.clear();
|
|
4421
4430
|
this.totalBytes = 0;
|
|
4422
|
-
log$
|
|
4431
|
+
log$e.debug('Cache cleared', { entries: 0, bytes: 0 });
|
|
4423
4432
|
}
|
|
4424
4433
|
async getSize() {
|
|
4425
4434
|
return {
|
|
@@ -4750,7 +4759,7 @@ replaceTraps((oldTraps) => ({
|
|
|
4750
4759
|
},
|
|
4751
4760
|
}));
|
|
4752
4761
|
|
|
4753
|
-
const log$
|
|
4762
|
+
const log$d = createPrefixedLogger('CACHE-WEB');
|
|
4754
4763
|
/**
|
|
4755
4764
|
* Web cache adapter using IndexedDB with automatic error recovery
|
|
4756
4765
|
* Cache never expires - data persists until explicitly invalidated
|
|
@@ -4773,18 +4782,18 @@ class WebCacheAdapter {
|
|
|
4773
4782
|
async initialize() {
|
|
4774
4783
|
if (this.db)
|
|
4775
4784
|
return;
|
|
4776
|
-
log$
|
|
4785
|
+
log$d.debug('Initializing IndexedDB cache', {
|
|
4777
4786
|
dbName: WebCacheAdapter.DB_NAME,
|
|
4778
4787
|
version: WebCacheAdapter.DB_VERSION,
|
|
4779
4788
|
});
|
|
4780
4789
|
try {
|
|
4781
4790
|
this.db = await this.openDatabase();
|
|
4782
|
-
log$
|
|
4791
|
+
log$d.debug('IndexedDB cache initialized successfully');
|
|
4783
4792
|
this.retryCount = 0; // Reset retry count on success
|
|
4784
4793
|
}
|
|
4785
4794
|
catch (error) {
|
|
4786
4795
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
4787
|
-
log$
|
|
4796
|
+
log$d.debug('Failed to initialize IndexedDB', { error: errorMessage });
|
|
4788
4797
|
// Check if this is a version conflict error
|
|
4789
4798
|
if (this.isVersionConflictError(errorMessage)) {
|
|
4790
4799
|
await this.handleVersionConflict();
|
|
@@ -4797,32 +4806,32 @@ class WebCacheAdapter {
|
|
|
4797
4806
|
async openDatabase() {
|
|
4798
4807
|
return await openDB(WebCacheAdapter.DB_NAME, WebCacheAdapter.DB_VERSION, {
|
|
4799
4808
|
upgrade: (db, oldVersion, newVersion, transaction) => {
|
|
4800
|
-
log$
|
|
4809
|
+
log$d.debug('Database upgrade needed', { oldVersion, newVersion });
|
|
4801
4810
|
this.handleUpgrade(db, oldVersion, newVersion, transaction);
|
|
4802
4811
|
},
|
|
4803
4812
|
blocked: () => {
|
|
4804
|
-
log$
|
|
4813
|
+
log$d.debug('Database blocked - another tab may be open');
|
|
4805
4814
|
},
|
|
4806
4815
|
blocking: () => {
|
|
4807
|
-
log$
|
|
4816
|
+
log$d.debug('Database blocking - will close connection');
|
|
4808
4817
|
if (this.db) {
|
|
4809
4818
|
this.db.close();
|
|
4810
4819
|
this.db = null;
|
|
4811
4820
|
}
|
|
4812
4821
|
},
|
|
4813
4822
|
terminated: () => {
|
|
4814
|
-
log$
|
|
4823
|
+
log$d.debug('Database connection terminated unexpectedly');
|
|
4815
4824
|
this.db = null;
|
|
4816
4825
|
},
|
|
4817
4826
|
});
|
|
4818
4827
|
}
|
|
4819
4828
|
handleUpgrade(db, oldVersion, newVersion, transaction) {
|
|
4820
|
-
log$
|
|
4829
|
+
log$d.debug('Handling database upgrade', { oldVersion, newVersion });
|
|
4821
4830
|
// Create cache store if it doesn't exist (initial setup)
|
|
4822
4831
|
if (!db.objectStoreNames.contains(WebCacheAdapter.STORE_NAME)) {
|
|
4823
4832
|
const store = db.createObjectStore(WebCacheAdapter.STORE_NAME, { keyPath: 'key' });
|
|
4824
4833
|
store.createIndex('timestamp', 'timestamp', { unique: false });
|
|
4825
|
-
log$
|
|
4834
|
+
log$d.debug('Created cache store and timestamp index');
|
|
4826
4835
|
}
|
|
4827
4836
|
// Handle migration from version 1 to 2
|
|
4828
4837
|
if (oldVersion < 2) {
|
|
@@ -4833,16 +4842,16 @@ class WebCacheAdapter {
|
|
|
4833
4842
|
try {
|
|
4834
4843
|
if (store.indexNames.contains(indexName)) {
|
|
4835
4844
|
store.deleteIndex(indexName);
|
|
4836
|
-
log$
|
|
4845
|
+
log$d.debug(`Removed unused index: ${indexName}`);
|
|
4837
4846
|
}
|
|
4838
4847
|
}
|
|
4839
4848
|
catch (error) {
|
|
4840
4849
|
// Ignore errors if indexes don't exist
|
|
4841
|
-
log$
|
|
4850
|
+
log$d.debug(`Warning: Could not remove index ${indexName}`, error);
|
|
4842
4851
|
}
|
|
4843
4852
|
});
|
|
4844
4853
|
}
|
|
4845
|
-
log$
|
|
4854
|
+
log$d.debug('Database upgrade completed');
|
|
4846
4855
|
}
|
|
4847
4856
|
isVersionConflictError(errorMessage) {
|
|
4848
4857
|
return (errorMessage.includes('less than the existing version') ||
|
|
@@ -4850,7 +4859,7 @@ class WebCacheAdapter {
|
|
|
4850
4859
|
errorMessage.includes('VersionError'));
|
|
4851
4860
|
}
|
|
4852
4861
|
async handleVersionConflict() {
|
|
4853
|
-
log$
|
|
4862
|
+
log$d.debug('Handling version conflict, attempting recovery...');
|
|
4854
4863
|
if (this.retryCount >= this.maxRetries) {
|
|
4855
4864
|
throw new Error('Failed to resolve IndexedDB version conflict after multiple attempts');
|
|
4856
4865
|
}
|
|
@@ -4862,19 +4871,19 @@ class WebCacheAdapter {
|
|
|
4862
4871
|
this.db = null;
|
|
4863
4872
|
}
|
|
4864
4873
|
// Delete the problematic database
|
|
4865
|
-
log$
|
|
4874
|
+
log$d.debug('Deleting problematic database to resolve version conflict');
|
|
4866
4875
|
await deleteDB(WebCacheAdapter.DB_NAME);
|
|
4867
4876
|
// Wait a bit for the deletion to complete
|
|
4868
4877
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
4869
4878
|
// Try to open the database again
|
|
4870
|
-
log$
|
|
4879
|
+
log$d.debug(`Retrying database initialization (attempt ${this.retryCount}/${this.maxRetries})`);
|
|
4871
4880
|
this.db = await this.openDatabase();
|
|
4872
|
-
log$
|
|
4881
|
+
log$d.debug('Successfully recovered from version conflict');
|
|
4873
4882
|
this.retryCount = 0; // Reset retry count on success
|
|
4874
4883
|
}
|
|
4875
4884
|
catch (retryError) {
|
|
4876
4885
|
const retryErrorMessage = retryError instanceof Error ? retryError.message : 'Unknown error';
|
|
4877
|
-
log$
|
|
4886
|
+
log$d.debug('Recovery attempt failed', { attempt: this.retryCount, error: retryErrorMessage });
|
|
4878
4887
|
if (this.retryCount < this.maxRetries) {
|
|
4879
4888
|
// Try again
|
|
4880
4889
|
await this.handleVersionConflict();
|
|
@@ -4886,7 +4895,7 @@ class WebCacheAdapter {
|
|
|
4886
4895
|
}
|
|
4887
4896
|
async get(key) {
|
|
4888
4897
|
await this.ensureInitialized();
|
|
4889
|
-
log$
|
|
4898
|
+
log$d.debug('Getting cache item', { key });
|
|
4890
4899
|
try {
|
|
4891
4900
|
const transaction = this.db.transaction([WebCacheAdapter.STORE_NAME], 'readonly');
|
|
4892
4901
|
const store = transaction.objectStore(WebCacheAdapter.STORE_NAME);
|
|
@@ -4912,7 +4921,7 @@ class WebCacheAdapter {
|
|
|
4912
4921
|
};
|
|
4913
4922
|
}
|
|
4914
4923
|
catch (error) {
|
|
4915
|
-
log$
|
|
4924
|
+
log$d.debug('Error getting cache item', { key, error });
|
|
4916
4925
|
return null; // Return null on error instead of throwing
|
|
4917
4926
|
}
|
|
4918
4927
|
}
|
|
@@ -4934,7 +4943,7 @@ class WebCacheAdapter {
|
|
|
4934
4943
|
if (compressionResult.compressed) {
|
|
4935
4944
|
finalData = compressionResult.data;
|
|
4936
4945
|
isCompressed = true;
|
|
4937
|
-
log$
|
|
4946
|
+
log$d.debug('Compression result', {
|
|
4938
4947
|
key,
|
|
4939
4948
|
originalSize: compressionResult.originalSize,
|
|
4940
4949
|
compressedSize: compressionResult.compressedSize,
|
|
@@ -4943,7 +4952,7 @@ class WebCacheAdapter {
|
|
|
4943
4952
|
});
|
|
4944
4953
|
}
|
|
4945
4954
|
}
|
|
4946
|
-
log$
|
|
4955
|
+
log$d.debug('Setting cache item', { key, timestamp: item.timestamp, compressed: isCompressed });
|
|
4947
4956
|
const storedItem = {
|
|
4948
4957
|
key,
|
|
4949
4958
|
data: finalData,
|
|
@@ -4956,7 +4965,7 @@ class WebCacheAdapter {
|
|
|
4956
4965
|
await store.put(storedItem);
|
|
4957
4966
|
}
|
|
4958
4967
|
catch (error) {
|
|
4959
|
-
log$
|
|
4968
|
+
log$d.debug('Error setting cache item', { key, error });
|
|
4960
4969
|
// Silently fail for cache writes
|
|
4961
4970
|
}
|
|
4962
4971
|
}
|
|
@@ -4964,7 +4973,7 @@ class WebCacheAdapter {
|
|
|
4964
4973
|
if (items.length === 0)
|
|
4965
4974
|
return;
|
|
4966
4975
|
await this.ensureInitialized();
|
|
4967
|
-
log$
|
|
4976
|
+
log$d.debug('Batch setting items', { count: items.length });
|
|
4968
4977
|
try {
|
|
4969
4978
|
const transaction = this.db.transaction([WebCacheAdapter.STORE_NAME], 'readwrite');
|
|
4970
4979
|
const store = transaction.objectStore(WebCacheAdapter.STORE_NAME);
|
|
@@ -4974,10 +4983,10 @@ class WebCacheAdapter {
|
|
|
4974
4983
|
return store.put(storedItem);
|
|
4975
4984
|
});
|
|
4976
4985
|
await Promise.all(promises);
|
|
4977
|
-
log$
|
|
4986
|
+
log$d.debug('Batch operation completed', { count: items.length });
|
|
4978
4987
|
}
|
|
4979
4988
|
catch (error) {
|
|
4980
|
-
log$
|
|
4989
|
+
log$d.debug('Error in batch operation', { count: items.length, error });
|
|
4981
4990
|
// Silently fail for batch writes
|
|
4982
4991
|
}
|
|
4983
4992
|
}
|
|
@@ -5012,10 +5021,10 @@ class WebCacheAdapter {
|
|
|
5012
5021
|
const transaction = this.db.transaction([WebCacheAdapter.STORE_NAME], 'readwrite');
|
|
5013
5022
|
const store = transaction.objectStore(WebCacheAdapter.STORE_NAME);
|
|
5014
5023
|
await store.clear();
|
|
5015
|
-
log$
|
|
5024
|
+
log$d.debug('Cache cleared successfully');
|
|
5016
5025
|
}
|
|
5017
5026
|
catch (error) {
|
|
5018
|
-
log$
|
|
5027
|
+
log$d.debug('Error clearing cache', error);
|
|
5019
5028
|
// Silently fail for cache clear
|
|
5020
5029
|
}
|
|
5021
5030
|
}
|
|
@@ -5041,7 +5050,7 @@ class WebCacheAdapter {
|
|
|
5041
5050
|
};
|
|
5042
5051
|
}
|
|
5043
5052
|
catch (error) {
|
|
5044
|
-
log$
|
|
5053
|
+
log$d.debug('Error getting cache size', error);
|
|
5045
5054
|
return {
|
|
5046
5055
|
entries: 0,
|
|
5047
5056
|
bytes: 0,
|
|
@@ -5066,7 +5075,7 @@ class WebCacheAdapter {
|
|
|
5066
5075
|
return allKeys.filter((key) => regex.test(key));
|
|
5067
5076
|
}
|
|
5068
5077
|
catch (error) {
|
|
5069
|
-
log$
|
|
5078
|
+
log$d.debug('Error getting cache keys', error);
|
|
5070
5079
|
return [];
|
|
5071
5080
|
}
|
|
5072
5081
|
}
|
|
@@ -5079,7 +5088,7 @@ class WebCacheAdapter {
|
|
|
5079
5088
|
return true;
|
|
5080
5089
|
}
|
|
5081
5090
|
catch (error) {
|
|
5082
|
-
log$
|
|
5091
|
+
log$d.debug('Error deleting cache item', { key, error });
|
|
5083
5092
|
return false;
|
|
5084
5093
|
}
|
|
5085
5094
|
}
|
|
@@ -5091,7 +5100,7 @@ class WebCacheAdapter {
|
|
|
5091
5100
|
await this.initPromise;
|
|
5092
5101
|
}
|
|
5093
5102
|
catch (error) {
|
|
5094
|
-
log$
|
|
5103
|
+
log$d.debug('Failed to ensure initialization', error);
|
|
5095
5104
|
// Reset and try once more
|
|
5096
5105
|
this.initPromise = null;
|
|
5097
5106
|
this.db = null;
|
|
@@ -5110,7 +5119,7 @@ WebCacheAdapter.DB_NAME = 'acube_cache';
|
|
|
5110
5119
|
WebCacheAdapter.DB_VERSION = 2;
|
|
5111
5120
|
WebCacheAdapter.STORE_NAME = 'cache_entries';
|
|
5112
5121
|
|
|
5113
|
-
const log$
|
|
5122
|
+
const log$c = createPrefixedLogger('CACHE-LOADER');
|
|
5114
5123
|
function loadCacheAdapter(platform) {
|
|
5115
5124
|
try {
|
|
5116
5125
|
switch (platform) {
|
|
@@ -5142,7 +5151,7 @@ function loadCacheAdapter(platform) {
|
|
|
5142
5151
|
}
|
|
5143
5152
|
}
|
|
5144
5153
|
catch (error) {
|
|
5145
|
-
log$
|
|
5154
|
+
log$c.warn(`Cache adapter not available for platform ${platform}:`, error);
|
|
5146
5155
|
return undefined;
|
|
5147
5156
|
}
|
|
5148
5157
|
}
|
|
@@ -5199,7 +5208,7 @@ class BaseSecureStorageAdapter {
|
|
|
5199
5208
|
}
|
|
5200
5209
|
}
|
|
5201
5210
|
|
|
5202
|
-
const log$
|
|
5211
|
+
const log$b = createPrefixedLogger('NETWORK-BASE');
|
|
5203
5212
|
class NetworkBase {
|
|
5204
5213
|
constructor(initialOnline = true, debounceMs = 300) {
|
|
5205
5214
|
this.destroy$ = new Subject();
|
|
@@ -5219,14 +5228,14 @@ class NetworkBase {
|
|
|
5219
5228
|
const current = this.statusSubject.getValue();
|
|
5220
5229
|
if (current.online !== online) {
|
|
5221
5230
|
this.statusSubject.next({ online, timestamp: Date.now() });
|
|
5222
|
-
log$
|
|
5231
|
+
log$b.debug(`Network status changed: ${online ? 'online' : 'offline'}`);
|
|
5223
5232
|
}
|
|
5224
5233
|
}
|
|
5225
5234
|
destroy() {
|
|
5226
5235
|
this.destroy$.next();
|
|
5227
5236
|
this.destroy$.complete();
|
|
5228
5237
|
this.statusSubject.complete();
|
|
5229
|
-
log$
|
|
5238
|
+
log$b.debug('Network monitor destroyed');
|
|
5230
5239
|
}
|
|
5231
5240
|
}
|
|
5232
5241
|
|
|
@@ -5286,7 +5295,7 @@ class NodeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
5286
5295
|
}
|
|
5287
5296
|
}
|
|
5288
5297
|
|
|
5289
|
-
const log$
|
|
5298
|
+
const log$a = createPrefixedLogger('RN-STORAGE');
|
|
5290
5299
|
/**
|
|
5291
5300
|
* React Native storage adapter using AsyncStorage
|
|
5292
5301
|
* Note: Uses native batch operations for better performance (not base class)
|
|
@@ -5318,7 +5327,7 @@ class ReactNativeStorageAdapter {
|
|
|
5318
5327
|
return await this.AsyncStorage.getItem(key);
|
|
5319
5328
|
}
|
|
5320
5329
|
catch (error) {
|
|
5321
|
-
log$
|
|
5330
|
+
log$a.error('Failed to get item from AsyncStorage:', error);
|
|
5322
5331
|
return null;
|
|
5323
5332
|
}
|
|
5324
5333
|
}
|
|
@@ -5359,7 +5368,7 @@ class ReactNativeStorageAdapter {
|
|
|
5359
5368
|
return await this.AsyncStorage.getAllKeys();
|
|
5360
5369
|
}
|
|
5361
5370
|
catch (error) {
|
|
5362
|
-
log$
|
|
5371
|
+
log$a.error('Failed to get all keys:', error);
|
|
5363
5372
|
return [];
|
|
5364
5373
|
}
|
|
5365
5374
|
}
|
|
@@ -5376,7 +5385,7 @@ class ReactNativeStorageAdapter {
|
|
|
5376
5385
|
return result;
|
|
5377
5386
|
}
|
|
5378
5387
|
catch (error) {
|
|
5379
|
-
log$
|
|
5388
|
+
log$a.error('Failed to get multiple items:', error);
|
|
5380
5389
|
const result = {};
|
|
5381
5390
|
keys.forEach((key) => {
|
|
5382
5391
|
result[key] = null;
|
|
@@ -5426,7 +5435,7 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
5426
5435
|
return;
|
|
5427
5436
|
}
|
|
5428
5437
|
catch {
|
|
5429
|
-
log$
|
|
5438
|
+
log$a.debug('expo-secure-store not available, trying react-native-keychain');
|
|
5430
5439
|
}
|
|
5431
5440
|
try {
|
|
5432
5441
|
const Keychain = require('react-native-keychain');
|
|
@@ -5435,7 +5444,7 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
5435
5444
|
return;
|
|
5436
5445
|
}
|
|
5437
5446
|
catch {
|
|
5438
|
-
log$
|
|
5447
|
+
log$a.error('react-native-keychain not available');
|
|
5439
5448
|
}
|
|
5440
5449
|
throw new Error('No secure storage available. Please install expo-secure-store or react-native-keychain');
|
|
5441
5450
|
}
|
|
@@ -5453,7 +5462,7 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
5453
5462
|
}
|
|
5454
5463
|
}
|
|
5455
5464
|
catch (error) {
|
|
5456
|
-
log$
|
|
5465
|
+
log$a.error('Failed to get secure item:', error);
|
|
5457
5466
|
}
|
|
5458
5467
|
return null;
|
|
5459
5468
|
}
|
|
@@ -5490,10 +5499,10 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
5490
5499
|
}
|
|
5491
5500
|
}
|
|
5492
5501
|
async clear() {
|
|
5493
|
-
log$
|
|
5502
|
+
log$a.warn('Clear all secure items not fully implemented for React Native');
|
|
5494
5503
|
}
|
|
5495
5504
|
async getAllKeys() {
|
|
5496
|
-
log$
|
|
5505
|
+
log$a.warn('Get all secure keys not implemented for React Native');
|
|
5497
5506
|
return [];
|
|
5498
5507
|
}
|
|
5499
5508
|
async isAvailable() {
|
|
@@ -5711,7 +5720,7 @@ class NodeNetworkMonitor extends NetworkBase {
|
|
|
5711
5720
|
}
|
|
5712
5721
|
}
|
|
5713
5722
|
|
|
5714
|
-
const log$
|
|
5723
|
+
const log$9 = createPrefixedLogger('RN-NETWORK');
|
|
5715
5724
|
/**
|
|
5716
5725
|
* React Native network monitor using RxJS
|
|
5717
5726
|
* Supports both @react-native-community/netinfo and expo-network
|
|
@@ -5724,7 +5733,7 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5724
5733
|
this.moduleReady$ = new Subject();
|
|
5725
5734
|
this.isExpo = detectPlatform().isExpo;
|
|
5726
5735
|
this.init().catch((error) => {
|
|
5727
|
-
log$
|
|
5736
|
+
log$9.error('Network monitor initialization failed:', error);
|
|
5728
5737
|
});
|
|
5729
5738
|
}
|
|
5730
5739
|
async init() {
|
|
@@ -5743,10 +5752,10 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5743
5752
|
try {
|
|
5744
5753
|
const module = require('@react-native-community/netinfo');
|
|
5745
5754
|
this.netInfoModule = module.default || module;
|
|
5746
|
-
log$
|
|
5755
|
+
log$9.debug('Loaded @react-native-community/netinfo module');
|
|
5747
5756
|
}
|
|
5748
5757
|
catch (error) {
|
|
5749
|
-
log$
|
|
5758
|
+
log$9.error('Failed to load React Native NetInfo module:', error);
|
|
5750
5759
|
this.netInfoModule = null;
|
|
5751
5760
|
}
|
|
5752
5761
|
}
|
|
@@ -5754,10 +5763,10 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5754
5763
|
try {
|
|
5755
5764
|
const module = require('expo-network');
|
|
5756
5765
|
this.netInfoModule = module.default || module;
|
|
5757
|
-
log$
|
|
5766
|
+
log$9.debug('Loaded expo-network module');
|
|
5758
5767
|
}
|
|
5759
5768
|
catch (error) {
|
|
5760
|
-
log$
|
|
5769
|
+
log$9.error('Failed to load Expo Network module:', error);
|
|
5761
5770
|
this.netInfoModule = null;
|
|
5762
5771
|
}
|
|
5763
5772
|
}
|
|
@@ -5774,16 +5783,16 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5774
5783
|
}
|
|
5775
5784
|
const online = !!(state.isConnected && state.isInternetReachable !== false);
|
|
5776
5785
|
this.updateStatus(online);
|
|
5777
|
-
log$
|
|
5786
|
+
log$9.debug('Initial network state:', online ? 'online' : 'offline');
|
|
5778
5787
|
}
|
|
5779
5788
|
catch (error) {
|
|
5780
|
-
log$
|
|
5789
|
+
log$9.warn('Could not fetch initial network state:', error);
|
|
5781
5790
|
}
|
|
5782
5791
|
}
|
|
5783
5792
|
subscribeToStateChanges() {
|
|
5784
5793
|
if (!this.netInfoModule)
|
|
5785
5794
|
return;
|
|
5786
|
-
log$
|
|
5795
|
+
log$9.debug('Subscribing to network state changes');
|
|
5787
5796
|
const handleState = (state) => {
|
|
5788
5797
|
const online = !!(state.isConnected && (state.isInternetReachable ?? true));
|
|
5789
5798
|
this.updateStatus(online);
|
|
@@ -5825,7 +5834,7 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5825
5834
|
};
|
|
5826
5835
|
}
|
|
5827
5836
|
catch (error) {
|
|
5828
|
-
log$
|
|
5837
|
+
log$9.error('Failed to fetch detailed network info:', error);
|
|
5829
5838
|
return null;
|
|
5830
5839
|
}
|
|
5831
5840
|
}
|
|
@@ -5961,7 +5970,7 @@ class CertificateValidator {
|
|
|
5961
5970
|
}
|
|
5962
5971
|
}
|
|
5963
5972
|
|
|
5964
|
-
const log$
|
|
5973
|
+
const log$8 = createPrefixedLogger('RN-MTLS');
|
|
5965
5974
|
/**
|
|
5966
5975
|
* React Native mTLS Adapter using @a-cube-io/expo-mutual-tls
|
|
5967
5976
|
*/
|
|
@@ -5983,7 +5992,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5983
5992
|
this.expoMTLS = ExpoMutualTls;
|
|
5984
5993
|
// Set up debug logging with the correct event signature
|
|
5985
5994
|
const debugListener = ExpoMutualTls.onDebugLog((event) => {
|
|
5986
|
-
log$
|
|
5995
|
+
log$8.debug(`${event.type}: ${event.message}`, {
|
|
5987
5996
|
method: event.method,
|
|
5988
5997
|
url: event.url,
|
|
5989
5998
|
statusCode: event.statusCode,
|
|
@@ -5993,28 +6002,28 @@ class ReactNativeMTLSAdapter {
|
|
|
5993
6002
|
this.eventListeners.push(debugListener);
|
|
5994
6003
|
// Set up error logging with the correct event signature
|
|
5995
6004
|
const errorListener = ExpoMutualTls.onError((event) => {
|
|
5996
|
-
log$
|
|
6005
|
+
log$8.error(event.message, {
|
|
5997
6006
|
code: event.code,
|
|
5998
6007
|
});
|
|
5999
6008
|
});
|
|
6000
6009
|
this.eventListeners.push(errorListener);
|
|
6001
6010
|
// Set up certificate expiry monitoring with the correct event signature
|
|
6002
6011
|
const expiryListener = ExpoMutualTls.onCertificateExpiry((event) => {
|
|
6003
|
-
log$
|
|
6012
|
+
log$8.warn(`Certificate ${event.subject} expires at ${new Date(event.expiry)}`, {
|
|
6004
6013
|
alias: event.alias,
|
|
6005
6014
|
warning: event.warning,
|
|
6006
6015
|
});
|
|
6007
6016
|
});
|
|
6008
6017
|
this.eventListeners.push(expiryListener);
|
|
6009
|
-
log$
|
|
6018
|
+
log$8.debug('Expo mTLS module loaded successfully');
|
|
6010
6019
|
}
|
|
6011
6020
|
catch (error) {
|
|
6012
|
-
log$
|
|
6021
|
+
log$8.warn('@a-cube-io/expo-mutual-tls not available:', error);
|
|
6013
6022
|
}
|
|
6014
6023
|
}
|
|
6015
6024
|
async isMTLSSupported() {
|
|
6016
6025
|
const supported = this.expoMTLS !== null;
|
|
6017
|
-
log$
|
|
6026
|
+
log$8.debug('mTLS support check:', {
|
|
6018
6027
|
supported,
|
|
6019
6028
|
platform: this.getPlatformInfo().platform,
|
|
6020
6029
|
moduleAvailable: !!this.expoMTLS,
|
|
@@ -6026,7 +6035,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6026
6035
|
throw new MTLSError(MTLSErrorType$1.NOT_SUPPORTED, 'Expo mTLS module not available');
|
|
6027
6036
|
}
|
|
6028
6037
|
this.config = config;
|
|
6029
|
-
log$
|
|
6038
|
+
log$8.debug('Initialized with config:', {
|
|
6030
6039
|
baseUrl: config.baseUrl,
|
|
6031
6040
|
port: config.port,
|
|
6032
6041
|
timeout: config.timeout,
|
|
@@ -6040,7 +6049,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6040
6049
|
if (!this.config) {
|
|
6041
6050
|
throw new MTLSError(MTLSErrorType$1.CONFIGURATION_ERROR, 'Adapter not initialized. Call initialize() first.');
|
|
6042
6051
|
}
|
|
6043
|
-
log$
|
|
6052
|
+
log$8.debug('Configuring certificate:', {
|
|
6044
6053
|
format: certificateData.format,
|
|
6045
6054
|
hasPassword: !!certificateData.password,
|
|
6046
6055
|
certificateLength: certificateData.certificate.length,
|
|
@@ -6057,14 +6066,14 @@ class ReactNativeMTLSAdapter {
|
|
|
6057
6066
|
'client-key-service', // keyService
|
|
6058
6067
|
true // enableLogging - let the native module handle its own debug logging
|
|
6059
6068
|
);
|
|
6060
|
-
log$
|
|
6069
|
+
log$8.debug('PEM services configured:', configResult);
|
|
6061
6070
|
if (!configResult.success) {
|
|
6062
6071
|
throw new MTLSError(MTLSErrorType$1.CONFIGURATION_ERROR, `PEM configuration failed: ${configResult.state}`);
|
|
6063
6072
|
}
|
|
6064
6073
|
// Step 2: Store the actual PEM certificate and private key
|
|
6065
6074
|
const storeResult = await this.expoMTLS.storePEM(certificateData.certificate, certificateData.privateKey, certificateData.password // passphrase (optional)
|
|
6066
6075
|
);
|
|
6067
|
-
log$
|
|
6076
|
+
log$8.debug('PEM certificate store result:', storeResult);
|
|
6068
6077
|
if (!storeResult) {
|
|
6069
6078
|
throw new MTLSError(MTLSErrorType$1.CERTIFICATE_INVALID, 'Failed to store PEM certificate');
|
|
6070
6079
|
}
|
|
@@ -6077,14 +6086,14 @@ class ReactNativeMTLSAdapter {
|
|
|
6077
6086
|
const configResult = await this.expoMTLS.configureP12('client-p12-service', // keychainService
|
|
6078
6087
|
true // enableLogging - let the native module handle its own debug logging
|
|
6079
6088
|
);
|
|
6080
|
-
log$
|
|
6089
|
+
log$8.debug('P12 service configured:', configResult);
|
|
6081
6090
|
if (!configResult.success) {
|
|
6082
6091
|
throw new MTLSError(MTLSErrorType$1.CONFIGURATION_ERROR, `P12 configuration failed: ${configResult.state}`);
|
|
6083
6092
|
}
|
|
6084
6093
|
// Step 2: Store the P12 certificate data
|
|
6085
6094
|
const storeResult = await this.expoMTLS.storeP12(certificateData.certificate, // P12 data in certificate field
|
|
6086
6095
|
certificateData.password);
|
|
6087
|
-
log$
|
|
6096
|
+
log$8.debug('P12 certificate store result:', storeResult);
|
|
6088
6097
|
if (!storeResult) {
|
|
6089
6098
|
throw new MTLSError(MTLSErrorType$1.CERTIFICATE_INVALID, 'Failed to store P12 certificate');
|
|
6090
6099
|
}
|
|
@@ -6098,7 +6107,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6098
6107
|
if (error instanceof MTLSError) {
|
|
6099
6108
|
throw error;
|
|
6100
6109
|
}
|
|
6101
|
-
log$
|
|
6110
|
+
log$8.error('Certificate configuration failed:', error);
|
|
6102
6111
|
throw new MTLSError(MTLSErrorType$1.CONFIGURATION_ERROR, 'Failed to configure certificate', error);
|
|
6103
6112
|
}
|
|
6104
6113
|
}
|
|
@@ -6109,38 +6118,38 @@ class ReactNativeMTLSAdapter {
|
|
|
6109
6118
|
try {
|
|
6110
6119
|
// Use static method call
|
|
6111
6120
|
const hasCert = await this.expoMTLS.hasCertificate();
|
|
6112
|
-
log$
|
|
6121
|
+
log$8.debug('Certificate availability check:', hasCert);
|
|
6113
6122
|
return hasCert;
|
|
6114
6123
|
}
|
|
6115
6124
|
catch (error) {
|
|
6116
|
-
log$
|
|
6125
|
+
log$8.error('Certificate check failed:', error);
|
|
6117
6126
|
return false;
|
|
6118
6127
|
}
|
|
6119
6128
|
}
|
|
6120
6129
|
async getCertificateInfo() {
|
|
6121
6130
|
if (!this.expoMTLS) {
|
|
6122
|
-
log$
|
|
6131
|
+
log$8.debug('Certificate info requested but module not available');
|
|
6123
6132
|
return null;
|
|
6124
6133
|
}
|
|
6125
6134
|
try {
|
|
6126
6135
|
const hasCert = await this.hasCertificate();
|
|
6127
6136
|
if (!hasCert) {
|
|
6128
|
-
log$
|
|
6137
|
+
log$8.debug('No certificate stored');
|
|
6129
6138
|
return null;
|
|
6130
6139
|
}
|
|
6131
6140
|
// Use getCertificatesInfo to retrieve information about stored certificates
|
|
6132
6141
|
const result = await this.expoMTLS.getCertificatesInfo();
|
|
6133
6142
|
if (!result || !result.certificates || result.certificates.length === 0) {
|
|
6134
|
-
log$
|
|
6143
|
+
log$8.debug('No certificate information available');
|
|
6135
6144
|
return null;
|
|
6136
6145
|
}
|
|
6137
6146
|
// Get the first certificate (primary client certificate)
|
|
6138
6147
|
const cert = result.certificates[0];
|
|
6139
6148
|
if (!cert) {
|
|
6140
|
-
log$
|
|
6149
|
+
log$8.debug('Certificate data is empty');
|
|
6141
6150
|
return null;
|
|
6142
6151
|
}
|
|
6143
|
-
log$
|
|
6152
|
+
log$8.debug('Retrieved certificate info:', {
|
|
6144
6153
|
subject: cert.subject.commonName,
|
|
6145
6154
|
issuer: cert.issuer.commonName,
|
|
6146
6155
|
validFrom: new Date(cert.validFrom),
|
|
@@ -6159,7 +6168,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6159
6168
|
};
|
|
6160
6169
|
}
|
|
6161
6170
|
catch (error) {
|
|
6162
|
-
log$
|
|
6171
|
+
log$8.error('Failed to get certificate info:', error);
|
|
6163
6172
|
return null;
|
|
6164
6173
|
}
|
|
6165
6174
|
}
|
|
@@ -6170,7 +6179,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6170
6179
|
*/
|
|
6171
6180
|
async parseCertificateData(certificateData) {
|
|
6172
6181
|
if (!this.expoMTLS) {
|
|
6173
|
-
log$
|
|
6182
|
+
log$8.debug('Parse certificate: Module not available');
|
|
6174
6183
|
return null;
|
|
6175
6184
|
}
|
|
6176
6185
|
try {
|
|
@@ -6187,14 +6196,14 @@ class ReactNativeMTLSAdapter {
|
|
|
6187
6196
|
else {
|
|
6188
6197
|
throw new MTLSError(MTLSErrorType$1.CERTIFICATE_INVALID, `Unsupported certificate format: ${certificateData.format}`);
|
|
6189
6198
|
}
|
|
6190
|
-
log$
|
|
6199
|
+
log$8.debug('Certificate parsed successfully:', {
|
|
6191
6200
|
certificateCount: result.certificates.length,
|
|
6192
6201
|
subjects: result.certificates.map((cert) => cert.subject.commonName),
|
|
6193
6202
|
});
|
|
6194
6203
|
return result;
|
|
6195
6204
|
}
|
|
6196
6205
|
catch (error) {
|
|
6197
|
-
log$
|
|
6206
|
+
log$8.error('Failed to parse certificate:', error);
|
|
6198
6207
|
if (error instanceof MTLSError) {
|
|
6199
6208
|
throw error;
|
|
6200
6209
|
}
|
|
@@ -6209,7 +6218,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6209
6218
|
if (!hasCert) {
|
|
6210
6219
|
throw new MTLSError(MTLSErrorType$1.CERTIFICATE_NOT_FOUND, 'No certificate configured');
|
|
6211
6220
|
}
|
|
6212
|
-
log$
|
|
6221
|
+
log$8.debug('Making mTLS request:', {
|
|
6213
6222
|
method: requestConfig.method || 'GET',
|
|
6214
6223
|
url: requestConfig.url,
|
|
6215
6224
|
headers: requestConfig.headers,
|
|
@@ -6217,7 +6226,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6217
6226
|
responseType: requestConfig.responseType,
|
|
6218
6227
|
});
|
|
6219
6228
|
if (requestConfig.data) {
|
|
6220
|
-
log$
|
|
6229
|
+
log$8.debug('mTLS request body:', requestConfig.data);
|
|
6221
6230
|
}
|
|
6222
6231
|
try {
|
|
6223
6232
|
const response = await this.expoMTLS.request(requestConfig.url, {
|
|
@@ -6226,7 +6235,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6226
6235
|
body: requestConfig.data ? JSON.stringify(requestConfig.data) : undefined,
|
|
6227
6236
|
responseType: requestConfig.responseType,
|
|
6228
6237
|
});
|
|
6229
|
-
log$
|
|
6238
|
+
log$8.debug('mTLS request successful:', response);
|
|
6230
6239
|
if (!response.success) {
|
|
6231
6240
|
throw new MTLSError(MTLSErrorType$1.CONNECTION_FAILED, `mTLS request failed: ${response.statusMessage} (${response.statusCode})`, undefined, response.statusCode);
|
|
6232
6241
|
}
|
|
@@ -6238,7 +6247,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6238
6247
|
data = JSON.parse(response.body);
|
|
6239
6248
|
}
|
|
6240
6249
|
catch (parseError) {
|
|
6241
|
-
log$
|
|
6250
|
+
log$8.warn('Failed to parse JSON response:', parseError);
|
|
6242
6251
|
// If parsing fails, keep raw body
|
|
6243
6252
|
}
|
|
6244
6253
|
}
|
|
@@ -6255,7 +6264,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6255
6264
|
};
|
|
6256
6265
|
}
|
|
6257
6266
|
catch (error) {
|
|
6258
|
-
log$
|
|
6267
|
+
log$8.error('mTLS request failed:', error);
|
|
6259
6268
|
throw new MTLSError(MTLSErrorType$1.CONNECTION_FAILED, 'mTLS request failed', error);
|
|
6260
6269
|
}
|
|
6261
6270
|
}
|
|
@@ -6268,18 +6277,18 @@ class ReactNativeMTLSAdapter {
|
|
|
6268
6277
|
*/
|
|
6269
6278
|
async testConnection() {
|
|
6270
6279
|
if (!this.expoMTLS || !this.config) {
|
|
6271
|
-
log$
|
|
6280
|
+
log$8.debug('Diagnostic test: No mTLS module or config available');
|
|
6272
6281
|
return false;
|
|
6273
6282
|
}
|
|
6274
6283
|
try {
|
|
6275
6284
|
const hasCert = await this.hasCertificate();
|
|
6276
6285
|
if (!hasCert) {
|
|
6277
|
-
log$
|
|
6286
|
+
log$8.debug('Diagnostic test: No certificate configured');
|
|
6278
6287
|
return false;
|
|
6279
6288
|
}
|
|
6280
|
-
log$
|
|
6289
|
+
log$8.debug('Running diagnostic test (may fail even if mTLS works):', this.config.baseUrl);
|
|
6281
6290
|
const result = await this.expoMTLS.testConnection(this.config.baseUrl);
|
|
6282
|
-
log$
|
|
6291
|
+
log$8.debug('Diagnostic test result (NOT validation):', {
|
|
6283
6292
|
success: result.success,
|
|
6284
6293
|
statusCode: result.statusCode,
|
|
6285
6294
|
statusMessage: result.statusMessage,
|
|
@@ -6290,13 +6299,13 @@ class ReactNativeMTLSAdapter {
|
|
|
6290
6299
|
return result.success;
|
|
6291
6300
|
}
|
|
6292
6301
|
catch (error) {
|
|
6293
|
-
log$
|
|
6302
|
+
log$8.warn('Diagnostic test failed (this is expected):', error);
|
|
6294
6303
|
return false;
|
|
6295
6304
|
}
|
|
6296
6305
|
}
|
|
6297
6306
|
async removeCertificate() {
|
|
6298
6307
|
if (!this.expoMTLS) {
|
|
6299
|
-
log$
|
|
6308
|
+
log$8.debug('Remove certificate: Module not available');
|
|
6300
6309
|
return;
|
|
6301
6310
|
}
|
|
6302
6311
|
try {
|
|
@@ -6305,10 +6314,10 @@ class ReactNativeMTLSAdapter {
|
|
|
6305
6314
|
this.isConfigured = false;
|
|
6306
6315
|
// Cleanup event listeners
|
|
6307
6316
|
this.cleanupEventListeners();
|
|
6308
|
-
log$
|
|
6317
|
+
log$8.debug('Certificate removed successfully');
|
|
6309
6318
|
}
|
|
6310
6319
|
catch (error) {
|
|
6311
|
-
log$
|
|
6320
|
+
log$8.error('Failed to remove certificate:', error);
|
|
6312
6321
|
throw new MTLSError(MTLSErrorType$1.CONFIGURATION_ERROR, 'Failed to remove certificate', error);
|
|
6313
6322
|
}
|
|
6314
6323
|
}
|
|
@@ -6317,7 +6326,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6317
6326
|
*/
|
|
6318
6327
|
cleanupEventListeners() {
|
|
6319
6328
|
if (this.eventListeners.length > 0) {
|
|
6320
|
-
log$
|
|
6329
|
+
log$8.debug(`Cleaning up ${this.eventListeners.length} event listeners`);
|
|
6321
6330
|
}
|
|
6322
6331
|
// Remove individual listeners if they have remove methods
|
|
6323
6332
|
this.eventListeners.forEach((listener) => {
|
|
@@ -6354,7 +6363,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6354
6363
|
}
|
|
6355
6364
|
}
|
|
6356
6365
|
|
|
6357
|
-
const log$
|
|
6366
|
+
const log$7 = createPrefixedLogger('WEB-MTLS');
|
|
6358
6367
|
/**
|
|
6359
6368
|
* Web mTLS Adapter - Graceful fallback for web browsers
|
|
6360
6369
|
*
|
|
@@ -6368,13 +6377,13 @@ const log$6 = createPrefixedLogger('WEB-MTLS');
|
|
|
6368
6377
|
*/
|
|
6369
6378
|
class WebMTLSAdapter {
|
|
6370
6379
|
constructor() {
|
|
6371
|
-
log$
|
|
6372
|
-
log$
|
|
6380
|
+
log$7.warn('Web browsers do not support programmatic mTLS configuration');
|
|
6381
|
+
log$7.info('Use JWT authentication or configure client certificates in browser settings');
|
|
6373
6382
|
}
|
|
6374
6383
|
async isMTLSSupported() {
|
|
6375
6384
|
// mTLS is not supported programmatically in web browsers
|
|
6376
6385
|
const supported = false;
|
|
6377
|
-
log$
|
|
6386
|
+
log$7.debug('mTLS support check:', {
|
|
6378
6387
|
supported,
|
|
6379
6388
|
platform: this.getPlatformInfo().platform,
|
|
6380
6389
|
reason: 'Browser security model prevents programmatic certificate configuration',
|
|
@@ -6383,14 +6392,14 @@ class WebMTLSAdapter {
|
|
|
6383
6392
|
return supported;
|
|
6384
6393
|
}
|
|
6385
6394
|
async initialize(config) {
|
|
6386
|
-
log$
|
|
6395
|
+
log$7.warn('Initialized but mTLS not available in web browsers:', {
|
|
6387
6396
|
baseUrl: config.baseUrl,
|
|
6388
6397
|
port: config.port,
|
|
6389
6398
|
recommendation: 'Use standard HTTPS with JWT authentication',
|
|
6390
6399
|
});
|
|
6391
6400
|
}
|
|
6392
6401
|
async configureCertificate(certificateData) {
|
|
6393
|
-
log$
|
|
6402
|
+
log$7.error('Certificate configuration attempted:', {
|
|
6394
6403
|
format: certificateData.format,
|
|
6395
6404
|
reason: 'Not supported in web browsers',
|
|
6396
6405
|
alternatives: [
|
|
@@ -6405,15 +6414,15 @@ class WebMTLSAdapter {
|
|
|
6405
6414
|
}
|
|
6406
6415
|
async hasCertificate() {
|
|
6407
6416
|
// We cannot detect if the browser has certificates configured
|
|
6408
|
-
log$
|
|
6417
|
+
log$7.debug('Certificate availability check: Cannot detect browser certificates programmatically');
|
|
6409
6418
|
return false;
|
|
6410
6419
|
}
|
|
6411
6420
|
async getCertificateInfo() {
|
|
6412
|
-
log$
|
|
6421
|
+
log$7.debug('Certificate info requested: Not accessible in web browsers');
|
|
6413
6422
|
return null;
|
|
6414
6423
|
}
|
|
6415
6424
|
async request(requestConfig) {
|
|
6416
|
-
log$
|
|
6425
|
+
log$7.error('mTLS request attempted:', {
|
|
6417
6426
|
method: requestConfig.method,
|
|
6418
6427
|
url: requestConfig.url,
|
|
6419
6428
|
reason: 'Not supported in web browsers',
|
|
@@ -6428,11 +6437,11 @@ class WebMTLSAdapter {
|
|
|
6428
6437
|
'are properly configured in the browser certificate store.');
|
|
6429
6438
|
}
|
|
6430
6439
|
async testConnection() {
|
|
6431
|
-
log$
|
|
6440
|
+
log$7.debug('Connection test: mTLS not available in web browsers');
|
|
6432
6441
|
return false;
|
|
6433
6442
|
}
|
|
6434
6443
|
async removeCertificate() {
|
|
6435
|
-
log$
|
|
6444
|
+
log$7.debug('Remove certificate: No certificates to remove (not supported in web browsers)');
|
|
6436
6445
|
// No-op - cannot remove certificates programmatically in browsers
|
|
6437
6446
|
}
|
|
6438
6447
|
/**
|
|
@@ -6440,7 +6449,7 @@ class WebMTLSAdapter {
|
|
|
6440
6449
|
* Always returns null for web browsers as mTLS is not supported
|
|
6441
6450
|
*/
|
|
6442
6451
|
getBaseUrl() {
|
|
6443
|
-
log$
|
|
6452
|
+
log$7.debug('Base URL requested: Not supported in web browsers');
|
|
6444
6453
|
return null;
|
|
6445
6454
|
}
|
|
6446
6455
|
getPlatformInfo() {
|
|
@@ -6473,7 +6482,7 @@ class WebMTLSAdapter {
|
|
|
6473
6482
|
}
|
|
6474
6483
|
}
|
|
6475
6484
|
|
|
6476
|
-
const log$
|
|
6485
|
+
const log$6 = createPrefixedLogger('MTLS-LOADER');
|
|
6477
6486
|
function loadMTLSAdapter(platform, config) {
|
|
6478
6487
|
try {
|
|
6479
6488
|
let adapter;
|
|
@@ -6507,7 +6516,7 @@ function loadMTLSAdapter(platform, config) {
|
|
|
6507
6516
|
return adapter;
|
|
6508
6517
|
}
|
|
6509
6518
|
catch (error) {
|
|
6510
|
-
log$
|
|
6519
|
+
log$6.warn(`mTLS adapter not available for platform ${platform}:`, error);
|
|
6511
6520
|
return null;
|
|
6512
6521
|
}
|
|
6513
6522
|
}
|
|
@@ -6517,7 +6526,7 @@ async function initializeAdapterAsync(adapter, config) {
|
|
|
6517
6526
|
if (isSupported) {
|
|
6518
6527
|
await adapter.initialize(config);
|
|
6519
6528
|
const platformInfo = adapter.getPlatformInfo();
|
|
6520
|
-
log$
|
|
6529
|
+
log$6.debug('mTLS adapter initialized:', {
|
|
6521
6530
|
platform: platformInfo.platform,
|
|
6522
6531
|
mtlsSupported: platformInfo.mtlsSupported,
|
|
6523
6532
|
certificateStorage: platformInfo.certificateStorage,
|
|
@@ -6526,20 +6535,20 @@ async function initializeAdapterAsync(adapter, config) {
|
|
|
6526
6535
|
}
|
|
6527
6536
|
}
|
|
6528
6537
|
catch (error) {
|
|
6529
|
-
log$
|
|
6538
|
+
log$6.warn('Failed to initialize mTLS adapter:', error);
|
|
6530
6539
|
}
|
|
6531
6540
|
}
|
|
6532
6541
|
|
|
6533
|
-
const log$
|
|
6542
|
+
const log$5 = createPrefixedLogger('ADAPTER-LOADER');
|
|
6534
6543
|
function loadPlatformAdapters(options = {}) {
|
|
6535
6544
|
const { mtlsConfig } = options;
|
|
6536
6545
|
const { platform } = detectPlatform();
|
|
6537
|
-
log$
|
|
6546
|
+
log$5.debug('Loading adapters for platform:', platform);
|
|
6538
6547
|
const storageAdapters = loadStorageAdapters(platform);
|
|
6539
6548
|
const networkMonitor = loadNetworkMonitor(platform);
|
|
6540
6549
|
const cache = loadCacheAdapter(platform);
|
|
6541
6550
|
const mtls = loadMTLSAdapter(platform, mtlsConfig);
|
|
6542
|
-
log$
|
|
6551
|
+
log$5.debug('Adapters loaded:', {
|
|
6543
6552
|
platform,
|
|
6544
6553
|
hasStorage: !!storageAdapters.storage,
|
|
6545
6554
|
hasSecureStorage: !!storageAdapters.secureStorage,
|
|
@@ -7565,7 +7574,7 @@ class TelemetryRepositoryImpl {
|
|
|
7565
7574
|
}
|
|
7566
7575
|
}
|
|
7567
7576
|
|
|
7568
|
-
const log$
|
|
7577
|
+
const log$4 = createPrefixedLogger('CACHE-KEY');
|
|
7569
7578
|
const URL_PATTERNS = [
|
|
7570
7579
|
// Receipt (mf1) - specific patterns first
|
|
7571
7580
|
{
|
|
@@ -7658,7 +7667,7 @@ const DEFAULT_TTL = 5 * 60 * 1000; // 5 minutes
|
|
|
7658
7667
|
class CacheKeyGenerator {
|
|
7659
7668
|
constructor(customConfig) {
|
|
7660
7669
|
this.config = { ...DEFAULT_TTL_CONFIG, ...customConfig };
|
|
7661
|
-
log$
|
|
7670
|
+
log$4.info('CacheKeyGenerator initialized with config:', {
|
|
7662
7671
|
resources: Object.keys(this.config),
|
|
7663
7672
|
});
|
|
7664
7673
|
}
|
|
@@ -7668,7 +7677,7 @@ class CacheKeyGenerator {
|
|
|
7668
7677
|
// Fallback: use URL as key
|
|
7669
7678
|
const paramStr = params ? this.serializeParams(params) : '';
|
|
7670
7679
|
const key = paramStr ? `${url}?${paramStr}` : url;
|
|
7671
|
-
log$
|
|
7680
|
+
log$4.debug('URL not matched, using fallback key:', { url, key });
|
|
7672
7681
|
return key;
|
|
7673
7682
|
}
|
|
7674
7683
|
const { resource, ids, action, isList, parent } = parsed;
|
|
@@ -7676,21 +7685,21 @@ class CacheKeyGenerator {
|
|
|
7676
7685
|
const paramStr = params ? this.serializeParams(params) : '';
|
|
7677
7686
|
const parentPart = parent && ids.length > 0 ? `${parent}=${ids[0]}&` : '';
|
|
7678
7687
|
const key = `${resource}:list:${parentPart}${paramStr}`;
|
|
7679
|
-
log$
|
|
7688
|
+
log$4.debug('Generated list cache key:', { url, key, resource });
|
|
7680
7689
|
return key;
|
|
7681
7690
|
}
|
|
7682
7691
|
// Single item
|
|
7683
7692
|
if (ids.length === 0 && action) {
|
|
7684
7693
|
// Special case for endpoints like /cashiers/me
|
|
7685
7694
|
const key = `${resource}:${action}`;
|
|
7686
|
-
log$
|
|
7695
|
+
log$4.debug('Generated special action cache key:', { url, key, resource, action });
|
|
7687
7696
|
return key;
|
|
7688
7697
|
}
|
|
7689
7698
|
let key = `${resource}:${ids.join(':')}`;
|
|
7690
7699
|
if (action) {
|
|
7691
7700
|
key += `:${action}`;
|
|
7692
7701
|
}
|
|
7693
|
-
log$
|
|
7702
|
+
log$4.debug('Generated item cache key:', { url, key, resource, ids, action });
|
|
7694
7703
|
return key;
|
|
7695
7704
|
}
|
|
7696
7705
|
parseResource(url) {
|
|
@@ -7700,23 +7709,23 @@ class CacheKeyGenerator {
|
|
|
7700
7709
|
getTTL(url) {
|
|
7701
7710
|
const resource = this.parseResource(url);
|
|
7702
7711
|
if (!resource) {
|
|
7703
|
-
log$
|
|
7712
|
+
log$4.debug('No resource found for URL, using default TTL:', { url, ttl: DEFAULT_TTL });
|
|
7704
7713
|
return DEFAULT_TTL;
|
|
7705
7714
|
}
|
|
7706
7715
|
const ttl = this.config[resource].ttlMs;
|
|
7707
|
-
log$
|
|
7716
|
+
log$4.debug('TTL for resource:', { url, resource, ttlMs: ttl, ttlMin: ttl / 60000 });
|
|
7708
7717
|
return ttl;
|
|
7709
7718
|
}
|
|
7710
7719
|
shouldCache(url) {
|
|
7711
7720
|
const parsed = this.parseUrl(url);
|
|
7712
7721
|
if (!parsed) {
|
|
7713
|
-
log$
|
|
7722
|
+
log$4.debug('URL not recognized, should not cache:', { url });
|
|
7714
7723
|
return false;
|
|
7715
7724
|
}
|
|
7716
7725
|
const { resource, isList } = parsed;
|
|
7717
7726
|
const config = this.config[resource];
|
|
7718
7727
|
if (isList) {
|
|
7719
|
-
log$
|
|
7728
|
+
log$4.debug('List endpoint cache decision:', {
|
|
7720
7729
|
url,
|
|
7721
7730
|
resource,
|
|
7722
7731
|
isList: true,
|
|
@@ -7724,7 +7733,7 @@ class CacheKeyGenerator {
|
|
|
7724
7733
|
});
|
|
7725
7734
|
return config.cacheList;
|
|
7726
7735
|
}
|
|
7727
|
-
log$
|
|
7736
|
+
log$4.debug('Item endpoint cache decision:', {
|
|
7728
7737
|
url,
|
|
7729
7738
|
resource,
|
|
7730
7739
|
isList: false,
|
|
@@ -7735,7 +7744,7 @@ class CacheKeyGenerator {
|
|
|
7735
7744
|
getInvalidationPatterns(url, method) {
|
|
7736
7745
|
const parsed = this.parseUrl(url);
|
|
7737
7746
|
if (!parsed) {
|
|
7738
|
-
log$
|
|
7747
|
+
log$4.debug('No patterns to invalidate for URL:', { url, method });
|
|
7739
7748
|
return [];
|
|
7740
7749
|
}
|
|
7741
7750
|
const { resource, ids, parent } = parsed;
|
|
@@ -7757,7 +7766,7 @@ class CacheKeyGenerator {
|
|
|
7757
7766
|
if (resource === 'cashier' && (method === 'PUT' || method === 'DELETE')) {
|
|
7758
7767
|
patterns.push('cashier:me');
|
|
7759
7768
|
}
|
|
7760
|
-
log$
|
|
7769
|
+
log$4.debug('Invalidation patterns:', { url, method, patterns });
|
|
7761
7770
|
return patterns;
|
|
7762
7771
|
}
|
|
7763
7772
|
parseUrl(url) {
|
|
@@ -7792,7 +7801,7 @@ class CacheKeyGenerator {
|
|
|
7792
7801
|
}
|
|
7793
7802
|
}
|
|
7794
7803
|
|
|
7795
|
-
const log$
|
|
7804
|
+
const log$3 = createPrefixedLogger('CACHE');
|
|
7796
7805
|
class CachingHttpDecorator {
|
|
7797
7806
|
constructor(http, cache, keyGenerator, networkMonitor, config = {}) {
|
|
7798
7807
|
this.http = http;
|
|
@@ -7802,7 +7811,7 @@ class CachingHttpDecorator {
|
|
|
7802
7811
|
this.config = config;
|
|
7803
7812
|
this.currentOnlineState = true;
|
|
7804
7813
|
this.authToken = null;
|
|
7805
|
-
log$
|
|
7814
|
+
log$3.info('CachingHttpDecorator initialized', {
|
|
7806
7815
|
enabled: config.enabled !== false,
|
|
7807
7816
|
hasNetworkMonitor: !!networkMonitor,
|
|
7808
7817
|
});
|
|
@@ -7812,7 +7821,7 @@ class CachingHttpDecorator {
|
|
|
7812
7821
|
if (this.networkMonitor) {
|
|
7813
7822
|
this.networkSubscription = this.networkMonitor.online$.subscribe((online) => {
|
|
7814
7823
|
if (this.currentOnlineState !== online) {
|
|
7815
|
-
log$
|
|
7824
|
+
log$3.info('Network state changed:', { online });
|
|
7816
7825
|
}
|
|
7817
7826
|
this.currentOnlineState = online;
|
|
7818
7827
|
});
|
|
@@ -7831,19 +7840,19 @@ class CachingHttpDecorator {
|
|
|
7831
7840
|
const startTime = Date.now();
|
|
7832
7841
|
// Check if caching is disabled globally
|
|
7833
7842
|
if (this.config.enabled === false) {
|
|
7834
|
-
log$
|
|
7843
|
+
log$3.debug('GET (cache disabled globally):', { url });
|
|
7835
7844
|
return this.http.get(url, config);
|
|
7836
7845
|
}
|
|
7837
7846
|
// Check if this URL should be cached
|
|
7838
7847
|
const shouldCache = this.keyGenerator.shouldCache(url);
|
|
7839
7848
|
if (!shouldCache) {
|
|
7840
|
-
log$
|
|
7849
|
+
log$3.debug('GET (not cacheable - likely a list endpoint):', { url });
|
|
7841
7850
|
return this.http.get(url, config);
|
|
7842
7851
|
}
|
|
7843
7852
|
const cacheKey = this.keyGenerator.generate(url, config?.params);
|
|
7844
7853
|
const ttl = this.keyGenerator.getTTL(url);
|
|
7845
7854
|
const resource = this.keyGenerator.parseResource(url);
|
|
7846
|
-
log$
|
|
7855
|
+
log$3.info('GET request starting:', {
|
|
7847
7856
|
url,
|
|
7848
7857
|
resource,
|
|
7849
7858
|
cacheKey,
|
|
@@ -7856,18 +7865,18 @@ class CachingHttpDecorator {
|
|
|
7856
7865
|
try {
|
|
7857
7866
|
cached = await this.cache.get(cacheKey);
|
|
7858
7867
|
if (cached) {
|
|
7859
|
-
log$
|
|
7868
|
+
log$3.debug('Cache entry found:', {
|
|
7860
7869
|
cacheKey,
|
|
7861
7870
|
timestamp: new Date(cached.timestamp).toISOString(),
|
|
7862
7871
|
ageMs: Date.now() - cached.timestamp,
|
|
7863
7872
|
});
|
|
7864
7873
|
}
|
|
7865
7874
|
else {
|
|
7866
|
-
log$
|
|
7875
|
+
log$3.debug('Cache entry not found:', { cacheKey });
|
|
7867
7876
|
}
|
|
7868
7877
|
}
|
|
7869
7878
|
catch (error) {
|
|
7870
|
-
log$
|
|
7879
|
+
log$3.warn('Cache lookup failed:', {
|
|
7871
7880
|
cacheKey,
|
|
7872
7881
|
error: error instanceof Error ? error.message : error,
|
|
7873
7882
|
});
|
|
@@ -7875,7 +7884,7 @@ class CachingHttpDecorator {
|
|
|
7875
7884
|
if (cached) {
|
|
7876
7885
|
const age = Date.now() - cached.timestamp;
|
|
7877
7886
|
const isExpired = age >= ttl;
|
|
7878
|
-
log$
|
|
7887
|
+
log$3.debug('Cache analysis:', {
|
|
7879
7888
|
cacheKey,
|
|
7880
7889
|
ageMs: age,
|
|
7881
7890
|
ageSec: Math.round(age / 1000),
|
|
@@ -7886,7 +7895,7 @@ class CachingHttpDecorator {
|
|
|
7886
7895
|
// If within TTL, return cached data
|
|
7887
7896
|
if (!isExpired) {
|
|
7888
7897
|
const duration = Date.now() - startTime;
|
|
7889
|
-
log$
|
|
7898
|
+
log$3.info('CACHE HIT:', {
|
|
7890
7899
|
url,
|
|
7891
7900
|
cacheKey,
|
|
7892
7901
|
ageMs: age,
|
|
@@ -7901,7 +7910,7 @@ class CachingHttpDecorator {
|
|
|
7901
7910
|
// If offline and cache is stale, return stale data
|
|
7902
7911
|
if (!this.isOnline()) {
|
|
7903
7912
|
const duration = Date.now() - startTime;
|
|
7904
|
-
log$
|
|
7913
|
+
log$3.info('CACHE STALE (offline):', {
|
|
7905
7914
|
url,
|
|
7906
7915
|
cacheKey,
|
|
7907
7916
|
ageMs: age,
|
|
@@ -7913,25 +7922,25 @@ class CachingHttpDecorator {
|
|
|
7913
7922
|
headers: { 'x-cache': 'STALE' },
|
|
7914
7923
|
};
|
|
7915
7924
|
}
|
|
7916
|
-
log$
|
|
7925
|
+
log$3.debug('Cache expired, fetching fresh data:', { cacheKey, ageMs: age, ttlMs: ttl });
|
|
7917
7926
|
}
|
|
7918
7927
|
// Fetch fresh data
|
|
7919
7928
|
try {
|
|
7920
|
-
log$
|
|
7929
|
+
log$3.debug('Fetching from network:', { url });
|
|
7921
7930
|
const response = await this.http.get(url, config);
|
|
7922
7931
|
// Cache the response
|
|
7923
7932
|
try {
|
|
7924
7933
|
await this.cache.set(cacheKey, response.data);
|
|
7925
|
-
log$
|
|
7934
|
+
log$3.debug('Response cached successfully:', { cacheKey });
|
|
7926
7935
|
}
|
|
7927
7936
|
catch (error) {
|
|
7928
|
-
log$
|
|
7937
|
+
log$3.error('Failed to cache response:', {
|
|
7929
7938
|
cacheKey,
|
|
7930
7939
|
error: error instanceof Error ? error.message : error,
|
|
7931
7940
|
});
|
|
7932
7941
|
}
|
|
7933
7942
|
const duration = Date.now() - startTime;
|
|
7934
|
-
log$
|
|
7943
|
+
log$3.info('CACHE MISS (fetched fresh):', {
|
|
7935
7944
|
url,
|
|
7936
7945
|
cacheKey,
|
|
7937
7946
|
status: response.status,
|
|
@@ -7946,7 +7955,7 @@ class CachingHttpDecorator {
|
|
|
7946
7955
|
// On error, return stale cache if available
|
|
7947
7956
|
if (cached) {
|
|
7948
7957
|
const duration = Date.now() - startTime;
|
|
7949
|
-
log$
|
|
7958
|
+
log$3.warn('CACHE STALE (network error):', {
|
|
7950
7959
|
url,
|
|
7951
7960
|
cacheKey,
|
|
7952
7961
|
error: error instanceof Error ? error.message : 'Unknown error',
|
|
@@ -7958,7 +7967,7 @@ class CachingHttpDecorator {
|
|
|
7958
7967
|
headers: { 'x-cache': 'STALE' },
|
|
7959
7968
|
};
|
|
7960
7969
|
}
|
|
7961
|
-
log$
|
|
7970
|
+
log$3.error('Network error with no cache fallback:', {
|
|
7962
7971
|
url,
|
|
7963
7972
|
error: error instanceof Error ? error.message : error,
|
|
7964
7973
|
});
|
|
@@ -7966,31 +7975,31 @@ class CachingHttpDecorator {
|
|
|
7966
7975
|
}
|
|
7967
7976
|
}
|
|
7968
7977
|
async post(url, data, config) {
|
|
7969
|
-
log$
|
|
7978
|
+
log$3.info('POST request:', { url });
|
|
7970
7979
|
const response = await this.http.post(url, data, config);
|
|
7971
7980
|
await this.invalidateRelated(url, 'POST');
|
|
7972
7981
|
return response;
|
|
7973
7982
|
}
|
|
7974
7983
|
async put(url, data, config) {
|
|
7975
|
-
log$
|
|
7984
|
+
log$3.info('PUT request:', { url });
|
|
7976
7985
|
const response = await this.http.put(url, data, config);
|
|
7977
7986
|
await this.invalidateRelated(url, 'PUT');
|
|
7978
7987
|
return response;
|
|
7979
7988
|
}
|
|
7980
7989
|
async patch(url, data, config) {
|
|
7981
|
-
log$
|
|
7990
|
+
log$3.info('PATCH request:', { url });
|
|
7982
7991
|
const response = await this.http.patch(url, data, config);
|
|
7983
7992
|
await this.invalidateRelated(url, 'PATCH');
|
|
7984
7993
|
return response;
|
|
7985
7994
|
}
|
|
7986
7995
|
async delete(url, config) {
|
|
7987
|
-
log$
|
|
7996
|
+
log$3.info('DELETE request:', { url });
|
|
7988
7997
|
const response = await this.http.delete(url, config);
|
|
7989
7998
|
await this.invalidateRelated(url, 'DELETE');
|
|
7990
7999
|
return response;
|
|
7991
8000
|
}
|
|
7992
8001
|
setAuthToken(token) {
|
|
7993
|
-
log$
|
|
8002
|
+
log$3.debug('CachingHttpDecorator.setAuthToken called:', {
|
|
7994
8003
|
hasToken: !!token,
|
|
7995
8004
|
tokenPrefix: token?.substring(0, 20),
|
|
7996
8005
|
underlyingHttpType: this.http.constructor.name,
|
|
@@ -8004,17 +8013,17 @@ class CachingHttpDecorator {
|
|
|
8004
8013
|
async invalidateRelated(url, method) {
|
|
8005
8014
|
const patterns = this.keyGenerator.getInvalidationPatterns(url, method);
|
|
8006
8015
|
if (patterns.length === 0) {
|
|
8007
|
-
log$
|
|
8016
|
+
log$3.debug('No cache patterns to invalidate:', { url, method });
|
|
8008
8017
|
return;
|
|
8009
8018
|
}
|
|
8010
|
-
log$
|
|
8019
|
+
log$3.info('Invalidating cache patterns:', { url, method, patterns });
|
|
8011
8020
|
for (const pattern of patterns) {
|
|
8012
8021
|
try {
|
|
8013
8022
|
await this.cache.invalidate(pattern);
|
|
8014
|
-
log$
|
|
8023
|
+
log$3.debug('Cache pattern invalidated:', { pattern });
|
|
8015
8024
|
}
|
|
8016
8025
|
catch (error) {
|
|
8017
|
-
log$
|
|
8026
|
+
log$3.error('Failed to invalidate pattern:', {
|
|
8018
8027
|
pattern,
|
|
8019
8028
|
error: error instanceof Error ? error.message : error,
|
|
8020
8029
|
});
|
|
@@ -8022,7 +8031,7 @@ class CachingHttpDecorator {
|
|
|
8022
8031
|
}
|
|
8023
8032
|
}
|
|
8024
8033
|
destroy() {
|
|
8025
|
-
log$
|
|
8034
|
+
log$3.debug('CachingHttpDecorator destroyed');
|
|
8026
8035
|
this.networkSubscription?.unsubscribe();
|
|
8027
8036
|
}
|
|
8028
8037
|
}
|
|
@@ -8398,7 +8407,7 @@ class SDKFactory {
|
|
|
8398
8407
|
}
|
|
8399
8408
|
}
|
|
8400
8409
|
|
|
8401
|
-
const log$
|
|
8410
|
+
const log$2 = createPrefixedLogger('SDK');
|
|
8402
8411
|
class ACubeSDK {
|
|
8403
8412
|
constructor(config, customAdapters, events = {}) {
|
|
8404
8413
|
this.events = events;
|
|
@@ -8412,22 +8421,22 @@ class ACubeSDK {
|
|
|
8412
8421
|
}
|
|
8413
8422
|
async initialize() {
|
|
8414
8423
|
if (this.isInitialized) {
|
|
8415
|
-
log$
|
|
8424
|
+
log$2.debug('SDK already initialized, skipping');
|
|
8416
8425
|
return;
|
|
8417
8426
|
}
|
|
8418
|
-
log$
|
|
8427
|
+
log$2.info('Initializing SDK', {
|
|
8419
8428
|
apiUrl: this.config.getApiUrl(),
|
|
8420
8429
|
authUrl: this.config.getAuthUrl(),
|
|
8421
8430
|
debugEnabled: this.config.isDebugEnabled(),
|
|
8422
8431
|
});
|
|
8423
8432
|
try {
|
|
8424
8433
|
if (!this.adapters) {
|
|
8425
|
-
log$
|
|
8434
|
+
log$2.debug('Loading platform adapters');
|
|
8426
8435
|
const mtlsConfig = createACubeMTLSConfig(this.config.getApiUrl(), this.config.getTimeout(), true);
|
|
8427
8436
|
this.adapters = loadPlatformAdapters({
|
|
8428
8437
|
mtlsConfig,
|
|
8429
8438
|
});
|
|
8430
|
-
log$
|
|
8439
|
+
log$2.info('Platform adapters loaded', {
|
|
8431
8440
|
hasCache: !!this.adapters.cache,
|
|
8432
8441
|
hasNetworkMonitor: !!this.adapters.networkMonitor,
|
|
8433
8442
|
hasMtls: !!this.adapters.mtls,
|
|
@@ -8440,30 +8449,30 @@ class ACubeSDK {
|
|
|
8440
8449
|
timeout: this.config.getTimeout(),
|
|
8441
8450
|
debugEnabled: this.config.isDebugEnabled(),
|
|
8442
8451
|
};
|
|
8443
|
-
log$
|
|
8452
|
+
log$2.debug('Creating DI container');
|
|
8444
8453
|
this.container = SDKFactory.createContainer(factoryConfig);
|
|
8445
|
-
log$
|
|
8454
|
+
log$2.debug('Registering auth services');
|
|
8446
8455
|
SDKFactory.registerAuthServices(this.container, this.adapters.secureStorage, factoryConfig);
|
|
8447
8456
|
if (this.adapters.cache) {
|
|
8448
|
-
log$
|
|
8457
|
+
log$2.info('Registering cache services', {
|
|
8449
8458
|
hasNetworkMonitor: !!this.adapters.networkMonitor,
|
|
8450
8459
|
});
|
|
8451
8460
|
SDKFactory.registerCacheServices(this.container, this.adapters.cache, this.adapters.networkMonitor);
|
|
8452
8461
|
}
|
|
8453
8462
|
else {
|
|
8454
|
-
log$
|
|
8463
|
+
log$2.debug('No cache adapter available, caching disabled');
|
|
8455
8464
|
}
|
|
8456
|
-
log$
|
|
8465
|
+
log$2.debug('Initializing certificate service');
|
|
8457
8466
|
this.certificateService = new CertificateService(this.adapters.secureStorage);
|
|
8458
8467
|
const tokenStorage = this.container.get(DI_TOKENS.TOKEN_STORAGE_PORT);
|
|
8459
8468
|
const httpPort = this.container.get(DI_TOKENS.HTTP_PORT);
|
|
8460
8469
|
const baseHttpPort = this.container.get(DI_TOKENS.BASE_HTTP_PORT);
|
|
8461
|
-
log$
|
|
8470
|
+
log$2.debug('HTTP ports initialized', {
|
|
8462
8471
|
httpPortType: httpPort.constructor.name,
|
|
8463
8472
|
baseHttpPortType: baseHttpPort.constructor.name,
|
|
8464
8473
|
areSameInstance: httpPort === baseHttpPort,
|
|
8465
8474
|
});
|
|
8466
|
-
log$
|
|
8475
|
+
log$2.debug('Initializing authentication service');
|
|
8467
8476
|
this.authService = new AuthenticationService(httpPort, tokenStorage, {
|
|
8468
8477
|
authUrl: this.config.getAuthUrl(),
|
|
8469
8478
|
timeout: this.config.getTimeout(),
|
|
@@ -8473,7 +8482,7 @@ class ACubeSDK {
|
|
|
8473
8482
|
this.events.onAuthError?.(new ACubeSDKError('AUTH_ERROR', error.message, error));
|
|
8474
8483
|
},
|
|
8475
8484
|
});
|
|
8476
|
-
log$
|
|
8485
|
+
log$2.debug('Initializing offline manager');
|
|
8477
8486
|
const queueEvents = {
|
|
8478
8487
|
onOperationAdded: (operation) => {
|
|
8479
8488
|
this.events.onOfflineOperationAdded?.(operation.id);
|
|
@@ -8496,28 +8505,28 @@ class ACubeSDK {
|
|
|
8496
8505
|
}
|
|
8497
8506
|
});
|
|
8498
8507
|
const isAuth = await this.authService.isAuthenticated();
|
|
8499
|
-
log$
|
|
8508
|
+
log$2.debug('Checking authentication status during init', { isAuthenticated: isAuth });
|
|
8500
8509
|
if (isAuth) {
|
|
8501
8510
|
const token = await this.authService.getAccessToken();
|
|
8502
|
-
log$
|
|
8511
|
+
log$2.debug('Token retrieved during init', {
|
|
8503
8512
|
hasToken: !!token,
|
|
8504
8513
|
tokenPrefix: token?.substring(0, 20),
|
|
8505
8514
|
});
|
|
8506
8515
|
if (token) {
|
|
8507
8516
|
httpPort.setAuthToken(token);
|
|
8508
|
-
log$
|
|
8517
|
+
log$2.info('Auth token set on HTTP port during initialization');
|
|
8509
8518
|
}
|
|
8510
8519
|
}
|
|
8511
8520
|
else {
|
|
8512
|
-
log$
|
|
8521
|
+
log$2.warn('User not authenticated during SDK init - token will be set after login');
|
|
8513
8522
|
}
|
|
8514
8523
|
if (this.adapters?.mtls && 'setMTLSAdapter' in baseHttpPort) {
|
|
8515
|
-
log$
|
|
8524
|
+
log$2.debug('Connecting mTLS adapter to HTTP port');
|
|
8516
8525
|
const httpWithMtls = baseHttpPort;
|
|
8517
8526
|
httpWithMtls.setMTLSAdapter(this.adapters.mtls);
|
|
8518
8527
|
}
|
|
8519
8528
|
if ('setAuthStrategy' in baseHttpPort) {
|
|
8520
|
-
log$
|
|
8529
|
+
log$2.debug('Configuring auth strategy');
|
|
8521
8530
|
const jwtHandler = new JwtAuthHandler(tokenStorage);
|
|
8522
8531
|
const certificatePort = this.certificateService
|
|
8523
8532
|
? {
|
|
@@ -8566,19 +8575,19 @@ class ACubeSDK {
|
|
|
8566
8575
|
}
|
|
8567
8576
|
}
|
|
8568
8577
|
catch (certError) {
|
|
8569
|
-
log$
|
|
8578
|
+
log$2.warn('Certificate auto-configuration failed, will retry on demand', {
|
|
8570
8579
|
error: certError instanceof Error ? certError.message : certError,
|
|
8571
8580
|
});
|
|
8572
8581
|
}
|
|
8573
8582
|
}
|
|
8574
8583
|
this.isInitialized = true;
|
|
8575
|
-
log$
|
|
8584
|
+
log$2.info('SDK initialized successfully', {
|
|
8576
8585
|
hasCache: !!this.adapters.cache,
|
|
8577
8586
|
hasMtls: !!this.adapters.mtls,
|
|
8578
8587
|
});
|
|
8579
8588
|
}
|
|
8580
8589
|
catch (error) {
|
|
8581
|
-
log$
|
|
8590
|
+
log$2.error('SDK initialization failed', {
|
|
8582
8591
|
error: error instanceof Error ? error.message : error,
|
|
8583
8592
|
});
|
|
8584
8593
|
throw new ACubeSDKError('SDK_INITIALIZATION_ERROR', `Failed to initialize SDK: ${error instanceof Error ? error.message : 'Unknown error'}`, error);
|
|
@@ -8634,19 +8643,19 @@ class ACubeSDK {
|
|
|
8634
8643
|
}
|
|
8635
8644
|
async login(credentials) {
|
|
8636
8645
|
this.ensureInitialized();
|
|
8637
|
-
log$
|
|
8646
|
+
log$2.info('Login attempt', { email: credentials.email });
|
|
8638
8647
|
const user = await this.authService.login(credentials);
|
|
8639
|
-
log$
|
|
8648
|
+
log$2.info('Login successful', { roles: user.roles });
|
|
8640
8649
|
const token = await this.authService.getAccessToken();
|
|
8641
8650
|
if (token) {
|
|
8642
8651
|
this.httpPort.setAuthToken(token);
|
|
8643
|
-
log$
|
|
8652
|
+
log$2.debug('Auth token set on HTTP port');
|
|
8644
8653
|
}
|
|
8645
8654
|
return user;
|
|
8646
8655
|
}
|
|
8647
8656
|
async logout() {
|
|
8648
8657
|
this.ensureInitialized();
|
|
8649
|
-
log$
|
|
8658
|
+
log$2.info('Logout');
|
|
8650
8659
|
await this.authService.logout();
|
|
8651
8660
|
this.httpPort.setAuthToken(null);
|
|
8652
8661
|
}
|
|
@@ -8820,6 +8829,7 @@ const INITIAL_STATE = {
|
|
|
8820
8829
|
remainingMs: 0,
|
|
8821
8830
|
},
|
|
8822
8831
|
lastNotification: null,
|
|
8832
|
+
certificateMissing: false,
|
|
8823
8833
|
};
|
|
8824
8834
|
class AppStateService {
|
|
8825
8835
|
get state$() {
|
|
@@ -8834,28 +8844,33 @@ class AppStateService {
|
|
|
8834
8844
|
get warning$() {
|
|
8835
8845
|
return this.state$.pipe(map((s) => s.warning), distinctUntilChanged((a, b) => a.active === b.active && a.remainingMs === b.remainingMs));
|
|
8836
8846
|
}
|
|
8837
|
-
|
|
8847
|
+
get certificateMissing$() {
|
|
8848
|
+
return this.state$.pipe(map((s) => s.certificateMissing), distinctUntilChanged());
|
|
8849
|
+
}
|
|
8850
|
+
constructor(notifications$, networkPort, certificateMissingInput$ = of(false)) {
|
|
8838
8851
|
this.notifications$ = notifications$;
|
|
8839
8852
|
this.networkPort = networkPort;
|
|
8853
|
+
this.certificateMissingInput$ = certificateMissingInput$;
|
|
8840
8854
|
this.stateSubject = new BehaviorSubject(INITIAL_STATE);
|
|
8841
8855
|
this.destroy$ = new Subject();
|
|
8842
8856
|
this.warningTimerId = null;
|
|
8843
8857
|
this.setupSubscriptions();
|
|
8844
8858
|
}
|
|
8845
8859
|
setupSubscriptions() {
|
|
8846
|
-
combineLatest([this.notifications$, this.networkPort.online$])
|
|
8860
|
+
combineLatest([this.notifications$, this.networkPort.online$, this.certificateMissingInput$])
|
|
8847
8861
|
.pipe(takeUntil(this.destroy$))
|
|
8848
|
-
.subscribe(([notifications, isOnline]) => {
|
|
8849
|
-
this.processState(notifications, isOnline);
|
|
8862
|
+
.subscribe(([notifications, isOnline, certificateMissing]) => {
|
|
8863
|
+
this.processState(notifications, isOnline, certificateMissing);
|
|
8850
8864
|
});
|
|
8851
8865
|
}
|
|
8852
|
-
processState(notifications, isOnline) {
|
|
8866
|
+
processState(notifications, isOnline, certificateMissing) {
|
|
8853
8867
|
if (!isOnline) {
|
|
8854
8868
|
this.updateState({
|
|
8855
8869
|
mode: 'OFFLINE',
|
|
8856
8870
|
isOnline: false,
|
|
8857
8871
|
warning: { active: false, blockAt: null, remainingMs: 0 },
|
|
8858
8872
|
lastNotification: null,
|
|
8873
|
+
certificateMissing,
|
|
8859
8874
|
});
|
|
8860
8875
|
this.stopWarningTimer();
|
|
8861
8876
|
return;
|
|
@@ -8917,6 +8932,7 @@ class AppStateService {
|
|
|
8917
8932
|
isOnline: true,
|
|
8918
8933
|
warning: warningState,
|
|
8919
8934
|
lastNotification,
|
|
8935
|
+
certificateMissing,
|
|
8920
8936
|
});
|
|
8921
8937
|
}
|
|
8922
8938
|
getLatestNotificationByCode(notifications) {
|
|
@@ -9182,6 +9198,7 @@ class TelemetryService {
|
|
|
9182
9198
|
}
|
|
9183
9199
|
}
|
|
9184
9200
|
|
|
9201
|
+
const log$1 = createPrefixedLogger('SDK-MANAGER');
|
|
9185
9202
|
/**
|
|
9186
9203
|
* SDKManager - Singleton wrapper for ACubeSDK with simplified API
|
|
9187
9204
|
*
|
|
@@ -9232,6 +9249,7 @@ class SDKManager {
|
|
|
9232
9249
|
this.appStateService = null;
|
|
9233
9250
|
this.isInitialized = false;
|
|
9234
9251
|
this.isPollingActive = false;
|
|
9252
|
+
this.certificateMissingSubject = new BehaviorSubject(false);
|
|
9235
9253
|
/**
|
|
9236
9254
|
* Handle user state changes (login/logout/token expiration)
|
|
9237
9255
|
* Manages polling lifecycle based on user role
|
|
@@ -9242,9 +9260,14 @@ class SDKManager {
|
|
|
9242
9260
|
if (!this.isInitialized)
|
|
9243
9261
|
return;
|
|
9244
9262
|
if (user) {
|
|
9245
|
-
// User logged in - check role and
|
|
9263
|
+
// User logged in - check role and certificate before starting polling
|
|
9246
9264
|
const canPoll = hasAnyRole(user.roles, ['ROLE_MERCHANT', 'ROLE_CASHIER']);
|
|
9247
9265
|
if (canPoll && !this.isPollingActive) {
|
|
9266
|
+
const hasCert = await this.checkCertificate();
|
|
9267
|
+
if (!hasCert) {
|
|
9268
|
+
log$1.warn('Certificate missing — polling blocked until certificate is installed');
|
|
9269
|
+
return;
|
|
9270
|
+
}
|
|
9248
9271
|
this.notificationService?.startPolling();
|
|
9249
9272
|
await this.startTelemetryPollingAuto();
|
|
9250
9273
|
this.isPollingActive = true;
|
|
@@ -9258,6 +9281,7 @@ class SDKManager {
|
|
|
9258
9281
|
this.telemetryService?.clearTelemetry();
|
|
9259
9282
|
this.isPollingActive = false;
|
|
9260
9283
|
}
|
|
9284
|
+
this.certificateMissingSubject.next(false);
|
|
9261
9285
|
}
|
|
9262
9286
|
};
|
|
9263
9287
|
}
|
|
@@ -9324,7 +9348,7 @@ class SDKManager {
|
|
|
9324
9348
|
this.telemetryService = new TelemetryService(telemetryRepo, networkPort, {
|
|
9325
9349
|
pollIntervalMs: this.config.telemetryPollIntervalMs ?? 60000,
|
|
9326
9350
|
});
|
|
9327
|
-
this.appStateService = new AppStateService(this.notificationService.notifications$, networkPort);
|
|
9351
|
+
this.appStateService = new AppStateService(this.notificationService.notifications$, networkPort, this.certificateMissingSubject.asObservable());
|
|
9328
9352
|
if (this.events?.onAppStateChanged) {
|
|
9329
9353
|
this.appStateService.state$.subscribe(this.events.onAppStateChanged);
|
|
9330
9354
|
}
|
|
@@ -9336,9 +9360,15 @@ class SDKManager {
|
|
|
9336
9360
|
const user = await this.sdk.getCurrentUser();
|
|
9337
9361
|
const canPoll = user && hasAnyRole(user.roles, ['ROLE_MERCHANT', 'ROLE_CASHIER']);
|
|
9338
9362
|
if (canPoll) {
|
|
9339
|
-
this.
|
|
9340
|
-
|
|
9341
|
-
|
|
9363
|
+
const hasCert = await this.checkCertificate();
|
|
9364
|
+
if (hasCert) {
|
|
9365
|
+
this.notificationService.startPolling();
|
|
9366
|
+
await this.startTelemetryPollingAuto();
|
|
9367
|
+
this.isPollingActive = true;
|
|
9368
|
+
}
|
|
9369
|
+
else {
|
|
9370
|
+
log$1.warn('Certificate missing at init — polling blocked until certificate is installed');
|
|
9371
|
+
}
|
|
9342
9372
|
}
|
|
9343
9373
|
// AppStateService remains active for all users (handles OFFLINE network state)
|
|
9344
9374
|
}
|
|
@@ -9372,6 +9402,13 @@ class SDKManager {
|
|
|
9372
9402
|
this.ensureInitialized();
|
|
9373
9403
|
return this.appStateService.warning$;
|
|
9374
9404
|
}
|
|
9405
|
+
/**
|
|
9406
|
+
* Observable stream indicating if certificate is missing
|
|
9407
|
+
* When true, polling is blocked and the user should install a certificate
|
|
9408
|
+
*/
|
|
9409
|
+
get certificateMissing$() {
|
|
9410
|
+
return this.certificateMissingSubject.asObservable();
|
|
9411
|
+
}
|
|
9375
9412
|
/**
|
|
9376
9413
|
* Observable stream of telemetry state (data, isLoading, isCached, error)
|
|
9377
9414
|
*/
|
|
@@ -9449,9 +9486,37 @@ class SDKManager {
|
|
|
9449
9486
|
logout: () => sdk.logout(),
|
|
9450
9487
|
getCurrentUser: () => sdk.getCurrentUser(),
|
|
9451
9488
|
isAuthenticated: () => sdk.isAuthenticated(),
|
|
9452
|
-
storeCertificate: (certificate, privateKey, options) =>
|
|
9489
|
+
storeCertificate: async (certificate, privateKey, options) => {
|
|
9490
|
+
await sdk.storeCertificate(certificate, privateKey, options);
|
|
9491
|
+
this.certificateMissingSubject.next(false);
|
|
9492
|
+
// Start polling if user can poll and polling is not active
|
|
9493
|
+
if (!this.isPollingActive) {
|
|
9494
|
+
const user = await sdk.getCurrentUser();
|
|
9495
|
+
const canPoll = user && hasAnyRole(user.roles, ['ROLE_MERCHANT', 'ROLE_CASHIER']);
|
|
9496
|
+
if (canPoll) {
|
|
9497
|
+
log$1.info('Certificate installed — starting polling');
|
|
9498
|
+
this.notificationService?.startPolling();
|
|
9499
|
+
await this.startTelemetryPollingAuto();
|
|
9500
|
+
this.isPollingActive = true;
|
|
9501
|
+
}
|
|
9502
|
+
}
|
|
9503
|
+
},
|
|
9453
9504
|
hasCertificate: () => sdk.hasCertificate(),
|
|
9454
|
-
clearCertificate: () =>
|
|
9505
|
+
clearCertificate: async () => {
|
|
9506
|
+
await sdk.clearCertificate();
|
|
9507
|
+
this.certificateMissingSubject.next(true);
|
|
9508
|
+
// Stop polling since certificate is required
|
|
9509
|
+
if (this.isPollingActive) {
|
|
9510
|
+
log$1.info('Certificate removed — stopping polling');
|
|
9511
|
+
this.notificationService?.stopPolling();
|
|
9512
|
+
this.telemetryService?.stopPolling();
|
|
9513
|
+
this.telemetryService?.clearTelemetry();
|
|
9514
|
+
this.isPollingActive = false;
|
|
9515
|
+
}
|
|
9516
|
+
},
|
|
9517
|
+
getCertificate: () => sdk.getCertificate(),
|
|
9518
|
+
getCertificatesInfo: () => sdk.getCertificatesInfo(),
|
|
9519
|
+
notifications: sdk.notifications,
|
|
9455
9520
|
isOnline: () => sdk.isOnline(),
|
|
9456
9521
|
};
|
|
9457
9522
|
}
|
|
@@ -9482,6 +9547,21 @@ class SDKManager {
|
|
|
9482
9547
|
this.ensureInitialized();
|
|
9483
9548
|
return this.sdk;
|
|
9484
9549
|
}
|
|
9550
|
+
/**
|
|
9551
|
+
* Check certificate availability and update certificateMissing state.
|
|
9552
|
+
* Returns true if certificate is available, false otherwise.
|
|
9553
|
+
*/
|
|
9554
|
+
async checkCertificate() {
|
|
9555
|
+
try {
|
|
9556
|
+
const hasCert = await this.sdk.hasCertificate();
|
|
9557
|
+
this.certificateMissingSubject.next(!hasCert);
|
|
9558
|
+
return hasCert;
|
|
9559
|
+
}
|
|
9560
|
+
catch {
|
|
9561
|
+
this.certificateMissingSubject.next(true);
|
|
9562
|
+
return false;
|
|
9563
|
+
}
|
|
9564
|
+
}
|
|
9485
9565
|
cleanup() {
|
|
9486
9566
|
this.notificationService?.destroy();
|
|
9487
9567
|
this.telemetryService?.destroy();
|