@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.cjs.js
CHANGED
|
@@ -1415,6 +1415,15 @@ function from(input, scheduler) {
|
|
|
1415
1415
|
return scheduler ? scheduled(input, scheduler) : innerFrom(input);
|
|
1416
1416
|
}
|
|
1417
1417
|
|
|
1418
|
+
function of() {
|
|
1419
|
+
var args = [];
|
|
1420
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
1421
|
+
args[_i] = arguments[_i];
|
|
1422
|
+
}
|
|
1423
|
+
var scheduler = popScheduler(args);
|
|
1424
|
+
return from(args, scheduler);
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1418
1427
|
function isValidDate(value) {
|
|
1419
1428
|
return value instanceof Date && !isNaN(value);
|
|
1420
1429
|
}
|
|
@@ -2126,7 +2135,7 @@ function formatDecimal(value, decimals = 2) {
|
|
|
2126
2135
|
return num.toFixed(decimals);
|
|
2127
2136
|
}
|
|
2128
2137
|
|
|
2129
|
-
const log$
|
|
2138
|
+
const log$h = createPrefixedLogger('AUTH-SERVICE');
|
|
2130
2139
|
class AuthenticationService {
|
|
2131
2140
|
get user$() {
|
|
2132
2141
|
return this.userSubject.asObservable();
|
|
@@ -2148,7 +2157,7 @@ class AuthenticationService {
|
|
|
2148
2157
|
}
|
|
2149
2158
|
async login(credentials) {
|
|
2150
2159
|
this.authStateSubject.next('authenticating');
|
|
2151
|
-
log$
|
|
2160
|
+
log$h.info('Login attempt', {
|
|
2152
2161
|
authUrl: this.config.authUrl,
|
|
2153
2162
|
email: credentials.email,
|
|
2154
2163
|
});
|
|
@@ -2159,7 +2168,7 @@ class AuthenticationService {
|
|
|
2159
2168
|
});
|
|
2160
2169
|
const jwtPayload = parseJwt(response.data.token);
|
|
2161
2170
|
const expiresAt = jwtPayload.exp * 1000;
|
|
2162
|
-
log$
|
|
2171
|
+
log$h.info('Login successful', {
|
|
2163
2172
|
authUrl: this.config.authUrl,
|
|
2164
2173
|
tokenPrefix: response.data.token.substring(0, 30) + '...',
|
|
2165
2174
|
expiresAt: new Date(expiresAt).toISOString(),
|
|
@@ -2188,21 +2197,21 @@ class AuthenticationService {
|
|
|
2188
2197
|
const token = await this.tokenStorage.getAccessToken();
|
|
2189
2198
|
if (!token) {
|
|
2190
2199
|
// No token - clear any stale user state
|
|
2191
|
-
log$
|
|
2200
|
+
log$h.debug('getCurrentUser: No token in storage');
|
|
2192
2201
|
if (this.userSubject.value) {
|
|
2193
2202
|
this.userSubject.next(null);
|
|
2194
2203
|
this.authStateSubject.next('idle');
|
|
2195
2204
|
}
|
|
2196
2205
|
return null;
|
|
2197
2206
|
}
|
|
2198
|
-
log$
|
|
2207
|
+
log$h.debug('getCurrentUser: Token found', {
|
|
2199
2208
|
tokenPrefix: token.substring(0, 30) + '...',
|
|
2200
2209
|
tokenLength: token.length,
|
|
2201
2210
|
});
|
|
2202
2211
|
const jwtPayload = parseJwt(token);
|
|
2203
2212
|
if (isTokenExpired(jwtPayload)) {
|
|
2204
2213
|
// Token expired - clear everything
|
|
2205
|
-
log$
|
|
2214
|
+
log$h.warn('getCurrentUser: Token expired');
|
|
2206
2215
|
await this.tokenStorage.clearTokens();
|
|
2207
2216
|
this.userSubject.next(null);
|
|
2208
2217
|
this.authStateSubject.next('idle');
|
|
@@ -2212,7 +2221,7 @@ class AuthenticationService {
|
|
|
2212
2221
|
// Token is valid - return cached user if available
|
|
2213
2222
|
const currentUser = this.userSubject.value;
|
|
2214
2223
|
if (currentUser) {
|
|
2215
|
-
log$
|
|
2224
|
+
log$h.debug('getCurrentUser: Returning cached user', {
|
|
2216
2225
|
email: currentUser.email,
|
|
2217
2226
|
roles: currentUser.roles,
|
|
2218
2227
|
});
|
|
@@ -2235,12 +2244,12 @@ class AuthenticationService {
|
|
|
2235
2244
|
async isAuthenticated() {
|
|
2236
2245
|
const token = await this.tokenStorage.getAccessToken();
|
|
2237
2246
|
if (!token) {
|
|
2238
|
-
log$
|
|
2247
|
+
log$h.debug('isAuthenticated: No token in storage');
|
|
2239
2248
|
return false;
|
|
2240
2249
|
}
|
|
2241
2250
|
const jwtPayload = parseJwt(token);
|
|
2242
2251
|
const expired = isTokenExpired(jwtPayload);
|
|
2243
|
-
log$
|
|
2252
|
+
log$h.debug('isAuthenticated: Token check', {
|
|
2244
2253
|
hasToken: true,
|
|
2245
2254
|
expired,
|
|
2246
2255
|
expiresAt: new Date(jwtPayload.exp * 1000).toISOString(),
|
|
@@ -2912,7 +2921,7 @@ class ACubeSDKError extends Error {
|
|
|
2912
2921
|
}
|
|
2913
2922
|
}
|
|
2914
2923
|
|
|
2915
|
-
const log$
|
|
2924
|
+
const log$g = createPrefixedLogger('AUTH-STRATEGY');
|
|
2916
2925
|
class AuthStrategy {
|
|
2917
2926
|
constructor(jwtHandler, mtlsHandler, userProvider, mtlsAdapter) {
|
|
2918
2927
|
this.jwtHandler = jwtHandler;
|
|
@@ -2927,7 +2936,7 @@ class AuthStrategy {
|
|
|
2927
2936
|
const platform = this.detectPlatform();
|
|
2928
2937
|
const userRole = await this.getUserRole();
|
|
2929
2938
|
const isReceiptEndpoint = this.isReceiptEndpoint(url);
|
|
2930
|
-
log$
|
|
2939
|
+
log$g.debug('Determining auth config', {
|
|
2931
2940
|
url,
|
|
2932
2941
|
method,
|
|
2933
2942
|
platform,
|
|
@@ -3058,7 +3067,7 @@ class JwtAuthHandler {
|
|
|
3058
3067
|
}
|
|
3059
3068
|
}
|
|
3060
3069
|
|
|
3061
|
-
const log$
|
|
3070
|
+
const log$f = createPrefixedLogger('MTLS-HANDLER');
|
|
3062
3071
|
class MtlsAuthHandler {
|
|
3063
3072
|
constructor(mtlsAdapter, certificatePort) {
|
|
3064
3073
|
this.mtlsAdapter = mtlsAdapter;
|
|
@@ -3100,7 +3109,7 @@ class MtlsAuthHandler {
|
|
|
3100
3109
|
async makeRequest(url, config, jwtToken) {
|
|
3101
3110
|
const requestKey = this.generateRequestKey(url, config, jwtToken);
|
|
3102
3111
|
if (this.pendingRequests.has(requestKey)) {
|
|
3103
|
-
log$
|
|
3112
|
+
log$f.debug('Deduplicating concurrent request:', url);
|
|
3104
3113
|
return this.pendingRequests.get(requestKey);
|
|
3105
3114
|
}
|
|
3106
3115
|
const requestPromise = this.executeRequest(url, config, jwtToken, false);
|
|
@@ -3124,10 +3133,10 @@ class MtlsAuthHandler {
|
|
|
3124
3133
|
};
|
|
3125
3134
|
if (jwtToken) {
|
|
3126
3135
|
headers['Authorization'] = `Bearer ${jwtToken}`;
|
|
3127
|
-
log$
|
|
3136
|
+
log$f.debug('JWT token present:', jwtToken.substring(0, 20) + '...');
|
|
3128
3137
|
}
|
|
3129
3138
|
else {
|
|
3130
|
-
log$
|
|
3139
|
+
log$f.warn('No JWT token provided for mTLS request');
|
|
3131
3140
|
}
|
|
3132
3141
|
const fullUrl = this.constructMtlsUrl(url);
|
|
3133
3142
|
const mtlsConfig = {
|
|
@@ -3138,25 +3147,25 @@ class MtlsAuthHandler {
|
|
|
3138
3147
|
timeout: config.timeout,
|
|
3139
3148
|
responseType: config.responseType,
|
|
3140
3149
|
};
|
|
3141
|
-
log$
|
|
3142
|
-
log$
|
|
3150
|
+
log$f.debug('header-mtls', headers);
|
|
3151
|
+
log$f.debug(`${config.method} ${fullUrl}`);
|
|
3143
3152
|
if (config.data) {
|
|
3144
|
-
log$
|
|
3153
|
+
log$f.debug('Request body:', config.data);
|
|
3145
3154
|
}
|
|
3146
3155
|
try {
|
|
3147
3156
|
const response = await this.mtlsAdapter.request(mtlsConfig);
|
|
3148
|
-
log$
|
|
3157
|
+
log$f.debug(`Response ${response.status} from ${fullUrl}`);
|
|
3149
3158
|
if (response.data) {
|
|
3150
|
-
log$
|
|
3159
|
+
log$f.debug('Response body:', response.data);
|
|
3151
3160
|
}
|
|
3152
3161
|
return response.data;
|
|
3153
3162
|
}
|
|
3154
3163
|
catch (error) {
|
|
3155
|
-
log$
|
|
3164
|
+
log$f.error(`Response error from ${fullUrl}:`, error);
|
|
3156
3165
|
if (error && typeof error === 'object' && 'response' in error) {
|
|
3157
3166
|
const axiosError = error;
|
|
3158
3167
|
if (axiosError.response?.data) {
|
|
3159
|
-
log$
|
|
3168
|
+
log$f.error('Response body:', axiosError.response.data);
|
|
3160
3169
|
}
|
|
3161
3170
|
}
|
|
3162
3171
|
if (isRetryAttempt) {
|
|
@@ -3166,7 +3175,7 @@ class MtlsAuthHandler {
|
|
|
3166
3175
|
if (!shouldRetry) {
|
|
3167
3176
|
throw error;
|
|
3168
3177
|
}
|
|
3169
|
-
log$
|
|
3178
|
+
log$f.debug('Request failed, reconfiguring certificate and retrying...');
|
|
3170
3179
|
try {
|
|
3171
3180
|
await this.configureCertificate(certificate);
|
|
3172
3181
|
return await this.executeRequest(url, config, jwtToken, true);
|
|
@@ -3893,7 +3902,7 @@ function decompressData(data, compressed) {
|
|
|
3893
3902
|
return new CompressionAdapter().decompress(data, compressed);
|
|
3894
3903
|
}
|
|
3895
3904
|
|
|
3896
|
-
const log$
|
|
3905
|
+
const log$e = createPrefixedLogger('CACHE-RN');
|
|
3897
3906
|
/**
|
|
3898
3907
|
* React Native cache adapter using SQLite (Expo or react-native-sqlite-storage)
|
|
3899
3908
|
* Cache never expires - data persists until explicitly invalidated
|
|
@@ -3977,26 +3986,26 @@ class ReactNativeCacheAdapter {
|
|
|
3977
3986
|
await this.runMigrations();
|
|
3978
3987
|
}
|
|
3979
3988
|
async runMigrations() {
|
|
3980
|
-
log$
|
|
3989
|
+
log$e.debug('Running database migrations...');
|
|
3981
3990
|
try {
|
|
3982
3991
|
// Check if compressed column exists
|
|
3983
3992
|
this.hasCompressedColumn = await this.checkColumnExists('compressed');
|
|
3984
3993
|
if (!this.hasCompressedColumn) {
|
|
3985
|
-
log$
|
|
3994
|
+
log$e.debug('Adding compressed column to cache table');
|
|
3986
3995
|
const addColumnSQL = `ALTER TABLE ${ReactNativeCacheAdapter.TABLE_NAME} ADD COLUMN compressed INTEGER DEFAULT 0`;
|
|
3987
3996
|
await this.executeSql(addColumnSQL);
|
|
3988
3997
|
this.hasCompressedColumn = true;
|
|
3989
|
-
log$
|
|
3998
|
+
log$e.debug('Successfully added compressed column');
|
|
3990
3999
|
}
|
|
3991
4000
|
else {
|
|
3992
|
-
log$
|
|
4001
|
+
log$e.debug('Compressed column already exists');
|
|
3993
4002
|
}
|
|
3994
|
-
log$
|
|
4003
|
+
log$e.debug('Database migrations completed', {
|
|
3995
4004
|
hasCompressedColumn: this.hasCompressedColumn,
|
|
3996
4005
|
});
|
|
3997
4006
|
}
|
|
3998
4007
|
catch (error) {
|
|
3999
|
-
log$
|
|
4008
|
+
log$e.debug('Migration failed, disabling compression features', error);
|
|
4000
4009
|
this.hasCompressedColumn = false;
|
|
4001
4010
|
// Don't throw - allow the app to continue even if migration fails
|
|
4002
4011
|
// The compressed feature will just be disabled
|
|
@@ -4007,20 +4016,20 @@ class ReactNativeCacheAdapter {
|
|
|
4007
4016
|
const pragmaSQL = `PRAGMA table_info(${ReactNativeCacheAdapter.TABLE_NAME})`;
|
|
4008
4017
|
const results = await this.executeSql(pragmaSQL);
|
|
4009
4018
|
const columns = this.normalizeResults(results);
|
|
4010
|
-
log$
|
|
4019
|
+
log$e.debug('Table columns found', { columns: columns.map((c) => c.name) });
|
|
4011
4020
|
return columns.some((column) => column.name === columnName);
|
|
4012
4021
|
}
|
|
4013
4022
|
catch (error) {
|
|
4014
|
-
log$
|
|
4023
|
+
log$e.debug('Error checking column existence', error);
|
|
4015
4024
|
return false;
|
|
4016
4025
|
}
|
|
4017
4026
|
}
|
|
4018
4027
|
async get(key) {
|
|
4019
4028
|
await this.ensureInitialized();
|
|
4020
4029
|
const sql = `SELECT * FROM ${ReactNativeCacheAdapter.TABLE_NAME} WHERE cache_key = ?`;
|
|
4021
|
-
log$
|
|
4030
|
+
log$e.debug('Executing get query', { sql, key });
|
|
4022
4031
|
const results = await this.executeSql(sql, [key]);
|
|
4023
|
-
log$
|
|
4032
|
+
log$e.debug('Get query results', { key, hasResults: !!results });
|
|
4024
4033
|
// Normalize results from different SQLite implementations
|
|
4025
4034
|
const rows = this.normalizeResults(results);
|
|
4026
4035
|
if (!rows || rows.length === 0) {
|
|
@@ -4043,7 +4052,7 @@ class ReactNativeCacheAdapter {
|
|
|
4043
4052
|
data,
|
|
4044
4053
|
timestamp: Date.now(),
|
|
4045
4054
|
};
|
|
4046
|
-
log$
|
|
4055
|
+
log$e.debug('Setting cache item', { key });
|
|
4047
4056
|
return this.setItem(key, item);
|
|
4048
4057
|
}
|
|
4049
4058
|
async setItem(key, item) {
|
|
@@ -4056,7 +4065,7 @@ class ReactNativeCacheAdapter {
|
|
|
4056
4065
|
const compressionResult = compressData(serializedData, this.options.compressionThreshold);
|
|
4057
4066
|
finalData = compressionResult.data;
|
|
4058
4067
|
isCompressed = compressionResult.compressed;
|
|
4059
|
-
log$
|
|
4068
|
+
log$e.debug('Compression result', {
|
|
4060
4069
|
key,
|
|
4061
4070
|
originalSize: compressionResult.originalSize,
|
|
4062
4071
|
compressedSize: compressionResult.compressedSize,
|
|
@@ -4064,7 +4073,7 @@ class ReactNativeCacheAdapter {
|
|
|
4064
4073
|
savings: compressionResult.originalSize - compressionResult.compressedSize,
|
|
4065
4074
|
});
|
|
4066
4075
|
}
|
|
4067
|
-
log$
|
|
4076
|
+
log$e.debug('Setting item with metadata', {
|
|
4068
4077
|
key,
|
|
4069
4078
|
timestamp: item.timestamp,
|
|
4070
4079
|
compressed: isCompressed,
|
|
@@ -4090,14 +4099,14 @@ class ReactNativeCacheAdapter {
|
|
|
4090
4099
|
`;
|
|
4091
4100
|
params = [key, finalData, item.timestamp];
|
|
4092
4101
|
}
|
|
4093
|
-
log$
|
|
4102
|
+
log$e.debug('Executing setItem SQL', { key, paramsCount: params.length });
|
|
4094
4103
|
await this.executeSql(sql, params);
|
|
4095
4104
|
}
|
|
4096
4105
|
async setBatch(items) {
|
|
4097
4106
|
if (items.length === 0)
|
|
4098
4107
|
return;
|
|
4099
4108
|
await this.ensureInitialized();
|
|
4100
|
-
log$
|
|
4109
|
+
log$e.debug('Batch setting items', { count: items.length });
|
|
4101
4110
|
if (this.isExpo) {
|
|
4102
4111
|
await this.db.withTransactionAsync(async () => {
|
|
4103
4112
|
for (const [key, item] of items) {
|
|
@@ -4115,7 +4124,7 @@ class ReactNativeCacheAdapter {
|
|
|
4115
4124
|
}, reject, () => resolve());
|
|
4116
4125
|
});
|
|
4117
4126
|
}
|
|
4118
|
-
log$
|
|
4127
|
+
log$e.debug('Batch operation completed', { count: items.length });
|
|
4119
4128
|
}
|
|
4120
4129
|
async setBatchItem(key, item) {
|
|
4121
4130
|
// Handle compression if enabled and compressed column is available
|
|
@@ -4289,10 +4298,10 @@ class MemoryCacheAdapter {
|
|
|
4289
4298
|
return keySize + itemSize;
|
|
4290
4299
|
}
|
|
4291
4300
|
async get(key) {
|
|
4292
|
-
log$
|
|
4301
|
+
log$e.debug('Getting cache item', { key });
|
|
4293
4302
|
const item = this.cache.get(key);
|
|
4294
4303
|
if (!item) {
|
|
4295
|
-
log$
|
|
4304
|
+
log$e.debug('Cache miss', { key });
|
|
4296
4305
|
return null;
|
|
4297
4306
|
}
|
|
4298
4307
|
// Handle decompression if needed
|
|
@@ -4302,7 +4311,7 @@ class MemoryCacheAdapter {
|
|
|
4302
4311
|
const decompressed = decompressData(item.data, true);
|
|
4303
4312
|
finalData = JSON.parse(decompressed.data);
|
|
4304
4313
|
}
|
|
4305
|
-
log$
|
|
4314
|
+
log$e.debug('Cache hit', { key, compressed: isCompressed });
|
|
4306
4315
|
return {
|
|
4307
4316
|
...item,
|
|
4308
4317
|
data: finalData,
|
|
@@ -4310,7 +4319,7 @@ class MemoryCacheAdapter {
|
|
|
4310
4319
|
};
|
|
4311
4320
|
}
|
|
4312
4321
|
async set(key, data) {
|
|
4313
|
-
log$
|
|
4322
|
+
log$e.debug('Setting cache item', { key });
|
|
4314
4323
|
// Handle compression if enabled
|
|
4315
4324
|
let finalData = data;
|
|
4316
4325
|
let isCompressed = false;
|
|
@@ -4320,7 +4329,7 @@ class MemoryCacheAdapter {
|
|
|
4320
4329
|
if (compressionResult.compressed) {
|
|
4321
4330
|
finalData = compressionResult.data;
|
|
4322
4331
|
isCompressed = true;
|
|
4323
|
-
log$
|
|
4332
|
+
log$e.debug('Compression result', {
|
|
4324
4333
|
key,
|
|
4325
4334
|
originalSize: compressionResult.originalSize,
|
|
4326
4335
|
compressedSize: compressionResult.compressedSize,
|
|
@@ -4352,13 +4361,13 @@ class MemoryCacheAdapter {
|
|
|
4352
4361
|
const oldestItemSize = this.calculateItemSize(oldestKey, oldestItem);
|
|
4353
4362
|
this.totalBytes -= oldestItemSize;
|
|
4354
4363
|
this.cache.delete(oldestKey);
|
|
4355
|
-
log$
|
|
4364
|
+
log$e.debug('Removed oldest item for capacity', { oldestKey, freedBytes: oldestItemSize });
|
|
4356
4365
|
}
|
|
4357
4366
|
}
|
|
4358
4367
|
// Set new item and update total size
|
|
4359
4368
|
this.cache.set(key, item);
|
|
4360
4369
|
this.totalBytes += newItemSize;
|
|
4361
|
-
log$
|
|
4370
|
+
log$e.debug('Updated cache size', {
|
|
4362
4371
|
entries: this.cache.size,
|
|
4363
4372
|
totalBytes: this.totalBytes,
|
|
4364
4373
|
newItemSize,
|
|
@@ -4367,7 +4376,7 @@ class MemoryCacheAdapter {
|
|
|
4367
4376
|
async setBatch(items) {
|
|
4368
4377
|
if (items.length === 0)
|
|
4369
4378
|
return;
|
|
4370
|
-
log$
|
|
4379
|
+
log$e.debug('Batch setting items', { count: items.length });
|
|
4371
4380
|
let totalNewBytes = 0;
|
|
4372
4381
|
let totalOldBytes = 0;
|
|
4373
4382
|
const itemsToRemove = [];
|
|
@@ -4396,7 +4405,7 @@ class MemoryCacheAdapter {
|
|
|
4396
4405
|
itemsToRemove.push(oldKey);
|
|
4397
4406
|
}
|
|
4398
4407
|
if (itemsToRemove.length > 0) {
|
|
4399
|
-
log$
|
|
4408
|
+
log$e.debug('Removed items for batch capacity', {
|
|
4400
4409
|
removedCount: itemsToRemove.length,
|
|
4401
4410
|
removedKeys: itemsToRemove,
|
|
4402
4411
|
});
|
|
@@ -4408,7 +4417,7 @@ class MemoryCacheAdapter {
|
|
|
4408
4417
|
for (const [key, item] of items) {
|
|
4409
4418
|
this.cache.set(key, item);
|
|
4410
4419
|
}
|
|
4411
|
-
log$
|
|
4420
|
+
log$e.debug('Batch operation completed', {
|
|
4412
4421
|
count: items.length,
|
|
4413
4422
|
totalBytes: this.totalBytes,
|
|
4414
4423
|
entries: this.cache.size,
|
|
@@ -4430,7 +4439,7 @@ class MemoryCacheAdapter {
|
|
|
4430
4439
|
}
|
|
4431
4440
|
}
|
|
4432
4441
|
if (removed > 0) {
|
|
4433
|
-
log$
|
|
4442
|
+
log$e.debug('Invalidation completed', {
|
|
4434
4443
|
pattern,
|
|
4435
4444
|
entriesRemoved: removed,
|
|
4436
4445
|
bytesFreed,
|
|
@@ -4442,7 +4451,7 @@ class MemoryCacheAdapter {
|
|
|
4442
4451
|
async clear() {
|
|
4443
4452
|
this.cache.clear();
|
|
4444
4453
|
this.totalBytes = 0;
|
|
4445
|
-
log$
|
|
4454
|
+
log$e.debug('Cache cleared', { entries: 0, bytes: 0 });
|
|
4446
4455
|
}
|
|
4447
4456
|
async getSize() {
|
|
4448
4457
|
return {
|
|
@@ -4773,7 +4782,7 @@ replaceTraps((oldTraps) => ({
|
|
|
4773
4782
|
},
|
|
4774
4783
|
}));
|
|
4775
4784
|
|
|
4776
|
-
const log$
|
|
4785
|
+
const log$d = createPrefixedLogger('CACHE-WEB');
|
|
4777
4786
|
/**
|
|
4778
4787
|
* Web cache adapter using IndexedDB with automatic error recovery
|
|
4779
4788
|
* Cache never expires - data persists until explicitly invalidated
|
|
@@ -4796,18 +4805,18 @@ class WebCacheAdapter {
|
|
|
4796
4805
|
async initialize() {
|
|
4797
4806
|
if (this.db)
|
|
4798
4807
|
return;
|
|
4799
|
-
log$
|
|
4808
|
+
log$d.debug('Initializing IndexedDB cache', {
|
|
4800
4809
|
dbName: WebCacheAdapter.DB_NAME,
|
|
4801
4810
|
version: WebCacheAdapter.DB_VERSION,
|
|
4802
4811
|
});
|
|
4803
4812
|
try {
|
|
4804
4813
|
this.db = await this.openDatabase();
|
|
4805
|
-
log$
|
|
4814
|
+
log$d.debug('IndexedDB cache initialized successfully');
|
|
4806
4815
|
this.retryCount = 0; // Reset retry count on success
|
|
4807
4816
|
}
|
|
4808
4817
|
catch (error) {
|
|
4809
4818
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
4810
|
-
log$
|
|
4819
|
+
log$d.debug('Failed to initialize IndexedDB', { error: errorMessage });
|
|
4811
4820
|
// Check if this is a version conflict error
|
|
4812
4821
|
if (this.isVersionConflictError(errorMessage)) {
|
|
4813
4822
|
await this.handleVersionConflict();
|
|
@@ -4820,32 +4829,32 @@ class WebCacheAdapter {
|
|
|
4820
4829
|
async openDatabase() {
|
|
4821
4830
|
return await openDB(WebCacheAdapter.DB_NAME, WebCacheAdapter.DB_VERSION, {
|
|
4822
4831
|
upgrade: (db, oldVersion, newVersion, transaction) => {
|
|
4823
|
-
log$
|
|
4832
|
+
log$d.debug('Database upgrade needed', { oldVersion, newVersion });
|
|
4824
4833
|
this.handleUpgrade(db, oldVersion, newVersion, transaction);
|
|
4825
4834
|
},
|
|
4826
4835
|
blocked: () => {
|
|
4827
|
-
log$
|
|
4836
|
+
log$d.debug('Database blocked - another tab may be open');
|
|
4828
4837
|
},
|
|
4829
4838
|
blocking: () => {
|
|
4830
|
-
log$
|
|
4839
|
+
log$d.debug('Database blocking - will close connection');
|
|
4831
4840
|
if (this.db) {
|
|
4832
4841
|
this.db.close();
|
|
4833
4842
|
this.db = null;
|
|
4834
4843
|
}
|
|
4835
4844
|
},
|
|
4836
4845
|
terminated: () => {
|
|
4837
|
-
log$
|
|
4846
|
+
log$d.debug('Database connection terminated unexpectedly');
|
|
4838
4847
|
this.db = null;
|
|
4839
4848
|
},
|
|
4840
4849
|
});
|
|
4841
4850
|
}
|
|
4842
4851
|
handleUpgrade(db, oldVersion, newVersion, transaction) {
|
|
4843
|
-
log$
|
|
4852
|
+
log$d.debug('Handling database upgrade', { oldVersion, newVersion });
|
|
4844
4853
|
// Create cache store if it doesn't exist (initial setup)
|
|
4845
4854
|
if (!db.objectStoreNames.contains(WebCacheAdapter.STORE_NAME)) {
|
|
4846
4855
|
const store = db.createObjectStore(WebCacheAdapter.STORE_NAME, { keyPath: 'key' });
|
|
4847
4856
|
store.createIndex('timestamp', 'timestamp', { unique: false });
|
|
4848
|
-
log$
|
|
4857
|
+
log$d.debug('Created cache store and timestamp index');
|
|
4849
4858
|
}
|
|
4850
4859
|
// Handle migration from version 1 to 2
|
|
4851
4860
|
if (oldVersion < 2) {
|
|
@@ -4856,16 +4865,16 @@ class WebCacheAdapter {
|
|
|
4856
4865
|
try {
|
|
4857
4866
|
if (store.indexNames.contains(indexName)) {
|
|
4858
4867
|
store.deleteIndex(indexName);
|
|
4859
|
-
log$
|
|
4868
|
+
log$d.debug(`Removed unused index: ${indexName}`);
|
|
4860
4869
|
}
|
|
4861
4870
|
}
|
|
4862
4871
|
catch (error) {
|
|
4863
4872
|
// Ignore errors if indexes don't exist
|
|
4864
|
-
log$
|
|
4873
|
+
log$d.debug(`Warning: Could not remove index ${indexName}`, error);
|
|
4865
4874
|
}
|
|
4866
4875
|
});
|
|
4867
4876
|
}
|
|
4868
|
-
log$
|
|
4877
|
+
log$d.debug('Database upgrade completed');
|
|
4869
4878
|
}
|
|
4870
4879
|
isVersionConflictError(errorMessage) {
|
|
4871
4880
|
return (errorMessage.includes('less than the existing version') ||
|
|
@@ -4873,7 +4882,7 @@ class WebCacheAdapter {
|
|
|
4873
4882
|
errorMessage.includes('VersionError'));
|
|
4874
4883
|
}
|
|
4875
4884
|
async handleVersionConflict() {
|
|
4876
|
-
log$
|
|
4885
|
+
log$d.debug('Handling version conflict, attempting recovery...');
|
|
4877
4886
|
if (this.retryCount >= this.maxRetries) {
|
|
4878
4887
|
throw new Error('Failed to resolve IndexedDB version conflict after multiple attempts');
|
|
4879
4888
|
}
|
|
@@ -4885,19 +4894,19 @@ class WebCacheAdapter {
|
|
|
4885
4894
|
this.db = null;
|
|
4886
4895
|
}
|
|
4887
4896
|
// Delete the problematic database
|
|
4888
|
-
log$
|
|
4897
|
+
log$d.debug('Deleting problematic database to resolve version conflict');
|
|
4889
4898
|
await deleteDB(WebCacheAdapter.DB_NAME);
|
|
4890
4899
|
// Wait a bit for the deletion to complete
|
|
4891
4900
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
4892
4901
|
// Try to open the database again
|
|
4893
|
-
log$
|
|
4902
|
+
log$d.debug(`Retrying database initialization (attempt ${this.retryCount}/${this.maxRetries})`);
|
|
4894
4903
|
this.db = await this.openDatabase();
|
|
4895
|
-
log$
|
|
4904
|
+
log$d.debug('Successfully recovered from version conflict');
|
|
4896
4905
|
this.retryCount = 0; // Reset retry count on success
|
|
4897
4906
|
}
|
|
4898
4907
|
catch (retryError) {
|
|
4899
4908
|
const retryErrorMessage = retryError instanceof Error ? retryError.message : 'Unknown error';
|
|
4900
|
-
log$
|
|
4909
|
+
log$d.debug('Recovery attempt failed', { attempt: this.retryCount, error: retryErrorMessage });
|
|
4901
4910
|
if (this.retryCount < this.maxRetries) {
|
|
4902
4911
|
// Try again
|
|
4903
4912
|
await this.handleVersionConflict();
|
|
@@ -4909,7 +4918,7 @@ class WebCacheAdapter {
|
|
|
4909
4918
|
}
|
|
4910
4919
|
async get(key) {
|
|
4911
4920
|
await this.ensureInitialized();
|
|
4912
|
-
log$
|
|
4921
|
+
log$d.debug('Getting cache item', { key });
|
|
4913
4922
|
try {
|
|
4914
4923
|
const transaction = this.db.transaction([WebCacheAdapter.STORE_NAME], 'readonly');
|
|
4915
4924
|
const store = transaction.objectStore(WebCacheAdapter.STORE_NAME);
|
|
@@ -4935,7 +4944,7 @@ class WebCacheAdapter {
|
|
|
4935
4944
|
};
|
|
4936
4945
|
}
|
|
4937
4946
|
catch (error) {
|
|
4938
|
-
log$
|
|
4947
|
+
log$d.debug('Error getting cache item', { key, error });
|
|
4939
4948
|
return null; // Return null on error instead of throwing
|
|
4940
4949
|
}
|
|
4941
4950
|
}
|
|
@@ -4957,7 +4966,7 @@ class WebCacheAdapter {
|
|
|
4957
4966
|
if (compressionResult.compressed) {
|
|
4958
4967
|
finalData = compressionResult.data;
|
|
4959
4968
|
isCompressed = true;
|
|
4960
|
-
log$
|
|
4969
|
+
log$d.debug('Compression result', {
|
|
4961
4970
|
key,
|
|
4962
4971
|
originalSize: compressionResult.originalSize,
|
|
4963
4972
|
compressedSize: compressionResult.compressedSize,
|
|
@@ -4966,7 +4975,7 @@ class WebCacheAdapter {
|
|
|
4966
4975
|
});
|
|
4967
4976
|
}
|
|
4968
4977
|
}
|
|
4969
|
-
log$
|
|
4978
|
+
log$d.debug('Setting cache item', { key, timestamp: item.timestamp, compressed: isCompressed });
|
|
4970
4979
|
const storedItem = {
|
|
4971
4980
|
key,
|
|
4972
4981
|
data: finalData,
|
|
@@ -4979,7 +4988,7 @@ class WebCacheAdapter {
|
|
|
4979
4988
|
await store.put(storedItem);
|
|
4980
4989
|
}
|
|
4981
4990
|
catch (error) {
|
|
4982
|
-
log$
|
|
4991
|
+
log$d.debug('Error setting cache item', { key, error });
|
|
4983
4992
|
// Silently fail for cache writes
|
|
4984
4993
|
}
|
|
4985
4994
|
}
|
|
@@ -4987,7 +4996,7 @@ class WebCacheAdapter {
|
|
|
4987
4996
|
if (items.length === 0)
|
|
4988
4997
|
return;
|
|
4989
4998
|
await this.ensureInitialized();
|
|
4990
|
-
log$
|
|
4999
|
+
log$d.debug('Batch setting items', { count: items.length });
|
|
4991
5000
|
try {
|
|
4992
5001
|
const transaction = this.db.transaction([WebCacheAdapter.STORE_NAME], 'readwrite');
|
|
4993
5002
|
const store = transaction.objectStore(WebCacheAdapter.STORE_NAME);
|
|
@@ -4997,10 +5006,10 @@ class WebCacheAdapter {
|
|
|
4997
5006
|
return store.put(storedItem);
|
|
4998
5007
|
});
|
|
4999
5008
|
await Promise.all(promises);
|
|
5000
|
-
log$
|
|
5009
|
+
log$d.debug('Batch operation completed', { count: items.length });
|
|
5001
5010
|
}
|
|
5002
5011
|
catch (error) {
|
|
5003
|
-
log$
|
|
5012
|
+
log$d.debug('Error in batch operation', { count: items.length, error });
|
|
5004
5013
|
// Silently fail for batch writes
|
|
5005
5014
|
}
|
|
5006
5015
|
}
|
|
@@ -5035,10 +5044,10 @@ class WebCacheAdapter {
|
|
|
5035
5044
|
const transaction = this.db.transaction([WebCacheAdapter.STORE_NAME], 'readwrite');
|
|
5036
5045
|
const store = transaction.objectStore(WebCacheAdapter.STORE_NAME);
|
|
5037
5046
|
await store.clear();
|
|
5038
|
-
log$
|
|
5047
|
+
log$d.debug('Cache cleared successfully');
|
|
5039
5048
|
}
|
|
5040
5049
|
catch (error) {
|
|
5041
|
-
log$
|
|
5050
|
+
log$d.debug('Error clearing cache', error);
|
|
5042
5051
|
// Silently fail for cache clear
|
|
5043
5052
|
}
|
|
5044
5053
|
}
|
|
@@ -5064,7 +5073,7 @@ class WebCacheAdapter {
|
|
|
5064
5073
|
};
|
|
5065
5074
|
}
|
|
5066
5075
|
catch (error) {
|
|
5067
|
-
log$
|
|
5076
|
+
log$d.debug('Error getting cache size', error);
|
|
5068
5077
|
return {
|
|
5069
5078
|
entries: 0,
|
|
5070
5079
|
bytes: 0,
|
|
@@ -5089,7 +5098,7 @@ class WebCacheAdapter {
|
|
|
5089
5098
|
return allKeys.filter((key) => regex.test(key));
|
|
5090
5099
|
}
|
|
5091
5100
|
catch (error) {
|
|
5092
|
-
log$
|
|
5101
|
+
log$d.debug('Error getting cache keys', error);
|
|
5093
5102
|
return [];
|
|
5094
5103
|
}
|
|
5095
5104
|
}
|
|
@@ -5102,7 +5111,7 @@ class WebCacheAdapter {
|
|
|
5102
5111
|
return true;
|
|
5103
5112
|
}
|
|
5104
5113
|
catch (error) {
|
|
5105
|
-
log$
|
|
5114
|
+
log$d.debug('Error deleting cache item', { key, error });
|
|
5106
5115
|
return false;
|
|
5107
5116
|
}
|
|
5108
5117
|
}
|
|
@@ -5114,7 +5123,7 @@ class WebCacheAdapter {
|
|
|
5114
5123
|
await this.initPromise;
|
|
5115
5124
|
}
|
|
5116
5125
|
catch (error) {
|
|
5117
|
-
log$
|
|
5126
|
+
log$d.debug('Failed to ensure initialization', error);
|
|
5118
5127
|
// Reset and try once more
|
|
5119
5128
|
this.initPromise = null;
|
|
5120
5129
|
this.db = null;
|
|
@@ -5133,7 +5142,7 @@ WebCacheAdapter.DB_NAME = 'acube_cache';
|
|
|
5133
5142
|
WebCacheAdapter.DB_VERSION = 2;
|
|
5134
5143
|
WebCacheAdapter.STORE_NAME = 'cache_entries';
|
|
5135
5144
|
|
|
5136
|
-
const log$
|
|
5145
|
+
const log$c = createPrefixedLogger('CACHE-LOADER');
|
|
5137
5146
|
function loadCacheAdapter(platform) {
|
|
5138
5147
|
try {
|
|
5139
5148
|
switch (platform) {
|
|
@@ -5165,7 +5174,7 @@ function loadCacheAdapter(platform) {
|
|
|
5165
5174
|
}
|
|
5166
5175
|
}
|
|
5167
5176
|
catch (error) {
|
|
5168
|
-
log$
|
|
5177
|
+
log$c.warn(`Cache adapter not available for platform ${platform}:`, error);
|
|
5169
5178
|
return undefined;
|
|
5170
5179
|
}
|
|
5171
5180
|
}
|
|
@@ -5222,7 +5231,7 @@ class BaseSecureStorageAdapter {
|
|
|
5222
5231
|
}
|
|
5223
5232
|
}
|
|
5224
5233
|
|
|
5225
|
-
const log$
|
|
5234
|
+
const log$b = createPrefixedLogger('NETWORK-BASE');
|
|
5226
5235
|
class NetworkBase {
|
|
5227
5236
|
constructor(initialOnline = true, debounceMs = 300) {
|
|
5228
5237
|
this.destroy$ = new Subject();
|
|
@@ -5242,14 +5251,14 @@ class NetworkBase {
|
|
|
5242
5251
|
const current = this.statusSubject.getValue();
|
|
5243
5252
|
if (current.online !== online) {
|
|
5244
5253
|
this.statusSubject.next({ online, timestamp: Date.now() });
|
|
5245
|
-
log$
|
|
5254
|
+
log$b.debug(`Network status changed: ${online ? 'online' : 'offline'}`);
|
|
5246
5255
|
}
|
|
5247
5256
|
}
|
|
5248
5257
|
destroy() {
|
|
5249
5258
|
this.destroy$.next();
|
|
5250
5259
|
this.destroy$.complete();
|
|
5251
5260
|
this.statusSubject.complete();
|
|
5252
|
-
log$
|
|
5261
|
+
log$b.debug('Network monitor destroyed');
|
|
5253
5262
|
}
|
|
5254
5263
|
}
|
|
5255
5264
|
|
|
@@ -5309,7 +5318,7 @@ class NodeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
5309
5318
|
}
|
|
5310
5319
|
}
|
|
5311
5320
|
|
|
5312
|
-
const log$
|
|
5321
|
+
const log$a = createPrefixedLogger('RN-STORAGE');
|
|
5313
5322
|
/**
|
|
5314
5323
|
* React Native storage adapter using AsyncStorage
|
|
5315
5324
|
* Note: Uses native batch operations for better performance (not base class)
|
|
@@ -5341,7 +5350,7 @@ class ReactNativeStorageAdapter {
|
|
|
5341
5350
|
return await this.AsyncStorage.getItem(key);
|
|
5342
5351
|
}
|
|
5343
5352
|
catch (error) {
|
|
5344
|
-
log$
|
|
5353
|
+
log$a.error('Failed to get item from AsyncStorage:', error);
|
|
5345
5354
|
return null;
|
|
5346
5355
|
}
|
|
5347
5356
|
}
|
|
@@ -5382,7 +5391,7 @@ class ReactNativeStorageAdapter {
|
|
|
5382
5391
|
return await this.AsyncStorage.getAllKeys();
|
|
5383
5392
|
}
|
|
5384
5393
|
catch (error) {
|
|
5385
|
-
log$
|
|
5394
|
+
log$a.error('Failed to get all keys:', error);
|
|
5386
5395
|
return [];
|
|
5387
5396
|
}
|
|
5388
5397
|
}
|
|
@@ -5399,7 +5408,7 @@ class ReactNativeStorageAdapter {
|
|
|
5399
5408
|
return result;
|
|
5400
5409
|
}
|
|
5401
5410
|
catch (error) {
|
|
5402
|
-
log$
|
|
5411
|
+
log$a.error('Failed to get multiple items:', error);
|
|
5403
5412
|
const result = {};
|
|
5404
5413
|
keys.forEach((key) => {
|
|
5405
5414
|
result[key] = null;
|
|
@@ -5449,7 +5458,7 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
5449
5458
|
return;
|
|
5450
5459
|
}
|
|
5451
5460
|
catch {
|
|
5452
|
-
log$
|
|
5461
|
+
log$a.debug('expo-secure-store not available, trying react-native-keychain');
|
|
5453
5462
|
}
|
|
5454
5463
|
try {
|
|
5455
5464
|
const Keychain = require('react-native-keychain');
|
|
@@ -5458,7 +5467,7 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
5458
5467
|
return;
|
|
5459
5468
|
}
|
|
5460
5469
|
catch {
|
|
5461
|
-
log$
|
|
5470
|
+
log$a.error('react-native-keychain not available');
|
|
5462
5471
|
}
|
|
5463
5472
|
throw new Error('No secure storage available. Please install expo-secure-store or react-native-keychain');
|
|
5464
5473
|
}
|
|
@@ -5476,7 +5485,7 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
5476
5485
|
}
|
|
5477
5486
|
}
|
|
5478
5487
|
catch (error) {
|
|
5479
|
-
log$
|
|
5488
|
+
log$a.error('Failed to get secure item:', error);
|
|
5480
5489
|
}
|
|
5481
5490
|
return null;
|
|
5482
5491
|
}
|
|
@@ -5513,10 +5522,10 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
5513
5522
|
}
|
|
5514
5523
|
}
|
|
5515
5524
|
async clear() {
|
|
5516
|
-
log$
|
|
5525
|
+
log$a.warn('Clear all secure items not fully implemented for React Native');
|
|
5517
5526
|
}
|
|
5518
5527
|
async getAllKeys() {
|
|
5519
|
-
log$
|
|
5528
|
+
log$a.warn('Get all secure keys not implemented for React Native');
|
|
5520
5529
|
return [];
|
|
5521
5530
|
}
|
|
5522
5531
|
async isAvailable() {
|
|
@@ -5734,7 +5743,7 @@ class NodeNetworkMonitor extends NetworkBase {
|
|
|
5734
5743
|
}
|
|
5735
5744
|
}
|
|
5736
5745
|
|
|
5737
|
-
const log$
|
|
5746
|
+
const log$9 = createPrefixedLogger('RN-NETWORK');
|
|
5738
5747
|
/**
|
|
5739
5748
|
* React Native network monitor using RxJS
|
|
5740
5749
|
* Supports both @react-native-community/netinfo and expo-network
|
|
@@ -5747,7 +5756,7 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5747
5756
|
this.moduleReady$ = new Subject();
|
|
5748
5757
|
this.isExpo = detectPlatform().isExpo;
|
|
5749
5758
|
this.init().catch((error) => {
|
|
5750
|
-
log$
|
|
5759
|
+
log$9.error('Network monitor initialization failed:', error);
|
|
5751
5760
|
});
|
|
5752
5761
|
}
|
|
5753
5762
|
async init() {
|
|
@@ -5766,10 +5775,10 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5766
5775
|
try {
|
|
5767
5776
|
const module = require('@react-native-community/netinfo');
|
|
5768
5777
|
this.netInfoModule = module.default || module;
|
|
5769
|
-
log$
|
|
5778
|
+
log$9.debug('Loaded @react-native-community/netinfo module');
|
|
5770
5779
|
}
|
|
5771
5780
|
catch (error) {
|
|
5772
|
-
log$
|
|
5781
|
+
log$9.error('Failed to load React Native NetInfo module:', error);
|
|
5773
5782
|
this.netInfoModule = null;
|
|
5774
5783
|
}
|
|
5775
5784
|
}
|
|
@@ -5777,10 +5786,10 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5777
5786
|
try {
|
|
5778
5787
|
const module = require('expo-network');
|
|
5779
5788
|
this.netInfoModule = module.default || module;
|
|
5780
|
-
log$
|
|
5789
|
+
log$9.debug('Loaded expo-network module');
|
|
5781
5790
|
}
|
|
5782
5791
|
catch (error) {
|
|
5783
|
-
log$
|
|
5792
|
+
log$9.error('Failed to load Expo Network module:', error);
|
|
5784
5793
|
this.netInfoModule = null;
|
|
5785
5794
|
}
|
|
5786
5795
|
}
|
|
@@ -5797,16 +5806,16 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5797
5806
|
}
|
|
5798
5807
|
const online = !!(state.isConnected && state.isInternetReachable !== false);
|
|
5799
5808
|
this.updateStatus(online);
|
|
5800
|
-
log$
|
|
5809
|
+
log$9.debug('Initial network state:', online ? 'online' : 'offline');
|
|
5801
5810
|
}
|
|
5802
5811
|
catch (error) {
|
|
5803
|
-
log$
|
|
5812
|
+
log$9.warn('Could not fetch initial network state:', error);
|
|
5804
5813
|
}
|
|
5805
5814
|
}
|
|
5806
5815
|
subscribeToStateChanges() {
|
|
5807
5816
|
if (!this.netInfoModule)
|
|
5808
5817
|
return;
|
|
5809
|
-
log$
|
|
5818
|
+
log$9.debug('Subscribing to network state changes');
|
|
5810
5819
|
const handleState = (state) => {
|
|
5811
5820
|
const online = !!(state.isConnected && (state.isInternetReachable ?? true));
|
|
5812
5821
|
this.updateStatus(online);
|
|
@@ -5848,7 +5857,7 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5848
5857
|
};
|
|
5849
5858
|
}
|
|
5850
5859
|
catch (error) {
|
|
5851
|
-
log$
|
|
5860
|
+
log$9.error('Failed to fetch detailed network info:', error);
|
|
5852
5861
|
return null;
|
|
5853
5862
|
}
|
|
5854
5863
|
}
|
|
@@ -5984,7 +5993,7 @@ class CertificateValidator {
|
|
|
5984
5993
|
}
|
|
5985
5994
|
}
|
|
5986
5995
|
|
|
5987
|
-
const log$
|
|
5996
|
+
const log$8 = createPrefixedLogger('RN-MTLS');
|
|
5988
5997
|
/**
|
|
5989
5998
|
* React Native mTLS Adapter using @a-cube-io/expo-mutual-tls
|
|
5990
5999
|
*/
|
|
@@ -6006,7 +6015,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6006
6015
|
this.expoMTLS = ExpoMutualTls;
|
|
6007
6016
|
// Set up debug logging with the correct event signature
|
|
6008
6017
|
const debugListener = ExpoMutualTls.onDebugLog((event) => {
|
|
6009
|
-
log$
|
|
6018
|
+
log$8.debug(`${event.type}: ${event.message}`, {
|
|
6010
6019
|
method: event.method,
|
|
6011
6020
|
url: event.url,
|
|
6012
6021
|
statusCode: event.statusCode,
|
|
@@ -6016,28 +6025,28 @@ class ReactNativeMTLSAdapter {
|
|
|
6016
6025
|
this.eventListeners.push(debugListener);
|
|
6017
6026
|
// Set up error logging with the correct event signature
|
|
6018
6027
|
const errorListener = ExpoMutualTls.onError((event) => {
|
|
6019
|
-
log$
|
|
6028
|
+
log$8.error(event.message, {
|
|
6020
6029
|
code: event.code,
|
|
6021
6030
|
});
|
|
6022
6031
|
});
|
|
6023
6032
|
this.eventListeners.push(errorListener);
|
|
6024
6033
|
// Set up certificate expiry monitoring with the correct event signature
|
|
6025
6034
|
const expiryListener = ExpoMutualTls.onCertificateExpiry((event) => {
|
|
6026
|
-
log$
|
|
6035
|
+
log$8.warn(`Certificate ${event.subject} expires at ${new Date(event.expiry)}`, {
|
|
6027
6036
|
alias: event.alias,
|
|
6028
6037
|
warning: event.warning,
|
|
6029
6038
|
});
|
|
6030
6039
|
});
|
|
6031
6040
|
this.eventListeners.push(expiryListener);
|
|
6032
|
-
log$
|
|
6041
|
+
log$8.debug('Expo mTLS module loaded successfully');
|
|
6033
6042
|
}
|
|
6034
6043
|
catch (error) {
|
|
6035
|
-
log$
|
|
6044
|
+
log$8.warn('@a-cube-io/expo-mutual-tls not available:', error);
|
|
6036
6045
|
}
|
|
6037
6046
|
}
|
|
6038
6047
|
async isMTLSSupported() {
|
|
6039
6048
|
const supported = this.expoMTLS !== null;
|
|
6040
|
-
log$
|
|
6049
|
+
log$8.debug('mTLS support check:', {
|
|
6041
6050
|
supported,
|
|
6042
6051
|
platform: this.getPlatformInfo().platform,
|
|
6043
6052
|
moduleAvailable: !!this.expoMTLS,
|
|
@@ -6049,7 +6058,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6049
6058
|
throw new MTLSError(exports.MTLSErrorType.NOT_SUPPORTED, 'Expo mTLS module not available');
|
|
6050
6059
|
}
|
|
6051
6060
|
this.config = config;
|
|
6052
|
-
log$
|
|
6061
|
+
log$8.debug('Initialized with config:', {
|
|
6053
6062
|
baseUrl: config.baseUrl,
|
|
6054
6063
|
port: config.port,
|
|
6055
6064
|
timeout: config.timeout,
|
|
@@ -6063,7 +6072,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6063
6072
|
if (!this.config) {
|
|
6064
6073
|
throw new MTLSError(exports.MTLSErrorType.CONFIGURATION_ERROR, 'Adapter not initialized. Call initialize() first.');
|
|
6065
6074
|
}
|
|
6066
|
-
log$
|
|
6075
|
+
log$8.debug('Configuring certificate:', {
|
|
6067
6076
|
format: certificateData.format,
|
|
6068
6077
|
hasPassword: !!certificateData.password,
|
|
6069
6078
|
certificateLength: certificateData.certificate.length,
|
|
@@ -6080,14 +6089,14 @@ class ReactNativeMTLSAdapter {
|
|
|
6080
6089
|
'client-key-service', // keyService
|
|
6081
6090
|
true // enableLogging - let the native module handle its own debug logging
|
|
6082
6091
|
);
|
|
6083
|
-
log$
|
|
6092
|
+
log$8.debug('PEM services configured:', configResult);
|
|
6084
6093
|
if (!configResult.success) {
|
|
6085
6094
|
throw new MTLSError(exports.MTLSErrorType.CONFIGURATION_ERROR, `PEM configuration failed: ${configResult.state}`);
|
|
6086
6095
|
}
|
|
6087
6096
|
// Step 2: Store the actual PEM certificate and private key
|
|
6088
6097
|
const storeResult = await this.expoMTLS.storePEM(certificateData.certificate, certificateData.privateKey, certificateData.password // passphrase (optional)
|
|
6089
6098
|
);
|
|
6090
|
-
log$
|
|
6099
|
+
log$8.debug('PEM certificate store result:', storeResult);
|
|
6091
6100
|
if (!storeResult) {
|
|
6092
6101
|
throw new MTLSError(exports.MTLSErrorType.CERTIFICATE_INVALID, 'Failed to store PEM certificate');
|
|
6093
6102
|
}
|
|
@@ -6100,14 +6109,14 @@ class ReactNativeMTLSAdapter {
|
|
|
6100
6109
|
const configResult = await this.expoMTLS.configureP12('client-p12-service', // keychainService
|
|
6101
6110
|
true // enableLogging - let the native module handle its own debug logging
|
|
6102
6111
|
);
|
|
6103
|
-
log$
|
|
6112
|
+
log$8.debug('P12 service configured:', configResult);
|
|
6104
6113
|
if (!configResult.success) {
|
|
6105
6114
|
throw new MTLSError(exports.MTLSErrorType.CONFIGURATION_ERROR, `P12 configuration failed: ${configResult.state}`);
|
|
6106
6115
|
}
|
|
6107
6116
|
// Step 2: Store the P12 certificate data
|
|
6108
6117
|
const storeResult = await this.expoMTLS.storeP12(certificateData.certificate, // P12 data in certificate field
|
|
6109
6118
|
certificateData.password);
|
|
6110
|
-
log$
|
|
6119
|
+
log$8.debug('P12 certificate store result:', storeResult);
|
|
6111
6120
|
if (!storeResult) {
|
|
6112
6121
|
throw new MTLSError(exports.MTLSErrorType.CERTIFICATE_INVALID, 'Failed to store P12 certificate');
|
|
6113
6122
|
}
|
|
@@ -6121,7 +6130,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6121
6130
|
if (error instanceof MTLSError) {
|
|
6122
6131
|
throw error;
|
|
6123
6132
|
}
|
|
6124
|
-
log$
|
|
6133
|
+
log$8.error('Certificate configuration failed:', error);
|
|
6125
6134
|
throw new MTLSError(exports.MTLSErrorType.CONFIGURATION_ERROR, 'Failed to configure certificate', error);
|
|
6126
6135
|
}
|
|
6127
6136
|
}
|
|
@@ -6132,38 +6141,38 @@ class ReactNativeMTLSAdapter {
|
|
|
6132
6141
|
try {
|
|
6133
6142
|
// Use static method call
|
|
6134
6143
|
const hasCert = await this.expoMTLS.hasCertificate();
|
|
6135
|
-
log$
|
|
6144
|
+
log$8.debug('Certificate availability check:', hasCert);
|
|
6136
6145
|
return hasCert;
|
|
6137
6146
|
}
|
|
6138
6147
|
catch (error) {
|
|
6139
|
-
log$
|
|
6148
|
+
log$8.error('Certificate check failed:', error);
|
|
6140
6149
|
return false;
|
|
6141
6150
|
}
|
|
6142
6151
|
}
|
|
6143
6152
|
async getCertificateInfo() {
|
|
6144
6153
|
if (!this.expoMTLS) {
|
|
6145
|
-
log$
|
|
6154
|
+
log$8.debug('Certificate info requested but module not available');
|
|
6146
6155
|
return null;
|
|
6147
6156
|
}
|
|
6148
6157
|
try {
|
|
6149
6158
|
const hasCert = await this.hasCertificate();
|
|
6150
6159
|
if (!hasCert) {
|
|
6151
|
-
log$
|
|
6160
|
+
log$8.debug('No certificate stored');
|
|
6152
6161
|
return null;
|
|
6153
6162
|
}
|
|
6154
6163
|
// Use getCertificatesInfo to retrieve information about stored certificates
|
|
6155
6164
|
const result = await this.expoMTLS.getCertificatesInfo();
|
|
6156
6165
|
if (!result || !result.certificates || result.certificates.length === 0) {
|
|
6157
|
-
log$
|
|
6166
|
+
log$8.debug('No certificate information available');
|
|
6158
6167
|
return null;
|
|
6159
6168
|
}
|
|
6160
6169
|
// Get the first certificate (primary client certificate)
|
|
6161
6170
|
const cert = result.certificates[0];
|
|
6162
6171
|
if (!cert) {
|
|
6163
|
-
log$
|
|
6172
|
+
log$8.debug('Certificate data is empty');
|
|
6164
6173
|
return null;
|
|
6165
6174
|
}
|
|
6166
|
-
log$
|
|
6175
|
+
log$8.debug('Retrieved certificate info:', {
|
|
6167
6176
|
subject: cert.subject.commonName,
|
|
6168
6177
|
issuer: cert.issuer.commonName,
|
|
6169
6178
|
validFrom: new Date(cert.validFrom),
|
|
@@ -6182,7 +6191,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6182
6191
|
};
|
|
6183
6192
|
}
|
|
6184
6193
|
catch (error) {
|
|
6185
|
-
log$
|
|
6194
|
+
log$8.error('Failed to get certificate info:', error);
|
|
6186
6195
|
return null;
|
|
6187
6196
|
}
|
|
6188
6197
|
}
|
|
@@ -6193,7 +6202,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6193
6202
|
*/
|
|
6194
6203
|
async parseCertificateData(certificateData) {
|
|
6195
6204
|
if (!this.expoMTLS) {
|
|
6196
|
-
log$
|
|
6205
|
+
log$8.debug('Parse certificate: Module not available');
|
|
6197
6206
|
return null;
|
|
6198
6207
|
}
|
|
6199
6208
|
try {
|
|
@@ -6210,14 +6219,14 @@ class ReactNativeMTLSAdapter {
|
|
|
6210
6219
|
else {
|
|
6211
6220
|
throw new MTLSError(exports.MTLSErrorType.CERTIFICATE_INVALID, `Unsupported certificate format: ${certificateData.format}`);
|
|
6212
6221
|
}
|
|
6213
|
-
log$
|
|
6222
|
+
log$8.debug('Certificate parsed successfully:', {
|
|
6214
6223
|
certificateCount: result.certificates.length,
|
|
6215
6224
|
subjects: result.certificates.map((cert) => cert.subject.commonName),
|
|
6216
6225
|
});
|
|
6217
6226
|
return result;
|
|
6218
6227
|
}
|
|
6219
6228
|
catch (error) {
|
|
6220
|
-
log$
|
|
6229
|
+
log$8.error('Failed to parse certificate:', error);
|
|
6221
6230
|
if (error instanceof MTLSError) {
|
|
6222
6231
|
throw error;
|
|
6223
6232
|
}
|
|
@@ -6232,7 +6241,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6232
6241
|
if (!hasCert) {
|
|
6233
6242
|
throw new MTLSError(exports.MTLSErrorType.CERTIFICATE_NOT_FOUND, 'No certificate configured');
|
|
6234
6243
|
}
|
|
6235
|
-
log$
|
|
6244
|
+
log$8.debug('Making mTLS request:', {
|
|
6236
6245
|
method: requestConfig.method || 'GET',
|
|
6237
6246
|
url: requestConfig.url,
|
|
6238
6247
|
headers: requestConfig.headers,
|
|
@@ -6240,7 +6249,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6240
6249
|
responseType: requestConfig.responseType,
|
|
6241
6250
|
});
|
|
6242
6251
|
if (requestConfig.data) {
|
|
6243
|
-
log$
|
|
6252
|
+
log$8.debug('mTLS request body:', requestConfig.data);
|
|
6244
6253
|
}
|
|
6245
6254
|
try {
|
|
6246
6255
|
const response = await this.expoMTLS.request(requestConfig.url, {
|
|
@@ -6249,7 +6258,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6249
6258
|
body: requestConfig.data ? JSON.stringify(requestConfig.data) : undefined,
|
|
6250
6259
|
responseType: requestConfig.responseType,
|
|
6251
6260
|
});
|
|
6252
|
-
log$
|
|
6261
|
+
log$8.debug('mTLS request successful:', response);
|
|
6253
6262
|
if (!response.success) {
|
|
6254
6263
|
throw new MTLSError(exports.MTLSErrorType.CONNECTION_FAILED, `mTLS request failed: ${response.statusMessage} (${response.statusCode})`, undefined, response.statusCode);
|
|
6255
6264
|
}
|
|
@@ -6261,7 +6270,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6261
6270
|
data = JSON.parse(response.body);
|
|
6262
6271
|
}
|
|
6263
6272
|
catch (parseError) {
|
|
6264
|
-
log$
|
|
6273
|
+
log$8.warn('Failed to parse JSON response:', parseError);
|
|
6265
6274
|
// If parsing fails, keep raw body
|
|
6266
6275
|
}
|
|
6267
6276
|
}
|
|
@@ -6278,7 +6287,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6278
6287
|
};
|
|
6279
6288
|
}
|
|
6280
6289
|
catch (error) {
|
|
6281
|
-
log$
|
|
6290
|
+
log$8.error('mTLS request failed:', error);
|
|
6282
6291
|
throw new MTLSError(exports.MTLSErrorType.CONNECTION_FAILED, 'mTLS request failed', error);
|
|
6283
6292
|
}
|
|
6284
6293
|
}
|
|
@@ -6291,18 +6300,18 @@ class ReactNativeMTLSAdapter {
|
|
|
6291
6300
|
*/
|
|
6292
6301
|
async testConnection() {
|
|
6293
6302
|
if (!this.expoMTLS || !this.config) {
|
|
6294
|
-
log$
|
|
6303
|
+
log$8.debug('Diagnostic test: No mTLS module or config available');
|
|
6295
6304
|
return false;
|
|
6296
6305
|
}
|
|
6297
6306
|
try {
|
|
6298
6307
|
const hasCert = await this.hasCertificate();
|
|
6299
6308
|
if (!hasCert) {
|
|
6300
|
-
log$
|
|
6309
|
+
log$8.debug('Diagnostic test: No certificate configured');
|
|
6301
6310
|
return false;
|
|
6302
6311
|
}
|
|
6303
|
-
log$
|
|
6312
|
+
log$8.debug('Running diagnostic test (may fail even if mTLS works):', this.config.baseUrl);
|
|
6304
6313
|
const result = await this.expoMTLS.testConnection(this.config.baseUrl);
|
|
6305
|
-
log$
|
|
6314
|
+
log$8.debug('Diagnostic test result (NOT validation):', {
|
|
6306
6315
|
success: result.success,
|
|
6307
6316
|
statusCode: result.statusCode,
|
|
6308
6317
|
statusMessage: result.statusMessage,
|
|
@@ -6313,13 +6322,13 @@ class ReactNativeMTLSAdapter {
|
|
|
6313
6322
|
return result.success;
|
|
6314
6323
|
}
|
|
6315
6324
|
catch (error) {
|
|
6316
|
-
log$
|
|
6325
|
+
log$8.warn('Diagnostic test failed (this is expected):', error);
|
|
6317
6326
|
return false;
|
|
6318
6327
|
}
|
|
6319
6328
|
}
|
|
6320
6329
|
async removeCertificate() {
|
|
6321
6330
|
if (!this.expoMTLS) {
|
|
6322
|
-
log$
|
|
6331
|
+
log$8.debug('Remove certificate: Module not available');
|
|
6323
6332
|
return;
|
|
6324
6333
|
}
|
|
6325
6334
|
try {
|
|
@@ -6328,10 +6337,10 @@ class ReactNativeMTLSAdapter {
|
|
|
6328
6337
|
this.isConfigured = false;
|
|
6329
6338
|
// Cleanup event listeners
|
|
6330
6339
|
this.cleanupEventListeners();
|
|
6331
|
-
log$
|
|
6340
|
+
log$8.debug('Certificate removed successfully');
|
|
6332
6341
|
}
|
|
6333
6342
|
catch (error) {
|
|
6334
|
-
log$
|
|
6343
|
+
log$8.error('Failed to remove certificate:', error);
|
|
6335
6344
|
throw new MTLSError(exports.MTLSErrorType.CONFIGURATION_ERROR, 'Failed to remove certificate', error);
|
|
6336
6345
|
}
|
|
6337
6346
|
}
|
|
@@ -6340,7 +6349,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6340
6349
|
*/
|
|
6341
6350
|
cleanupEventListeners() {
|
|
6342
6351
|
if (this.eventListeners.length > 0) {
|
|
6343
|
-
log$
|
|
6352
|
+
log$8.debug(`Cleaning up ${this.eventListeners.length} event listeners`);
|
|
6344
6353
|
}
|
|
6345
6354
|
// Remove individual listeners if they have remove methods
|
|
6346
6355
|
this.eventListeners.forEach((listener) => {
|
|
@@ -6377,7 +6386,7 @@ class ReactNativeMTLSAdapter {
|
|
|
6377
6386
|
}
|
|
6378
6387
|
}
|
|
6379
6388
|
|
|
6380
|
-
const log$
|
|
6389
|
+
const log$7 = createPrefixedLogger('WEB-MTLS');
|
|
6381
6390
|
/**
|
|
6382
6391
|
* Web mTLS Adapter - Graceful fallback for web browsers
|
|
6383
6392
|
*
|
|
@@ -6391,13 +6400,13 @@ const log$6 = createPrefixedLogger('WEB-MTLS');
|
|
|
6391
6400
|
*/
|
|
6392
6401
|
class WebMTLSAdapter {
|
|
6393
6402
|
constructor() {
|
|
6394
|
-
log$
|
|
6395
|
-
log$
|
|
6403
|
+
log$7.warn('Web browsers do not support programmatic mTLS configuration');
|
|
6404
|
+
log$7.info('Use JWT authentication or configure client certificates in browser settings');
|
|
6396
6405
|
}
|
|
6397
6406
|
async isMTLSSupported() {
|
|
6398
6407
|
// mTLS is not supported programmatically in web browsers
|
|
6399
6408
|
const supported = false;
|
|
6400
|
-
log$
|
|
6409
|
+
log$7.debug('mTLS support check:', {
|
|
6401
6410
|
supported,
|
|
6402
6411
|
platform: this.getPlatformInfo().platform,
|
|
6403
6412
|
reason: 'Browser security model prevents programmatic certificate configuration',
|
|
@@ -6406,14 +6415,14 @@ class WebMTLSAdapter {
|
|
|
6406
6415
|
return supported;
|
|
6407
6416
|
}
|
|
6408
6417
|
async initialize(config) {
|
|
6409
|
-
log$
|
|
6418
|
+
log$7.warn('Initialized but mTLS not available in web browsers:', {
|
|
6410
6419
|
baseUrl: config.baseUrl,
|
|
6411
6420
|
port: config.port,
|
|
6412
6421
|
recommendation: 'Use standard HTTPS with JWT authentication',
|
|
6413
6422
|
});
|
|
6414
6423
|
}
|
|
6415
6424
|
async configureCertificate(certificateData) {
|
|
6416
|
-
log$
|
|
6425
|
+
log$7.error('Certificate configuration attempted:', {
|
|
6417
6426
|
format: certificateData.format,
|
|
6418
6427
|
reason: 'Not supported in web browsers',
|
|
6419
6428
|
alternatives: [
|
|
@@ -6428,15 +6437,15 @@ class WebMTLSAdapter {
|
|
|
6428
6437
|
}
|
|
6429
6438
|
async hasCertificate() {
|
|
6430
6439
|
// We cannot detect if the browser has certificates configured
|
|
6431
|
-
log$
|
|
6440
|
+
log$7.debug('Certificate availability check: Cannot detect browser certificates programmatically');
|
|
6432
6441
|
return false;
|
|
6433
6442
|
}
|
|
6434
6443
|
async getCertificateInfo() {
|
|
6435
|
-
log$
|
|
6444
|
+
log$7.debug('Certificate info requested: Not accessible in web browsers');
|
|
6436
6445
|
return null;
|
|
6437
6446
|
}
|
|
6438
6447
|
async request(requestConfig) {
|
|
6439
|
-
log$
|
|
6448
|
+
log$7.error('mTLS request attempted:', {
|
|
6440
6449
|
method: requestConfig.method,
|
|
6441
6450
|
url: requestConfig.url,
|
|
6442
6451
|
reason: 'Not supported in web browsers',
|
|
@@ -6451,11 +6460,11 @@ class WebMTLSAdapter {
|
|
|
6451
6460
|
'are properly configured in the browser certificate store.');
|
|
6452
6461
|
}
|
|
6453
6462
|
async testConnection() {
|
|
6454
|
-
log$
|
|
6463
|
+
log$7.debug('Connection test: mTLS not available in web browsers');
|
|
6455
6464
|
return false;
|
|
6456
6465
|
}
|
|
6457
6466
|
async removeCertificate() {
|
|
6458
|
-
log$
|
|
6467
|
+
log$7.debug('Remove certificate: No certificates to remove (not supported in web browsers)');
|
|
6459
6468
|
// No-op - cannot remove certificates programmatically in browsers
|
|
6460
6469
|
}
|
|
6461
6470
|
/**
|
|
@@ -6463,7 +6472,7 @@ class WebMTLSAdapter {
|
|
|
6463
6472
|
* Always returns null for web browsers as mTLS is not supported
|
|
6464
6473
|
*/
|
|
6465
6474
|
getBaseUrl() {
|
|
6466
|
-
log$
|
|
6475
|
+
log$7.debug('Base URL requested: Not supported in web browsers');
|
|
6467
6476
|
return null;
|
|
6468
6477
|
}
|
|
6469
6478
|
getPlatformInfo() {
|
|
@@ -6496,7 +6505,7 @@ class WebMTLSAdapter {
|
|
|
6496
6505
|
}
|
|
6497
6506
|
}
|
|
6498
6507
|
|
|
6499
|
-
const log$
|
|
6508
|
+
const log$6 = createPrefixedLogger('MTLS-LOADER');
|
|
6500
6509
|
function loadMTLSAdapter(platform, config) {
|
|
6501
6510
|
try {
|
|
6502
6511
|
let adapter;
|
|
@@ -6530,7 +6539,7 @@ function loadMTLSAdapter(platform, config) {
|
|
|
6530
6539
|
return adapter;
|
|
6531
6540
|
}
|
|
6532
6541
|
catch (error) {
|
|
6533
|
-
log$
|
|
6542
|
+
log$6.warn(`mTLS adapter not available for platform ${platform}:`, error);
|
|
6534
6543
|
return null;
|
|
6535
6544
|
}
|
|
6536
6545
|
}
|
|
@@ -6540,7 +6549,7 @@ async function initializeAdapterAsync(adapter, config) {
|
|
|
6540
6549
|
if (isSupported) {
|
|
6541
6550
|
await adapter.initialize(config);
|
|
6542
6551
|
const platformInfo = adapter.getPlatformInfo();
|
|
6543
|
-
log$
|
|
6552
|
+
log$6.debug('mTLS adapter initialized:', {
|
|
6544
6553
|
platform: platformInfo.platform,
|
|
6545
6554
|
mtlsSupported: platformInfo.mtlsSupported,
|
|
6546
6555
|
certificateStorage: platformInfo.certificateStorage,
|
|
@@ -6549,20 +6558,20 @@ async function initializeAdapterAsync(adapter, config) {
|
|
|
6549
6558
|
}
|
|
6550
6559
|
}
|
|
6551
6560
|
catch (error) {
|
|
6552
|
-
log$
|
|
6561
|
+
log$6.warn('Failed to initialize mTLS adapter:', error);
|
|
6553
6562
|
}
|
|
6554
6563
|
}
|
|
6555
6564
|
|
|
6556
|
-
const log$
|
|
6565
|
+
const log$5 = createPrefixedLogger('ADAPTER-LOADER');
|
|
6557
6566
|
function loadPlatformAdapters(options = {}) {
|
|
6558
6567
|
const { mtlsConfig } = options;
|
|
6559
6568
|
const { platform } = detectPlatform();
|
|
6560
|
-
log$
|
|
6569
|
+
log$5.debug('Loading adapters for platform:', platform);
|
|
6561
6570
|
const storageAdapters = loadStorageAdapters(platform);
|
|
6562
6571
|
const networkMonitor = loadNetworkMonitor(platform);
|
|
6563
6572
|
const cache = loadCacheAdapter(platform);
|
|
6564
6573
|
const mtls = loadMTLSAdapter(platform, mtlsConfig);
|
|
6565
|
-
log$
|
|
6574
|
+
log$5.debug('Adapters loaded:', {
|
|
6566
6575
|
platform,
|
|
6567
6576
|
hasStorage: !!storageAdapters.storage,
|
|
6568
6577
|
hasSecureStorage: !!storageAdapters.secureStorage,
|
|
@@ -7588,7 +7597,7 @@ class TelemetryRepositoryImpl {
|
|
|
7588
7597
|
}
|
|
7589
7598
|
}
|
|
7590
7599
|
|
|
7591
|
-
const log$
|
|
7600
|
+
const log$4 = createPrefixedLogger('CACHE-KEY');
|
|
7592
7601
|
const URL_PATTERNS = [
|
|
7593
7602
|
// Receipt (mf1) - specific patterns first
|
|
7594
7603
|
{
|
|
@@ -7681,7 +7690,7 @@ const DEFAULT_TTL = 5 * 60 * 1000; // 5 minutes
|
|
|
7681
7690
|
class CacheKeyGenerator {
|
|
7682
7691
|
constructor(customConfig) {
|
|
7683
7692
|
this.config = { ...DEFAULT_TTL_CONFIG, ...customConfig };
|
|
7684
|
-
log$
|
|
7693
|
+
log$4.info('CacheKeyGenerator initialized with config:', {
|
|
7685
7694
|
resources: Object.keys(this.config),
|
|
7686
7695
|
});
|
|
7687
7696
|
}
|
|
@@ -7691,7 +7700,7 @@ class CacheKeyGenerator {
|
|
|
7691
7700
|
// Fallback: use URL as key
|
|
7692
7701
|
const paramStr = params ? this.serializeParams(params) : '';
|
|
7693
7702
|
const key = paramStr ? `${url}?${paramStr}` : url;
|
|
7694
|
-
log$
|
|
7703
|
+
log$4.debug('URL not matched, using fallback key:', { url, key });
|
|
7695
7704
|
return key;
|
|
7696
7705
|
}
|
|
7697
7706
|
const { resource, ids, action, isList, parent } = parsed;
|
|
@@ -7699,21 +7708,21 @@ class CacheKeyGenerator {
|
|
|
7699
7708
|
const paramStr = params ? this.serializeParams(params) : '';
|
|
7700
7709
|
const parentPart = parent && ids.length > 0 ? `${parent}=${ids[0]}&` : '';
|
|
7701
7710
|
const key = `${resource}:list:${parentPart}${paramStr}`;
|
|
7702
|
-
log$
|
|
7711
|
+
log$4.debug('Generated list cache key:', { url, key, resource });
|
|
7703
7712
|
return key;
|
|
7704
7713
|
}
|
|
7705
7714
|
// Single item
|
|
7706
7715
|
if (ids.length === 0 && action) {
|
|
7707
7716
|
// Special case for endpoints like /cashiers/me
|
|
7708
7717
|
const key = `${resource}:${action}`;
|
|
7709
|
-
log$
|
|
7718
|
+
log$4.debug('Generated special action cache key:', { url, key, resource, action });
|
|
7710
7719
|
return key;
|
|
7711
7720
|
}
|
|
7712
7721
|
let key = `${resource}:${ids.join(':')}`;
|
|
7713
7722
|
if (action) {
|
|
7714
7723
|
key += `:${action}`;
|
|
7715
7724
|
}
|
|
7716
|
-
log$
|
|
7725
|
+
log$4.debug('Generated item cache key:', { url, key, resource, ids, action });
|
|
7717
7726
|
return key;
|
|
7718
7727
|
}
|
|
7719
7728
|
parseResource(url) {
|
|
@@ -7723,23 +7732,23 @@ class CacheKeyGenerator {
|
|
|
7723
7732
|
getTTL(url) {
|
|
7724
7733
|
const resource = this.parseResource(url);
|
|
7725
7734
|
if (!resource) {
|
|
7726
|
-
log$
|
|
7735
|
+
log$4.debug('No resource found for URL, using default TTL:', { url, ttl: DEFAULT_TTL });
|
|
7727
7736
|
return DEFAULT_TTL;
|
|
7728
7737
|
}
|
|
7729
7738
|
const ttl = this.config[resource].ttlMs;
|
|
7730
|
-
log$
|
|
7739
|
+
log$4.debug('TTL for resource:', { url, resource, ttlMs: ttl, ttlMin: ttl / 60000 });
|
|
7731
7740
|
return ttl;
|
|
7732
7741
|
}
|
|
7733
7742
|
shouldCache(url) {
|
|
7734
7743
|
const parsed = this.parseUrl(url);
|
|
7735
7744
|
if (!parsed) {
|
|
7736
|
-
log$
|
|
7745
|
+
log$4.debug('URL not recognized, should not cache:', { url });
|
|
7737
7746
|
return false;
|
|
7738
7747
|
}
|
|
7739
7748
|
const { resource, isList } = parsed;
|
|
7740
7749
|
const config = this.config[resource];
|
|
7741
7750
|
if (isList) {
|
|
7742
|
-
log$
|
|
7751
|
+
log$4.debug('List endpoint cache decision:', {
|
|
7743
7752
|
url,
|
|
7744
7753
|
resource,
|
|
7745
7754
|
isList: true,
|
|
@@ -7747,7 +7756,7 @@ class CacheKeyGenerator {
|
|
|
7747
7756
|
});
|
|
7748
7757
|
return config.cacheList;
|
|
7749
7758
|
}
|
|
7750
|
-
log$
|
|
7759
|
+
log$4.debug('Item endpoint cache decision:', {
|
|
7751
7760
|
url,
|
|
7752
7761
|
resource,
|
|
7753
7762
|
isList: false,
|
|
@@ -7758,7 +7767,7 @@ class CacheKeyGenerator {
|
|
|
7758
7767
|
getInvalidationPatterns(url, method) {
|
|
7759
7768
|
const parsed = this.parseUrl(url);
|
|
7760
7769
|
if (!parsed) {
|
|
7761
|
-
log$
|
|
7770
|
+
log$4.debug('No patterns to invalidate for URL:', { url, method });
|
|
7762
7771
|
return [];
|
|
7763
7772
|
}
|
|
7764
7773
|
const { resource, ids, parent } = parsed;
|
|
@@ -7780,7 +7789,7 @@ class CacheKeyGenerator {
|
|
|
7780
7789
|
if (resource === 'cashier' && (method === 'PUT' || method === 'DELETE')) {
|
|
7781
7790
|
patterns.push('cashier:me');
|
|
7782
7791
|
}
|
|
7783
|
-
log$
|
|
7792
|
+
log$4.debug('Invalidation patterns:', { url, method, patterns });
|
|
7784
7793
|
return patterns;
|
|
7785
7794
|
}
|
|
7786
7795
|
parseUrl(url) {
|
|
@@ -7815,7 +7824,7 @@ class CacheKeyGenerator {
|
|
|
7815
7824
|
}
|
|
7816
7825
|
}
|
|
7817
7826
|
|
|
7818
|
-
const log$
|
|
7827
|
+
const log$3 = createPrefixedLogger('CACHE');
|
|
7819
7828
|
class CachingHttpDecorator {
|
|
7820
7829
|
constructor(http, cache, keyGenerator, networkMonitor, config = {}) {
|
|
7821
7830
|
this.http = http;
|
|
@@ -7825,7 +7834,7 @@ class CachingHttpDecorator {
|
|
|
7825
7834
|
this.config = config;
|
|
7826
7835
|
this.currentOnlineState = true;
|
|
7827
7836
|
this.authToken = null;
|
|
7828
|
-
log$
|
|
7837
|
+
log$3.info('CachingHttpDecorator initialized', {
|
|
7829
7838
|
enabled: config.enabled !== false,
|
|
7830
7839
|
hasNetworkMonitor: !!networkMonitor,
|
|
7831
7840
|
});
|
|
@@ -7835,7 +7844,7 @@ class CachingHttpDecorator {
|
|
|
7835
7844
|
if (this.networkMonitor) {
|
|
7836
7845
|
this.networkSubscription = this.networkMonitor.online$.subscribe((online) => {
|
|
7837
7846
|
if (this.currentOnlineState !== online) {
|
|
7838
|
-
log$
|
|
7847
|
+
log$3.info('Network state changed:', { online });
|
|
7839
7848
|
}
|
|
7840
7849
|
this.currentOnlineState = online;
|
|
7841
7850
|
});
|
|
@@ -7854,19 +7863,19 @@ class CachingHttpDecorator {
|
|
|
7854
7863
|
const startTime = Date.now();
|
|
7855
7864
|
// Check if caching is disabled globally
|
|
7856
7865
|
if (this.config.enabled === false) {
|
|
7857
|
-
log$
|
|
7866
|
+
log$3.debug('GET (cache disabled globally):', { url });
|
|
7858
7867
|
return this.http.get(url, config);
|
|
7859
7868
|
}
|
|
7860
7869
|
// Check if this URL should be cached
|
|
7861
7870
|
const shouldCache = this.keyGenerator.shouldCache(url);
|
|
7862
7871
|
if (!shouldCache) {
|
|
7863
|
-
log$
|
|
7872
|
+
log$3.debug('GET (not cacheable - likely a list endpoint):', { url });
|
|
7864
7873
|
return this.http.get(url, config);
|
|
7865
7874
|
}
|
|
7866
7875
|
const cacheKey = this.keyGenerator.generate(url, config?.params);
|
|
7867
7876
|
const ttl = this.keyGenerator.getTTL(url);
|
|
7868
7877
|
const resource = this.keyGenerator.parseResource(url);
|
|
7869
|
-
log$
|
|
7878
|
+
log$3.info('GET request starting:', {
|
|
7870
7879
|
url,
|
|
7871
7880
|
resource,
|
|
7872
7881
|
cacheKey,
|
|
@@ -7879,18 +7888,18 @@ class CachingHttpDecorator {
|
|
|
7879
7888
|
try {
|
|
7880
7889
|
cached = await this.cache.get(cacheKey);
|
|
7881
7890
|
if (cached) {
|
|
7882
|
-
log$
|
|
7891
|
+
log$3.debug('Cache entry found:', {
|
|
7883
7892
|
cacheKey,
|
|
7884
7893
|
timestamp: new Date(cached.timestamp).toISOString(),
|
|
7885
7894
|
ageMs: Date.now() - cached.timestamp,
|
|
7886
7895
|
});
|
|
7887
7896
|
}
|
|
7888
7897
|
else {
|
|
7889
|
-
log$
|
|
7898
|
+
log$3.debug('Cache entry not found:', { cacheKey });
|
|
7890
7899
|
}
|
|
7891
7900
|
}
|
|
7892
7901
|
catch (error) {
|
|
7893
|
-
log$
|
|
7902
|
+
log$3.warn('Cache lookup failed:', {
|
|
7894
7903
|
cacheKey,
|
|
7895
7904
|
error: error instanceof Error ? error.message : error,
|
|
7896
7905
|
});
|
|
@@ -7898,7 +7907,7 @@ class CachingHttpDecorator {
|
|
|
7898
7907
|
if (cached) {
|
|
7899
7908
|
const age = Date.now() - cached.timestamp;
|
|
7900
7909
|
const isExpired = age >= ttl;
|
|
7901
|
-
log$
|
|
7910
|
+
log$3.debug('Cache analysis:', {
|
|
7902
7911
|
cacheKey,
|
|
7903
7912
|
ageMs: age,
|
|
7904
7913
|
ageSec: Math.round(age / 1000),
|
|
@@ -7909,7 +7918,7 @@ class CachingHttpDecorator {
|
|
|
7909
7918
|
// If within TTL, return cached data
|
|
7910
7919
|
if (!isExpired) {
|
|
7911
7920
|
const duration = Date.now() - startTime;
|
|
7912
|
-
log$
|
|
7921
|
+
log$3.info('CACHE HIT:', {
|
|
7913
7922
|
url,
|
|
7914
7923
|
cacheKey,
|
|
7915
7924
|
ageMs: age,
|
|
@@ -7924,7 +7933,7 @@ class CachingHttpDecorator {
|
|
|
7924
7933
|
// If offline and cache is stale, return stale data
|
|
7925
7934
|
if (!this.isOnline()) {
|
|
7926
7935
|
const duration = Date.now() - startTime;
|
|
7927
|
-
log$
|
|
7936
|
+
log$3.info('CACHE STALE (offline):', {
|
|
7928
7937
|
url,
|
|
7929
7938
|
cacheKey,
|
|
7930
7939
|
ageMs: age,
|
|
@@ -7936,25 +7945,25 @@ class CachingHttpDecorator {
|
|
|
7936
7945
|
headers: { 'x-cache': 'STALE' },
|
|
7937
7946
|
};
|
|
7938
7947
|
}
|
|
7939
|
-
log$
|
|
7948
|
+
log$3.debug('Cache expired, fetching fresh data:', { cacheKey, ageMs: age, ttlMs: ttl });
|
|
7940
7949
|
}
|
|
7941
7950
|
// Fetch fresh data
|
|
7942
7951
|
try {
|
|
7943
|
-
log$
|
|
7952
|
+
log$3.debug('Fetching from network:', { url });
|
|
7944
7953
|
const response = await this.http.get(url, config);
|
|
7945
7954
|
// Cache the response
|
|
7946
7955
|
try {
|
|
7947
7956
|
await this.cache.set(cacheKey, response.data);
|
|
7948
|
-
log$
|
|
7957
|
+
log$3.debug('Response cached successfully:', { cacheKey });
|
|
7949
7958
|
}
|
|
7950
7959
|
catch (error) {
|
|
7951
|
-
log$
|
|
7960
|
+
log$3.error('Failed to cache response:', {
|
|
7952
7961
|
cacheKey,
|
|
7953
7962
|
error: error instanceof Error ? error.message : error,
|
|
7954
7963
|
});
|
|
7955
7964
|
}
|
|
7956
7965
|
const duration = Date.now() - startTime;
|
|
7957
|
-
log$
|
|
7966
|
+
log$3.info('CACHE MISS (fetched fresh):', {
|
|
7958
7967
|
url,
|
|
7959
7968
|
cacheKey,
|
|
7960
7969
|
status: response.status,
|
|
@@ -7969,7 +7978,7 @@ class CachingHttpDecorator {
|
|
|
7969
7978
|
// On error, return stale cache if available
|
|
7970
7979
|
if (cached) {
|
|
7971
7980
|
const duration = Date.now() - startTime;
|
|
7972
|
-
log$
|
|
7981
|
+
log$3.warn('CACHE STALE (network error):', {
|
|
7973
7982
|
url,
|
|
7974
7983
|
cacheKey,
|
|
7975
7984
|
error: error instanceof Error ? error.message : 'Unknown error',
|
|
@@ -7981,7 +7990,7 @@ class CachingHttpDecorator {
|
|
|
7981
7990
|
headers: { 'x-cache': 'STALE' },
|
|
7982
7991
|
};
|
|
7983
7992
|
}
|
|
7984
|
-
log$
|
|
7993
|
+
log$3.error('Network error with no cache fallback:', {
|
|
7985
7994
|
url,
|
|
7986
7995
|
error: error instanceof Error ? error.message : error,
|
|
7987
7996
|
});
|
|
@@ -7989,31 +7998,31 @@ class CachingHttpDecorator {
|
|
|
7989
7998
|
}
|
|
7990
7999
|
}
|
|
7991
8000
|
async post(url, data, config) {
|
|
7992
|
-
log$
|
|
8001
|
+
log$3.info('POST request:', { url });
|
|
7993
8002
|
const response = await this.http.post(url, data, config);
|
|
7994
8003
|
await this.invalidateRelated(url, 'POST');
|
|
7995
8004
|
return response;
|
|
7996
8005
|
}
|
|
7997
8006
|
async put(url, data, config) {
|
|
7998
|
-
log$
|
|
8007
|
+
log$3.info('PUT request:', { url });
|
|
7999
8008
|
const response = await this.http.put(url, data, config);
|
|
8000
8009
|
await this.invalidateRelated(url, 'PUT');
|
|
8001
8010
|
return response;
|
|
8002
8011
|
}
|
|
8003
8012
|
async patch(url, data, config) {
|
|
8004
|
-
log$
|
|
8013
|
+
log$3.info('PATCH request:', { url });
|
|
8005
8014
|
const response = await this.http.patch(url, data, config);
|
|
8006
8015
|
await this.invalidateRelated(url, 'PATCH');
|
|
8007
8016
|
return response;
|
|
8008
8017
|
}
|
|
8009
8018
|
async delete(url, config) {
|
|
8010
|
-
log$
|
|
8019
|
+
log$3.info('DELETE request:', { url });
|
|
8011
8020
|
const response = await this.http.delete(url, config);
|
|
8012
8021
|
await this.invalidateRelated(url, 'DELETE');
|
|
8013
8022
|
return response;
|
|
8014
8023
|
}
|
|
8015
8024
|
setAuthToken(token) {
|
|
8016
|
-
log$
|
|
8025
|
+
log$3.debug('CachingHttpDecorator.setAuthToken called:', {
|
|
8017
8026
|
hasToken: !!token,
|
|
8018
8027
|
tokenPrefix: token?.substring(0, 20),
|
|
8019
8028
|
underlyingHttpType: this.http.constructor.name,
|
|
@@ -8027,17 +8036,17 @@ class CachingHttpDecorator {
|
|
|
8027
8036
|
async invalidateRelated(url, method) {
|
|
8028
8037
|
const patterns = this.keyGenerator.getInvalidationPatterns(url, method);
|
|
8029
8038
|
if (patterns.length === 0) {
|
|
8030
|
-
log$
|
|
8039
|
+
log$3.debug('No cache patterns to invalidate:', { url, method });
|
|
8031
8040
|
return;
|
|
8032
8041
|
}
|
|
8033
|
-
log$
|
|
8042
|
+
log$3.info('Invalidating cache patterns:', { url, method, patterns });
|
|
8034
8043
|
for (const pattern of patterns) {
|
|
8035
8044
|
try {
|
|
8036
8045
|
await this.cache.invalidate(pattern);
|
|
8037
|
-
log$
|
|
8046
|
+
log$3.debug('Cache pattern invalidated:', { pattern });
|
|
8038
8047
|
}
|
|
8039
8048
|
catch (error) {
|
|
8040
|
-
log$
|
|
8049
|
+
log$3.error('Failed to invalidate pattern:', {
|
|
8041
8050
|
pattern,
|
|
8042
8051
|
error: error instanceof Error ? error.message : error,
|
|
8043
8052
|
});
|
|
@@ -8045,7 +8054,7 @@ class CachingHttpDecorator {
|
|
|
8045
8054
|
}
|
|
8046
8055
|
}
|
|
8047
8056
|
destroy() {
|
|
8048
|
-
log$
|
|
8057
|
+
log$3.debug('CachingHttpDecorator destroyed');
|
|
8049
8058
|
this.networkSubscription?.unsubscribe();
|
|
8050
8059
|
}
|
|
8051
8060
|
}
|
|
@@ -8421,7 +8430,7 @@ class SDKFactory {
|
|
|
8421
8430
|
}
|
|
8422
8431
|
}
|
|
8423
8432
|
|
|
8424
|
-
const log$
|
|
8433
|
+
const log$2 = createPrefixedLogger('SDK');
|
|
8425
8434
|
class ACubeSDK {
|
|
8426
8435
|
constructor(config, customAdapters, events = {}) {
|
|
8427
8436
|
this.events = events;
|
|
@@ -8435,22 +8444,22 @@ class ACubeSDK {
|
|
|
8435
8444
|
}
|
|
8436
8445
|
async initialize() {
|
|
8437
8446
|
if (this.isInitialized) {
|
|
8438
|
-
log$
|
|
8447
|
+
log$2.debug('SDK already initialized, skipping');
|
|
8439
8448
|
return;
|
|
8440
8449
|
}
|
|
8441
|
-
log$
|
|
8450
|
+
log$2.info('Initializing SDK', {
|
|
8442
8451
|
apiUrl: this.config.getApiUrl(),
|
|
8443
8452
|
authUrl: this.config.getAuthUrl(),
|
|
8444
8453
|
debugEnabled: this.config.isDebugEnabled(),
|
|
8445
8454
|
});
|
|
8446
8455
|
try {
|
|
8447
8456
|
if (!this.adapters) {
|
|
8448
|
-
log$
|
|
8457
|
+
log$2.debug('Loading platform adapters');
|
|
8449
8458
|
const mtlsConfig = createACubeMTLSConfig(this.config.getApiUrl(), this.config.getTimeout(), true);
|
|
8450
8459
|
this.adapters = loadPlatformAdapters({
|
|
8451
8460
|
mtlsConfig,
|
|
8452
8461
|
});
|
|
8453
|
-
log$
|
|
8462
|
+
log$2.info('Platform adapters loaded', {
|
|
8454
8463
|
hasCache: !!this.adapters.cache,
|
|
8455
8464
|
hasNetworkMonitor: !!this.adapters.networkMonitor,
|
|
8456
8465
|
hasMtls: !!this.adapters.mtls,
|
|
@@ -8463,30 +8472,30 @@ class ACubeSDK {
|
|
|
8463
8472
|
timeout: this.config.getTimeout(),
|
|
8464
8473
|
debugEnabled: this.config.isDebugEnabled(),
|
|
8465
8474
|
};
|
|
8466
|
-
log$
|
|
8475
|
+
log$2.debug('Creating DI container');
|
|
8467
8476
|
this.container = SDKFactory.createContainer(factoryConfig);
|
|
8468
|
-
log$
|
|
8477
|
+
log$2.debug('Registering auth services');
|
|
8469
8478
|
SDKFactory.registerAuthServices(this.container, this.adapters.secureStorage, factoryConfig);
|
|
8470
8479
|
if (this.adapters.cache) {
|
|
8471
|
-
log$
|
|
8480
|
+
log$2.info('Registering cache services', {
|
|
8472
8481
|
hasNetworkMonitor: !!this.adapters.networkMonitor,
|
|
8473
8482
|
});
|
|
8474
8483
|
SDKFactory.registerCacheServices(this.container, this.adapters.cache, this.adapters.networkMonitor);
|
|
8475
8484
|
}
|
|
8476
8485
|
else {
|
|
8477
|
-
log$
|
|
8486
|
+
log$2.debug('No cache adapter available, caching disabled');
|
|
8478
8487
|
}
|
|
8479
|
-
log$
|
|
8488
|
+
log$2.debug('Initializing certificate service');
|
|
8480
8489
|
this.certificateService = new CertificateService(this.adapters.secureStorage);
|
|
8481
8490
|
const tokenStorage = this.container.get(DI_TOKENS.TOKEN_STORAGE_PORT);
|
|
8482
8491
|
const httpPort = this.container.get(DI_TOKENS.HTTP_PORT);
|
|
8483
8492
|
const baseHttpPort = this.container.get(DI_TOKENS.BASE_HTTP_PORT);
|
|
8484
|
-
log$
|
|
8493
|
+
log$2.debug('HTTP ports initialized', {
|
|
8485
8494
|
httpPortType: httpPort.constructor.name,
|
|
8486
8495
|
baseHttpPortType: baseHttpPort.constructor.name,
|
|
8487
8496
|
areSameInstance: httpPort === baseHttpPort,
|
|
8488
8497
|
});
|
|
8489
|
-
log$
|
|
8498
|
+
log$2.debug('Initializing authentication service');
|
|
8490
8499
|
this.authService = new AuthenticationService(httpPort, tokenStorage, {
|
|
8491
8500
|
authUrl: this.config.getAuthUrl(),
|
|
8492
8501
|
timeout: this.config.getTimeout(),
|
|
@@ -8496,7 +8505,7 @@ class ACubeSDK {
|
|
|
8496
8505
|
this.events.onAuthError?.(new ACubeSDKError('AUTH_ERROR', error.message, error));
|
|
8497
8506
|
},
|
|
8498
8507
|
});
|
|
8499
|
-
log$
|
|
8508
|
+
log$2.debug('Initializing offline manager');
|
|
8500
8509
|
const queueEvents = {
|
|
8501
8510
|
onOperationAdded: (operation) => {
|
|
8502
8511
|
this.events.onOfflineOperationAdded?.(operation.id);
|
|
@@ -8519,28 +8528,28 @@ class ACubeSDK {
|
|
|
8519
8528
|
}
|
|
8520
8529
|
});
|
|
8521
8530
|
const isAuth = await this.authService.isAuthenticated();
|
|
8522
|
-
log$
|
|
8531
|
+
log$2.debug('Checking authentication status during init', { isAuthenticated: isAuth });
|
|
8523
8532
|
if (isAuth) {
|
|
8524
8533
|
const token = await this.authService.getAccessToken();
|
|
8525
|
-
log$
|
|
8534
|
+
log$2.debug('Token retrieved during init', {
|
|
8526
8535
|
hasToken: !!token,
|
|
8527
8536
|
tokenPrefix: token?.substring(0, 20),
|
|
8528
8537
|
});
|
|
8529
8538
|
if (token) {
|
|
8530
8539
|
httpPort.setAuthToken(token);
|
|
8531
|
-
log$
|
|
8540
|
+
log$2.info('Auth token set on HTTP port during initialization');
|
|
8532
8541
|
}
|
|
8533
8542
|
}
|
|
8534
8543
|
else {
|
|
8535
|
-
log$
|
|
8544
|
+
log$2.warn('User not authenticated during SDK init - token will be set after login');
|
|
8536
8545
|
}
|
|
8537
8546
|
if (this.adapters?.mtls && 'setMTLSAdapter' in baseHttpPort) {
|
|
8538
|
-
log$
|
|
8547
|
+
log$2.debug('Connecting mTLS adapter to HTTP port');
|
|
8539
8548
|
const httpWithMtls = baseHttpPort;
|
|
8540
8549
|
httpWithMtls.setMTLSAdapter(this.adapters.mtls);
|
|
8541
8550
|
}
|
|
8542
8551
|
if ('setAuthStrategy' in baseHttpPort) {
|
|
8543
|
-
log$
|
|
8552
|
+
log$2.debug('Configuring auth strategy');
|
|
8544
8553
|
const jwtHandler = new JwtAuthHandler(tokenStorage);
|
|
8545
8554
|
const certificatePort = this.certificateService
|
|
8546
8555
|
? {
|
|
@@ -8589,19 +8598,19 @@ class ACubeSDK {
|
|
|
8589
8598
|
}
|
|
8590
8599
|
}
|
|
8591
8600
|
catch (certError) {
|
|
8592
|
-
log$
|
|
8601
|
+
log$2.warn('Certificate auto-configuration failed, will retry on demand', {
|
|
8593
8602
|
error: certError instanceof Error ? certError.message : certError,
|
|
8594
8603
|
});
|
|
8595
8604
|
}
|
|
8596
8605
|
}
|
|
8597
8606
|
this.isInitialized = true;
|
|
8598
|
-
log$
|
|
8607
|
+
log$2.info('SDK initialized successfully', {
|
|
8599
8608
|
hasCache: !!this.adapters.cache,
|
|
8600
8609
|
hasMtls: !!this.adapters.mtls,
|
|
8601
8610
|
});
|
|
8602
8611
|
}
|
|
8603
8612
|
catch (error) {
|
|
8604
|
-
log$
|
|
8613
|
+
log$2.error('SDK initialization failed', {
|
|
8605
8614
|
error: error instanceof Error ? error.message : error,
|
|
8606
8615
|
});
|
|
8607
8616
|
throw new ACubeSDKError('SDK_INITIALIZATION_ERROR', `Failed to initialize SDK: ${error instanceof Error ? error.message : 'Unknown error'}`, error);
|
|
@@ -8657,19 +8666,19 @@ class ACubeSDK {
|
|
|
8657
8666
|
}
|
|
8658
8667
|
async login(credentials) {
|
|
8659
8668
|
this.ensureInitialized();
|
|
8660
|
-
log$
|
|
8669
|
+
log$2.info('Login attempt', { email: credentials.email });
|
|
8661
8670
|
const user = await this.authService.login(credentials);
|
|
8662
|
-
log$
|
|
8671
|
+
log$2.info('Login successful', { roles: user.roles });
|
|
8663
8672
|
const token = await this.authService.getAccessToken();
|
|
8664
8673
|
if (token) {
|
|
8665
8674
|
this.httpPort.setAuthToken(token);
|
|
8666
|
-
log$
|
|
8675
|
+
log$2.debug('Auth token set on HTTP port');
|
|
8667
8676
|
}
|
|
8668
8677
|
return user;
|
|
8669
8678
|
}
|
|
8670
8679
|
async logout() {
|
|
8671
8680
|
this.ensureInitialized();
|
|
8672
|
-
log$
|
|
8681
|
+
log$2.info('Logout');
|
|
8673
8682
|
await this.authService.logout();
|
|
8674
8683
|
this.httpPort.setAuthToken(null);
|
|
8675
8684
|
}
|
|
@@ -8843,6 +8852,7 @@ const INITIAL_STATE = {
|
|
|
8843
8852
|
remainingMs: 0,
|
|
8844
8853
|
},
|
|
8845
8854
|
lastNotification: null,
|
|
8855
|
+
certificateMissing: false,
|
|
8846
8856
|
};
|
|
8847
8857
|
class AppStateService {
|
|
8848
8858
|
get state$() {
|
|
@@ -8857,28 +8867,33 @@ class AppStateService {
|
|
|
8857
8867
|
get warning$() {
|
|
8858
8868
|
return this.state$.pipe(map((s) => s.warning), distinctUntilChanged((a, b) => a.active === b.active && a.remainingMs === b.remainingMs));
|
|
8859
8869
|
}
|
|
8860
|
-
|
|
8870
|
+
get certificateMissing$() {
|
|
8871
|
+
return this.state$.pipe(map((s) => s.certificateMissing), distinctUntilChanged());
|
|
8872
|
+
}
|
|
8873
|
+
constructor(notifications$, networkPort, certificateMissingInput$ = of(false)) {
|
|
8861
8874
|
this.notifications$ = notifications$;
|
|
8862
8875
|
this.networkPort = networkPort;
|
|
8876
|
+
this.certificateMissingInput$ = certificateMissingInput$;
|
|
8863
8877
|
this.stateSubject = new BehaviorSubject(INITIAL_STATE);
|
|
8864
8878
|
this.destroy$ = new Subject();
|
|
8865
8879
|
this.warningTimerId = null;
|
|
8866
8880
|
this.setupSubscriptions();
|
|
8867
8881
|
}
|
|
8868
8882
|
setupSubscriptions() {
|
|
8869
|
-
combineLatest([this.notifications$, this.networkPort.online$])
|
|
8883
|
+
combineLatest([this.notifications$, this.networkPort.online$, this.certificateMissingInput$])
|
|
8870
8884
|
.pipe(takeUntil(this.destroy$))
|
|
8871
|
-
.subscribe(([notifications, isOnline]) => {
|
|
8872
|
-
this.processState(notifications, isOnline);
|
|
8885
|
+
.subscribe(([notifications, isOnline, certificateMissing]) => {
|
|
8886
|
+
this.processState(notifications, isOnline, certificateMissing);
|
|
8873
8887
|
});
|
|
8874
8888
|
}
|
|
8875
|
-
processState(notifications, isOnline) {
|
|
8889
|
+
processState(notifications, isOnline, certificateMissing) {
|
|
8876
8890
|
if (!isOnline) {
|
|
8877
8891
|
this.updateState({
|
|
8878
8892
|
mode: 'OFFLINE',
|
|
8879
8893
|
isOnline: false,
|
|
8880
8894
|
warning: { active: false, blockAt: null, remainingMs: 0 },
|
|
8881
8895
|
lastNotification: null,
|
|
8896
|
+
certificateMissing,
|
|
8882
8897
|
});
|
|
8883
8898
|
this.stopWarningTimer();
|
|
8884
8899
|
return;
|
|
@@ -8940,6 +8955,7 @@ class AppStateService {
|
|
|
8940
8955
|
isOnline: true,
|
|
8941
8956
|
warning: warningState,
|
|
8942
8957
|
lastNotification,
|
|
8958
|
+
certificateMissing,
|
|
8943
8959
|
});
|
|
8944
8960
|
}
|
|
8945
8961
|
getLatestNotificationByCode(notifications) {
|
|
@@ -9205,6 +9221,7 @@ class TelemetryService {
|
|
|
9205
9221
|
}
|
|
9206
9222
|
}
|
|
9207
9223
|
|
|
9224
|
+
const log$1 = createPrefixedLogger('SDK-MANAGER');
|
|
9208
9225
|
/**
|
|
9209
9226
|
* SDKManager - Singleton wrapper for ACubeSDK with simplified API
|
|
9210
9227
|
*
|
|
@@ -9255,6 +9272,7 @@ class SDKManager {
|
|
|
9255
9272
|
this.appStateService = null;
|
|
9256
9273
|
this.isInitialized = false;
|
|
9257
9274
|
this.isPollingActive = false;
|
|
9275
|
+
this.certificateMissingSubject = new BehaviorSubject(false);
|
|
9258
9276
|
/**
|
|
9259
9277
|
* Handle user state changes (login/logout/token expiration)
|
|
9260
9278
|
* Manages polling lifecycle based on user role
|
|
@@ -9265,9 +9283,14 @@ class SDKManager {
|
|
|
9265
9283
|
if (!this.isInitialized)
|
|
9266
9284
|
return;
|
|
9267
9285
|
if (user) {
|
|
9268
|
-
// User logged in - check role and
|
|
9286
|
+
// User logged in - check role and certificate before starting polling
|
|
9269
9287
|
const canPoll = hasAnyRole(user.roles, ['ROLE_MERCHANT', 'ROLE_CASHIER']);
|
|
9270
9288
|
if (canPoll && !this.isPollingActive) {
|
|
9289
|
+
const hasCert = await this.checkCertificate();
|
|
9290
|
+
if (!hasCert) {
|
|
9291
|
+
log$1.warn('Certificate missing — polling blocked until certificate is installed');
|
|
9292
|
+
return;
|
|
9293
|
+
}
|
|
9271
9294
|
this.notificationService?.startPolling();
|
|
9272
9295
|
await this.startTelemetryPollingAuto();
|
|
9273
9296
|
this.isPollingActive = true;
|
|
@@ -9281,6 +9304,7 @@ class SDKManager {
|
|
|
9281
9304
|
this.telemetryService?.clearTelemetry();
|
|
9282
9305
|
this.isPollingActive = false;
|
|
9283
9306
|
}
|
|
9307
|
+
this.certificateMissingSubject.next(false);
|
|
9284
9308
|
}
|
|
9285
9309
|
};
|
|
9286
9310
|
}
|
|
@@ -9347,7 +9371,7 @@ class SDKManager {
|
|
|
9347
9371
|
this.telemetryService = new TelemetryService(telemetryRepo, networkPort, {
|
|
9348
9372
|
pollIntervalMs: this.config.telemetryPollIntervalMs ?? 60000,
|
|
9349
9373
|
});
|
|
9350
|
-
this.appStateService = new AppStateService(this.notificationService.notifications$, networkPort);
|
|
9374
|
+
this.appStateService = new AppStateService(this.notificationService.notifications$, networkPort, this.certificateMissingSubject.asObservable());
|
|
9351
9375
|
if (this.events?.onAppStateChanged) {
|
|
9352
9376
|
this.appStateService.state$.subscribe(this.events.onAppStateChanged);
|
|
9353
9377
|
}
|
|
@@ -9359,9 +9383,15 @@ class SDKManager {
|
|
|
9359
9383
|
const user = await this.sdk.getCurrentUser();
|
|
9360
9384
|
const canPoll = user && hasAnyRole(user.roles, ['ROLE_MERCHANT', 'ROLE_CASHIER']);
|
|
9361
9385
|
if (canPoll) {
|
|
9362
|
-
this.
|
|
9363
|
-
|
|
9364
|
-
|
|
9386
|
+
const hasCert = await this.checkCertificate();
|
|
9387
|
+
if (hasCert) {
|
|
9388
|
+
this.notificationService.startPolling();
|
|
9389
|
+
await this.startTelemetryPollingAuto();
|
|
9390
|
+
this.isPollingActive = true;
|
|
9391
|
+
}
|
|
9392
|
+
else {
|
|
9393
|
+
log$1.warn('Certificate missing at init — polling blocked until certificate is installed');
|
|
9394
|
+
}
|
|
9365
9395
|
}
|
|
9366
9396
|
// AppStateService remains active for all users (handles OFFLINE network state)
|
|
9367
9397
|
}
|
|
@@ -9395,6 +9425,13 @@ class SDKManager {
|
|
|
9395
9425
|
this.ensureInitialized();
|
|
9396
9426
|
return this.appStateService.warning$;
|
|
9397
9427
|
}
|
|
9428
|
+
/**
|
|
9429
|
+
* Observable stream indicating if certificate is missing
|
|
9430
|
+
* When true, polling is blocked and the user should install a certificate
|
|
9431
|
+
*/
|
|
9432
|
+
get certificateMissing$() {
|
|
9433
|
+
return this.certificateMissingSubject.asObservable();
|
|
9434
|
+
}
|
|
9398
9435
|
/**
|
|
9399
9436
|
* Observable stream of telemetry state (data, isLoading, isCached, error)
|
|
9400
9437
|
*/
|
|
@@ -9472,9 +9509,37 @@ class SDKManager {
|
|
|
9472
9509
|
logout: () => sdk.logout(),
|
|
9473
9510
|
getCurrentUser: () => sdk.getCurrentUser(),
|
|
9474
9511
|
isAuthenticated: () => sdk.isAuthenticated(),
|
|
9475
|
-
storeCertificate: (certificate, privateKey, options) =>
|
|
9512
|
+
storeCertificate: async (certificate, privateKey, options) => {
|
|
9513
|
+
await sdk.storeCertificate(certificate, privateKey, options);
|
|
9514
|
+
this.certificateMissingSubject.next(false);
|
|
9515
|
+
// Start polling if user can poll and polling is not active
|
|
9516
|
+
if (!this.isPollingActive) {
|
|
9517
|
+
const user = await sdk.getCurrentUser();
|
|
9518
|
+
const canPoll = user && hasAnyRole(user.roles, ['ROLE_MERCHANT', 'ROLE_CASHIER']);
|
|
9519
|
+
if (canPoll) {
|
|
9520
|
+
log$1.info('Certificate installed — starting polling');
|
|
9521
|
+
this.notificationService?.startPolling();
|
|
9522
|
+
await this.startTelemetryPollingAuto();
|
|
9523
|
+
this.isPollingActive = true;
|
|
9524
|
+
}
|
|
9525
|
+
}
|
|
9526
|
+
},
|
|
9476
9527
|
hasCertificate: () => sdk.hasCertificate(),
|
|
9477
|
-
clearCertificate: () =>
|
|
9528
|
+
clearCertificate: async () => {
|
|
9529
|
+
await sdk.clearCertificate();
|
|
9530
|
+
this.certificateMissingSubject.next(true);
|
|
9531
|
+
// Stop polling since certificate is required
|
|
9532
|
+
if (this.isPollingActive) {
|
|
9533
|
+
log$1.info('Certificate removed — stopping polling');
|
|
9534
|
+
this.notificationService?.stopPolling();
|
|
9535
|
+
this.telemetryService?.stopPolling();
|
|
9536
|
+
this.telemetryService?.clearTelemetry();
|
|
9537
|
+
this.isPollingActive = false;
|
|
9538
|
+
}
|
|
9539
|
+
},
|
|
9540
|
+
getCertificate: () => sdk.getCertificate(),
|
|
9541
|
+
getCertificatesInfo: () => sdk.getCertificatesInfo(),
|
|
9542
|
+
notifications: sdk.notifications,
|
|
9478
9543
|
isOnline: () => sdk.isOnline(),
|
|
9479
9544
|
};
|
|
9480
9545
|
}
|
|
@@ -9505,6 +9570,21 @@ class SDKManager {
|
|
|
9505
9570
|
this.ensureInitialized();
|
|
9506
9571
|
return this.sdk;
|
|
9507
9572
|
}
|
|
9573
|
+
/**
|
|
9574
|
+
* Check certificate availability and update certificateMissing state.
|
|
9575
|
+
* Returns true if certificate is available, false otherwise.
|
|
9576
|
+
*/
|
|
9577
|
+
async checkCertificate() {
|
|
9578
|
+
try {
|
|
9579
|
+
const hasCert = await this.sdk.hasCertificate();
|
|
9580
|
+
this.certificateMissingSubject.next(!hasCert);
|
|
9581
|
+
return hasCert;
|
|
9582
|
+
}
|
|
9583
|
+
catch {
|
|
9584
|
+
this.certificateMissingSubject.next(true);
|
|
9585
|
+
return false;
|
|
9586
|
+
}
|
|
9587
|
+
}
|
|
9508
9588
|
cleanup() {
|
|
9509
9589
|
this.notificationService?.destroy();
|
|
9510
9590
|
this.telemetryService?.destroy();
|