@a-cube-io/ereceipts-js-sdk 2.0.7 → 2.0.8
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 +1664 -1575
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +1664 -1575
- package/dist/index.esm.js.map +1 -1
- package/dist/index.native.js +1664 -1575
- package/dist/index.native.js.map +1 -1
- package/package.json +1 -1
- package/dist/react.cjs.js +0 -7972
- package/dist/react.cjs.js.map +0 -1
- package/dist/react.d.ts +0 -1615
- package/dist/react.esm.js +0 -7948
- package/dist/react.esm.js.map +0 -1
package/dist/index.native.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import axios from 'axios';
|
|
2
1
|
import * as z from 'zod';
|
|
2
|
+
import axios from 'axios';
|
|
3
3
|
|
|
4
4
|
/******************************************************************************
|
|
5
5
|
Copyright (c) Microsoft Corporation.
|
|
@@ -1838,135 +1838,440 @@ function extractRoles(jwtRoles) {
|
|
|
1838
1838
|
return allRoles;
|
|
1839
1839
|
}
|
|
1840
1840
|
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
return
|
|
1844
|
-
}
|
|
1845
|
-
get isAuthenticated$() {
|
|
1846
|
-
return this.userSubject.pipe(map((user) => user !== null), distinctUntilChanged(), takeUntil(this.destroy$));
|
|
1847
|
-
}
|
|
1848
|
-
get authState$() {
|
|
1849
|
-
return this.authStateSubject.asObservable();
|
|
1841
|
+
function clearObject(input) {
|
|
1842
|
+
if (input === null || input === undefined || input === '') {
|
|
1843
|
+
return undefined;
|
|
1850
1844
|
}
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
this.userSubject = new BehaviorSubject(null);
|
|
1857
|
-
this.authStateSubject = new BehaviorSubject('idle');
|
|
1858
|
-
this.destroy$ = new Subject();
|
|
1845
|
+
if (Array.isArray(input)) {
|
|
1846
|
+
const cleanedArray = input
|
|
1847
|
+
.map((item) => clearObject(item))
|
|
1848
|
+
.filter((item) => item !== undefined);
|
|
1849
|
+
return cleanedArray;
|
|
1859
1850
|
}
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
const
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
}
|
|
1867
|
-
const jwtPayload = parseJwt(response.data.token);
|
|
1868
|
-
const expiresAt = jwtPayload.exp * 1000;
|
|
1869
|
-
await this.tokenStorage.saveAccessToken(response.data.token, expiresAt);
|
|
1870
|
-
const user = this.createUserFromPayload(jwtPayload);
|
|
1871
|
-
await this.tokenStorage.saveUser(user);
|
|
1872
|
-
this.userSubject.next(user);
|
|
1873
|
-
this.authStateSubject.next('authenticated');
|
|
1874
|
-
this.events.onUserChanged?.(user);
|
|
1875
|
-
return user;
|
|
1876
|
-
}
|
|
1877
|
-
catch (error) {
|
|
1878
|
-
this.authStateSubject.next('error');
|
|
1879
|
-
throw error;
|
|
1851
|
+
if (typeof input === 'object' && input.constructor === Object) {
|
|
1852
|
+
const cleaned = {};
|
|
1853
|
+
for (const [key, value] of Object.entries(input)) {
|
|
1854
|
+
const cleanedValue = clearObject(value);
|
|
1855
|
+
if (cleanedValue !== undefined) {
|
|
1856
|
+
cleaned[key] = cleanedValue;
|
|
1857
|
+
}
|
|
1880
1858
|
}
|
|
1859
|
+
return cleaned;
|
|
1881
1860
|
}
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1861
|
+
return input;
|
|
1862
|
+
}
|
|
1863
|
+
function clearObjectShallow(obj) {
|
|
1864
|
+
if (!obj || typeof obj !== 'object') {
|
|
1865
|
+
return {};
|
|
1887
1866
|
}
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
if (
|
|
1891
|
-
|
|
1892
|
-
}
|
|
1893
|
-
const storedUser = await this.tokenStorage.getUser();
|
|
1894
|
-
if (storedUser) {
|
|
1895
|
-
this.userSubject.next(storedUser);
|
|
1896
|
-
this.authStateSubject.next('authenticated');
|
|
1897
|
-
return storedUser;
|
|
1898
|
-
}
|
|
1899
|
-
const token = await this.tokenStorage.getAccessToken();
|
|
1900
|
-
if (!token) {
|
|
1901
|
-
return null;
|
|
1902
|
-
}
|
|
1903
|
-
const jwtPayload = parseJwt(token);
|
|
1904
|
-
if (isTokenExpired(jwtPayload)) {
|
|
1905
|
-
await this.tokenStorage.clearTokens();
|
|
1906
|
-
return null;
|
|
1867
|
+
const cleaned = {};
|
|
1868
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
1869
|
+
if (value !== null && value !== undefined && value !== '') {
|
|
1870
|
+
cleaned[key] = value;
|
|
1907
1871
|
}
|
|
1908
|
-
const user = this.createUserFromPayload(jwtPayload);
|
|
1909
|
-
await this.tokenStorage.saveUser(user);
|
|
1910
|
-
this.userSubject.next(user);
|
|
1911
|
-
this.authStateSubject.next('authenticated');
|
|
1912
|
-
return user;
|
|
1913
1872
|
}
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1873
|
+
return cleaned;
|
|
1874
|
+
}
|
|
1875
|
+
function isEmpty(value) {
|
|
1876
|
+
return value === null || value === undefined || value === '';
|
|
1877
|
+
}
|
|
1878
|
+
function hasNonEmptyValues(obj) {
|
|
1879
|
+
if (!obj || typeof obj !== 'object') {
|
|
1880
|
+
return false;
|
|
1921
1881
|
}
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1882
|
+
return Object.values(obj).some((value) => !isEmpty(value));
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
/**
|
|
1886
|
+
* Platform detection utilities
|
|
1887
|
+
*/
|
|
1888
|
+
/**
|
|
1889
|
+
* Detect the current platform
|
|
1890
|
+
*/
|
|
1891
|
+
function detectPlatform() {
|
|
1892
|
+
// Check for React Native
|
|
1893
|
+
if (typeof global !== 'undefined' &&
|
|
1894
|
+
global.__DEV__ !== undefined &&
|
|
1895
|
+
typeof global.navigator !== 'undefined' &&
|
|
1896
|
+
global.navigator.product === 'ReactNative') {
|
|
1897
|
+
return {
|
|
1898
|
+
platform: 'react-native',
|
|
1899
|
+
isReactNative: true,
|
|
1900
|
+
isWeb: false,
|
|
1901
|
+
isNode: false,
|
|
1902
|
+
isExpo: checkExpo(),
|
|
1903
|
+
};
|
|
1936
1904
|
}
|
|
1937
|
-
|
|
1905
|
+
// Check for Web/Browser
|
|
1906
|
+
if (typeof window !== 'undefined' &&
|
|
1907
|
+
typeof window.document !== 'undefined' &&
|
|
1908
|
+
typeof window.navigator !== 'undefined') {
|
|
1938
1909
|
return {
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
pid: jwtPayload.pid,
|
|
1945
|
-
expiresAt: jwtPayload.exp * 1000,
|
|
1910
|
+
platform: 'web',
|
|
1911
|
+
isReactNative: false,
|
|
1912
|
+
isWeb: true,
|
|
1913
|
+
isNode: false,
|
|
1914
|
+
isExpo: false,
|
|
1946
1915
|
};
|
|
1947
1916
|
}
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1917
|
+
// Check for Node.js
|
|
1918
|
+
if (typeof process !== 'undefined' && process.versions && process.versions.node) {
|
|
1919
|
+
return {
|
|
1920
|
+
platform: 'node',
|
|
1921
|
+
isReactNative: false,
|
|
1922
|
+
isWeb: false,
|
|
1923
|
+
isNode: true,
|
|
1924
|
+
isExpo: false,
|
|
1925
|
+
};
|
|
1951
1926
|
}
|
|
1927
|
+
// Unknown platform
|
|
1928
|
+
return {
|
|
1929
|
+
platform: 'unknown',
|
|
1930
|
+
isReactNative: false,
|
|
1931
|
+
isWeb: false,
|
|
1932
|
+
isNode: false,
|
|
1933
|
+
isExpo: false,
|
|
1934
|
+
};
|
|
1952
1935
|
}
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1936
|
+
/**
|
|
1937
|
+
* Check if running in Expo
|
|
1938
|
+
*/
|
|
1939
|
+
function checkExpo() {
|
|
1940
|
+
try {
|
|
1941
|
+
return (typeof global !== 'undefined' &&
|
|
1942
|
+
(typeof global.Expo !== 'undefined' || typeof global.expo !== 'undefined'));
|
|
1958
1943
|
}
|
|
1959
|
-
|
|
1960
|
-
return
|
|
1944
|
+
catch {
|
|
1945
|
+
return false;
|
|
1961
1946
|
}
|
|
1962
|
-
|
|
1963
|
-
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
function getDefaultExportFromCjs (x) {
|
|
1950
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
var picocolors_browser = {exports: {}};
|
|
1954
|
+
|
|
1955
|
+
var x=String;
|
|
1956
|
+
var create=function() {return {isColorSupported:false,reset:x,bold:x,dim:x,italic:x,underline:x,inverse:x,hidden:x,strikethrough:x,black:x,red:x,green:x,yellow:x,blue:x,magenta:x,cyan:x,white:x,gray:x,bgBlack:x,bgRed:x,bgGreen:x,bgYellow:x,bgBlue:x,bgMagenta:x,bgCyan:x,bgWhite:x,blackBright:x,redBright:x,greenBright:x,yellowBright:x,blueBright:x,magentaBright:x,cyanBright:x,whiteBright:x,bgBlackBright:x,bgRedBright:x,bgGreenBright:x,bgYellowBright:x,bgBlueBright:x,bgMagentaBright:x,bgCyanBright:x,bgWhiteBright:x}};
|
|
1957
|
+
picocolors_browser.exports=create();
|
|
1958
|
+
picocolors_browser.exports.createColors = create;
|
|
1959
|
+
|
|
1960
|
+
var picocolors_browserExports = picocolors_browser.exports;
|
|
1961
|
+
var pc = /*@__PURE__*/getDefaultExportFromCjs(picocolors_browserExports);
|
|
1962
|
+
|
|
1963
|
+
const levelColors = {
|
|
1964
|
+
debug: {
|
|
1965
|
+
prefix: pc.gray,
|
|
1966
|
+
message: pc.gray,
|
|
1967
|
+
data: pc.dim,
|
|
1968
|
+
},
|
|
1969
|
+
info: {
|
|
1970
|
+
prefix: pc.cyan,
|
|
1971
|
+
message: pc.white,
|
|
1972
|
+
data: pc.dim,
|
|
1973
|
+
},
|
|
1974
|
+
warn: {
|
|
1975
|
+
prefix: pc.yellow,
|
|
1976
|
+
message: pc.yellow,
|
|
1977
|
+
data: pc.dim,
|
|
1978
|
+
},
|
|
1979
|
+
error: {
|
|
1980
|
+
prefix: pc.red,
|
|
1981
|
+
message: pc.red,
|
|
1982
|
+
data: pc.dim,
|
|
1983
|
+
},
|
|
1984
|
+
};
|
|
1985
|
+
const levelIcons = {
|
|
1986
|
+
debug: '🔍',
|
|
1987
|
+
info: 'ℹ️ ',
|
|
1988
|
+
warn: '⚠️ ',
|
|
1989
|
+
error: '❌',
|
|
1990
|
+
};
|
|
1991
|
+
/**
|
|
1992
|
+
* Format data for pretty display in logs
|
|
1993
|
+
*/
|
|
1994
|
+
function formatData(data, level) {
|
|
1995
|
+
if (data === undefined || data === null) {
|
|
1996
|
+
return String(data);
|
|
1964
1997
|
}
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1998
|
+
// For primitive types, just return as string
|
|
1999
|
+
if (typeof data !== 'object') {
|
|
2000
|
+
return String(data);
|
|
2001
|
+
}
|
|
2002
|
+
// For Error objects, format nicely
|
|
2003
|
+
if (data instanceof Error) {
|
|
2004
|
+
return `${data.name}: ${data.message}${data.stack ? `\n${pc.dim(data.stack)}` : ''}`;
|
|
2005
|
+
}
|
|
2006
|
+
try {
|
|
2007
|
+
// Apply level-specific coloring
|
|
2008
|
+
const colors = levelColors[level];
|
|
2009
|
+
return colors.data(JSON.stringify(data, null, 2));
|
|
2010
|
+
}
|
|
2011
|
+
catch {
|
|
2012
|
+
// Fallback to JSON.stringify if pretty-format fails
|
|
2013
|
+
try {
|
|
2014
|
+
return JSON.stringify(data, null, 2);
|
|
2015
|
+
}
|
|
2016
|
+
catch {
|
|
2017
|
+
return String(data);
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
class Logger {
|
|
2022
|
+
constructor() {
|
|
2023
|
+
this.enabled = false;
|
|
2024
|
+
}
|
|
2025
|
+
setEnabled(enabled) {
|
|
2026
|
+
this.enabled = enabled;
|
|
2027
|
+
}
|
|
2028
|
+
isEnabled() {
|
|
2029
|
+
return this.enabled;
|
|
2030
|
+
}
|
|
2031
|
+
debug(prefix, message, data) {
|
|
2032
|
+
if (!this.enabled)
|
|
2033
|
+
return;
|
|
2034
|
+
this.log('debug', prefix, message, data);
|
|
2035
|
+
}
|
|
2036
|
+
info(prefix, message, data) {
|
|
2037
|
+
if (!this.enabled)
|
|
2038
|
+
return;
|
|
2039
|
+
this.log('info', prefix, message, data);
|
|
2040
|
+
}
|
|
2041
|
+
warn(prefix, message, data) {
|
|
2042
|
+
this.log('warn', prefix, message, data);
|
|
2043
|
+
}
|
|
2044
|
+
error(prefix, message, data) {
|
|
2045
|
+
this.log('error', prefix, message, data);
|
|
2046
|
+
}
|
|
2047
|
+
log(level, prefix, message, data) {
|
|
2048
|
+
const colors = levelColors[level];
|
|
2049
|
+
const icon = levelIcons[level];
|
|
2050
|
+
const isoTime = new Date().toISOString().split('T')[1] ?? '';
|
|
2051
|
+
const timestamp = pc.dim(isoTime.slice(0, 12));
|
|
2052
|
+
const formattedPrefix = colors.prefix(`[${prefix}]`);
|
|
2053
|
+
const formattedMessage = colors.message(message);
|
|
2054
|
+
const consoleMethod = level === 'debug'
|
|
2055
|
+
? console.debug
|
|
2056
|
+
: level === 'info'
|
|
2057
|
+
? console.info
|
|
2058
|
+
: level === 'warn'
|
|
2059
|
+
? console.warn
|
|
2060
|
+
: console.error;
|
|
2061
|
+
const header = `${timestamp} ${icon} ${formattedPrefix} ${formattedMessage}`;
|
|
2062
|
+
if (data !== undefined) {
|
|
2063
|
+
const formattedData = formatData(data, level);
|
|
2064
|
+
// Check if data is an object (multi-line) or primitive (single-line)
|
|
2065
|
+
const isMultiLine = typeof data === 'object' && data !== null;
|
|
2066
|
+
if (isMultiLine) {
|
|
2067
|
+
consoleMethod(`${header}\n${formattedData}`);
|
|
2068
|
+
}
|
|
2069
|
+
else {
|
|
2070
|
+
consoleMethod(`${header}`, formattedData);
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
else {
|
|
2074
|
+
consoleMethod(header);
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
const logger = new Logger();
|
|
2079
|
+
function createPrefixedLogger(prefix) {
|
|
2080
|
+
return {
|
|
2081
|
+
debug: (message, data) => logger.debug(prefix, message, data),
|
|
2082
|
+
info: (message, data) => logger.info(prefix, message, data),
|
|
2083
|
+
warn: (message, data) => logger.warn(prefix, message, data),
|
|
2084
|
+
error: (message, data) => logger.error(prefix, message, data),
|
|
2085
|
+
};
|
|
2086
|
+
}
|
|
2087
|
+
|
|
2088
|
+
/**
|
|
2089
|
+
* Formats a numeric string value to have exactly the specified number of decimal places.
|
|
2090
|
+
* E.g., "1" → "1.00", "10" → "10.00", "1.5" → "1.50"
|
|
2091
|
+
* Returns undefined for undefined input (preserves optional fields).
|
|
2092
|
+
*
|
|
2093
|
+
* @param value - The string value to format
|
|
2094
|
+
* @param decimals - Number of decimal places (default: 2)
|
|
2095
|
+
* @returns Formatted string or undefined if input is undefined
|
|
2096
|
+
*/
|
|
2097
|
+
function formatDecimal(value, decimals = 2) {
|
|
2098
|
+
if (value === undefined)
|
|
2099
|
+
return undefined;
|
|
2100
|
+
const num = parseFloat(value);
|
|
2101
|
+
if (isNaN(num))
|
|
2102
|
+
return value;
|
|
2103
|
+
return num.toFixed(decimals);
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
const log$g = createPrefixedLogger('AUTH-SERVICE');
|
|
2107
|
+
class AuthenticationService {
|
|
2108
|
+
get user$() {
|
|
2109
|
+
return this.userSubject.asObservable();
|
|
2110
|
+
}
|
|
2111
|
+
get isAuthenticated$() {
|
|
2112
|
+
return this.userSubject.pipe(map((user) => user !== null), distinctUntilChanged(), takeUntil(this.destroy$));
|
|
2113
|
+
}
|
|
2114
|
+
get authState$() {
|
|
2115
|
+
return this.authStateSubject.asObservable();
|
|
2116
|
+
}
|
|
2117
|
+
constructor(httpPort, tokenStorage, config, events = {}) {
|
|
2118
|
+
this.httpPort = httpPort;
|
|
2119
|
+
this.tokenStorage = tokenStorage;
|
|
2120
|
+
this.config = config;
|
|
2121
|
+
this.events = events;
|
|
2122
|
+
this.userSubject = new BehaviorSubject(null);
|
|
2123
|
+
this.authStateSubject = new BehaviorSubject('idle');
|
|
2124
|
+
this.destroy$ = new Subject();
|
|
2125
|
+
}
|
|
2126
|
+
async login(credentials) {
|
|
2127
|
+
this.authStateSubject.next('authenticating');
|
|
2128
|
+
log$g.info('Login attempt', {
|
|
2129
|
+
authUrl: this.config.authUrl,
|
|
2130
|
+
email: credentials.email,
|
|
2131
|
+
});
|
|
2132
|
+
try {
|
|
2133
|
+
const response = await this.httpPort.post(`${this.config.authUrl}/login`, {
|
|
2134
|
+
email: credentials.email,
|
|
2135
|
+
password: credentials.password,
|
|
2136
|
+
});
|
|
2137
|
+
const jwtPayload = parseJwt(response.data.token);
|
|
2138
|
+
const expiresAt = jwtPayload.exp * 1000;
|
|
2139
|
+
log$g.info('Login successful', {
|
|
2140
|
+
authUrl: this.config.authUrl,
|
|
2141
|
+
tokenPrefix: response.data.token.substring(0, 30) + '...',
|
|
2142
|
+
expiresAt: new Date(expiresAt).toISOString(),
|
|
2143
|
+
});
|
|
2144
|
+
await this.tokenStorage.saveAccessToken(response.data.token, expiresAt);
|
|
2145
|
+
const user = this.createUserFromPayload(jwtPayload);
|
|
2146
|
+
await this.tokenStorage.saveUser(user);
|
|
2147
|
+
this.userSubject.next(user);
|
|
2148
|
+
this.authStateSubject.next('authenticated');
|
|
2149
|
+
this.events.onUserChanged?.(user);
|
|
2150
|
+
return user;
|
|
2151
|
+
}
|
|
2152
|
+
catch (error) {
|
|
2153
|
+
this.authStateSubject.next('error');
|
|
2154
|
+
throw error;
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
async logout() {
|
|
2158
|
+
await this.tokenStorage.clearTokens();
|
|
2159
|
+
this.userSubject.next(null);
|
|
2160
|
+
this.authStateSubject.next('idle');
|
|
2161
|
+
this.events.onUserChanged?.(null);
|
|
2162
|
+
}
|
|
2163
|
+
async getCurrentUser() {
|
|
2164
|
+
// Always verify token is valid before returning user
|
|
2165
|
+
const token = await this.tokenStorage.getAccessToken();
|
|
2166
|
+
if (!token) {
|
|
2167
|
+
// No token - clear any stale user state
|
|
2168
|
+
log$g.debug('getCurrentUser: No token in storage');
|
|
2169
|
+
if (this.userSubject.value) {
|
|
2170
|
+
this.userSubject.next(null);
|
|
2171
|
+
this.authStateSubject.next('idle');
|
|
2172
|
+
}
|
|
2173
|
+
return null;
|
|
2174
|
+
}
|
|
2175
|
+
log$g.debug('getCurrentUser: Token found', {
|
|
2176
|
+
tokenPrefix: token.substring(0, 30) + '...',
|
|
2177
|
+
tokenLength: token.length,
|
|
2178
|
+
});
|
|
2179
|
+
const jwtPayload = parseJwt(token);
|
|
2180
|
+
if (isTokenExpired(jwtPayload)) {
|
|
2181
|
+
// Token expired - clear everything
|
|
2182
|
+
log$g.warn('getCurrentUser: Token expired');
|
|
2183
|
+
await this.tokenStorage.clearTokens();
|
|
2184
|
+
this.userSubject.next(null);
|
|
2185
|
+
this.authStateSubject.next('idle');
|
|
2186
|
+
this.events.onUserChanged?.(null);
|
|
2187
|
+
return null;
|
|
2188
|
+
}
|
|
2189
|
+
// Token is valid - return cached user if available
|
|
2190
|
+
const currentUser = this.userSubject.value;
|
|
2191
|
+
if (currentUser) {
|
|
2192
|
+
log$g.debug('getCurrentUser: Returning cached user', {
|
|
2193
|
+
email: currentUser.email,
|
|
2194
|
+
roles: currentUser.roles,
|
|
2195
|
+
});
|
|
2196
|
+
return currentUser;
|
|
2197
|
+
}
|
|
2198
|
+
// Check stored user
|
|
2199
|
+
const storedUser = await this.tokenStorage.getUser();
|
|
2200
|
+
if (storedUser) {
|
|
2201
|
+
this.userSubject.next(storedUser);
|
|
2202
|
+
this.authStateSubject.next('authenticated');
|
|
2203
|
+
return storedUser;
|
|
2204
|
+
}
|
|
2205
|
+
// Create user from token
|
|
2206
|
+
const user = this.createUserFromPayload(jwtPayload);
|
|
2207
|
+
await this.tokenStorage.saveUser(user);
|
|
2208
|
+
this.userSubject.next(user);
|
|
2209
|
+
this.authStateSubject.next('authenticated');
|
|
2210
|
+
return user;
|
|
2211
|
+
}
|
|
2212
|
+
async isAuthenticated() {
|
|
2213
|
+
const token = await this.tokenStorage.getAccessToken();
|
|
2214
|
+
if (!token) {
|
|
2215
|
+
log$g.debug('isAuthenticated: No token in storage');
|
|
2216
|
+
return false;
|
|
2217
|
+
}
|
|
2218
|
+
const jwtPayload = parseJwt(token);
|
|
2219
|
+
const expired = isTokenExpired(jwtPayload);
|
|
2220
|
+
log$g.debug('isAuthenticated: Token check', {
|
|
2221
|
+
hasToken: true,
|
|
2222
|
+
expired,
|
|
2223
|
+
expiresAt: new Date(jwtPayload.exp * 1000).toISOString(),
|
|
2224
|
+
});
|
|
2225
|
+
return !expired;
|
|
2226
|
+
}
|
|
2227
|
+
async getAccessToken() {
|
|
2228
|
+
const token = await this.tokenStorage.getAccessToken();
|
|
2229
|
+
if (!token) {
|
|
2230
|
+
return null;
|
|
2231
|
+
}
|
|
2232
|
+
const jwtPayload = parseJwt(token);
|
|
2233
|
+
if (isTokenExpired(jwtPayload)) {
|
|
2234
|
+
await this.tokenStorage.clearTokens();
|
|
2235
|
+
this.userSubject.next(null);
|
|
2236
|
+
this.authStateSubject.next('idle');
|
|
2237
|
+
this.events.onUserChanged?.(null);
|
|
2238
|
+
return null;
|
|
2239
|
+
}
|
|
2240
|
+
return token;
|
|
2241
|
+
}
|
|
2242
|
+
createUserFromPayload(jwtPayload) {
|
|
2243
|
+
return {
|
|
2244
|
+
id: jwtPayload.uid.toString(),
|
|
2245
|
+
email: jwtPayload.username,
|
|
2246
|
+
username: jwtPayload.username,
|
|
2247
|
+
roles: jwtPayload.roles,
|
|
2248
|
+
fid: jwtPayload.fid,
|
|
2249
|
+
pid: jwtPayload.pid,
|
|
2250
|
+
expiresAt: jwtPayload.exp * 1000,
|
|
2251
|
+
};
|
|
2252
|
+
}
|
|
2253
|
+
destroy() {
|
|
2254
|
+
this.destroy$.next();
|
|
2255
|
+
this.destroy$.complete();
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
const CERTIFICATE_KEY = 'acube_certificate';
|
|
2260
|
+
class CertificateService {
|
|
2261
|
+
get certificate$() {
|
|
2262
|
+
return this.certificateSubject.asObservable();
|
|
2263
|
+
}
|
|
2264
|
+
get hasCertificate$() {
|
|
2265
|
+
return this.certificateSubject.pipe(map((cert) => cert !== null), distinctUntilChanged());
|
|
2266
|
+
}
|
|
2267
|
+
get state$() {
|
|
2268
|
+
return this.stateSubject.asObservable();
|
|
2269
|
+
}
|
|
2270
|
+
constructor(secureStorage) {
|
|
2271
|
+
this.secureStorage = secureStorage;
|
|
2272
|
+
this.certificateSubject = new BehaviorSubject(null);
|
|
2273
|
+
this.stateSubject = new BehaviorSubject('idle');
|
|
2274
|
+
this.destroy$ = new Subject();
|
|
1970
2275
|
}
|
|
1971
2276
|
async hasCertificate() {
|
|
1972
2277
|
const cert = await this.getCertificate();
|
|
@@ -2057,409 +2362,680 @@ function hasAnyRole(userRoles, required) {
|
|
|
2057
2362
|
return Object.values(userRoles).some((roles) => required.some((role) => roles.includes(role)));
|
|
2058
2363
|
}
|
|
2059
2364
|
|
|
2060
|
-
class
|
|
2061
|
-
constructor(
|
|
2062
|
-
this.
|
|
2063
|
-
this.mtlsHandler = mtlsHandler;
|
|
2064
|
-
this.userProvider = userProvider;
|
|
2065
|
-
this.mtlsAdapter = mtlsAdapter;
|
|
2365
|
+
class ConfigManager {
|
|
2366
|
+
constructor(userConfig) {
|
|
2367
|
+
this.config = this.buildConfig(userConfig);
|
|
2066
2368
|
}
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
}
|
|
2077
|
-
if (userRole === 'CASHIER') {
|
|
2078
|
-
if (!isReceiptEndpoint) {
|
|
2079
|
-
return { mode: 'jwt', usePort444: false };
|
|
2080
|
-
}
|
|
2081
|
-
if (platform === 'mobile') {
|
|
2082
|
-
return { mode: 'mtls', usePort444: true };
|
|
2083
|
-
}
|
|
2084
|
-
return { mode: 'jwt', usePort444: true };
|
|
2085
|
-
}
|
|
2086
|
-
if (userRole === 'MERCHANT') {
|
|
2087
|
-
if (!isReceiptEndpoint) {
|
|
2088
|
-
return { mode: 'jwt', usePort444: false };
|
|
2089
|
-
}
|
|
2090
|
-
if (this.isReturnableItemsEndpoint(url)) {
|
|
2091
|
-
return { mode: 'mtls', usePort444: true };
|
|
2092
|
-
}
|
|
2093
|
-
if (method === 'GET') {
|
|
2094
|
-
if (this.isDetailedReceiptEndpoint(url)) {
|
|
2095
|
-
if (platform === 'mobile') {
|
|
2096
|
-
return { mode: 'mtls', usePort444: true };
|
|
2097
|
-
}
|
|
2098
|
-
return { mode: 'jwt', usePort444: true };
|
|
2099
|
-
}
|
|
2100
|
-
return { mode: 'jwt', usePort444: false };
|
|
2101
|
-
}
|
|
2102
|
-
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {
|
|
2103
|
-
if (platform === 'mobile') {
|
|
2104
|
-
return { mode: 'mtls', usePort444: true };
|
|
2105
|
-
}
|
|
2106
|
-
return { mode: 'jwt', usePort444: true };
|
|
2107
|
-
}
|
|
2108
|
-
return { mode: 'jwt', usePort444: false };
|
|
2109
|
-
}
|
|
2110
|
-
if (explicitMode) {
|
|
2111
|
-
if (userRole === 'SUPPLIER' && explicitMode === 'mtls') {
|
|
2112
|
-
return { mode: 'jwt', usePort444: false };
|
|
2113
|
-
}
|
|
2114
|
-
return {
|
|
2115
|
-
mode: explicitMode,
|
|
2116
|
-
usePort444: explicitMode === 'mtls' || (platform === 'web' && isReceiptEndpoint),
|
|
2117
|
-
};
|
|
2118
|
-
}
|
|
2119
|
-
if (platform === 'web') {
|
|
2120
|
-
return { mode: 'jwt', usePort444: isReceiptEndpoint };
|
|
2121
|
-
}
|
|
2122
|
-
if (isReceiptEndpoint && platform === 'mobile') {
|
|
2123
|
-
return { mode: 'mtls', usePort444: true };
|
|
2124
|
-
}
|
|
2125
|
-
return { mode: 'jwt', usePort444: false };
|
|
2126
|
-
}
|
|
2127
|
-
async getAuthHeaders() {
|
|
2128
|
-
return this.jwtHandler.getAuthHeaders();
|
|
2129
|
-
}
|
|
2130
|
-
getMtlsHandler() {
|
|
2131
|
-
return this.mtlsHandler;
|
|
2132
|
-
}
|
|
2133
|
-
getJwtHandler() {
|
|
2134
|
-
return this.jwtHandler;
|
|
2369
|
+
buildConfig(userConfig) {
|
|
2370
|
+
return {
|
|
2371
|
+
environment: userConfig.environment,
|
|
2372
|
+
apiUrl: this.getDefaultApiUrl(userConfig.environment),
|
|
2373
|
+
authUrl: this.getDefaultAuthUrl(userConfig.environment),
|
|
2374
|
+
timeout: 30000,
|
|
2375
|
+
retryAttempts: 3,
|
|
2376
|
+
debug: userConfig.debug ?? false,
|
|
2377
|
+
customHeaders: {},
|
|
2378
|
+
};
|
|
2135
2379
|
}
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2380
|
+
getDefaultApiUrl(environment) {
|
|
2381
|
+
switch (environment) {
|
|
2382
|
+
case 'production':
|
|
2383
|
+
return 'https://ereceipts-it.acubeapi.com';
|
|
2384
|
+
case 'development':
|
|
2385
|
+
return 'https://ereceipts-it.dev.acubeapi.com';
|
|
2386
|
+
case 'sandbox':
|
|
2387
|
+
default:
|
|
2388
|
+
return 'https://ereceipts-it-sandbox.acubeapi.com';
|
|
2139
2389
|
}
|
|
2140
|
-
const platformInfo = this.mtlsAdapter.getPlatformInfo();
|
|
2141
|
-
return platformInfo.platform === 'web' ? 'web' : 'mobile';
|
|
2142
2390
|
}
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
if (hasRole(user.roles, 'ROLE_SUPPLIER')) {
|
|
2152
|
-
return 'SUPPLIER';
|
|
2153
|
-
}
|
|
2154
|
-
if (hasRole(user.roles, 'ROLE_MERCHANT')) {
|
|
2155
|
-
return 'MERCHANT';
|
|
2156
|
-
}
|
|
2157
|
-
if (hasRole(user.roles, 'ROLE_CASHIER')) {
|
|
2158
|
-
return 'CASHIER';
|
|
2391
|
+
getDefaultAuthUrl(environment) {
|
|
2392
|
+
switch (environment) {
|
|
2393
|
+
case 'production':
|
|
2394
|
+
return 'https://common.api.acubeapi.com';
|
|
2395
|
+
case 'development':
|
|
2396
|
+
case 'sandbox':
|
|
2397
|
+
default:
|
|
2398
|
+
return 'https://common-sandbox.api.acubeapi.com';
|
|
2159
2399
|
}
|
|
2160
|
-
return null;
|
|
2161
2400
|
}
|
|
2162
|
-
|
|
2163
|
-
return
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
url.match(/\/mf1\/receipts\/[a-f0-9-]+\/returnable-items$/));
|
|
2168
|
-
}
|
|
2169
|
-
isDetailedReceiptEndpoint(url) {
|
|
2170
|
-
return !!(url.match(/\/receipts\/[a-f0-9-]+\/details$/) ||
|
|
2171
|
-
url.match(/\/mf1\/receipts\/[a-f0-9-]+\/details$/));
|
|
2401
|
+
getConfig() {
|
|
2402
|
+
return {
|
|
2403
|
+
environment: this.config.environment,
|
|
2404
|
+
debug: this.config.debug,
|
|
2405
|
+
};
|
|
2172
2406
|
}
|
|
2173
|
-
|
|
2174
|
-
return
|
|
2407
|
+
getApiUrl() {
|
|
2408
|
+
return this.config.apiUrl;
|
|
2175
2409
|
}
|
|
2176
|
-
|
|
2177
|
-
return
|
|
2410
|
+
getAuthUrl() {
|
|
2411
|
+
return this.config.authUrl;
|
|
2178
2412
|
}
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
class JwtAuthHandler {
|
|
2182
|
-
constructor(tokenStorage) {
|
|
2183
|
-
this.tokenStorage = tokenStorage;
|
|
2413
|
+
getEnvironment() {
|
|
2414
|
+
return this.config.environment;
|
|
2184
2415
|
}
|
|
2185
|
-
|
|
2186
|
-
return
|
|
2416
|
+
isDebugEnabled() {
|
|
2417
|
+
return this.config.debug;
|
|
2187
2418
|
}
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
if (!token) {
|
|
2191
|
-
return {};
|
|
2192
|
-
}
|
|
2193
|
-
return { Authorization: `Bearer ${token}` };
|
|
2419
|
+
getTimeout() {
|
|
2420
|
+
return this.config.timeout;
|
|
2194
2421
|
}
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
function clearObject(input) {
|
|
2198
|
-
if (input === null || input === undefined || input === '') {
|
|
2199
|
-
return undefined;
|
|
2422
|
+
getRetryAttempts() {
|
|
2423
|
+
return this.config.retryAttempts;
|
|
2200
2424
|
}
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
.map((item) => clearObject(item))
|
|
2204
|
-
.filter((item) => item !== undefined);
|
|
2205
|
-
return cleanedArray;
|
|
2425
|
+
getCustomHeaders() {
|
|
2426
|
+
return { ...this.config.customHeaders };
|
|
2206
2427
|
}
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
cleaned[key] = cleanedValue;
|
|
2213
|
-
}
|
|
2428
|
+
updateConfig(updates) {
|
|
2429
|
+
if (updates.environment) {
|
|
2430
|
+
this.config.environment = updates.environment;
|
|
2431
|
+
this.config.apiUrl = this.getDefaultApiUrl(updates.environment);
|
|
2432
|
+
this.config.authUrl = this.getDefaultAuthUrl(updates.environment);
|
|
2214
2433
|
}
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
return input;
|
|
2218
|
-
}
|
|
2219
|
-
function clearObjectShallow(obj) {
|
|
2220
|
-
if (!obj || typeof obj !== 'object') {
|
|
2221
|
-
return {};
|
|
2222
|
-
}
|
|
2223
|
-
const cleaned = {};
|
|
2224
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
2225
|
-
if (value !== null && value !== undefined && value !== '') {
|
|
2226
|
-
cleaned[key] = value;
|
|
2434
|
+
if (updates.debug !== undefined) {
|
|
2435
|
+
this.config.debug = updates.debug;
|
|
2227
2436
|
}
|
|
2228
2437
|
}
|
|
2229
|
-
return cleaned;
|
|
2230
|
-
}
|
|
2231
|
-
function isEmpty(value) {
|
|
2232
|
-
return value === null || value === undefined || value === '';
|
|
2233
|
-
}
|
|
2234
|
-
function hasNonEmptyValues(obj) {
|
|
2235
|
-
if (!obj || typeof obj !== 'object') {
|
|
2236
|
-
return false;
|
|
2237
|
-
}
|
|
2238
|
-
return Object.values(obj).some((value) => !isEmpty(value));
|
|
2239
2438
|
}
|
|
2240
2439
|
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2440
|
+
// Enum options arrays
|
|
2441
|
+
const VAT_RATE_CODE_OPTIONS = [
|
|
2442
|
+
'4.00',
|
|
2443
|
+
'5.00',
|
|
2444
|
+
'10.00',
|
|
2445
|
+
'22.00',
|
|
2446
|
+
'2.00',
|
|
2447
|
+
'6.40',
|
|
2448
|
+
'7.00',
|
|
2449
|
+
'7.30',
|
|
2450
|
+
'7.50',
|
|
2451
|
+
'7.65',
|
|
2452
|
+
'7.95',
|
|
2453
|
+
'8.30',
|
|
2454
|
+
'8.50',
|
|
2455
|
+
'8.80',
|
|
2456
|
+
'9.50',
|
|
2457
|
+
'12.30',
|
|
2458
|
+
'N1',
|
|
2459
|
+
'N2',
|
|
2460
|
+
'N3',
|
|
2461
|
+
'N4',
|
|
2462
|
+
'N5',
|
|
2463
|
+
'N6',
|
|
2464
|
+
];
|
|
2465
|
+
const GOOD_OR_SERVICE_OPTIONS = ['goods', 'service'];
|
|
2466
|
+
const RECEIPT_PROOF_TYPE_OPTIONS = ['POS', 'VR', 'ND'];
|
|
2467
|
+
// Enum types for receipt validation
|
|
2468
|
+
const VatRateCodeSchema = z.enum(VAT_RATE_CODE_OPTIONS);
|
|
2469
|
+
const GoodOrServiceSchema = z.enum(GOOD_OR_SERVICE_OPTIONS);
|
|
2470
|
+
const ReceiptProofTypeSchema = z.enum(RECEIPT_PROOF_TYPE_OPTIONS);
|
|
2471
|
+
// Receipt Item Schema
|
|
2472
|
+
const ReceiptItemSchema = z.object({
|
|
2473
|
+
type: GoodOrServiceSchema.optional(),
|
|
2474
|
+
quantity: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2475
|
+
description: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2476
|
+
unit_price: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2477
|
+
vat_rate_code: VatRateCodeSchema.optional(),
|
|
2478
|
+
simplified_vat_allocation: z.boolean().optional(),
|
|
2479
|
+
discount: z.string().nullable().optional(),
|
|
2480
|
+
is_down_payment_or_voucher_redemption: z.boolean().optional(),
|
|
2481
|
+
complimentary: z.boolean().optional(),
|
|
2482
|
+
});
|
|
2483
|
+
// Main Receipt Input Schema
|
|
2484
|
+
const ReceiptInputSchema = z
|
|
2485
|
+
.object({
|
|
2486
|
+
items: z.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
|
|
2487
|
+
customer_tax_code: z.string().optional(),
|
|
2488
|
+
customer_lottery_code: z.string().optional(),
|
|
2489
|
+
discount: z.string().nullable().optional(),
|
|
2490
|
+
invoice_issuing: z.boolean().optional(),
|
|
2491
|
+
uncollected_dcr_to_ssn: z.boolean().optional(),
|
|
2492
|
+
services_uncollected_amount: z.string().nullable().optional(),
|
|
2493
|
+
goods_uncollected_amount: z.string().nullable().optional(),
|
|
2494
|
+
cash_payment_amount: z.string().nullable().optional(),
|
|
2495
|
+
electronic_payment_amount: z.string().nullable().optional(),
|
|
2496
|
+
ticket_restaurant_payment_amount: z.string().nullable().optional(),
|
|
2497
|
+
ticket_restaurant_quantity: z.number().optional(),
|
|
2498
|
+
})
|
|
2499
|
+
.refine((data) => {
|
|
2500
|
+
// At least one payment method should be provided
|
|
2501
|
+
const hasCashPayment = data.cash_payment_amount && parseFloat(data.cash_payment_amount) > 0;
|
|
2502
|
+
const hasElectronicPayment = data.electronic_payment_amount && parseFloat(data.electronic_payment_amount) > 0;
|
|
2503
|
+
const hasTicketPayment = data.ticket_restaurant_payment_amount &&
|
|
2504
|
+
parseFloat(data.ticket_restaurant_payment_amount) > 0;
|
|
2505
|
+
return hasCashPayment || hasElectronicPayment || hasTicketPayment;
|
|
2506
|
+
}, {
|
|
2507
|
+
error: 'At least one payment method is required',
|
|
2508
|
+
path: ['payment_methods'],
|
|
2509
|
+
})
|
|
2510
|
+
.refine((data) => {
|
|
2511
|
+
// only one between customer_tax_code and customer_lottery_code can be provided
|
|
2512
|
+
return !data.customer_tax_code || !data.customer_lottery_code;
|
|
2513
|
+
}, {
|
|
2514
|
+
error: 'Only one between customer_tax_code and customer_lottery_code can be provided',
|
|
2515
|
+
path: ['customer_tax_code', 'customer_lottery_code'],
|
|
2516
|
+
});
|
|
2517
|
+
// Receipt Return or Void via PEM Schema
|
|
2518
|
+
const ReceiptReturnOrVoidViaPEMInputSchema = z.object({
|
|
2519
|
+
device_id: z.string().optional(),
|
|
2520
|
+
items: z.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
|
|
2521
|
+
document_number: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2522
|
+
document_datetime: z.string().optional(),
|
|
2523
|
+
lottery_code: z.string().optional(),
|
|
2524
|
+
});
|
|
2525
|
+
// Receipt Return or Void with Proof Schema
|
|
2526
|
+
const ReceiptReturnOrVoidWithProofInputSchema = z.object({
|
|
2527
|
+
items: z.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
|
|
2528
|
+
proof: ReceiptProofTypeSchema,
|
|
2529
|
+
document_datetime: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2530
|
+
});
|
|
2531
|
+
// Void Receipt Schema
|
|
2532
|
+
const VoidReceiptInputSchema = z.object({
|
|
2533
|
+
document_number: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2534
|
+
});
|
|
2535
|
+
const ReceiptReturnItemSchema = z
|
|
2536
|
+
.array(z.object({
|
|
2537
|
+
id: z.number(),
|
|
2538
|
+
quantity: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2539
|
+
}))
|
|
2540
|
+
.min(1, { error: 'arrayMin1' });
|
|
2541
|
+
// Receipt Return Schema
|
|
2542
|
+
const ReceiptReturnInputSchema = z.object({
|
|
2543
|
+
items: z.array(ReceiptReturnItemSchema).min(1, { error: 'arrayMin1' }),
|
|
2544
|
+
document_number: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2545
|
+
});
|
|
2546
|
+
|
|
2547
|
+
// Cashier Create Input Schema (MF1)
|
|
2548
|
+
const CashierCreateInputSchema = z.object({
|
|
2549
|
+
email: z
|
|
2550
|
+
.string()
|
|
2551
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2552
|
+
.max(255, { error: 'emailMaxLength' })
|
|
2553
|
+
.email({ error: 'invalidEmail' }),
|
|
2554
|
+
password: z
|
|
2555
|
+
.string()
|
|
2556
|
+
.min(8, { error: 'passwordMinLength' })
|
|
2557
|
+
.max(40, { error: 'passwordMaxLength' }),
|
|
2558
|
+
name: z.string().min(1, { error: 'fieldIsRequired' }).max(255, { error: 'nameMaxLength' }),
|
|
2559
|
+
display_name: z
|
|
2560
|
+
.string()
|
|
2561
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2562
|
+
.max(255, { error: 'displayNameMaxLength' }),
|
|
2563
|
+
});
|
|
2564
|
+
|
|
2565
|
+
// Enum options arrays
|
|
2566
|
+
const PEM_STATUS_OPTIONS = [
|
|
2567
|
+
'NEW',
|
|
2568
|
+
'REGISTERED',
|
|
2569
|
+
'ACTIVATED',
|
|
2570
|
+
'ONLINE',
|
|
2571
|
+
'OFFLINE',
|
|
2572
|
+
'DISCARDED',
|
|
2573
|
+
];
|
|
2574
|
+
// Address Schema (reusable)
|
|
2575
|
+
const AddressSchema = z.object({
|
|
2576
|
+
street_address: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2577
|
+
street_number: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2578
|
+
zip_code: z
|
|
2579
|
+
.string()
|
|
2580
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2581
|
+
.regex(/^\d{5}$/, { error: 'invalidZipCode' }),
|
|
2582
|
+
city: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2583
|
+
province: z
|
|
2584
|
+
.string()
|
|
2585
|
+
.min(2, { error: 'provinceMinLength' })
|
|
2586
|
+
.max(2, { error: 'provinceMaxLength' })
|
|
2587
|
+
.toUpperCase(),
|
|
2588
|
+
});
|
|
2589
|
+
// PEM Status Schema
|
|
2590
|
+
const PEMStatusSchema = z.enum(PEM_STATUS_OPTIONS);
|
|
2591
|
+
// Activation Request Schema
|
|
2592
|
+
const ActivationRequestSchema = z.object({
|
|
2593
|
+
registration_key: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2594
|
+
});
|
|
2595
|
+
// PEM Status Offline Request Schema
|
|
2596
|
+
const PEMStatusOfflineRequestSchema = z.object({
|
|
2597
|
+
timestamp: z
|
|
2598
|
+
.string()
|
|
2599
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2600
|
+
.refine((val) => !isNaN(Date.parse(val)), {
|
|
2601
|
+
error: 'invalidDateFormat',
|
|
2602
|
+
}),
|
|
2603
|
+
reason: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2604
|
+
});
|
|
2605
|
+
|
|
2606
|
+
// Cash Register Create Schema
|
|
2607
|
+
const CashRegisterCreateSchema = z.object({
|
|
2608
|
+
pem_serial_number: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2609
|
+
name: z.string().min(1, { error: 'fieldIsRequired' }).max(100, { error: 'nameMaxLength' }),
|
|
2610
|
+
});
|
|
2611
|
+
|
|
2612
|
+
// VAT number validation regex (Partita IVA - 11 digits)
|
|
2613
|
+
const VAT_NUMBER_REGEX = /^\d{11}$/;
|
|
2614
|
+
// Fiscal code validation regex (Codice Fiscale - 11 digits only for merchants)
|
|
2615
|
+
const FISCAL_CODE_REGEX = /^\d{11}$/;
|
|
2616
|
+
// Password validation regex (from OpenAPI spec)
|
|
2617
|
+
const PASSWORD_REGEX = /^((?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{10,}).*)$/;
|
|
2618
|
+
// Merchant Create Input Schema
|
|
2619
|
+
const MerchantCreateInputSchema = z
|
|
2620
|
+
.object({
|
|
2621
|
+
vat_number: z
|
|
2622
|
+
.string()
|
|
2623
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2624
|
+
.regex(VAT_NUMBER_REGEX, { error: 'invalidVatNumber' }),
|
|
2625
|
+
fiscal_code: z.string().regex(FISCAL_CODE_REGEX, { error: 'invalidFiscalCode' }).optional(),
|
|
2626
|
+
business_name: z.string().max(200, { error: 'businessNameMaxLength' }).optional().nullable(),
|
|
2627
|
+
first_name: z.string().max(100, { error: 'firstNameMaxLength' }).optional().nullable(),
|
|
2628
|
+
last_name: z.string().max(100, { error: 'lastNameMaxLength' }).optional().nullable(),
|
|
2629
|
+
email: z.string().min(1, { error: 'fieldIsRequired' }).email({ error: 'invalidEmail' }),
|
|
2630
|
+
password: z
|
|
2631
|
+
.string()
|
|
2632
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2633
|
+
.regex(PASSWORD_REGEX, { error: 'passwordComplexity' }),
|
|
2634
|
+
address: AddressSchema.optional(),
|
|
2635
|
+
})
|
|
2636
|
+
.refine((data) => {
|
|
2637
|
+
const hasBusinessName = data.business_name && data.business_name.trim() !== '';
|
|
2638
|
+
const hasPersonalNames = (data.first_name && data.first_name.trim() !== '') ||
|
|
2639
|
+
(data.last_name && data.last_name.trim() !== '');
|
|
2640
|
+
// If business name is set, first/last name must not be provided
|
|
2641
|
+
if (hasBusinessName && hasPersonalNames) {
|
|
2642
|
+
return false;
|
|
2299
2643
|
}
|
|
2300
|
-
|
|
2644
|
+
// At least one naming method must be provided
|
|
2645
|
+
if (!hasBusinessName && !hasPersonalNames) {
|
|
2301
2646
|
return false;
|
|
2302
2647
|
}
|
|
2303
|
-
|
|
2648
|
+
return true;
|
|
2649
|
+
}, {
|
|
2650
|
+
error: 'businessNameOrPersonalNamesRequired',
|
|
2651
|
+
path: ['business_name'],
|
|
2652
|
+
});
|
|
2653
|
+
// Merchant Update Input Schema
|
|
2654
|
+
const MerchantUpdateInputSchema = z.object({
|
|
2655
|
+
business_name: z.string().max(200, { error: 'businessNameMaxLength' }).optional().nullable(),
|
|
2656
|
+
first_name: z.string().max(100, { error: 'firstNameMaxLength' }).optional().nullable(),
|
|
2657
|
+
last_name: z.string().max(100, { error: 'lastNameMaxLength' }).optional().nullable(),
|
|
2658
|
+
address: AddressSchema.optional().nullable(),
|
|
2659
|
+
});
|
|
2660
|
+
|
|
2661
|
+
// Enum options arrays
|
|
2662
|
+
const PEM_TYPE_OPTIONS = ['AP', 'SP', 'TM', 'PV'];
|
|
2663
|
+
// PEM Data Schema
|
|
2664
|
+
const PemDataSchema = z.object({
|
|
2665
|
+
version: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2666
|
+
type: z.enum(PEM_TYPE_OPTIONS, {
|
|
2667
|
+
error: 'invalidPemType',
|
|
2668
|
+
}),
|
|
2669
|
+
});
|
|
2670
|
+
// PEM Create Input Schema
|
|
2671
|
+
const PemCreateInputSchema = z.object({
|
|
2672
|
+
merchant_uuid: z.string().min(1, { error: 'fieldIsRequired' }).uuid({ error: 'invalidUuid' }),
|
|
2673
|
+
address: AddressSchema.optional(),
|
|
2674
|
+
/* external_pem_data: PemDataSchema.optional(), */
|
|
2675
|
+
});
|
|
2676
|
+
|
|
2677
|
+
// Italian Fiscal ID validation regex (Codice Fiscale for individuals or Partita IVA for companies)
|
|
2678
|
+
const FISCAL_ID_REGEX = /^([A-Z]{6}[0-9LMNPQRSTUV]{2}[ABCDEHLMPRST][0-9LMNPQRSTUV]{2}[A-Z][0-9LMNPQRSTUV]{3}[A-Z]|[0-9]{11})$/;
|
|
2679
|
+
// Supplier Create Input Schema
|
|
2680
|
+
const SupplierCreateInputSchema = z.object({
|
|
2681
|
+
fiscal_id: z
|
|
2682
|
+
.string()
|
|
2683
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2684
|
+
.regex(FISCAL_ID_REGEX, { error: 'invalidFiscalId' })
|
|
2685
|
+
.toUpperCase(),
|
|
2686
|
+
name: z.string().min(1, { error: 'fieldIsRequired' }).max(200, { error: 'nameMaxLength' }),
|
|
2687
|
+
address: AddressSchema.optional(),
|
|
2688
|
+
});
|
|
2689
|
+
// Supplier Update Input Schema
|
|
2690
|
+
const SupplierUpdateInputSchema = z.object({
|
|
2691
|
+
name: z.string().min(1, { error: 'fieldIsRequired' }).max(200, { error: 'nameMaxLength' }),
|
|
2692
|
+
address: AddressSchema.optional(),
|
|
2693
|
+
});
|
|
2694
|
+
|
|
2695
|
+
// Journal Close Input Schema
|
|
2696
|
+
const JournalCloseInputSchema = z.object({
|
|
2697
|
+
closing_timestamp: z
|
|
2698
|
+
.string()
|
|
2699
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2700
|
+
.refine((val) => !isNaN(Date.parse(val)), {
|
|
2701
|
+
error: 'invalidDateFormat',
|
|
2702
|
+
}),
|
|
2703
|
+
reason: z.string().max(255, { error: 'reasonMaxLength' }).optional(),
|
|
2704
|
+
});
|
|
2705
|
+
|
|
2706
|
+
// Daily Report Status Options
|
|
2707
|
+
const DAILY_REPORT_STATUS_OPTIONS = ['pending', 'sent', 'error'];
|
|
2708
|
+
// Daily Report Status Schema
|
|
2709
|
+
const DailyReportStatusSchema = z.enum(DAILY_REPORT_STATUS_OPTIONS, {
|
|
2710
|
+
error: 'invalidDailyReportStatus',
|
|
2711
|
+
});
|
|
2712
|
+
// Daily Reports List Parameters Schema
|
|
2713
|
+
const DailyReportsParamsSchema = z.object({
|
|
2714
|
+
pem_serial_number: z.string().min(1, { error: 'fieldIsRequired' }).optional(),
|
|
2715
|
+
date_from: z
|
|
2716
|
+
.string()
|
|
2717
|
+
.refine((val) => !isNaN(Date.parse(val)), {
|
|
2718
|
+
error: 'invalidDateFormat',
|
|
2719
|
+
})
|
|
2720
|
+
.optional(),
|
|
2721
|
+
date_to: z
|
|
2722
|
+
.string()
|
|
2723
|
+
.refine((val) => !isNaN(Date.parse(val)), {
|
|
2724
|
+
error: 'invalidDateFormat',
|
|
2725
|
+
})
|
|
2726
|
+
.optional(),
|
|
2727
|
+
status: DailyReportStatusSchema.optional(),
|
|
2728
|
+
page: z.number().min(1, { error: 'pageMinValue' }).optional(),
|
|
2729
|
+
});
|
|
2730
|
+
|
|
2731
|
+
const NotificationScopeSchema = z.object({
|
|
2732
|
+
type: z.literal('global'),
|
|
2733
|
+
});
|
|
2734
|
+
const PemStatusSchema = z.enum(['ONLINE', 'OFFLINE']);
|
|
2735
|
+
const NotificationDataBlockAtSchema = z.object({
|
|
2736
|
+
block_at: z.string(),
|
|
2737
|
+
});
|
|
2738
|
+
const NotificationDataPemStatusSchema = z.object({
|
|
2739
|
+
from: PemStatusSchema,
|
|
2740
|
+
to: PemStatusSchema,
|
|
2741
|
+
});
|
|
2742
|
+
const NotificationBaseSchema = z.object({
|
|
2743
|
+
uuid: z.string().uuid({ error: 'invalidUuid' }),
|
|
2744
|
+
scope: NotificationScopeSchema,
|
|
2745
|
+
source: z.enum(['system', 'Italian Tax Authority']),
|
|
2746
|
+
level: z.enum(['info', 'warning', 'error', 'critical']),
|
|
2747
|
+
created_at: z.string(),
|
|
2748
|
+
});
|
|
2749
|
+
const NotificationMf2UnreachableSchema = NotificationBaseSchema.extend({
|
|
2750
|
+
type: z.literal('INTERNAL_COMMUNICATION_FAILURE'),
|
|
2751
|
+
code: z.literal('SYS-W-01'),
|
|
2752
|
+
data: NotificationDataBlockAtSchema,
|
|
2753
|
+
});
|
|
2754
|
+
const NotificationPemsBlockedSchema = NotificationBaseSchema.extend({
|
|
2755
|
+
type: z.literal('PEM_STATUS_CHANGED'),
|
|
2756
|
+
code: z.literal('SYS-C-01'),
|
|
2757
|
+
data: NotificationDataPemStatusSchema,
|
|
2758
|
+
});
|
|
2759
|
+
const NotificationPemBackOnlineSchema = NotificationBaseSchema.extend({
|
|
2760
|
+
type: z.literal('PEM_STATUS_CHANGED'),
|
|
2761
|
+
code: z.literal('SYS-I-01'),
|
|
2762
|
+
data: NotificationDataPemStatusSchema,
|
|
2763
|
+
});
|
|
2764
|
+
const NotificationSchema = z.discriminatedUnion('code', [
|
|
2765
|
+
NotificationMf2UnreachableSchema,
|
|
2766
|
+
NotificationPemsBlockedSchema,
|
|
2767
|
+
NotificationPemBackOnlineSchema,
|
|
2768
|
+
]);
|
|
2769
|
+
const NotificationListResponseSchema = z.object({
|
|
2770
|
+
members: z.array(NotificationSchema),
|
|
2771
|
+
});
|
|
2772
|
+
|
|
2773
|
+
const TelemetryMerchantSchema = z.object({
|
|
2774
|
+
vat_number: z.string(),
|
|
2775
|
+
fiscal_code: z.string().nullable(),
|
|
2776
|
+
business_name: z.string(),
|
|
2777
|
+
});
|
|
2778
|
+
const TelemetrySupplierSchema = z.object({
|
|
2779
|
+
vat_number: z.string(),
|
|
2780
|
+
fiscal_code: z.string().nullable(),
|
|
2781
|
+
business_name: z.string(),
|
|
2782
|
+
});
|
|
2783
|
+
const TelemetrySoftwareVersionSchema = z.object({
|
|
2784
|
+
version: z.string(),
|
|
2785
|
+
swid: z.string(),
|
|
2786
|
+
installed_at: z.string(),
|
|
2787
|
+
status: z.enum(['active', 'inactive', 'archived']),
|
|
2788
|
+
});
|
|
2789
|
+
const TelemetrySoftwareSchema = z.object({
|
|
2790
|
+
code: z.string(),
|
|
2791
|
+
name: z.string(),
|
|
2792
|
+
approval_reference: z.string(),
|
|
2793
|
+
version_info: TelemetrySoftwareVersionSchema,
|
|
2794
|
+
});
|
|
2795
|
+
const PendingReceiptsSchema = z.object({
|
|
2796
|
+
count: z.number().int().nonnegative(),
|
|
2797
|
+
total_amount: z.string(),
|
|
2798
|
+
});
|
|
2799
|
+
const TransmissionSchema = z.object({
|
|
2800
|
+
attempted_at: z.string(),
|
|
2801
|
+
outcome: z.enum(['success', 'failed', 'pending']),
|
|
2802
|
+
});
|
|
2803
|
+
const MessageSchema = z.object({
|
|
2804
|
+
received_at: z.string(),
|
|
2805
|
+
content: z.string(),
|
|
2806
|
+
});
|
|
2807
|
+
const LotterySecretRequestSchema = z.object({
|
|
2808
|
+
requested_at: z.string(),
|
|
2809
|
+
outcome: z.enum(['success', 'failed', 'pending']),
|
|
2810
|
+
});
|
|
2811
|
+
const LotterySchema = z.object({
|
|
2812
|
+
last_transmission: TransmissionSchema,
|
|
2813
|
+
secret_request: LotterySecretRequestSchema,
|
|
2814
|
+
});
|
|
2815
|
+
const TelemetrySchema = z.object({
|
|
2816
|
+
pem_id: z.string(),
|
|
2817
|
+
pem_status: z.enum(['ONLINE', 'OFFLINE', 'ERROR']),
|
|
2818
|
+
pem_status_changed_at: z.string(),
|
|
2819
|
+
merchant: TelemetryMerchantSchema,
|
|
2820
|
+
supplier: TelemetrySupplierSchema,
|
|
2821
|
+
software: TelemetrySoftwareSchema,
|
|
2822
|
+
last_communication_at: z.string(),
|
|
2823
|
+
pending_receipts: PendingReceiptsSchema,
|
|
2824
|
+
last_receipt_transmission: TransmissionSchema,
|
|
2825
|
+
last_message_from_mf2: MessageSchema,
|
|
2826
|
+
ade_corrispettivi_transmission: TransmissionSchema,
|
|
2827
|
+
last_message_from_ade: MessageSchema,
|
|
2828
|
+
lottery: LotterySchema,
|
|
2829
|
+
});
|
|
2830
|
+
|
|
2831
|
+
// Receipt schemas and types
|
|
2832
|
+
// Common validation utilities
|
|
2833
|
+
const ValidationMessages = {
|
|
2834
|
+
fieldIsRequired: 'This field is required',
|
|
2835
|
+
arrayMin1: 'At least one item is required',
|
|
2836
|
+
paymentMethodRequired: 'At least one payment method is required',
|
|
2837
|
+
invalidEmail: 'Please enter a valid email address',
|
|
2838
|
+
passwordMinLength: 'Password must be at least 8 characters long',
|
|
2839
|
+
passwordComplexity: 'Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character',
|
|
2840
|
+
invalidZipCode: 'Please enter a valid 5-digit zip code',
|
|
2841
|
+
provinceMinLength: 'Province code must be 2 characters',
|
|
2842
|
+
provinceMaxLength: 'Province code must be 2 characters',
|
|
2843
|
+
invalidDateFormat: 'Please enter a valid date',
|
|
2844
|
+
nameMaxLength: 'Name is too long',
|
|
2845
|
+
invalidFiscalId: 'Please enter a valid Italian fiscal ID (Codice Fiscale or Partita IVA)',
|
|
2846
|
+
invalidVatNumber: 'Please enter a valid VAT number (11 digits)',
|
|
2847
|
+
invalidFiscalCode: 'Please enter a valid fiscal code (11 digits)',
|
|
2848
|
+
businessNameMaxLength: 'Business name is too long (max 200 characters)',
|
|
2849
|
+
businessNameOrPersonalNamesRequired: 'Please provide either a business name or first/last name, but not both',
|
|
2850
|
+
firstNameMaxLength: 'First name is too long (max 100 characters)',
|
|
2851
|
+
lastNameMaxLength: 'Last name is too long (max 100 characters)',
|
|
2852
|
+
invalidUuid: 'Please enter a valid UUID',
|
|
2853
|
+
invalidPemType: 'PEM type must be one of: AP, SP, TM, PV',
|
|
2854
|
+
reasonMaxLength: 'Reason is too long (max 255 characters)',
|
|
2855
|
+
pageMinValue: 'Page number must be at least 1',
|
|
2856
|
+
invalidDailyReportStatus: 'Daily report status must be one of: pending, sent, error',
|
|
2857
|
+
displayNameMaxLength: 'Display name is too long (max 255 characters)',
|
|
2858
|
+
};
|
|
2859
|
+
// Validation helper functions
|
|
2860
|
+
const validateInput = (schema, data) => {
|
|
2861
|
+
const result = schema.safeParse(data);
|
|
2862
|
+
if (!result.success) {
|
|
2863
|
+
const errors = result.error.issues.map((error) => ({
|
|
2864
|
+
field: error.path.join('.'),
|
|
2865
|
+
message: error.message,
|
|
2866
|
+
code: error.code,
|
|
2867
|
+
}));
|
|
2868
|
+
return {
|
|
2869
|
+
success: false,
|
|
2870
|
+
errors,
|
|
2871
|
+
data: null,
|
|
2872
|
+
};
|
|
2873
|
+
}
|
|
2874
|
+
return {
|
|
2875
|
+
success: true,
|
|
2876
|
+
errors: [],
|
|
2877
|
+
data: result.data,
|
|
2878
|
+
};
|
|
2879
|
+
};
|
|
2304
2880
|
|
|
2305
|
-
|
|
2306
|
-
|
|
2881
|
+
class ACubeSDKError extends Error {
|
|
2882
|
+
constructor(type, message, originalError, statusCode, violations) {
|
|
2883
|
+
super(message);
|
|
2884
|
+
this.type = type;
|
|
2885
|
+
this.originalError = originalError;
|
|
2886
|
+
this.statusCode = statusCode;
|
|
2887
|
+
this.name = 'ACubeSDKError';
|
|
2888
|
+
this.violations = violations;
|
|
2889
|
+
}
|
|
2307
2890
|
}
|
|
2308
2891
|
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
var picocolors_browserExports = picocolors_browser.exports;
|
|
2317
|
-
var pc = /*@__PURE__*/getDefaultExportFromCjs(picocolors_browserExports);
|
|
2318
|
-
|
|
2319
|
-
const levelColors = {
|
|
2320
|
-
debug: {
|
|
2321
|
-
prefix: pc.gray,
|
|
2322
|
-
message: pc.gray,
|
|
2323
|
-
data: pc.dim,
|
|
2324
|
-
},
|
|
2325
|
-
info: {
|
|
2326
|
-
prefix: pc.cyan,
|
|
2327
|
-
message: pc.white,
|
|
2328
|
-
data: pc.dim,
|
|
2329
|
-
},
|
|
2330
|
-
warn: {
|
|
2331
|
-
prefix: pc.yellow,
|
|
2332
|
-
message: pc.yellow,
|
|
2333
|
-
data: pc.dim,
|
|
2334
|
-
},
|
|
2335
|
-
error: {
|
|
2336
|
-
prefix: pc.red,
|
|
2337
|
-
message: pc.red,
|
|
2338
|
-
data: pc.dim,
|
|
2339
|
-
},
|
|
2340
|
-
};
|
|
2341
|
-
const levelIcons = {
|
|
2342
|
-
debug: '🔍',
|
|
2343
|
-
info: 'ℹ️ ',
|
|
2344
|
-
warn: '⚠️ ',
|
|
2345
|
-
error: '❌',
|
|
2346
|
-
};
|
|
2347
|
-
/**
|
|
2348
|
-
* Format data for pretty display in logs
|
|
2349
|
-
*/
|
|
2350
|
-
function formatData(data, level) {
|
|
2351
|
-
if (data === undefined || data === null) {
|
|
2352
|
-
return String(data);
|
|
2892
|
+
const log$f = createPrefixedLogger('AUTH-STRATEGY');
|
|
2893
|
+
class AuthStrategy {
|
|
2894
|
+
constructor(jwtHandler, mtlsHandler, userProvider, mtlsAdapter) {
|
|
2895
|
+
this.jwtHandler = jwtHandler;
|
|
2896
|
+
this.mtlsHandler = mtlsHandler;
|
|
2897
|
+
this.userProvider = userProvider;
|
|
2898
|
+
this.mtlsAdapter = mtlsAdapter;
|
|
2353
2899
|
}
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2900
|
+
async determineAuthConfig(url, method, explicitMode) {
|
|
2901
|
+
if (this.isNotificationEndpoint(url) || this.isTelemetryEndpoint(url)) {
|
|
2902
|
+
return { mode: 'mtls', usePort444: true };
|
|
2903
|
+
}
|
|
2904
|
+
const platform = this.detectPlatform();
|
|
2905
|
+
const userRole = await this.getUserRole();
|
|
2906
|
+
const isReceiptEndpoint = this.isReceiptEndpoint(url);
|
|
2907
|
+
log$f.debug('Determining auth config', {
|
|
2908
|
+
url,
|
|
2909
|
+
method,
|
|
2910
|
+
platform,
|
|
2911
|
+
userRole,
|
|
2912
|
+
isReceiptEndpoint,
|
|
2913
|
+
explicitMode,
|
|
2914
|
+
});
|
|
2915
|
+
if (userRole === 'SUPPLIER') {
|
|
2916
|
+
return { mode: 'jwt', usePort444: false };
|
|
2917
|
+
}
|
|
2918
|
+
if (userRole === 'CASHIER') {
|
|
2919
|
+
if (!isReceiptEndpoint) {
|
|
2920
|
+
return { mode: 'jwt', usePort444: false };
|
|
2921
|
+
}
|
|
2922
|
+
if (platform === 'mobile') {
|
|
2923
|
+
return { mode: 'mtls', usePort444: true };
|
|
2924
|
+
}
|
|
2925
|
+
return { mode: 'jwt', usePort444: true };
|
|
2926
|
+
}
|
|
2927
|
+
if (userRole === 'MERCHANT') {
|
|
2928
|
+
if (!isReceiptEndpoint) {
|
|
2929
|
+
return { mode: 'jwt', usePort444: false };
|
|
2930
|
+
}
|
|
2931
|
+
if (this.isReturnableItemsEndpoint(url)) {
|
|
2932
|
+
return { mode: 'mtls', usePort444: true };
|
|
2933
|
+
}
|
|
2934
|
+
if (method === 'GET') {
|
|
2935
|
+
if (this.isDetailedReceiptEndpoint(url)) {
|
|
2936
|
+
if (platform === 'mobile') {
|
|
2937
|
+
return { mode: 'mtls', usePort444: true };
|
|
2938
|
+
}
|
|
2939
|
+
return { mode: 'jwt', usePort444: true };
|
|
2940
|
+
}
|
|
2941
|
+
return { mode: 'jwt', usePort444: false };
|
|
2942
|
+
}
|
|
2943
|
+
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {
|
|
2944
|
+
if (platform === 'mobile') {
|
|
2945
|
+
return { mode: 'mtls', usePort444: true };
|
|
2946
|
+
}
|
|
2947
|
+
return { mode: 'jwt', usePort444: true };
|
|
2948
|
+
}
|
|
2949
|
+
return { mode: 'jwt', usePort444: false };
|
|
2950
|
+
}
|
|
2951
|
+
if (explicitMode) {
|
|
2952
|
+
if (userRole === 'SUPPLIER' && explicitMode === 'mtls') {
|
|
2953
|
+
return { mode: 'jwt', usePort444: false };
|
|
2954
|
+
}
|
|
2955
|
+
return {
|
|
2956
|
+
mode: explicitMode,
|
|
2957
|
+
usePort444: explicitMode === 'mtls' || (platform === 'web' && isReceiptEndpoint),
|
|
2958
|
+
};
|
|
2959
|
+
}
|
|
2960
|
+
if (platform === 'web') {
|
|
2961
|
+
return { mode: 'jwt', usePort444: isReceiptEndpoint };
|
|
2962
|
+
}
|
|
2963
|
+
if (isReceiptEndpoint && platform === 'mobile') {
|
|
2964
|
+
return { mode: 'mtls', usePort444: true };
|
|
2965
|
+
}
|
|
2966
|
+
return { mode: 'jwt', usePort444: false };
|
|
2357
2967
|
}
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
return `${data.name}: ${data.message}${data.stack ? `\n${pc.dim(data.stack)}` : ''}`;
|
|
2968
|
+
async getAuthHeaders() {
|
|
2969
|
+
return this.jwtHandler.getAuthHeaders();
|
|
2361
2970
|
}
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
const colors = levelColors[level];
|
|
2365
|
-
return colors.data(JSON.stringify(data, null, 2));
|
|
2971
|
+
getMtlsHandler() {
|
|
2972
|
+
return this.mtlsHandler;
|
|
2366
2973
|
}
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2974
|
+
getJwtHandler() {
|
|
2975
|
+
return this.jwtHandler;
|
|
2976
|
+
}
|
|
2977
|
+
detectPlatform() {
|
|
2978
|
+
if (!this.mtlsAdapter) {
|
|
2979
|
+
return 'web';
|
|
2371
2980
|
}
|
|
2372
|
-
|
|
2373
|
-
|
|
2981
|
+
const platformInfo = this.mtlsAdapter.getPlatformInfo();
|
|
2982
|
+
return platformInfo.platform === 'web' ? 'web' : 'mobile';
|
|
2983
|
+
}
|
|
2984
|
+
async getUserRole() {
|
|
2985
|
+
if (!this.userProvider) {
|
|
2986
|
+
return null;
|
|
2987
|
+
}
|
|
2988
|
+
const user = await this.userProvider.getCurrentUser();
|
|
2989
|
+
if (!user || !user.roles) {
|
|
2990
|
+
return null;
|
|
2991
|
+
}
|
|
2992
|
+
if (hasRole(user.roles, 'ROLE_SUPPLIER')) {
|
|
2993
|
+
return 'SUPPLIER';
|
|
2994
|
+
}
|
|
2995
|
+
if (hasRole(user.roles, 'ROLE_MERCHANT')) {
|
|
2996
|
+
return 'MERCHANT';
|
|
2374
2997
|
}
|
|
2998
|
+
if (hasRole(user.roles, 'ROLE_CASHIER')) {
|
|
2999
|
+
return 'CASHIER';
|
|
3000
|
+
}
|
|
3001
|
+
return null;
|
|
2375
3002
|
}
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
constructor() {
|
|
2379
|
-
this.enabled = false;
|
|
3003
|
+
isReceiptEndpoint(url) {
|
|
3004
|
+
return url.includes('/receipts') || url.includes('/mf1/receipts');
|
|
2380
3005
|
}
|
|
2381
|
-
|
|
2382
|
-
|
|
3006
|
+
isReturnableItemsEndpoint(url) {
|
|
3007
|
+
return !!(url.match(/\/receipts\/[a-f0-9-]+\/returnable-items$/) ||
|
|
3008
|
+
url.match(/\/mf1\/receipts\/[a-f0-9-]+\/returnable-items$/));
|
|
2383
3009
|
}
|
|
2384
|
-
|
|
2385
|
-
return
|
|
3010
|
+
isDetailedReceiptEndpoint(url) {
|
|
3011
|
+
return !!(url.match(/\/receipts\/[a-f0-9-]+\/details$/) ||
|
|
3012
|
+
url.match(/\/mf1\/receipts\/[a-f0-9-]+\/details$/));
|
|
2386
3013
|
}
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
return;
|
|
2390
|
-
this.log('debug', prefix, message, data);
|
|
3014
|
+
isNotificationEndpoint(url) {
|
|
3015
|
+
return url.includes('/mf1/notifications');
|
|
2391
3016
|
}
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
return;
|
|
2395
|
-
this.log('info', prefix, message, data);
|
|
3017
|
+
isTelemetryEndpoint(url) {
|
|
3018
|
+
return !!url.match(/\/mf1\/pems\/[^/]+\/telemetry/);
|
|
2396
3019
|
}
|
|
2397
|
-
|
|
2398
|
-
|
|
3020
|
+
}
|
|
3021
|
+
|
|
3022
|
+
class JwtAuthHandler {
|
|
3023
|
+
constructor(tokenStorage) {
|
|
3024
|
+
this.tokenStorage = tokenStorage;
|
|
2399
3025
|
}
|
|
2400
|
-
|
|
2401
|
-
|
|
3026
|
+
async getAuthConfig(_url, _method) {
|
|
3027
|
+
return { mode: 'jwt', usePort444: false };
|
|
2402
3028
|
}
|
|
2403
|
-
|
|
2404
|
-
const
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
const timestamp = pc.dim(isoTime.slice(0, 12));
|
|
2408
|
-
const formattedPrefix = colors.prefix(`[${prefix}]`);
|
|
2409
|
-
const formattedMessage = colors.message(message);
|
|
2410
|
-
const consoleMethod = level === 'debug'
|
|
2411
|
-
? console.debug
|
|
2412
|
-
: level === 'info'
|
|
2413
|
-
? console.info
|
|
2414
|
-
: level === 'warn'
|
|
2415
|
-
? console.warn
|
|
2416
|
-
: console.error;
|
|
2417
|
-
const header = `${timestamp} ${icon} ${formattedPrefix} ${formattedMessage}`;
|
|
2418
|
-
if (data !== undefined) {
|
|
2419
|
-
const formattedData = formatData(data, level);
|
|
2420
|
-
// Check if data is an object (multi-line) or primitive (single-line)
|
|
2421
|
-
const isMultiLine = typeof data === 'object' && data !== null;
|
|
2422
|
-
if (isMultiLine) {
|
|
2423
|
-
consoleMethod(`${header}\n${formattedData}`);
|
|
2424
|
-
}
|
|
2425
|
-
else {
|
|
2426
|
-
consoleMethod(`${header}`, formattedData);
|
|
2427
|
-
}
|
|
2428
|
-
}
|
|
2429
|
-
else {
|
|
2430
|
-
consoleMethod(header);
|
|
3029
|
+
async getAuthHeaders() {
|
|
3030
|
+
const token = await this.tokenStorage.getAccessToken();
|
|
3031
|
+
if (!token) {
|
|
3032
|
+
return {};
|
|
2431
3033
|
}
|
|
3034
|
+
return { Authorization: `Bearer ${token}` };
|
|
2432
3035
|
}
|
|
2433
3036
|
}
|
|
2434
|
-
const logger = new Logger();
|
|
2435
|
-
function createPrefixedLogger(prefix) {
|
|
2436
|
-
return {
|
|
2437
|
-
debug: (message, data) => logger.debug(prefix, message, data),
|
|
2438
|
-
info: (message, data) => logger.info(prefix, message, data),
|
|
2439
|
-
warn: (message, data) => logger.warn(prefix, message, data),
|
|
2440
|
-
error: (message, data) => logger.error(prefix, message, data),
|
|
2441
|
-
};
|
|
2442
|
-
}
|
|
2443
|
-
|
|
2444
|
-
/**
|
|
2445
|
-
* Formats a numeric string value to have exactly the specified number of decimal places.
|
|
2446
|
-
* E.g., "1" → "1.00", "10" → "10.00", "1.5" → "1.50"
|
|
2447
|
-
* Returns undefined for undefined input (preserves optional fields).
|
|
2448
|
-
*
|
|
2449
|
-
* @param value - The string value to format
|
|
2450
|
-
* @param decimals - Number of decimal places (default: 2)
|
|
2451
|
-
* @returns Formatted string or undefined if input is undefined
|
|
2452
|
-
*/
|
|
2453
|
-
function formatDecimal(value, decimals = 2) {
|
|
2454
|
-
if (value === undefined)
|
|
2455
|
-
return undefined;
|
|
2456
|
-
const num = parseFloat(value);
|
|
2457
|
-
if (isNaN(num))
|
|
2458
|
-
return value;
|
|
2459
|
-
return num.toFixed(decimals);
|
|
2460
|
-
}
|
|
2461
3037
|
|
|
2462
|
-
const log$
|
|
3038
|
+
const log$e = createPrefixedLogger('MTLS-HANDLER');
|
|
2463
3039
|
class MtlsAuthHandler {
|
|
2464
3040
|
constructor(mtlsAdapter, certificatePort) {
|
|
2465
3041
|
this.mtlsAdapter = mtlsAdapter;
|
|
@@ -2501,7 +3077,7 @@ class MtlsAuthHandler {
|
|
|
2501
3077
|
async makeRequest(url, config, jwtToken) {
|
|
2502
3078
|
const requestKey = this.generateRequestKey(url, config, jwtToken);
|
|
2503
3079
|
if (this.pendingRequests.has(requestKey)) {
|
|
2504
|
-
log$
|
|
3080
|
+
log$e.debug('Deduplicating concurrent request:', url);
|
|
2505
3081
|
return this.pendingRequests.get(requestKey);
|
|
2506
3082
|
}
|
|
2507
3083
|
const requestPromise = this.executeRequest(url, config, jwtToken, false);
|
|
@@ -2525,10 +3101,10 @@ class MtlsAuthHandler {
|
|
|
2525
3101
|
};
|
|
2526
3102
|
if (jwtToken) {
|
|
2527
3103
|
headers['Authorization'] = `Bearer ${jwtToken}`;
|
|
2528
|
-
log$
|
|
3104
|
+
log$e.debug('JWT token present:', jwtToken.substring(0, 20) + '...');
|
|
2529
3105
|
}
|
|
2530
3106
|
else {
|
|
2531
|
-
log$
|
|
3107
|
+
log$e.warn('No JWT token provided for mTLS request');
|
|
2532
3108
|
}
|
|
2533
3109
|
const fullUrl = this.constructMtlsUrl(url);
|
|
2534
3110
|
const mtlsConfig = {
|
|
@@ -2539,25 +3115,25 @@ class MtlsAuthHandler {
|
|
|
2539
3115
|
timeout: config.timeout,
|
|
2540
3116
|
responseType: config.responseType,
|
|
2541
3117
|
};
|
|
2542
|
-
log$
|
|
2543
|
-
log$
|
|
3118
|
+
log$e.debug('header-mtls', headers);
|
|
3119
|
+
log$e.debug(`${config.method} ${fullUrl}`);
|
|
2544
3120
|
if (config.data) {
|
|
2545
|
-
log$
|
|
3121
|
+
log$e.debug('Request body:', config.data);
|
|
2546
3122
|
}
|
|
2547
3123
|
try {
|
|
2548
3124
|
const response = await this.mtlsAdapter.request(mtlsConfig);
|
|
2549
|
-
log$
|
|
3125
|
+
log$e.debug(`Response ${response.status} from ${fullUrl}`);
|
|
2550
3126
|
if (response.data) {
|
|
2551
|
-
log$
|
|
3127
|
+
log$e.debug('Response body:', response.data);
|
|
2552
3128
|
}
|
|
2553
3129
|
return response.data;
|
|
2554
3130
|
}
|
|
2555
3131
|
catch (error) {
|
|
2556
|
-
log$
|
|
3132
|
+
log$e.error(`Response error from ${fullUrl}:`, error);
|
|
2557
3133
|
if (error && typeof error === 'object' && 'response' in error) {
|
|
2558
3134
|
const axiosError = error;
|
|
2559
3135
|
if (axiosError.response?.data) {
|
|
2560
|
-
log$
|
|
3136
|
+
log$e.error('Response body:', axiosError.response.data);
|
|
2561
3137
|
}
|
|
2562
3138
|
}
|
|
2563
3139
|
if (isRetryAttempt) {
|
|
@@ -2567,7 +3143,7 @@ class MtlsAuthHandler {
|
|
|
2567
3143
|
if (!shouldRetry) {
|
|
2568
3144
|
throw error;
|
|
2569
3145
|
}
|
|
2570
|
-
log$
|
|
3146
|
+
log$e.debug('Request failed, reconfiguring certificate and retrying...');
|
|
2571
3147
|
try {
|
|
2572
3148
|
await this.configureCertificate(certificate);
|
|
2573
3149
|
return await this.executeRequest(url, config, jwtToken, true);
|
|
@@ -3294,7 +3870,7 @@ function decompressData(data, compressed) {
|
|
|
3294
3870
|
return new CompressionAdapter().decompress(data, compressed);
|
|
3295
3871
|
}
|
|
3296
3872
|
|
|
3297
|
-
const log$
|
|
3873
|
+
const log$d = createPrefixedLogger('CACHE-RN');
|
|
3298
3874
|
/**
|
|
3299
3875
|
* React Native cache adapter using SQLite (Expo or react-native-sqlite-storage)
|
|
3300
3876
|
* Cache never expires - data persists until explicitly invalidated
|
|
@@ -3378,26 +3954,26 @@ class ReactNativeCacheAdapter {
|
|
|
3378
3954
|
await this.runMigrations();
|
|
3379
3955
|
}
|
|
3380
3956
|
async runMigrations() {
|
|
3381
|
-
log$
|
|
3957
|
+
log$d.debug('Running database migrations...');
|
|
3382
3958
|
try {
|
|
3383
3959
|
// Check if compressed column exists
|
|
3384
3960
|
this.hasCompressedColumn = await this.checkColumnExists('compressed');
|
|
3385
3961
|
if (!this.hasCompressedColumn) {
|
|
3386
|
-
log$
|
|
3962
|
+
log$d.debug('Adding compressed column to cache table');
|
|
3387
3963
|
const addColumnSQL = `ALTER TABLE ${ReactNativeCacheAdapter.TABLE_NAME} ADD COLUMN compressed INTEGER DEFAULT 0`;
|
|
3388
3964
|
await this.executeSql(addColumnSQL);
|
|
3389
3965
|
this.hasCompressedColumn = true;
|
|
3390
|
-
log$
|
|
3966
|
+
log$d.debug('Successfully added compressed column');
|
|
3391
3967
|
}
|
|
3392
3968
|
else {
|
|
3393
|
-
log$
|
|
3969
|
+
log$d.debug('Compressed column already exists');
|
|
3394
3970
|
}
|
|
3395
|
-
log$
|
|
3971
|
+
log$d.debug('Database migrations completed', {
|
|
3396
3972
|
hasCompressedColumn: this.hasCompressedColumn,
|
|
3397
3973
|
});
|
|
3398
3974
|
}
|
|
3399
3975
|
catch (error) {
|
|
3400
|
-
log$
|
|
3976
|
+
log$d.debug('Migration failed, disabling compression features', error);
|
|
3401
3977
|
this.hasCompressedColumn = false;
|
|
3402
3978
|
// Don't throw - allow the app to continue even if migration fails
|
|
3403
3979
|
// The compressed feature will just be disabled
|
|
@@ -3408,20 +3984,20 @@ class ReactNativeCacheAdapter {
|
|
|
3408
3984
|
const pragmaSQL = `PRAGMA table_info(${ReactNativeCacheAdapter.TABLE_NAME})`;
|
|
3409
3985
|
const results = await this.executeSql(pragmaSQL);
|
|
3410
3986
|
const columns = this.normalizeResults(results);
|
|
3411
|
-
log$
|
|
3987
|
+
log$d.debug('Table columns found', { columns: columns.map((c) => c.name) });
|
|
3412
3988
|
return columns.some((column) => column.name === columnName);
|
|
3413
3989
|
}
|
|
3414
3990
|
catch (error) {
|
|
3415
|
-
log$
|
|
3991
|
+
log$d.debug('Error checking column existence', error);
|
|
3416
3992
|
return false;
|
|
3417
3993
|
}
|
|
3418
3994
|
}
|
|
3419
3995
|
async get(key) {
|
|
3420
3996
|
await this.ensureInitialized();
|
|
3421
3997
|
const sql = `SELECT * FROM ${ReactNativeCacheAdapter.TABLE_NAME} WHERE cache_key = ?`;
|
|
3422
|
-
log$
|
|
3998
|
+
log$d.debug('Executing get query', { sql, key });
|
|
3423
3999
|
const results = await this.executeSql(sql, [key]);
|
|
3424
|
-
log$
|
|
4000
|
+
log$d.debug('Get query results', { key, hasResults: !!results });
|
|
3425
4001
|
// Normalize results from different SQLite implementations
|
|
3426
4002
|
const rows = this.normalizeResults(results);
|
|
3427
4003
|
if (!rows || rows.length === 0) {
|
|
@@ -3444,7 +4020,7 @@ class ReactNativeCacheAdapter {
|
|
|
3444
4020
|
data,
|
|
3445
4021
|
timestamp: Date.now(),
|
|
3446
4022
|
};
|
|
3447
|
-
log$
|
|
4023
|
+
log$d.debug('Setting cache item', { key });
|
|
3448
4024
|
return this.setItem(key, item);
|
|
3449
4025
|
}
|
|
3450
4026
|
async setItem(key, item) {
|
|
@@ -3457,7 +4033,7 @@ class ReactNativeCacheAdapter {
|
|
|
3457
4033
|
const compressionResult = compressData(serializedData, this.options.compressionThreshold);
|
|
3458
4034
|
finalData = compressionResult.data;
|
|
3459
4035
|
isCompressed = compressionResult.compressed;
|
|
3460
|
-
log$
|
|
4036
|
+
log$d.debug('Compression result', {
|
|
3461
4037
|
key,
|
|
3462
4038
|
originalSize: compressionResult.originalSize,
|
|
3463
4039
|
compressedSize: compressionResult.compressedSize,
|
|
@@ -3465,7 +4041,7 @@ class ReactNativeCacheAdapter {
|
|
|
3465
4041
|
savings: compressionResult.originalSize - compressionResult.compressedSize,
|
|
3466
4042
|
});
|
|
3467
4043
|
}
|
|
3468
|
-
log$
|
|
4044
|
+
log$d.debug('Setting item with metadata', {
|
|
3469
4045
|
key,
|
|
3470
4046
|
timestamp: item.timestamp,
|
|
3471
4047
|
compressed: isCompressed,
|
|
@@ -3491,14 +4067,14 @@ class ReactNativeCacheAdapter {
|
|
|
3491
4067
|
`;
|
|
3492
4068
|
params = [key, finalData, item.timestamp];
|
|
3493
4069
|
}
|
|
3494
|
-
log$
|
|
4070
|
+
log$d.debug('Executing setItem SQL', { key, paramsCount: params.length });
|
|
3495
4071
|
await this.executeSql(sql, params);
|
|
3496
4072
|
}
|
|
3497
4073
|
async setBatch(items) {
|
|
3498
4074
|
if (items.length === 0)
|
|
3499
4075
|
return;
|
|
3500
4076
|
await this.ensureInitialized();
|
|
3501
|
-
log$
|
|
4077
|
+
log$d.debug('Batch setting items', { count: items.length });
|
|
3502
4078
|
if (this.isExpo) {
|
|
3503
4079
|
await this.db.withTransactionAsync(async () => {
|
|
3504
4080
|
for (const [key, item] of items) {
|
|
@@ -3516,7 +4092,7 @@ class ReactNativeCacheAdapter {
|
|
|
3516
4092
|
}, reject, () => resolve());
|
|
3517
4093
|
});
|
|
3518
4094
|
}
|
|
3519
|
-
log$
|
|
4095
|
+
log$d.debug('Batch operation completed', { count: items.length });
|
|
3520
4096
|
}
|
|
3521
4097
|
async setBatchItem(key, item) {
|
|
3522
4098
|
// Handle compression if enabled and compressed column is available
|
|
@@ -3690,10 +4266,10 @@ class MemoryCacheAdapter {
|
|
|
3690
4266
|
return keySize + itemSize;
|
|
3691
4267
|
}
|
|
3692
4268
|
async get(key) {
|
|
3693
|
-
log$
|
|
4269
|
+
log$d.debug('Getting cache item', { key });
|
|
3694
4270
|
const item = this.cache.get(key);
|
|
3695
4271
|
if (!item) {
|
|
3696
|
-
log$
|
|
4272
|
+
log$d.debug('Cache miss', { key });
|
|
3697
4273
|
return null;
|
|
3698
4274
|
}
|
|
3699
4275
|
// Handle decompression if needed
|
|
@@ -3703,7 +4279,7 @@ class MemoryCacheAdapter {
|
|
|
3703
4279
|
const decompressed = decompressData(item.data, true);
|
|
3704
4280
|
finalData = JSON.parse(decompressed.data);
|
|
3705
4281
|
}
|
|
3706
|
-
log$
|
|
4282
|
+
log$d.debug('Cache hit', { key, compressed: isCompressed });
|
|
3707
4283
|
return {
|
|
3708
4284
|
...item,
|
|
3709
4285
|
data: finalData,
|
|
@@ -3711,7 +4287,7 @@ class MemoryCacheAdapter {
|
|
|
3711
4287
|
};
|
|
3712
4288
|
}
|
|
3713
4289
|
async set(key, data) {
|
|
3714
|
-
log$
|
|
4290
|
+
log$d.debug('Setting cache item', { key });
|
|
3715
4291
|
// Handle compression if enabled
|
|
3716
4292
|
let finalData = data;
|
|
3717
4293
|
let isCompressed = false;
|
|
@@ -3721,7 +4297,7 @@ class MemoryCacheAdapter {
|
|
|
3721
4297
|
if (compressionResult.compressed) {
|
|
3722
4298
|
finalData = compressionResult.data;
|
|
3723
4299
|
isCompressed = true;
|
|
3724
|
-
log$
|
|
4300
|
+
log$d.debug('Compression result', {
|
|
3725
4301
|
key,
|
|
3726
4302
|
originalSize: compressionResult.originalSize,
|
|
3727
4303
|
compressedSize: compressionResult.compressedSize,
|
|
@@ -3753,13 +4329,13 @@ class MemoryCacheAdapter {
|
|
|
3753
4329
|
const oldestItemSize = this.calculateItemSize(oldestKey, oldestItem);
|
|
3754
4330
|
this.totalBytes -= oldestItemSize;
|
|
3755
4331
|
this.cache.delete(oldestKey);
|
|
3756
|
-
log$
|
|
4332
|
+
log$d.debug('Removed oldest item for capacity', { oldestKey, freedBytes: oldestItemSize });
|
|
3757
4333
|
}
|
|
3758
4334
|
}
|
|
3759
4335
|
// Set new item and update total size
|
|
3760
4336
|
this.cache.set(key, item);
|
|
3761
4337
|
this.totalBytes += newItemSize;
|
|
3762
|
-
log$
|
|
4338
|
+
log$d.debug('Updated cache size', {
|
|
3763
4339
|
entries: this.cache.size,
|
|
3764
4340
|
totalBytes: this.totalBytes,
|
|
3765
4341
|
newItemSize,
|
|
@@ -3768,7 +4344,7 @@ class MemoryCacheAdapter {
|
|
|
3768
4344
|
async setBatch(items) {
|
|
3769
4345
|
if (items.length === 0)
|
|
3770
4346
|
return;
|
|
3771
|
-
log$
|
|
4347
|
+
log$d.debug('Batch setting items', { count: items.length });
|
|
3772
4348
|
let totalNewBytes = 0;
|
|
3773
4349
|
let totalOldBytes = 0;
|
|
3774
4350
|
const itemsToRemove = [];
|
|
@@ -3797,7 +4373,7 @@ class MemoryCacheAdapter {
|
|
|
3797
4373
|
itemsToRemove.push(oldKey);
|
|
3798
4374
|
}
|
|
3799
4375
|
if (itemsToRemove.length > 0) {
|
|
3800
|
-
log$
|
|
4376
|
+
log$d.debug('Removed items for batch capacity', {
|
|
3801
4377
|
removedCount: itemsToRemove.length,
|
|
3802
4378
|
removedKeys: itemsToRemove,
|
|
3803
4379
|
});
|
|
@@ -3809,7 +4385,7 @@ class MemoryCacheAdapter {
|
|
|
3809
4385
|
for (const [key, item] of items) {
|
|
3810
4386
|
this.cache.set(key, item);
|
|
3811
4387
|
}
|
|
3812
|
-
log$
|
|
4388
|
+
log$d.debug('Batch operation completed', {
|
|
3813
4389
|
count: items.length,
|
|
3814
4390
|
totalBytes: this.totalBytes,
|
|
3815
4391
|
entries: this.cache.size,
|
|
@@ -3831,7 +4407,7 @@ class MemoryCacheAdapter {
|
|
|
3831
4407
|
}
|
|
3832
4408
|
}
|
|
3833
4409
|
if (removed > 0) {
|
|
3834
|
-
log$
|
|
4410
|
+
log$d.debug('Invalidation completed', {
|
|
3835
4411
|
pattern,
|
|
3836
4412
|
entriesRemoved: removed,
|
|
3837
4413
|
bytesFreed,
|
|
@@ -3843,7 +4419,7 @@ class MemoryCacheAdapter {
|
|
|
3843
4419
|
async clear() {
|
|
3844
4420
|
this.cache.clear();
|
|
3845
4421
|
this.totalBytes = 0;
|
|
3846
|
-
log$
|
|
4422
|
+
log$d.debug('Cache cleared', { entries: 0, bytes: 0 });
|
|
3847
4423
|
}
|
|
3848
4424
|
async getSize() {
|
|
3849
4425
|
return {
|
|
@@ -4174,7 +4750,7 @@ replaceTraps((oldTraps) => ({
|
|
|
4174
4750
|
},
|
|
4175
4751
|
}));
|
|
4176
4752
|
|
|
4177
|
-
const log$
|
|
4753
|
+
const log$c = createPrefixedLogger('CACHE-WEB');
|
|
4178
4754
|
/**
|
|
4179
4755
|
* Web cache adapter using IndexedDB with automatic error recovery
|
|
4180
4756
|
* Cache never expires - data persists until explicitly invalidated
|
|
@@ -4197,18 +4773,18 @@ class WebCacheAdapter {
|
|
|
4197
4773
|
async initialize() {
|
|
4198
4774
|
if (this.db)
|
|
4199
4775
|
return;
|
|
4200
|
-
log$
|
|
4776
|
+
log$c.debug('Initializing IndexedDB cache', {
|
|
4201
4777
|
dbName: WebCacheAdapter.DB_NAME,
|
|
4202
4778
|
version: WebCacheAdapter.DB_VERSION,
|
|
4203
4779
|
});
|
|
4204
4780
|
try {
|
|
4205
4781
|
this.db = await this.openDatabase();
|
|
4206
|
-
log$
|
|
4782
|
+
log$c.debug('IndexedDB cache initialized successfully');
|
|
4207
4783
|
this.retryCount = 0; // Reset retry count on success
|
|
4208
4784
|
}
|
|
4209
4785
|
catch (error) {
|
|
4210
4786
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
4211
|
-
log$
|
|
4787
|
+
log$c.debug('Failed to initialize IndexedDB', { error: errorMessage });
|
|
4212
4788
|
// Check if this is a version conflict error
|
|
4213
4789
|
if (this.isVersionConflictError(errorMessage)) {
|
|
4214
4790
|
await this.handleVersionConflict();
|
|
@@ -4221,32 +4797,32 @@ class WebCacheAdapter {
|
|
|
4221
4797
|
async openDatabase() {
|
|
4222
4798
|
return await openDB(WebCacheAdapter.DB_NAME, WebCacheAdapter.DB_VERSION, {
|
|
4223
4799
|
upgrade: (db, oldVersion, newVersion, transaction) => {
|
|
4224
|
-
log$
|
|
4800
|
+
log$c.debug('Database upgrade needed', { oldVersion, newVersion });
|
|
4225
4801
|
this.handleUpgrade(db, oldVersion, newVersion, transaction);
|
|
4226
4802
|
},
|
|
4227
4803
|
blocked: () => {
|
|
4228
|
-
log$
|
|
4804
|
+
log$c.debug('Database blocked - another tab may be open');
|
|
4229
4805
|
},
|
|
4230
4806
|
blocking: () => {
|
|
4231
|
-
log$
|
|
4807
|
+
log$c.debug('Database blocking - will close connection');
|
|
4232
4808
|
if (this.db) {
|
|
4233
4809
|
this.db.close();
|
|
4234
4810
|
this.db = null;
|
|
4235
4811
|
}
|
|
4236
4812
|
},
|
|
4237
4813
|
terminated: () => {
|
|
4238
|
-
log$
|
|
4814
|
+
log$c.debug('Database connection terminated unexpectedly');
|
|
4239
4815
|
this.db = null;
|
|
4240
4816
|
},
|
|
4241
4817
|
});
|
|
4242
4818
|
}
|
|
4243
4819
|
handleUpgrade(db, oldVersion, newVersion, transaction) {
|
|
4244
|
-
log$
|
|
4820
|
+
log$c.debug('Handling database upgrade', { oldVersion, newVersion });
|
|
4245
4821
|
// Create cache store if it doesn't exist (initial setup)
|
|
4246
4822
|
if (!db.objectStoreNames.contains(WebCacheAdapter.STORE_NAME)) {
|
|
4247
4823
|
const store = db.createObjectStore(WebCacheAdapter.STORE_NAME, { keyPath: 'key' });
|
|
4248
4824
|
store.createIndex('timestamp', 'timestamp', { unique: false });
|
|
4249
|
-
log$
|
|
4825
|
+
log$c.debug('Created cache store and timestamp index');
|
|
4250
4826
|
}
|
|
4251
4827
|
// Handle migration from version 1 to 2
|
|
4252
4828
|
if (oldVersion < 2) {
|
|
@@ -4257,16 +4833,16 @@ class WebCacheAdapter {
|
|
|
4257
4833
|
try {
|
|
4258
4834
|
if (store.indexNames.contains(indexName)) {
|
|
4259
4835
|
store.deleteIndex(indexName);
|
|
4260
|
-
log$
|
|
4836
|
+
log$c.debug(`Removed unused index: ${indexName}`);
|
|
4261
4837
|
}
|
|
4262
4838
|
}
|
|
4263
4839
|
catch (error) {
|
|
4264
4840
|
// Ignore errors if indexes don't exist
|
|
4265
|
-
log$
|
|
4841
|
+
log$c.debug(`Warning: Could not remove index ${indexName}`, error);
|
|
4266
4842
|
}
|
|
4267
4843
|
});
|
|
4268
4844
|
}
|
|
4269
|
-
log$
|
|
4845
|
+
log$c.debug('Database upgrade completed');
|
|
4270
4846
|
}
|
|
4271
4847
|
isVersionConflictError(errorMessage) {
|
|
4272
4848
|
return (errorMessage.includes('less than the existing version') ||
|
|
@@ -4274,7 +4850,7 @@ class WebCacheAdapter {
|
|
|
4274
4850
|
errorMessage.includes('VersionError'));
|
|
4275
4851
|
}
|
|
4276
4852
|
async handleVersionConflict() {
|
|
4277
|
-
log$
|
|
4853
|
+
log$c.debug('Handling version conflict, attempting recovery...');
|
|
4278
4854
|
if (this.retryCount >= this.maxRetries) {
|
|
4279
4855
|
throw new Error('Failed to resolve IndexedDB version conflict after multiple attempts');
|
|
4280
4856
|
}
|
|
@@ -4286,19 +4862,19 @@ class WebCacheAdapter {
|
|
|
4286
4862
|
this.db = null;
|
|
4287
4863
|
}
|
|
4288
4864
|
// Delete the problematic database
|
|
4289
|
-
log$
|
|
4865
|
+
log$c.debug('Deleting problematic database to resolve version conflict');
|
|
4290
4866
|
await deleteDB(WebCacheAdapter.DB_NAME);
|
|
4291
4867
|
// Wait a bit for the deletion to complete
|
|
4292
4868
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
4293
4869
|
// Try to open the database again
|
|
4294
|
-
log$
|
|
4870
|
+
log$c.debug(`Retrying database initialization (attempt ${this.retryCount}/${this.maxRetries})`);
|
|
4295
4871
|
this.db = await this.openDatabase();
|
|
4296
|
-
log$
|
|
4872
|
+
log$c.debug('Successfully recovered from version conflict');
|
|
4297
4873
|
this.retryCount = 0; // Reset retry count on success
|
|
4298
4874
|
}
|
|
4299
4875
|
catch (retryError) {
|
|
4300
4876
|
const retryErrorMessage = retryError instanceof Error ? retryError.message : 'Unknown error';
|
|
4301
|
-
log$
|
|
4877
|
+
log$c.debug('Recovery attempt failed', { attempt: this.retryCount, error: retryErrorMessage });
|
|
4302
4878
|
if (this.retryCount < this.maxRetries) {
|
|
4303
4879
|
// Try again
|
|
4304
4880
|
await this.handleVersionConflict();
|
|
@@ -4310,7 +4886,7 @@ class WebCacheAdapter {
|
|
|
4310
4886
|
}
|
|
4311
4887
|
async get(key) {
|
|
4312
4888
|
await this.ensureInitialized();
|
|
4313
|
-
log$
|
|
4889
|
+
log$c.debug('Getting cache item', { key });
|
|
4314
4890
|
try {
|
|
4315
4891
|
const transaction = this.db.transaction([WebCacheAdapter.STORE_NAME], 'readonly');
|
|
4316
4892
|
const store = transaction.objectStore(WebCacheAdapter.STORE_NAME);
|
|
@@ -4336,7 +4912,7 @@ class WebCacheAdapter {
|
|
|
4336
4912
|
};
|
|
4337
4913
|
}
|
|
4338
4914
|
catch (error) {
|
|
4339
|
-
log$
|
|
4915
|
+
log$c.debug('Error getting cache item', { key, error });
|
|
4340
4916
|
return null; // Return null on error instead of throwing
|
|
4341
4917
|
}
|
|
4342
4918
|
}
|
|
@@ -4358,7 +4934,7 @@ class WebCacheAdapter {
|
|
|
4358
4934
|
if (compressionResult.compressed) {
|
|
4359
4935
|
finalData = compressionResult.data;
|
|
4360
4936
|
isCompressed = true;
|
|
4361
|
-
log$
|
|
4937
|
+
log$c.debug('Compression result', {
|
|
4362
4938
|
key,
|
|
4363
4939
|
originalSize: compressionResult.originalSize,
|
|
4364
4940
|
compressedSize: compressionResult.compressedSize,
|
|
@@ -4367,7 +4943,7 @@ class WebCacheAdapter {
|
|
|
4367
4943
|
});
|
|
4368
4944
|
}
|
|
4369
4945
|
}
|
|
4370
|
-
log$
|
|
4946
|
+
log$c.debug('Setting cache item', { key, timestamp: item.timestamp, compressed: isCompressed });
|
|
4371
4947
|
const storedItem = {
|
|
4372
4948
|
key,
|
|
4373
4949
|
data: finalData,
|
|
@@ -4380,7 +4956,7 @@ class WebCacheAdapter {
|
|
|
4380
4956
|
await store.put(storedItem);
|
|
4381
4957
|
}
|
|
4382
4958
|
catch (error) {
|
|
4383
|
-
log$
|
|
4959
|
+
log$c.debug('Error setting cache item', { key, error });
|
|
4384
4960
|
// Silently fail for cache writes
|
|
4385
4961
|
}
|
|
4386
4962
|
}
|
|
@@ -4388,7 +4964,7 @@ class WebCacheAdapter {
|
|
|
4388
4964
|
if (items.length === 0)
|
|
4389
4965
|
return;
|
|
4390
4966
|
await this.ensureInitialized();
|
|
4391
|
-
log$
|
|
4967
|
+
log$c.debug('Batch setting items', { count: items.length });
|
|
4392
4968
|
try {
|
|
4393
4969
|
const transaction = this.db.transaction([WebCacheAdapter.STORE_NAME], 'readwrite');
|
|
4394
4970
|
const store = transaction.objectStore(WebCacheAdapter.STORE_NAME);
|
|
@@ -4398,10 +4974,10 @@ class WebCacheAdapter {
|
|
|
4398
4974
|
return store.put(storedItem);
|
|
4399
4975
|
});
|
|
4400
4976
|
await Promise.all(promises);
|
|
4401
|
-
log$
|
|
4977
|
+
log$c.debug('Batch operation completed', { count: items.length });
|
|
4402
4978
|
}
|
|
4403
4979
|
catch (error) {
|
|
4404
|
-
log$
|
|
4980
|
+
log$c.debug('Error in batch operation', { count: items.length, error });
|
|
4405
4981
|
// Silently fail for batch writes
|
|
4406
4982
|
}
|
|
4407
4983
|
}
|
|
@@ -4436,10 +5012,10 @@ class WebCacheAdapter {
|
|
|
4436
5012
|
const transaction = this.db.transaction([WebCacheAdapter.STORE_NAME], 'readwrite');
|
|
4437
5013
|
const store = transaction.objectStore(WebCacheAdapter.STORE_NAME);
|
|
4438
5014
|
await store.clear();
|
|
4439
|
-
log$
|
|
5015
|
+
log$c.debug('Cache cleared successfully');
|
|
4440
5016
|
}
|
|
4441
5017
|
catch (error) {
|
|
4442
|
-
log$
|
|
5018
|
+
log$c.debug('Error clearing cache', error);
|
|
4443
5019
|
// Silently fail for cache clear
|
|
4444
5020
|
}
|
|
4445
5021
|
}
|
|
@@ -4465,7 +5041,7 @@ class WebCacheAdapter {
|
|
|
4465
5041
|
};
|
|
4466
5042
|
}
|
|
4467
5043
|
catch (error) {
|
|
4468
|
-
log$
|
|
5044
|
+
log$c.debug('Error getting cache size', error);
|
|
4469
5045
|
return {
|
|
4470
5046
|
entries: 0,
|
|
4471
5047
|
bytes: 0,
|
|
@@ -4490,7 +5066,7 @@ class WebCacheAdapter {
|
|
|
4490
5066
|
return allKeys.filter((key) => regex.test(key));
|
|
4491
5067
|
}
|
|
4492
5068
|
catch (error) {
|
|
4493
|
-
log$
|
|
5069
|
+
log$c.debug('Error getting cache keys', error);
|
|
4494
5070
|
return [];
|
|
4495
5071
|
}
|
|
4496
5072
|
}
|
|
@@ -4503,7 +5079,7 @@ class WebCacheAdapter {
|
|
|
4503
5079
|
return true;
|
|
4504
5080
|
}
|
|
4505
5081
|
catch (error) {
|
|
4506
|
-
log$
|
|
5082
|
+
log$c.debug('Error deleting cache item', { key, error });
|
|
4507
5083
|
return false;
|
|
4508
5084
|
}
|
|
4509
5085
|
}
|
|
@@ -4515,7 +5091,7 @@ class WebCacheAdapter {
|
|
|
4515
5091
|
await this.initPromise;
|
|
4516
5092
|
}
|
|
4517
5093
|
catch (error) {
|
|
4518
|
-
log$
|
|
5094
|
+
log$c.debug('Failed to ensure initialization', error);
|
|
4519
5095
|
// Reset and try once more
|
|
4520
5096
|
this.initPromise = null;
|
|
4521
5097
|
this.db = null;
|
|
@@ -4534,7 +5110,7 @@ WebCacheAdapter.DB_NAME = 'acube_cache';
|
|
|
4534
5110
|
WebCacheAdapter.DB_VERSION = 2;
|
|
4535
5111
|
WebCacheAdapter.STORE_NAME = 'cache_entries';
|
|
4536
5112
|
|
|
4537
|
-
const log$
|
|
5113
|
+
const log$b = createPrefixedLogger('CACHE-LOADER');
|
|
4538
5114
|
function loadCacheAdapter(platform) {
|
|
4539
5115
|
try {
|
|
4540
5116
|
switch (platform) {
|
|
@@ -4566,7 +5142,7 @@ function loadCacheAdapter(platform) {
|
|
|
4566
5142
|
}
|
|
4567
5143
|
}
|
|
4568
5144
|
catch (error) {
|
|
4569
|
-
log$
|
|
5145
|
+
log$b.warn(`Cache adapter not available for platform ${platform}:`, error);
|
|
4570
5146
|
return undefined;
|
|
4571
5147
|
}
|
|
4572
5148
|
}
|
|
@@ -4623,7 +5199,7 @@ class BaseSecureStorageAdapter {
|
|
|
4623
5199
|
}
|
|
4624
5200
|
}
|
|
4625
5201
|
|
|
4626
|
-
const log$
|
|
5202
|
+
const log$a = createPrefixedLogger('NETWORK-BASE');
|
|
4627
5203
|
class NetworkBase {
|
|
4628
5204
|
constructor(initialOnline = true, debounceMs = 300) {
|
|
4629
5205
|
this.destroy$ = new Subject();
|
|
@@ -4643,14 +5219,14 @@ class NetworkBase {
|
|
|
4643
5219
|
const current = this.statusSubject.getValue();
|
|
4644
5220
|
if (current.online !== online) {
|
|
4645
5221
|
this.statusSubject.next({ online, timestamp: Date.now() });
|
|
4646
|
-
log$
|
|
5222
|
+
log$a.debug(`Network status changed: ${online ? 'online' : 'offline'}`);
|
|
4647
5223
|
}
|
|
4648
5224
|
}
|
|
4649
5225
|
destroy() {
|
|
4650
5226
|
this.destroy$.next();
|
|
4651
5227
|
this.destroy$.complete();
|
|
4652
5228
|
this.statusSubject.complete();
|
|
4653
|
-
log$
|
|
5229
|
+
log$a.debug('Network monitor destroyed');
|
|
4654
5230
|
}
|
|
4655
5231
|
}
|
|
4656
5232
|
|
|
@@ -4710,7 +5286,7 @@ class NodeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
4710
5286
|
}
|
|
4711
5287
|
}
|
|
4712
5288
|
|
|
4713
|
-
const log$
|
|
5289
|
+
const log$9 = createPrefixedLogger('RN-STORAGE');
|
|
4714
5290
|
/**
|
|
4715
5291
|
* React Native storage adapter using AsyncStorage
|
|
4716
5292
|
* Note: Uses native batch operations for better performance (not base class)
|
|
@@ -4742,7 +5318,7 @@ class ReactNativeStorageAdapter {
|
|
|
4742
5318
|
return await this.AsyncStorage.getItem(key);
|
|
4743
5319
|
}
|
|
4744
5320
|
catch (error) {
|
|
4745
|
-
log$
|
|
5321
|
+
log$9.error('Failed to get item from AsyncStorage:', error);
|
|
4746
5322
|
return null;
|
|
4747
5323
|
}
|
|
4748
5324
|
}
|
|
@@ -4783,7 +5359,7 @@ class ReactNativeStorageAdapter {
|
|
|
4783
5359
|
return await this.AsyncStorage.getAllKeys();
|
|
4784
5360
|
}
|
|
4785
5361
|
catch (error) {
|
|
4786
|
-
log$
|
|
5362
|
+
log$9.error('Failed to get all keys:', error);
|
|
4787
5363
|
return [];
|
|
4788
5364
|
}
|
|
4789
5365
|
}
|
|
@@ -4800,7 +5376,7 @@ class ReactNativeStorageAdapter {
|
|
|
4800
5376
|
return result;
|
|
4801
5377
|
}
|
|
4802
5378
|
catch (error) {
|
|
4803
|
-
log$
|
|
5379
|
+
log$9.error('Failed to get multiple items:', error);
|
|
4804
5380
|
const result = {};
|
|
4805
5381
|
keys.forEach((key) => {
|
|
4806
5382
|
result[key] = null;
|
|
@@ -4850,7 +5426,7 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
4850
5426
|
return;
|
|
4851
5427
|
}
|
|
4852
5428
|
catch {
|
|
4853
|
-
log$
|
|
5429
|
+
log$9.debug('expo-secure-store not available, trying react-native-keychain');
|
|
4854
5430
|
}
|
|
4855
5431
|
try {
|
|
4856
5432
|
const Keychain = require('react-native-keychain');
|
|
@@ -4859,7 +5435,7 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
4859
5435
|
return;
|
|
4860
5436
|
}
|
|
4861
5437
|
catch {
|
|
4862
|
-
log$
|
|
5438
|
+
log$9.error('react-native-keychain not available');
|
|
4863
5439
|
}
|
|
4864
5440
|
throw new Error('No secure storage available. Please install expo-secure-store or react-native-keychain');
|
|
4865
5441
|
}
|
|
@@ -4877,7 +5453,7 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
4877
5453
|
}
|
|
4878
5454
|
}
|
|
4879
5455
|
catch (error) {
|
|
4880
|
-
log$
|
|
5456
|
+
log$9.error('Failed to get secure item:', error);
|
|
4881
5457
|
}
|
|
4882
5458
|
return null;
|
|
4883
5459
|
}
|
|
@@ -4914,10 +5490,10 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
4914
5490
|
}
|
|
4915
5491
|
}
|
|
4916
5492
|
async clear() {
|
|
4917
|
-
log$
|
|
5493
|
+
log$9.warn('Clear all secure items not fully implemented for React Native');
|
|
4918
5494
|
}
|
|
4919
5495
|
async getAllKeys() {
|
|
4920
|
-
log$
|
|
5496
|
+
log$9.warn('Get all secure keys not implemented for React Native');
|
|
4921
5497
|
return [];
|
|
4922
5498
|
}
|
|
4923
5499
|
async isAvailable() {
|
|
@@ -5135,7 +5711,7 @@ class NodeNetworkMonitor extends NetworkBase {
|
|
|
5135
5711
|
}
|
|
5136
5712
|
}
|
|
5137
5713
|
|
|
5138
|
-
const log$
|
|
5714
|
+
const log$8 = createPrefixedLogger('RN-NETWORK');
|
|
5139
5715
|
/**
|
|
5140
5716
|
* React Native network monitor using RxJS
|
|
5141
5717
|
* Supports both @react-native-community/netinfo and expo-network
|
|
@@ -5148,7 +5724,7 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5148
5724
|
this.moduleReady$ = new Subject();
|
|
5149
5725
|
this.isExpo = detectPlatform().isExpo;
|
|
5150
5726
|
this.init().catch((error) => {
|
|
5151
|
-
log$
|
|
5727
|
+
log$8.error('Network monitor initialization failed:', error);
|
|
5152
5728
|
});
|
|
5153
5729
|
}
|
|
5154
5730
|
async init() {
|
|
@@ -5167,10 +5743,10 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5167
5743
|
try {
|
|
5168
5744
|
const module = require('@react-native-community/netinfo');
|
|
5169
5745
|
this.netInfoModule = module.default || module;
|
|
5170
|
-
log$
|
|
5746
|
+
log$8.debug('Loaded @react-native-community/netinfo module');
|
|
5171
5747
|
}
|
|
5172
5748
|
catch (error) {
|
|
5173
|
-
log$
|
|
5749
|
+
log$8.error('Failed to load React Native NetInfo module:', error);
|
|
5174
5750
|
this.netInfoModule = null;
|
|
5175
5751
|
}
|
|
5176
5752
|
}
|
|
@@ -5178,10 +5754,10 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5178
5754
|
try {
|
|
5179
5755
|
const module = require('expo-network');
|
|
5180
5756
|
this.netInfoModule = module.default || module;
|
|
5181
|
-
log$
|
|
5757
|
+
log$8.debug('Loaded expo-network module');
|
|
5182
5758
|
}
|
|
5183
5759
|
catch (error) {
|
|
5184
|
-
log$
|
|
5760
|
+
log$8.error('Failed to load Expo Network module:', error);
|
|
5185
5761
|
this.netInfoModule = null;
|
|
5186
5762
|
}
|
|
5187
5763
|
}
|
|
@@ -5198,16 +5774,16 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5198
5774
|
}
|
|
5199
5775
|
const online = !!(state.isConnected && state.isInternetReachable !== false);
|
|
5200
5776
|
this.updateStatus(online);
|
|
5201
|
-
log$
|
|
5777
|
+
log$8.debug('Initial network state:', online ? 'online' : 'offline');
|
|
5202
5778
|
}
|
|
5203
5779
|
catch (error) {
|
|
5204
|
-
log$
|
|
5780
|
+
log$8.warn('Could not fetch initial network state:', error);
|
|
5205
5781
|
}
|
|
5206
5782
|
}
|
|
5207
5783
|
subscribeToStateChanges() {
|
|
5208
5784
|
if (!this.netInfoModule)
|
|
5209
5785
|
return;
|
|
5210
|
-
log$
|
|
5786
|
+
log$8.debug('Subscribing to network state changes');
|
|
5211
5787
|
const handleState = (state) => {
|
|
5212
5788
|
const online = !!(state.isConnected && (state.isInternetReachable ?? true));
|
|
5213
5789
|
this.updateStatus(online);
|
|
@@ -5249,7 +5825,7 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5249
5825
|
};
|
|
5250
5826
|
}
|
|
5251
5827
|
catch (error) {
|
|
5252
|
-
log$
|
|
5828
|
+
log$8.error('Failed to fetch detailed network info:', error);
|
|
5253
5829
|
return null;
|
|
5254
5830
|
}
|
|
5255
5831
|
}
|
|
@@ -5385,7 +5961,7 @@ class CertificateValidator {
|
|
|
5385
5961
|
}
|
|
5386
5962
|
}
|
|
5387
5963
|
|
|
5388
|
-
const log$
|
|
5964
|
+
const log$7 = createPrefixedLogger('RN-MTLS');
|
|
5389
5965
|
/**
|
|
5390
5966
|
* React Native mTLS Adapter using @a-cube-io/expo-mutual-tls
|
|
5391
5967
|
*/
|
|
@@ -5407,7 +5983,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5407
5983
|
this.expoMTLS = ExpoMutualTls;
|
|
5408
5984
|
// Set up debug logging with the correct event signature
|
|
5409
5985
|
const debugListener = ExpoMutualTls.onDebugLog((event) => {
|
|
5410
|
-
log$
|
|
5986
|
+
log$7.debug(`${event.type}: ${event.message}`, {
|
|
5411
5987
|
method: event.method,
|
|
5412
5988
|
url: event.url,
|
|
5413
5989
|
statusCode: event.statusCode,
|
|
@@ -5417,28 +5993,28 @@ class ReactNativeMTLSAdapter {
|
|
|
5417
5993
|
this.eventListeners.push(debugListener);
|
|
5418
5994
|
// Set up error logging with the correct event signature
|
|
5419
5995
|
const errorListener = ExpoMutualTls.onError((event) => {
|
|
5420
|
-
log$
|
|
5996
|
+
log$7.error(event.message, {
|
|
5421
5997
|
code: event.code,
|
|
5422
5998
|
});
|
|
5423
5999
|
});
|
|
5424
6000
|
this.eventListeners.push(errorListener);
|
|
5425
6001
|
// Set up certificate expiry monitoring with the correct event signature
|
|
5426
6002
|
const expiryListener = ExpoMutualTls.onCertificateExpiry((event) => {
|
|
5427
|
-
log$
|
|
6003
|
+
log$7.warn(`Certificate ${event.subject} expires at ${new Date(event.expiry)}`, {
|
|
5428
6004
|
alias: event.alias,
|
|
5429
6005
|
warning: event.warning,
|
|
5430
6006
|
});
|
|
5431
6007
|
});
|
|
5432
6008
|
this.eventListeners.push(expiryListener);
|
|
5433
|
-
log$
|
|
6009
|
+
log$7.debug('Expo mTLS module loaded successfully');
|
|
5434
6010
|
}
|
|
5435
6011
|
catch (error) {
|
|
5436
|
-
log$
|
|
6012
|
+
log$7.warn('@a-cube-io/expo-mutual-tls not available:', error);
|
|
5437
6013
|
}
|
|
5438
6014
|
}
|
|
5439
6015
|
async isMTLSSupported() {
|
|
5440
6016
|
const supported = this.expoMTLS !== null;
|
|
5441
|
-
log$
|
|
6017
|
+
log$7.debug('mTLS support check:', {
|
|
5442
6018
|
supported,
|
|
5443
6019
|
platform: this.getPlatformInfo().platform,
|
|
5444
6020
|
moduleAvailable: !!this.expoMTLS,
|
|
@@ -5450,7 +6026,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5450
6026
|
throw new MTLSError(MTLSErrorType$1.NOT_SUPPORTED, 'Expo mTLS module not available');
|
|
5451
6027
|
}
|
|
5452
6028
|
this.config = config;
|
|
5453
|
-
log$
|
|
6029
|
+
log$7.debug('Initialized with config:', {
|
|
5454
6030
|
baseUrl: config.baseUrl,
|
|
5455
6031
|
port: config.port,
|
|
5456
6032
|
timeout: config.timeout,
|
|
@@ -5464,7 +6040,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5464
6040
|
if (!this.config) {
|
|
5465
6041
|
throw new MTLSError(MTLSErrorType$1.CONFIGURATION_ERROR, 'Adapter not initialized. Call initialize() first.');
|
|
5466
6042
|
}
|
|
5467
|
-
log$
|
|
6043
|
+
log$7.debug('Configuring certificate:', {
|
|
5468
6044
|
format: certificateData.format,
|
|
5469
6045
|
hasPassword: !!certificateData.password,
|
|
5470
6046
|
certificateLength: certificateData.certificate.length,
|
|
@@ -5481,14 +6057,14 @@ class ReactNativeMTLSAdapter {
|
|
|
5481
6057
|
'client-key-service', // keyService
|
|
5482
6058
|
true // enableLogging - let the native module handle its own debug logging
|
|
5483
6059
|
);
|
|
5484
|
-
log$
|
|
6060
|
+
log$7.debug('PEM services configured:', configResult);
|
|
5485
6061
|
if (!configResult.success) {
|
|
5486
6062
|
throw new MTLSError(MTLSErrorType$1.CONFIGURATION_ERROR, `PEM configuration failed: ${configResult.state}`);
|
|
5487
6063
|
}
|
|
5488
6064
|
// Step 2: Store the actual PEM certificate and private key
|
|
5489
6065
|
const storeResult = await this.expoMTLS.storePEM(certificateData.certificate, certificateData.privateKey, certificateData.password // passphrase (optional)
|
|
5490
6066
|
);
|
|
5491
|
-
log$
|
|
6067
|
+
log$7.debug('PEM certificate store result:', storeResult);
|
|
5492
6068
|
if (!storeResult) {
|
|
5493
6069
|
throw new MTLSError(MTLSErrorType$1.CERTIFICATE_INVALID, 'Failed to store PEM certificate');
|
|
5494
6070
|
}
|
|
@@ -5501,14 +6077,14 @@ class ReactNativeMTLSAdapter {
|
|
|
5501
6077
|
const configResult = await this.expoMTLS.configureP12('client-p12-service', // keychainService
|
|
5502
6078
|
true // enableLogging - let the native module handle its own debug logging
|
|
5503
6079
|
);
|
|
5504
|
-
log$
|
|
6080
|
+
log$7.debug('P12 service configured:', configResult);
|
|
5505
6081
|
if (!configResult.success) {
|
|
5506
6082
|
throw new MTLSError(MTLSErrorType$1.CONFIGURATION_ERROR, `P12 configuration failed: ${configResult.state}`);
|
|
5507
6083
|
}
|
|
5508
6084
|
// Step 2: Store the P12 certificate data
|
|
5509
6085
|
const storeResult = await this.expoMTLS.storeP12(certificateData.certificate, // P12 data in certificate field
|
|
5510
6086
|
certificateData.password);
|
|
5511
|
-
log$
|
|
6087
|
+
log$7.debug('P12 certificate store result:', storeResult);
|
|
5512
6088
|
if (!storeResult) {
|
|
5513
6089
|
throw new MTLSError(MTLSErrorType$1.CERTIFICATE_INVALID, 'Failed to store P12 certificate');
|
|
5514
6090
|
}
|
|
@@ -5522,7 +6098,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5522
6098
|
if (error instanceof MTLSError) {
|
|
5523
6099
|
throw error;
|
|
5524
6100
|
}
|
|
5525
|
-
log$
|
|
6101
|
+
log$7.error('Certificate configuration failed:', error);
|
|
5526
6102
|
throw new MTLSError(MTLSErrorType$1.CONFIGURATION_ERROR, 'Failed to configure certificate', error);
|
|
5527
6103
|
}
|
|
5528
6104
|
}
|
|
@@ -5533,38 +6109,38 @@ class ReactNativeMTLSAdapter {
|
|
|
5533
6109
|
try {
|
|
5534
6110
|
// Use static method call
|
|
5535
6111
|
const hasCert = await this.expoMTLS.hasCertificate();
|
|
5536
|
-
log$
|
|
6112
|
+
log$7.debug('Certificate availability check:', hasCert);
|
|
5537
6113
|
return hasCert;
|
|
5538
6114
|
}
|
|
5539
6115
|
catch (error) {
|
|
5540
|
-
log$
|
|
6116
|
+
log$7.error('Certificate check failed:', error);
|
|
5541
6117
|
return false;
|
|
5542
6118
|
}
|
|
5543
6119
|
}
|
|
5544
6120
|
async getCertificateInfo() {
|
|
5545
6121
|
if (!this.expoMTLS) {
|
|
5546
|
-
log$
|
|
6122
|
+
log$7.debug('Certificate info requested but module not available');
|
|
5547
6123
|
return null;
|
|
5548
6124
|
}
|
|
5549
6125
|
try {
|
|
5550
6126
|
const hasCert = await this.hasCertificate();
|
|
5551
6127
|
if (!hasCert) {
|
|
5552
|
-
log$
|
|
6128
|
+
log$7.debug('No certificate stored');
|
|
5553
6129
|
return null;
|
|
5554
6130
|
}
|
|
5555
6131
|
// Use getCertificatesInfo to retrieve information about stored certificates
|
|
5556
6132
|
const result = await this.expoMTLS.getCertificatesInfo();
|
|
5557
6133
|
if (!result || !result.certificates || result.certificates.length === 0) {
|
|
5558
|
-
log$
|
|
6134
|
+
log$7.debug('No certificate information available');
|
|
5559
6135
|
return null;
|
|
5560
6136
|
}
|
|
5561
6137
|
// Get the first certificate (primary client certificate)
|
|
5562
6138
|
const cert = result.certificates[0];
|
|
5563
6139
|
if (!cert) {
|
|
5564
|
-
log$
|
|
6140
|
+
log$7.debug('Certificate data is empty');
|
|
5565
6141
|
return null;
|
|
5566
6142
|
}
|
|
5567
|
-
log$
|
|
6143
|
+
log$7.debug('Retrieved certificate info:', {
|
|
5568
6144
|
subject: cert.subject.commonName,
|
|
5569
6145
|
issuer: cert.issuer.commonName,
|
|
5570
6146
|
validFrom: new Date(cert.validFrom),
|
|
@@ -5583,7 +6159,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5583
6159
|
};
|
|
5584
6160
|
}
|
|
5585
6161
|
catch (error) {
|
|
5586
|
-
log$
|
|
6162
|
+
log$7.error('Failed to get certificate info:', error);
|
|
5587
6163
|
return null;
|
|
5588
6164
|
}
|
|
5589
6165
|
}
|
|
@@ -5594,7 +6170,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5594
6170
|
*/
|
|
5595
6171
|
async parseCertificateData(certificateData) {
|
|
5596
6172
|
if (!this.expoMTLS) {
|
|
5597
|
-
log$
|
|
6173
|
+
log$7.debug('Parse certificate: Module not available');
|
|
5598
6174
|
return null;
|
|
5599
6175
|
}
|
|
5600
6176
|
try {
|
|
@@ -5611,14 +6187,14 @@ class ReactNativeMTLSAdapter {
|
|
|
5611
6187
|
else {
|
|
5612
6188
|
throw new MTLSError(MTLSErrorType$1.CERTIFICATE_INVALID, `Unsupported certificate format: ${certificateData.format}`);
|
|
5613
6189
|
}
|
|
5614
|
-
log$
|
|
6190
|
+
log$7.debug('Certificate parsed successfully:', {
|
|
5615
6191
|
certificateCount: result.certificates.length,
|
|
5616
6192
|
subjects: result.certificates.map((cert) => cert.subject.commonName),
|
|
5617
6193
|
});
|
|
5618
6194
|
return result;
|
|
5619
6195
|
}
|
|
5620
6196
|
catch (error) {
|
|
5621
|
-
log$
|
|
6197
|
+
log$7.error('Failed to parse certificate:', error);
|
|
5622
6198
|
if (error instanceof MTLSError) {
|
|
5623
6199
|
throw error;
|
|
5624
6200
|
}
|
|
@@ -5633,7 +6209,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5633
6209
|
if (!hasCert) {
|
|
5634
6210
|
throw new MTLSError(MTLSErrorType$1.CERTIFICATE_NOT_FOUND, 'No certificate configured');
|
|
5635
6211
|
}
|
|
5636
|
-
log$
|
|
6212
|
+
log$7.debug('Making mTLS request:', {
|
|
5637
6213
|
method: requestConfig.method || 'GET',
|
|
5638
6214
|
url: requestConfig.url,
|
|
5639
6215
|
headers: requestConfig.headers,
|
|
@@ -5641,7 +6217,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5641
6217
|
responseType: requestConfig.responseType,
|
|
5642
6218
|
});
|
|
5643
6219
|
if (requestConfig.data) {
|
|
5644
|
-
log$
|
|
6220
|
+
log$7.debug('mTLS request body:', requestConfig.data);
|
|
5645
6221
|
}
|
|
5646
6222
|
try {
|
|
5647
6223
|
const response = await this.expoMTLS.request(requestConfig.url, {
|
|
@@ -5650,7 +6226,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5650
6226
|
body: requestConfig.data ? JSON.stringify(requestConfig.data) : undefined,
|
|
5651
6227
|
responseType: requestConfig.responseType,
|
|
5652
6228
|
});
|
|
5653
|
-
log$
|
|
6229
|
+
log$7.debug('mTLS request successful:', response);
|
|
5654
6230
|
if (!response.success) {
|
|
5655
6231
|
throw new MTLSError(MTLSErrorType$1.CONNECTION_FAILED, `mTLS request failed: ${response.statusMessage} (${response.statusCode})`, undefined, response.statusCode);
|
|
5656
6232
|
}
|
|
@@ -5662,7 +6238,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5662
6238
|
data = JSON.parse(response.body);
|
|
5663
6239
|
}
|
|
5664
6240
|
catch (parseError) {
|
|
5665
|
-
log$
|
|
6241
|
+
log$7.warn('Failed to parse JSON response:', parseError);
|
|
5666
6242
|
// If parsing fails, keep raw body
|
|
5667
6243
|
}
|
|
5668
6244
|
}
|
|
@@ -5679,7 +6255,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5679
6255
|
};
|
|
5680
6256
|
}
|
|
5681
6257
|
catch (error) {
|
|
5682
|
-
log$
|
|
6258
|
+
log$7.error('mTLS request failed:', error);
|
|
5683
6259
|
throw new MTLSError(MTLSErrorType$1.CONNECTION_FAILED, 'mTLS request failed', error);
|
|
5684
6260
|
}
|
|
5685
6261
|
}
|
|
@@ -5692,18 +6268,18 @@ class ReactNativeMTLSAdapter {
|
|
|
5692
6268
|
*/
|
|
5693
6269
|
async testConnection() {
|
|
5694
6270
|
if (!this.expoMTLS || !this.config) {
|
|
5695
|
-
log$
|
|
6271
|
+
log$7.debug('Diagnostic test: No mTLS module or config available');
|
|
5696
6272
|
return false;
|
|
5697
6273
|
}
|
|
5698
6274
|
try {
|
|
5699
6275
|
const hasCert = await this.hasCertificate();
|
|
5700
6276
|
if (!hasCert) {
|
|
5701
|
-
log$
|
|
6277
|
+
log$7.debug('Diagnostic test: No certificate configured');
|
|
5702
6278
|
return false;
|
|
5703
6279
|
}
|
|
5704
|
-
log$
|
|
6280
|
+
log$7.debug('Running diagnostic test (may fail even if mTLS works):', this.config.baseUrl);
|
|
5705
6281
|
const result = await this.expoMTLS.testConnection(this.config.baseUrl);
|
|
5706
|
-
log$
|
|
6282
|
+
log$7.debug('Diagnostic test result (NOT validation):', {
|
|
5707
6283
|
success: result.success,
|
|
5708
6284
|
statusCode: result.statusCode,
|
|
5709
6285
|
statusMessage: result.statusMessage,
|
|
@@ -5714,13 +6290,13 @@ class ReactNativeMTLSAdapter {
|
|
|
5714
6290
|
return result.success;
|
|
5715
6291
|
}
|
|
5716
6292
|
catch (error) {
|
|
5717
|
-
log$
|
|
6293
|
+
log$7.warn('Diagnostic test failed (this is expected):', error);
|
|
5718
6294
|
return false;
|
|
5719
6295
|
}
|
|
5720
6296
|
}
|
|
5721
6297
|
async removeCertificate() {
|
|
5722
6298
|
if (!this.expoMTLS) {
|
|
5723
|
-
log$
|
|
6299
|
+
log$7.debug('Remove certificate: Module not available');
|
|
5724
6300
|
return;
|
|
5725
6301
|
}
|
|
5726
6302
|
try {
|
|
@@ -5729,10 +6305,10 @@ class ReactNativeMTLSAdapter {
|
|
|
5729
6305
|
this.isConfigured = false;
|
|
5730
6306
|
// Cleanup event listeners
|
|
5731
6307
|
this.cleanupEventListeners();
|
|
5732
|
-
log$
|
|
6308
|
+
log$7.debug('Certificate removed successfully');
|
|
5733
6309
|
}
|
|
5734
6310
|
catch (error) {
|
|
5735
|
-
log$
|
|
6311
|
+
log$7.error('Failed to remove certificate:', error);
|
|
5736
6312
|
throw new MTLSError(MTLSErrorType$1.CONFIGURATION_ERROR, 'Failed to remove certificate', error);
|
|
5737
6313
|
}
|
|
5738
6314
|
}
|
|
@@ -5741,7 +6317,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5741
6317
|
*/
|
|
5742
6318
|
cleanupEventListeners() {
|
|
5743
6319
|
if (this.eventListeners.length > 0) {
|
|
5744
|
-
log$
|
|
6320
|
+
log$7.debug(`Cleaning up ${this.eventListeners.length} event listeners`);
|
|
5745
6321
|
}
|
|
5746
6322
|
// Remove individual listeners if they have remove methods
|
|
5747
6323
|
this.eventListeners.forEach((listener) => {
|
|
@@ -5778,7 +6354,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5778
6354
|
}
|
|
5779
6355
|
}
|
|
5780
6356
|
|
|
5781
|
-
const log$
|
|
6357
|
+
const log$6 = createPrefixedLogger('WEB-MTLS');
|
|
5782
6358
|
/**
|
|
5783
6359
|
* Web mTLS Adapter - Graceful fallback for web browsers
|
|
5784
6360
|
*
|
|
@@ -5792,13 +6368,13 @@ const log$7 = createPrefixedLogger('WEB-MTLS');
|
|
|
5792
6368
|
*/
|
|
5793
6369
|
class WebMTLSAdapter {
|
|
5794
6370
|
constructor() {
|
|
5795
|
-
log$
|
|
5796
|
-
log$
|
|
6371
|
+
log$6.warn('Web browsers do not support programmatic mTLS configuration');
|
|
6372
|
+
log$6.info('Use JWT authentication or configure client certificates in browser settings');
|
|
5797
6373
|
}
|
|
5798
6374
|
async isMTLSSupported() {
|
|
5799
6375
|
// mTLS is not supported programmatically in web browsers
|
|
5800
6376
|
const supported = false;
|
|
5801
|
-
log$
|
|
6377
|
+
log$6.debug('mTLS support check:', {
|
|
5802
6378
|
supported,
|
|
5803
6379
|
platform: this.getPlatformInfo().platform,
|
|
5804
6380
|
reason: 'Browser security model prevents programmatic certificate configuration',
|
|
@@ -5807,14 +6383,14 @@ class WebMTLSAdapter {
|
|
|
5807
6383
|
return supported;
|
|
5808
6384
|
}
|
|
5809
6385
|
async initialize(config) {
|
|
5810
|
-
log$
|
|
6386
|
+
log$6.warn('Initialized but mTLS not available in web browsers:', {
|
|
5811
6387
|
baseUrl: config.baseUrl,
|
|
5812
6388
|
port: config.port,
|
|
5813
6389
|
recommendation: 'Use standard HTTPS with JWT authentication',
|
|
5814
6390
|
});
|
|
5815
6391
|
}
|
|
5816
6392
|
async configureCertificate(certificateData) {
|
|
5817
|
-
log$
|
|
6393
|
+
log$6.error('Certificate configuration attempted:', {
|
|
5818
6394
|
format: certificateData.format,
|
|
5819
6395
|
reason: 'Not supported in web browsers',
|
|
5820
6396
|
alternatives: [
|
|
@@ -5829,15 +6405,15 @@ class WebMTLSAdapter {
|
|
|
5829
6405
|
}
|
|
5830
6406
|
async hasCertificate() {
|
|
5831
6407
|
// We cannot detect if the browser has certificates configured
|
|
5832
|
-
log$
|
|
6408
|
+
log$6.debug('Certificate availability check: Cannot detect browser certificates programmatically');
|
|
5833
6409
|
return false;
|
|
5834
6410
|
}
|
|
5835
6411
|
async getCertificateInfo() {
|
|
5836
|
-
log$
|
|
6412
|
+
log$6.debug('Certificate info requested: Not accessible in web browsers');
|
|
5837
6413
|
return null;
|
|
5838
6414
|
}
|
|
5839
6415
|
async request(requestConfig) {
|
|
5840
|
-
log$
|
|
6416
|
+
log$6.error('mTLS request attempted:', {
|
|
5841
6417
|
method: requestConfig.method,
|
|
5842
6418
|
url: requestConfig.url,
|
|
5843
6419
|
reason: 'Not supported in web browsers',
|
|
@@ -5852,11 +6428,11 @@ class WebMTLSAdapter {
|
|
|
5852
6428
|
'are properly configured in the browser certificate store.');
|
|
5853
6429
|
}
|
|
5854
6430
|
async testConnection() {
|
|
5855
|
-
log$
|
|
6431
|
+
log$6.debug('Connection test: mTLS not available in web browsers');
|
|
5856
6432
|
return false;
|
|
5857
6433
|
}
|
|
5858
6434
|
async removeCertificate() {
|
|
5859
|
-
log$
|
|
6435
|
+
log$6.debug('Remove certificate: No certificates to remove (not supported in web browsers)');
|
|
5860
6436
|
// No-op - cannot remove certificates programmatically in browsers
|
|
5861
6437
|
}
|
|
5862
6438
|
/**
|
|
@@ -5864,7 +6440,7 @@ class WebMTLSAdapter {
|
|
|
5864
6440
|
* Always returns null for web browsers as mTLS is not supported
|
|
5865
6441
|
*/
|
|
5866
6442
|
getBaseUrl() {
|
|
5867
|
-
log$
|
|
6443
|
+
log$6.debug('Base URL requested: Not supported in web browsers');
|
|
5868
6444
|
return null;
|
|
5869
6445
|
}
|
|
5870
6446
|
getPlatformInfo() {
|
|
@@ -5897,7 +6473,7 @@ class WebMTLSAdapter {
|
|
|
5897
6473
|
}
|
|
5898
6474
|
}
|
|
5899
6475
|
|
|
5900
|
-
const log$
|
|
6476
|
+
const log$5 = createPrefixedLogger('MTLS-LOADER');
|
|
5901
6477
|
function loadMTLSAdapter(platform, config) {
|
|
5902
6478
|
try {
|
|
5903
6479
|
let adapter;
|
|
@@ -5931,7 +6507,7 @@ function loadMTLSAdapter(platform, config) {
|
|
|
5931
6507
|
return adapter;
|
|
5932
6508
|
}
|
|
5933
6509
|
catch (error) {
|
|
5934
|
-
log$
|
|
6510
|
+
log$5.warn(`mTLS adapter not available for platform ${platform}:`, error);
|
|
5935
6511
|
return null;
|
|
5936
6512
|
}
|
|
5937
6513
|
}
|
|
@@ -5941,7 +6517,7 @@ async function initializeAdapterAsync(adapter, config) {
|
|
|
5941
6517
|
if (isSupported) {
|
|
5942
6518
|
await adapter.initialize(config);
|
|
5943
6519
|
const platformInfo = adapter.getPlatformInfo();
|
|
5944
|
-
log$
|
|
6520
|
+
log$5.debug('mTLS adapter initialized:', {
|
|
5945
6521
|
platform: platformInfo.platform,
|
|
5946
6522
|
mtlsSupported: platformInfo.mtlsSupported,
|
|
5947
6523
|
certificateStorage: platformInfo.certificateStorage,
|
|
@@ -5950,20 +6526,20 @@ async function initializeAdapterAsync(adapter, config) {
|
|
|
5950
6526
|
}
|
|
5951
6527
|
}
|
|
5952
6528
|
catch (error) {
|
|
5953
|
-
log$
|
|
6529
|
+
log$5.warn('Failed to initialize mTLS adapter:', error);
|
|
5954
6530
|
}
|
|
5955
6531
|
}
|
|
5956
6532
|
|
|
5957
|
-
const log$
|
|
6533
|
+
const log$4 = createPrefixedLogger('ADAPTER-LOADER');
|
|
5958
6534
|
function loadPlatformAdapters(options = {}) {
|
|
5959
6535
|
const { mtlsConfig } = options;
|
|
5960
6536
|
const { platform } = detectPlatform();
|
|
5961
|
-
log$
|
|
6537
|
+
log$4.debug('Loading adapters for platform:', platform);
|
|
5962
6538
|
const storageAdapters = loadStorageAdapters(platform);
|
|
5963
6539
|
const networkMonitor = loadNetworkMonitor(platform);
|
|
5964
6540
|
const cache = loadCacheAdapter(platform);
|
|
5965
6541
|
const mtls = loadMTLSAdapter(platform, mtlsConfig);
|
|
5966
|
-
log$
|
|
6542
|
+
log$4.debug('Adapters loaded:', {
|
|
5967
6543
|
platform,
|
|
5968
6544
|
hasStorage: !!storageAdapters.storage,
|
|
5969
6545
|
hasSecureStorage: !!storageAdapters.secureStorage,
|
|
@@ -5989,92 +6565,6 @@ function createACubeMTLSConfig(baseUrl, timeout, autoInitialize = true, forcePor
|
|
|
5989
6565
|
};
|
|
5990
6566
|
}
|
|
5991
6567
|
|
|
5992
|
-
class ConfigManager {
|
|
5993
|
-
constructor(userConfig) {
|
|
5994
|
-
this.config = this.buildConfig(userConfig);
|
|
5995
|
-
}
|
|
5996
|
-
buildConfig(userConfig) {
|
|
5997
|
-
return {
|
|
5998
|
-
environment: userConfig.environment,
|
|
5999
|
-
apiUrl: this.getDefaultApiUrl(userConfig.environment),
|
|
6000
|
-
authUrl: this.getDefaultAuthUrl(userConfig.environment),
|
|
6001
|
-
timeout: 30000,
|
|
6002
|
-
retryAttempts: 3,
|
|
6003
|
-
debug: userConfig.debug ?? false,
|
|
6004
|
-
customHeaders: {},
|
|
6005
|
-
};
|
|
6006
|
-
}
|
|
6007
|
-
getDefaultApiUrl(environment) {
|
|
6008
|
-
switch (environment) {
|
|
6009
|
-
case 'production':
|
|
6010
|
-
return 'https://ereceipts-it.acubeapi.com';
|
|
6011
|
-
case 'development':
|
|
6012
|
-
return 'https://ereceipts-it.dev.acubeapi.com';
|
|
6013
|
-
case 'sandbox':
|
|
6014
|
-
default:
|
|
6015
|
-
return 'https://ereceipts-it-sandbox.acubeapi.com';
|
|
6016
|
-
}
|
|
6017
|
-
}
|
|
6018
|
-
getDefaultAuthUrl(environment) {
|
|
6019
|
-
switch (environment) {
|
|
6020
|
-
case 'production':
|
|
6021
|
-
return 'https://common.api.acubeapi.com';
|
|
6022
|
-
case 'development':
|
|
6023
|
-
case 'sandbox':
|
|
6024
|
-
default:
|
|
6025
|
-
return 'https://common-sandbox.api.acubeapi.com';
|
|
6026
|
-
}
|
|
6027
|
-
}
|
|
6028
|
-
getConfig() {
|
|
6029
|
-
return {
|
|
6030
|
-
environment: this.config.environment,
|
|
6031
|
-
debug: this.config.debug,
|
|
6032
|
-
};
|
|
6033
|
-
}
|
|
6034
|
-
getApiUrl() {
|
|
6035
|
-
return this.config.apiUrl;
|
|
6036
|
-
}
|
|
6037
|
-
getAuthUrl() {
|
|
6038
|
-
return this.config.authUrl;
|
|
6039
|
-
}
|
|
6040
|
-
getEnvironment() {
|
|
6041
|
-
return this.config.environment;
|
|
6042
|
-
}
|
|
6043
|
-
isDebugEnabled() {
|
|
6044
|
-
return this.config.debug;
|
|
6045
|
-
}
|
|
6046
|
-
getTimeout() {
|
|
6047
|
-
return this.config.timeout;
|
|
6048
|
-
}
|
|
6049
|
-
getRetryAttempts() {
|
|
6050
|
-
return this.config.retryAttempts;
|
|
6051
|
-
}
|
|
6052
|
-
getCustomHeaders() {
|
|
6053
|
-
return { ...this.config.customHeaders };
|
|
6054
|
-
}
|
|
6055
|
-
updateConfig(updates) {
|
|
6056
|
-
if (updates.environment) {
|
|
6057
|
-
this.config.environment = updates.environment;
|
|
6058
|
-
this.config.apiUrl = this.getDefaultApiUrl(updates.environment);
|
|
6059
|
-
this.config.authUrl = this.getDefaultAuthUrl(updates.environment);
|
|
6060
|
-
}
|
|
6061
|
-
if (updates.debug !== undefined) {
|
|
6062
|
-
this.config.debug = updates.debug;
|
|
6063
|
-
}
|
|
6064
|
-
}
|
|
6065
|
-
}
|
|
6066
|
-
|
|
6067
|
-
class ACubeSDKError extends Error {
|
|
6068
|
-
constructor(type, message, originalError, statusCode, violations) {
|
|
6069
|
-
super(message);
|
|
6070
|
-
this.type = type;
|
|
6071
|
-
this.originalError = originalError;
|
|
6072
|
-
this.statusCode = statusCode;
|
|
6073
|
-
this.name = 'ACubeSDKError';
|
|
6074
|
-
this.violations = violations;
|
|
6075
|
-
}
|
|
6076
|
-
}
|
|
6077
|
-
|
|
6078
6568
|
const DI_TOKENS = {
|
|
6079
6569
|
HTTP_PORT: Symbol('HTTP_PORT'),
|
|
6080
6570
|
BASE_HTTP_PORT: Symbol('BASE_HTTP_PORT'),
|
|
@@ -7075,7 +7565,7 @@ class TelemetryRepositoryImpl {
|
|
|
7075
7565
|
}
|
|
7076
7566
|
}
|
|
7077
7567
|
|
|
7078
|
-
const log$
|
|
7568
|
+
const log$3 = createPrefixedLogger('CACHE-KEY');
|
|
7079
7569
|
const URL_PATTERNS = [
|
|
7080
7570
|
// Receipt (mf1) - specific patterns first
|
|
7081
7571
|
{
|
|
@@ -7168,7 +7658,7 @@ const DEFAULT_TTL = 5 * 60 * 1000; // 5 minutes
|
|
|
7168
7658
|
class CacheKeyGenerator {
|
|
7169
7659
|
constructor(customConfig) {
|
|
7170
7660
|
this.config = { ...DEFAULT_TTL_CONFIG, ...customConfig };
|
|
7171
|
-
log$
|
|
7661
|
+
log$3.info('CacheKeyGenerator initialized with config:', {
|
|
7172
7662
|
resources: Object.keys(this.config),
|
|
7173
7663
|
});
|
|
7174
7664
|
}
|
|
@@ -7178,7 +7668,7 @@ class CacheKeyGenerator {
|
|
|
7178
7668
|
// Fallback: use URL as key
|
|
7179
7669
|
const paramStr = params ? this.serializeParams(params) : '';
|
|
7180
7670
|
const key = paramStr ? `${url}?${paramStr}` : url;
|
|
7181
|
-
log$
|
|
7671
|
+
log$3.debug('URL not matched, using fallback key:', { url, key });
|
|
7182
7672
|
return key;
|
|
7183
7673
|
}
|
|
7184
7674
|
const { resource, ids, action, isList, parent } = parsed;
|
|
@@ -7186,21 +7676,21 @@ class CacheKeyGenerator {
|
|
|
7186
7676
|
const paramStr = params ? this.serializeParams(params) : '';
|
|
7187
7677
|
const parentPart = parent && ids.length > 0 ? `${parent}=${ids[0]}&` : '';
|
|
7188
7678
|
const key = `${resource}:list:${parentPart}${paramStr}`;
|
|
7189
|
-
log$
|
|
7679
|
+
log$3.debug('Generated list cache key:', { url, key, resource });
|
|
7190
7680
|
return key;
|
|
7191
7681
|
}
|
|
7192
7682
|
// Single item
|
|
7193
7683
|
if (ids.length === 0 && action) {
|
|
7194
7684
|
// Special case for endpoints like /cashiers/me
|
|
7195
7685
|
const key = `${resource}:${action}`;
|
|
7196
|
-
log$
|
|
7686
|
+
log$3.debug('Generated special action cache key:', { url, key, resource, action });
|
|
7197
7687
|
return key;
|
|
7198
7688
|
}
|
|
7199
7689
|
let key = `${resource}:${ids.join(':')}`;
|
|
7200
7690
|
if (action) {
|
|
7201
7691
|
key += `:${action}`;
|
|
7202
7692
|
}
|
|
7203
|
-
log$
|
|
7693
|
+
log$3.debug('Generated item cache key:', { url, key, resource, ids, action });
|
|
7204
7694
|
return key;
|
|
7205
7695
|
}
|
|
7206
7696
|
parseResource(url) {
|
|
@@ -7210,23 +7700,23 @@ class CacheKeyGenerator {
|
|
|
7210
7700
|
getTTL(url) {
|
|
7211
7701
|
const resource = this.parseResource(url);
|
|
7212
7702
|
if (!resource) {
|
|
7213
|
-
log$
|
|
7703
|
+
log$3.debug('No resource found for URL, using default TTL:', { url, ttl: DEFAULT_TTL });
|
|
7214
7704
|
return DEFAULT_TTL;
|
|
7215
7705
|
}
|
|
7216
7706
|
const ttl = this.config[resource].ttlMs;
|
|
7217
|
-
log$
|
|
7707
|
+
log$3.debug('TTL for resource:', { url, resource, ttlMs: ttl, ttlMin: ttl / 60000 });
|
|
7218
7708
|
return ttl;
|
|
7219
7709
|
}
|
|
7220
7710
|
shouldCache(url) {
|
|
7221
7711
|
const parsed = this.parseUrl(url);
|
|
7222
7712
|
if (!parsed) {
|
|
7223
|
-
log$
|
|
7713
|
+
log$3.debug('URL not recognized, should not cache:', { url });
|
|
7224
7714
|
return false;
|
|
7225
7715
|
}
|
|
7226
7716
|
const { resource, isList } = parsed;
|
|
7227
7717
|
const config = this.config[resource];
|
|
7228
7718
|
if (isList) {
|
|
7229
|
-
log$
|
|
7719
|
+
log$3.debug('List endpoint cache decision:', {
|
|
7230
7720
|
url,
|
|
7231
7721
|
resource,
|
|
7232
7722
|
isList: true,
|
|
@@ -7234,7 +7724,7 @@ class CacheKeyGenerator {
|
|
|
7234
7724
|
});
|
|
7235
7725
|
return config.cacheList;
|
|
7236
7726
|
}
|
|
7237
|
-
log$
|
|
7727
|
+
log$3.debug('Item endpoint cache decision:', {
|
|
7238
7728
|
url,
|
|
7239
7729
|
resource,
|
|
7240
7730
|
isList: false,
|
|
@@ -7245,7 +7735,7 @@ class CacheKeyGenerator {
|
|
|
7245
7735
|
getInvalidationPatterns(url, method) {
|
|
7246
7736
|
const parsed = this.parseUrl(url);
|
|
7247
7737
|
if (!parsed) {
|
|
7248
|
-
log$
|
|
7738
|
+
log$3.debug('No patterns to invalidate for URL:', { url, method });
|
|
7249
7739
|
return [];
|
|
7250
7740
|
}
|
|
7251
7741
|
const { resource, ids, parent } = parsed;
|
|
@@ -7267,7 +7757,7 @@ class CacheKeyGenerator {
|
|
|
7267
7757
|
if (resource === 'cashier' && (method === 'PUT' || method === 'DELETE')) {
|
|
7268
7758
|
patterns.push('cashier:me');
|
|
7269
7759
|
}
|
|
7270
|
-
log$
|
|
7760
|
+
log$3.debug('Invalidation patterns:', { url, method, patterns });
|
|
7271
7761
|
return patterns;
|
|
7272
7762
|
}
|
|
7273
7763
|
parseUrl(url) {
|
|
@@ -7302,7 +7792,7 @@ class CacheKeyGenerator {
|
|
|
7302
7792
|
}
|
|
7303
7793
|
}
|
|
7304
7794
|
|
|
7305
|
-
const log$
|
|
7795
|
+
const log$2 = createPrefixedLogger('CACHE');
|
|
7306
7796
|
class CachingHttpDecorator {
|
|
7307
7797
|
constructor(http, cache, keyGenerator, networkMonitor, config = {}) {
|
|
7308
7798
|
this.http = http;
|
|
@@ -7312,7 +7802,7 @@ class CachingHttpDecorator {
|
|
|
7312
7802
|
this.config = config;
|
|
7313
7803
|
this.currentOnlineState = true;
|
|
7314
7804
|
this.authToken = null;
|
|
7315
|
-
log$
|
|
7805
|
+
log$2.info('CachingHttpDecorator initialized', {
|
|
7316
7806
|
enabled: config.enabled !== false,
|
|
7317
7807
|
hasNetworkMonitor: !!networkMonitor,
|
|
7318
7808
|
});
|
|
@@ -7322,7 +7812,7 @@ class CachingHttpDecorator {
|
|
|
7322
7812
|
if (this.networkMonitor) {
|
|
7323
7813
|
this.networkSubscription = this.networkMonitor.online$.subscribe((online) => {
|
|
7324
7814
|
if (this.currentOnlineState !== online) {
|
|
7325
|
-
log$
|
|
7815
|
+
log$2.info('Network state changed:', { online });
|
|
7326
7816
|
}
|
|
7327
7817
|
this.currentOnlineState = online;
|
|
7328
7818
|
});
|
|
@@ -7341,19 +7831,19 @@ class CachingHttpDecorator {
|
|
|
7341
7831
|
const startTime = Date.now();
|
|
7342
7832
|
// Check if caching is disabled globally
|
|
7343
7833
|
if (this.config.enabled === false) {
|
|
7344
|
-
log$
|
|
7834
|
+
log$2.debug('GET (cache disabled globally):', { url });
|
|
7345
7835
|
return this.http.get(url, config);
|
|
7346
7836
|
}
|
|
7347
7837
|
// Check if this URL should be cached
|
|
7348
7838
|
const shouldCache = this.keyGenerator.shouldCache(url);
|
|
7349
7839
|
if (!shouldCache) {
|
|
7350
|
-
log$
|
|
7840
|
+
log$2.debug('GET (not cacheable - likely a list endpoint):', { url });
|
|
7351
7841
|
return this.http.get(url, config);
|
|
7352
7842
|
}
|
|
7353
7843
|
const cacheKey = this.keyGenerator.generate(url, config?.params);
|
|
7354
7844
|
const ttl = this.keyGenerator.getTTL(url);
|
|
7355
7845
|
const resource = this.keyGenerator.parseResource(url);
|
|
7356
|
-
log$
|
|
7846
|
+
log$2.info('GET request starting:', {
|
|
7357
7847
|
url,
|
|
7358
7848
|
resource,
|
|
7359
7849
|
cacheKey,
|
|
@@ -7366,18 +7856,18 @@ class CachingHttpDecorator {
|
|
|
7366
7856
|
try {
|
|
7367
7857
|
cached = await this.cache.get(cacheKey);
|
|
7368
7858
|
if (cached) {
|
|
7369
|
-
log$
|
|
7859
|
+
log$2.debug('Cache entry found:', {
|
|
7370
7860
|
cacheKey,
|
|
7371
7861
|
timestamp: new Date(cached.timestamp).toISOString(),
|
|
7372
7862
|
ageMs: Date.now() - cached.timestamp,
|
|
7373
7863
|
});
|
|
7374
7864
|
}
|
|
7375
7865
|
else {
|
|
7376
|
-
log$
|
|
7866
|
+
log$2.debug('Cache entry not found:', { cacheKey });
|
|
7377
7867
|
}
|
|
7378
7868
|
}
|
|
7379
7869
|
catch (error) {
|
|
7380
|
-
log$
|
|
7870
|
+
log$2.warn('Cache lookup failed:', {
|
|
7381
7871
|
cacheKey,
|
|
7382
7872
|
error: error instanceof Error ? error.message : error,
|
|
7383
7873
|
});
|
|
@@ -7385,7 +7875,7 @@ class CachingHttpDecorator {
|
|
|
7385
7875
|
if (cached) {
|
|
7386
7876
|
const age = Date.now() - cached.timestamp;
|
|
7387
7877
|
const isExpired = age >= ttl;
|
|
7388
|
-
log$
|
|
7878
|
+
log$2.debug('Cache analysis:', {
|
|
7389
7879
|
cacheKey,
|
|
7390
7880
|
ageMs: age,
|
|
7391
7881
|
ageSec: Math.round(age / 1000),
|
|
@@ -7396,7 +7886,7 @@ class CachingHttpDecorator {
|
|
|
7396
7886
|
// If within TTL, return cached data
|
|
7397
7887
|
if (!isExpired) {
|
|
7398
7888
|
const duration = Date.now() - startTime;
|
|
7399
|
-
log$
|
|
7889
|
+
log$2.info('CACHE HIT:', {
|
|
7400
7890
|
url,
|
|
7401
7891
|
cacheKey,
|
|
7402
7892
|
ageMs: age,
|
|
@@ -7411,7 +7901,7 @@ class CachingHttpDecorator {
|
|
|
7411
7901
|
// If offline and cache is stale, return stale data
|
|
7412
7902
|
if (!this.isOnline()) {
|
|
7413
7903
|
const duration = Date.now() - startTime;
|
|
7414
|
-
log$
|
|
7904
|
+
log$2.info('CACHE STALE (offline):', {
|
|
7415
7905
|
url,
|
|
7416
7906
|
cacheKey,
|
|
7417
7907
|
ageMs: age,
|
|
@@ -7423,25 +7913,25 @@ class CachingHttpDecorator {
|
|
|
7423
7913
|
headers: { 'x-cache': 'STALE' },
|
|
7424
7914
|
};
|
|
7425
7915
|
}
|
|
7426
|
-
log$
|
|
7916
|
+
log$2.debug('Cache expired, fetching fresh data:', { cacheKey, ageMs: age, ttlMs: ttl });
|
|
7427
7917
|
}
|
|
7428
7918
|
// Fetch fresh data
|
|
7429
7919
|
try {
|
|
7430
|
-
log$
|
|
7920
|
+
log$2.debug('Fetching from network:', { url });
|
|
7431
7921
|
const response = await this.http.get(url, config);
|
|
7432
7922
|
// Cache the response
|
|
7433
7923
|
try {
|
|
7434
7924
|
await this.cache.set(cacheKey, response.data);
|
|
7435
|
-
log$
|
|
7925
|
+
log$2.debug('Response cached successfully:', { cacheKey });
|
|
7436
7926
|
}
|
|
7437
7927
|
catch (error) {
|
|
7438
|
-
log$
|
|
7928
|
+
log$2.error('Failed to cache response:', {
|
|
7439
7929
|
cacheKey,
|
|
7440
7930
|
error: error instanceof Error ? error.message : error,
|
|
7441
7931
|
});
|
|
7442
7932
|
}
|
|
7443
7933
|
const duration = Date.now() - startTime;
|
|
7444
|
-
log$
|
|
7934
|
+
log$2.info('CACHE MISS (fetched fresh):', {
|
|
7445
7935
|
url,
|
|
7446
7936
|
cacheKey,
|
|
7447
7937
|
status: response.status,
|
|
@@ -7456,7 +7946,7 @@ class CachingHttpDecorator {
|
|
|
7456
7946
|
// On error, return stale cache if available
|
|
7457
7947
|
if (cached) {
|
|
7458
7948
|
const duration = Date.now() - startTime;
|
|
7459
|
-
log$
|
|
7949
|
+
log$2.warn('CACHE STALE (network error):', {
|
|
7460
7950
|
url,
|
|
7461
7951
|
cacheKey,
|
|
7462
7952
|
error: error instanceof Error ? error.message : 'Unknown error',
|
|
@@ -7468,7 +7958,7 @@ class CachingHttpDecorator {
|
|
|
7468
7958
|
headers: { 'x-cache': 'STALE' },
|
|
7469
7959
|
};
|
|
7470
7960
|
}
|
|
7471
|
-
log$
|
|
7961
|
+
log$2.error('Network error with no cache fallback:', {
|
|
7472
7962
|
url,
|
|
7473
7963
|
error: error instanceof Error ? error.message : error,
|
|
7474
7964
|
});
|
|
@@ -7476,31 +7966,35 @@ class CachingHttpDecorator {
|
|
|
7476
7966
|
}
|
|
7477
7967
|
}
|
|
7478
7968
|
async post(url, data, config) {
|
|
7479
|
-
log$
|
|
7969
|
+
log$2.info('POST request:', { url });
|
|
7480
7970
|
const response = await this.http.post(url, data, config);
|
|
7481
7971
|
await this.invalidateRelated(url, 'POST');
|
|
7482
7972
|
return response;
|
|
7483
7973
|
}
|
|
7484
7974
|
async put(url, data, config) {
|
|
7485
|
-
log$
|
|
7975
|
+
log$2.info('PUT request:', { url });
|
|
7486
7976
|
const response = await this.http.put(url, data, config);
|
|
7487
7977
|
await this.invalidateRelated(url, 'PUT');
|
|
7488
7978
|
return response;
|
|
7489
7979
|
}
|
|
7490
7980
|
async patch(url, data, config) {
|
|
7491
|
-
log$
|
|
7981
|
+
log$2.info('PATCH request:', { url });
|
|
7492
7982
|
const response = await this.http.patch(url, data, config);
|
|
7493
7983
|
await this.invalidateRelated(url, 'PATCH');
|
|
7494
7984
|
return response;
|
|
7495
7985
|
}
|
|
7496
7986
|
async delete(url, config) {
|
|
7497
|
-
log$
|
|
7987
|
+
log$2.info('DELETE request:', { url });
|
|
7498
7988
|
const response = await this.http.delete(url, config);
|
|
7499
7989
|
await this.invalidateRelated(url, 'DELETE');
|
|
7500
7990
|
return response;
|
|
7501
7991
|
}
|
|
7502
7992
|
setAuthToken(token) {
|
|
7503
|
-
log$
|
|
7993
|
+
log$2.debug('CachingHttpDecorator.setAuthToken called:', {
|
|
7994
|
+
hasToken: !!token,
|
|
7995
|
+
tokenPrefix: token?.substring(0, 20),
|
|
7996
|
+
underlyingHttpType: this.http.constructor.name,
|
|
7997
|
+
});
|
|
7504
7998
|
this.authToken = token;
|
|
7505
7999
|
this.http.setAuthToken(token);
|
|
7506
8000
|
}
|
|
@@ -7510,17 +8004,17 @@ class CachingHttpDecorator {
|
|
|
7510
8004
|
async invalidateRelated(url, method) {
|
|
7511
8005
|
const patterns = this.keyGenerator.getInvalidationPatterns(url, method);
|
|
7512
8006
|
if (patterns.length === 0) {
|
|
7513
|
-
log$
|
|
8007
|
+
log$2.debug('No cache patterns to invalidate:', { url, method });
|
|
7514
8008
|
return;
|
|
7515
8009
|
}
|
|
7516
|
-
log$
|
|
8010
|
+
log$2.info('Invalidating cache patterns:', { url, method, patterns });
|
|
7517
8011
|
for (const pattern of patterns) {
|
|
7518
8012
|
try {
|
|
7519
8013
|
await this.cache.invalidate(pattern);
|
|
7520
|
-
log$
|
|
8014
|
+
log$2.debug('Cache pattern invalidated:', { pattern });
|
|
7521
8015
|
}
|
|
7522
8016
|
catch (error) {
|
|
7523
|
-
log$
|
|
8017
|
+
log$2.error('Failed to invalidate pattern:', {
|
|
7524
8018
|
pattern,
|
|
7525
8019
|
error: error instanceof Error ? error.message : error,
|
|
7526
8020
|
});
|
|
@@ -7528,12 +8022,13 @@ class CachingHttpDecorator {
|
|
|
7528
8022
|
}
|
|
7529
8023
|
}
|
|
7530
8024
|
destroy() {
|
|
7531
|
-
log$
|
|
8025
|
+
log$2.debug('CachingHttpDecorator destroyed');
|
|
7532
8026
|
this.networkSubscription?.unsubscribe();
|
|
7533
8027
|
}
|
|
7534
8028
|
}
|
|
7535
8029
|
|
|
7536
|
-
const
|
|
8030
|
+
const logJwt = createPrefixedLogger('HTTP-JWT');
|
|
8031
|
+
const logMtls = createPrefixedLogger('HTTP-MTLS');
|
|
7537
8032
|
class AxiosHttpAdapter {
|
|
7538
8033
|
constructor(config) {
|
|
7539
8034
|
this.authToken = null;
|
|
@@ -7551,23 +8046,30 @@ class AxiosHttpAdapter {
|
|
|
7551
8046
|
}
|
|
7552
8047
|
setMTLSAdapter(adapter) {
|
|
7553
8048
|
this.mtlsAdapter = adapter;
|
|
7554
|
-
|
|
8049
|
+
logMtls.debug('mTLS adapter configured:', !!adapter);
|
|
7555
8050
|
}
|
|
7556
8051
|
setAuthStrategy(strategy) {
|
|
7557
8052
|
this.authStrategy = strategy;
|
|
7558
|
-
|
|
8053
|
+
logJwt.debug('Auth strategy configured:', !!strategy);
|
|
7559
8054
|
}
|
|
7560
8055
|
async shouldUseMTLS(url, method) {
|
|
7561
8056
|
if (!this.mtlsAdapter) {
|
|
8057
|
+
logJwt.debug(`No mTLS adapter, using JWT for ${method} ${url}`);
|
|
7562
8058
|
return false;
|
|
7563
8059
|
}
|
|
7564
8060
|
if (this.authStrategy) {
|
|
7565
8061
|
const config = await this.authStrategy.determineAuthConfig(url, method);
|
|
7566
|
-
|
|
8062
|
+
const logger = config.mode === 'mtls' ? logMtls : logJwt;
|
|
8063
|
+
logger.debug(`Auth config for ${method} ${url}:`, config);
|
|
7567
8064
|
return config.mode === 'mtls';
|
|
7568
8065
|
}
|
|
7569
8066
|
// Fallback: use mTLS for mf1/mf2 endpoints if no strategy
|
|
7570
|
-
|
|
8067
|
+
// This should rarely happen - only before SDK is fully initialized
|
|
8068
|
+
const useMtls = url.startsWith('/mf1') || url.startsWith('/mf2');
|
|
8069
|
+
if (useMtls) {
|
|
8070
|
+
logMtls.warn(`No auth strategy set, falling back to mTLS for ${method} ${url}`);
|
|
8071
|
+
}
|
|
8072
|
+
return useMtls;
|
|
7571
8073
|
}
|
|
7572
8074
|
async makeMTLSRequest(url, method, data, config) {
|
|
7573
8075
|
if (!this.mtlsAdapter) {
|
|
@@ -7580,10 +8082,10 @@ class AxiosHttpAdapter {
|
|
|
7580
8082
|
};
|
|
7581
8083
|
if (this.authToken) {
|
|
7582
8084
|
headers['Authorization'] = `Bearer ${this.authToken}`;
|
|
7583
|
-
|
|
8085
|
+
logMtls.debug('JWT token present for mTLS request');
|
|
7584
8086
|
}
|
|
7585
8087
|
else {
|
|
7586
|
-
|
|
8088
|
+
logMtls.warn('No JWT token for mTLS request');
|
|
7587
8089
|
}
|
|
7588
8090
|
const mtlsConfig = {
|
|
7589
8091
|
url: fullUrl,
|
|
@@ -7592,15 +8094,15 @@ class AxiosHttpAdapter {
|
|
|
7592
8094
|
data,
|
|
7593
8095
|
timeout: config?.timeout,
|
|
7594
8096
|
};
|
|
7595
|
-
|
|
8097
|
+
logMtls.debug(`mTLS ${method} ${fullUrl}`);
|
|
7596
8098
|
if (data) {
|
|
7597
|
-
|
|
8099
|
+
logMtls.debug('Request body:', data);
|
|
7598
8100
|
}
|
|
7599
8101
|
try {
|
|
7600
8102
|
const response = await this.mtlsAdapter.request(mtlsConfig);
|
|
7601
|
-
|
|
8103
|
+
logMtls.debug(`mTLS Response ${response.status} from ${fullUrl}`);
|
|
7602
8104
|
if (response.data) {
|
|
7603
|
-
|
|
8105
|
+
logMtls.debug('Response body:', response.data);
|
|
7604
8106
|
}
|
|
7605
8107
|
return {
|
|
7606
8108
|
data: response.data,
|
|
@@ -7609,11 +8111,11 @@ class AxiosHttpAdapter {
|
|
|
7609
8111
|
};
|
|
7610
8112
|
}
|
|
7611
8113
|
catch (error) {
|
|
7612
|
-
|
|
8114
|
+
logMtls.error(`mTLS Response error from ${fullUrl}:`, error);
|
|
7613
8115
|
if (error && typeof error === 'object' && 'response' in error) {
|
|
7614
8116
|
const axiosError = error;
|
|
7615
8117
|
if (axiosError.response?.data) {
|
|
7616
|
-
|
|
8118
|
+
logMtls.error('Response body:', axiosError.response.data);
|
|
7617
8119
|
}
|
|
7618
8120
|
}
|
|
7619
8121
|
throw error;
|
|
@@ -7628,36 +8130,46 @@ class AxiosHttpAdapter {
|
|
|
7628
8130
|
this.client.interceptors.request.use((config) => {
|
|
7629
8131
|
if (this.authToken) {
|
|
7630
8132
|
config.headers.Authorization = `Bearer ${this.authToken}`;
|
|
7631
|
-
|
|
8133
|
+
logJwt.debug('Adding JWT token to request', {
|
|
8134
|
+
tokenPrefix: this.authToken.substring(0, 30) + '...',
|
|
8135
|
+
tokenLength: this.authToken.length,
|
|
8136
|
+
});
|
|
7632
8137
|
}
|
|
7633
8138
|
else {
|
|
7634
|
-
|
|
8139
|
+
logJwt.warn('No JWT token available for request:', { url: config.url });
|
|
7635
8140
|
}
|
|
7636
8141
|
const method = config.method?.toUpperCase() ?? 'UNKNOWN';
|
|
7637
|
-
|
|
8142
|
+
const authHeader = config.headers.Authorization;
|
|
8143
|
+
// Log full request details for debugging
|
|
8144
|
+
logJwt.info(`→ ${method} ${config.url}`, {
|
|
8145
|
+
baseURL: config.baseURL,
|
|
8146
|
+
fullURL: `${config.baseURL}${config.url}`,
|
|
8147
|
+
hasAuthHeader: !!authHeader,
|
|
8148
|
+
authHeaderValue: authHeader ? `${String(authHeader).substring(0, 50)}...` : 'MISSING',
|
|
8149
|
+
});
|
|
7638
8150
|
if (config.params && Object.keys(config.params).length > 0) {
|
|
7639
|
-
|
|
8151
|
+
logJwt.debug('Request params:', config.params);
|
|
7640
8152
|
}
|
|
7641
8153
|
if (config.data) {
|
|
7642
|
-
|
|
8154
|
+
logJwt.debug('Request body:', config.data);
|
|
7643
8155
|
}
|
|
7644
8156
|
return config;
|
|
7645
8157
|
}, (error) => {
|
|
7646
|
-
|
|
8158
|
+
logJwt.error('Request error:', error);
|
|
7647
8159
|
return Promise.reject(error);
|
|
7648
8160
|
});
|
|
7649
8161
|
this.client.interceptors.response.use((response) => {
|
|
7650
8162
|
const method = response.config.method?.toUpperCase() ?? 'UNKNOWN';
|
|
7651
|
-
|
|
8163
|
+
logJwt.debug(`← ${method} ${response.status} ${response.config.url}`);
|
|
7652
8164
|
if (response.data) {
|
|
7653
|
-
|
|
8165
|
+
logJwt.debug('Response body:', response.data);
|
|
7654
8166
|
}
|
|
7655
8167
|
return response;
|
|
7656
8168
|
}, (error) => {
|
|
7657
8169
|
const method = error.config?.method?.toUpperCase() ?? 'UNKNOWN';
|
|
7658
|
-
|
|
8170
|
+
logJwt.error(`← ${method} ${error.response?.status ?? 'ERR'} ${error.config?.url ?? 'unknown'}`);
|
|
7659
8171
|
if (error.response?.data) {
|
|
7660
|
-
|
|
8172
|
+
logJwt.error('Response body:', error.response.data);
|
|
7661
8173
|
}
|
|
7662
8174
|
return Promise.reject(error);
|
|
7663
8175
|
});
|
|
@@ -7727,7 +8239,10 @@ class AxiosHttpAdapter {
|
|
|
7727
8239
|
return this.mapResponse(response);
|
|
7728
8240
|
}
|
|
7729
8241
|
setAuthToken(token) {
|
|
7730
|
-
|
|
8242
|
+
logJwt.info('setAuthToken called:', {
|
|
8243
|
+
hasToken: !!token,
|
|
8244
|
+
tokenPrefix: token?.substring(0, 20),
|
|
8245
|
+
});
|
|
7731
8246
|
this.authToken = token;
|
|
7732
8247
|
}
|
|
7733
8248
|
getAuthToken() {
|
|
@@ -7943,6 +8458,11 @@ class ACubeSDK {
|
|
|
7943
8458
|
const tokenStorage = this.container.get(DI_TOKENS.TOKEN_STORAGE_PORT);
|
|
7944
8459
|
const httpPort = this.container.get(DI_TOKENS.HTTP_PORT);
|
|
7945
8460
|
const baseHttpPort = this.container.get(DI_TOKENS.BASE_HTTP_PORT);
|
|
8461
|
+
log$1.debug('HTTP ports initialized', {
|
|
8462
|
+
httpPortType: httpPort.constructor.name,
|
|
8463
|
+
baseHttpPortType: baseHttpPort.constructor.name,
|
|
8464
|
+
areSameInstance: httpPort === baseHttpPort,
|
|
8465
|
+
});
|
|
7946
8466
|
log$1.debug('Initializing authentication service');
|
|
7947
8467
|
this.authService = new AuthenticationService(httpPort, tokenStorage, {
|
|
7948
8468
|
authUrl: this.config.getAuthUrl(),
|
|
@@ -7975,12 +8495,22 @@ class ACubeSDK {
|
|
|
7975
8495
|
this.offlineManager.sync().catch(() => { });
|
|
7976
8496
|
}
|
|
7977
8497
|
});
|
|
7978
|
-
|
|
8498
|
+
const isAuth = await this.authService.isAuthenticated();
|
|
8499
|
+
log$1.debug('Checking authentication status during init', { isAuthenticated: isAuth });
|
|
8500
|
+
if (isAuth) {
|
|
7979
8501
|
const token = await this.authService.getAccessToken();
|
|
8502
|
+
log$1.debug('Token retrieved during init', {
|
|
8503
|
+
hasToken: !!token,
|
|
8504
|
+
tokenPrefix: token?.substring(0, 20),
|
|
8505
|
+
});
|
|
7980
8506
|
if (token) {
|
|
7981
8507
|
httpPort.setAuthToken(token);
|
|
8508
|
+
log$1.info('Auth token set on HTTP port during initialization');
|
|
7982
8509
|
}
|
|
7983
8510
|
}
|
|
8511
|
+
else {
|
|
8512
|
+
log$1.warn('User not authenticated during SDK init - token will be set after login');
|
|
8513
|
+
}
|
|
7984
8514
|
if (this.adapters?.mtls && 'setMTLSAdapter' in baseHttpPort) {
|
|
7985
8515
|
log$1.debug('Connecting mTLS adapter to HTTP port');
|
|
7986
8516
|
const httpWithMtls = baseHttpPort;
|
|
@@ -8799,842 +9329,401 @@ class SDKManager {
|
|
|
8799
9329
|
this.appStateService.state$.subscribe(this.events.onAppStateChanged);
|
|
8800
9330
|
}
|
|
8801
9331
|
if (this.events?.onTelemetryStateChanged) {
|
|
8802
|
-
this.telemetryService.state$.subscribe(this.events.onTelemetryStateChanged);
|
|
8803
|
-
}
|
|
8804
|
-
this.isInitialized = true;
|
|
8805
|
-
// Only start polling for MERCHANT/CASHIER users (SUPPLIER gets 401 on these endpoints)
|
|
8806
|
-
const user = await this.sdk.getCurrentUser();
|
|
8807
|
-
const canPoll = user && hasAnyRole(user.roles, ['ROLE_MERCHANT', 'ROLE_CASHIER']);
|
|
8808
|
-
if (canPoll) {
|
|
8809
|
-
this.notificationService.startPolling();
|
|
8810
|
-
await this.startTelemetryPollingAuto();
|
|
8811
|
-
this.isPollingActive = true;
|
|
8812
|
-
}
|
|
8813
|
-
// AppStateService remains active for all users (handles OFFLINE network state)
|
|
8814
|
-
}
|
|
8815
|
-
/**
|
|
8816
|
-
* Observable stream of app state
|
|
8817
|
-
* Emits AppState with mode (NORMAL, WARNING, BLOCKED, OFFLINE)
|
|
8818
|
-
*/
|
|
8819
|
-
get appState$() {
|
|
8820
|
-
this.ensureInitialized();
|
|
8821
|
-
return this.appStateService.state$;
|
|
8822
|
-
}
|
|
8823
|
-
/**
|
|
8824
|
-
* Observable stream of app mode only
|
|
8825
|
-
* Emits AppMode with distinctUntilChanged
|
|
8826
|
-
*/
|
|
8827
|
-
get mode$() {
|
|
8828
|
-
this.ensureInitialized();
|
|
8829
|
-
return this.appStateService.mode$;
|
|
8830
|
-
}
|
|
8831
|
-
/**
|
|
8832
|
-
* Observable stream indicating if app is blocked
|
|
8833
|
-
*/
|
|
8834
|
-
get isBlocked$() {
|
|
8835
|
-
this.ensureInitialized();
|
|
8836
|
-
return this.appStateService.isBlocked$;
|
|
8837
|
-
}
|
|
8838
|
-
/**
|
|
8839
|
-
* Observable stream of warning state with countdown
|
|
8840
|
-
*/
|
|
8841
|
-
get warning$() {
|
|
8842
|
-
this.ensureInitialized();
|
|
8843
|
-
return this.appStateService.warning$;
|
|
8844
|
-
}
|
|
8845
|
-
/**
|
|
8846
|
-
* Observable stream of telemetry state (data, isLoading, isCached, error)
|
|
8847
|
-
*/
|
|
8848
|
-
get telemetryState$() {
|
|
8849
|
-
this.ensureInitialized();
|
|
8850
|
-
return this.telemetryService.state$;
|
|
8851
|
-
}
|
|
8852
|
-
/**
|
|
8853
|
-
* Get the pemId from the installed certificate
|
|
8854
|
-
*/
|
|
8855
|
-
async getPemId() {
|
|
8856
|
-
this.ensureInitialized();
|
|
8857
|
-
try {
|
|
8858
|
-
const certInfo = await this.sdk.getCertificatesInfo();
|
|
8859
|
-
return certInfo?.pemId ?? null;
|
|
8860
|
-
}
|
|
8861
|
-
catch {
|
|
8862
|
-
return null;
|
|
8863
|
-
}
|
|
8864
|
-
}
|
|
8865
|
-
/**
|
|
8866
|
-
* Start polling telemetry using the pemId from installed certificate
|
|
8867
|
-
* Returns the pemId if successful, null if no certificate is installed
|
|
8868
|
-
*/
|
|
8869
|
-
async startTelemetryPollingAuto() {
|
|
8870
|
-
this.ensureInitialized();
|
|
8871
|
-
const pemId = await this.getPemId();
|
|
8872
|
-
if (pemId) {
|
|
8873
|
-
this.telemetryService.startPolling(pemId);
|
|
8874
|
-
}
|
|
8875
|
-
return pemId;
|
|
8876
|
-
}
|
|
8877
|
-
/**
|
|
8878
|
-
* Start polling telemetry for a specific PEM
|
|
8879
|
-
*/
|
|
8880
|
-
startTelemetryPolling(pemId) {
|
|
8881
|
-
this.ensureInitialized();
|
|
8882
|
-
this.telemetryService.startPolling(pemId);
|
|
8883
|
-
}
|
|
8884
|
-
/**
|
|
8885
|
-
* Stop telemetry polling
|
|
8886
|
-
*/
|
|
8887
|
-
stopTelemetryPolling() {
|
|
8888
|
-
this.ensureInitialized();
|
|
8889
|
-
this.telemetryService.stopPolling();
|
|
8890
|
-
}
|
|
8891
|
-
/**
|
|
8892
|
-
* Get simplified services for product use
|
|
8893
|
-
*/
|
|
8894
|
-
getServices() {
|
|
8895
|
-
this.ensureInitialized();
|
|
8896
|
-
const sdk = this.sdk;
|
|
8897
|
-
const telemetryService = this.telemetryService;
|
|
8898
|
-
return {
|
|
8899
|
-
receipts: sdk.receipts,
|
|
8900
|
-
merchants: sdk.merchants,
|
|
8901
|
-
cashiers: sdk.cashiers,
|
|
8902
|
-
cashRegisters: sdk.cashRegisters,
|
|
8903
|
-
pointOfSales: sdk.pointOfSales,
|
|
8904
|
-
suppliers: sdk.suppliers,
|
|
8905
|
-
pems: sdk.pems,
|
|
8906
|
-
dailyReports: sdk.dailyReports,
|
|
8907
|
-
journals: sdk.journals,
|
|
8908
|
-
telemetry: {
|
|
8909
|
-
startPollingAuto: () => this.startTelemetryPollingAuto(),
|
|
8910
|
-
startPolling: (pemId) => telemetryService.startPolling(pemId),
|
|
8911
|
-
stopPolling: () => telemetryService.stopPolling(),
|
|
8912
|
-
getTelemetry: (pemId) => telemetryService.getTelemetry(pemId),
|
|
8913
|
-
refreshTelemetry: (pemId) => telemetryService.refreshTelemetry(pemId),
|
|
8914
|
-
triggerSync: () => telemetryService.triggerSync(),
|
|
8915
|
-
clearTelemetry: () => telemetryService.clearTelemetry(),
|
|
8916
|
-
getPemId: () => this.getPemId(),
|
|
8917
|
-
},
|
|
8918
|
-
login: (credentials) => sdk.login(credentials),
|
|
8919
|
-
logout: () => sdk.logout(),
|
|
8920
|
-
getCurrentUser: () => sdk.getCurrentUser(),
|
|
8921
|
-
isAuthenticated: () => sdk.isAuthenticated(),
|
|
8922
|
-
storeCertificate: (certificate, privateKey, options) => sdk.storeCertificate(certificate, privateKey, options),
|
|
8923
|
-
hasCertificate: () => sdk.hasCertificate(),
|
|
8924
|
-
clearCertificate: () => sdk.clearCertificate(),
|
|
8925
|
-
isOnline: () => sdk.isOnline(),
|
|
8926
|
-
};
|
|
9332
|
+
this.telemetryService.state$.subscribe(this.events.onTelemetryStateChanged);
|
|
9333
|
+
}
|
|
9334
|
+
this.isInitialized = true;
|
|
9335
|
+
// Only start polling for MERCHANT/CASHIER users (SUPPLIER gets 401 on these endpoints)
|
|
9336
|
+
const user = await this.sdk.getCurrentUser();
|
|
9337
|
+
const canPoll = user && hasAnyRole(user.roles, ['ROLE_MERCHANT', 'ROLE_CASHIER']);
|
|
9338
|
+
if (canPoll) {
|
|
9339
|
+
this.notificationService.startPolling();
|
|
9340
|
+
await this.startTelemetryPollingAuto();
|
|
9341
|
+
this.isPollingActive = true;
|
|
9342
|
+
}
|
|
9343
|
+
// AppStateService remains active for all users (handles OFFLINE network state)
|
|
8927
9344
|
}
|
|
8928
9345
|
/**
|
|
8929
|
-
*
|
|
9346
|
+
* Observable stream of app state
|
|
9347
|
+
* Emits AppState with mode (NORMAL, WARNING, BLOCKED, OFFLINE)
|
|
8930
9348
|
*/
|
|
8931
|
-
|
|
9349
|
+
get appState$() {
|
|
8932
9350
|
this.ensureInitialized();
|
|
8933
|
-
|
|
9351
|
+
return this.appStateService.state$;
|
|
8934
9352
|
}
|
|
8935
9353
|
/**
|
|
8936
|
-
*
|
|
9354
|
+
* Observable stream of app mode only
|
|
9355
|
+
* Emits AppMode with distinctUntilChanged
|
|
8937
9356
|
*/
|
|
8938
|
-
|
|
9357
|
+
get mode$() {
|
|
8939
9358
|
this.ensureInitialized();
|
|
8940
|
-
return this.
|
|
9359
|
+
return this.appStateService.mode$;
|
|
8941
9360
|
}
|
|
8942
9361
|
/**
|
|
8943
|
-
*
|
|
9362
|
+
* Observable stream indicating if app is blocked
|
|
8944
9363
|
*/
|
|
8945
|
-
|
|
8946
|
-
|
|
9364
|
+
get isBlocked$() {
|
|
9365
|
+
this.ensureInitialized();
|
|
9366
|
+
return this.appStateService.isBlocked$;
|
|
8947
9367
|
}
|
|
8948
9368
|
/**
|
|
8949
|
-
*
|
|
9369
|
+
* Observable stream of warning state with countdown
|
|
8950
9370
|
*/
|
|
8951
|
-
|
|
9371
|
+
get warning$() {
|
|
8952
9372
|
this.ensureInitialized();
|
|
8953
|
-
return this.
|
|
8954
|
-
}
|
|
8955
|
-
cleanup() {
|
|
8956
|
-
this.notificationService?.destroy();
|
|
8957
|
-
this.telemetryService?.destroy();
|
|
8958
|
-
this.appStateService?.destroy();
|
|
8959
|
-
this.sdk?.destroy();
|
|
8960
|
-
this.notificationService = null;
|
|
8961
|
-
this.telemetryService = null;
|
|
8962
|
-
this.appStateService = null;
|
|
8963
|
-
this.sdk = null;
|
|
8964
|
-
this.isInitialized = false;
|
|
8965
|
-
this.isPollingActive = false;
|
|
8966
|
-
}
|
|
8967
|
-
ensureInitialized() {
|
|
8968
|
-
if (!this.isInitialized) {
|
|
8969
|
-
throw new ACubeSDKError('SDK_NOT_INITIALIZED', 'SDKManager not initialized. Call initialize() first.');
|
|
8970
|
-
}
|
|
8971
|
-
}
|
|
8972
|
-
}
|
|
8973
|
-
SDKManager.instance = null;
|
|
8974
|
-
|
|
8975
|
-
const RECEIPT_READY = 'ready';
|
|
8976
|
-
const RECEIPT_SENT = 'sent';
|
|
8977
|
-
const RECEIPT_SORT_DESCENDING = 'descending';
|
|
8978
|
-
const RECEIPT_SORT_ASCENDING = 'ascending';
|
|
8979
|
-
|
|
8980
|
-
const log = createPrefixedLogger('CACHE-HANDLER');
|
|
8981
|
-
class CacheHandler {
|
|
8982
|
-
constructor(cache, networkMonitor) {
|
|
8983
|
-
this.cache = cache;
|
|
8984
|
-
this.networkMonitor = networkMonitor;
|
|
8985
|
-
this.currentOnlineState = true;
|
|
8986
|
-
this.setupNetworkMonitoring();
|
|
8987
|
-
}
|
|
8988
|
-
setupNetworkMonitoring() {
|
|
8989
|
-
if (this.networkMonitor) {
|
|
8990
|
-
this.networkSubscription = this.networkMonitor.online$.subscribe((online) => {
|
|
8991
|
-
this.currentOnlineState = online;
|
|
8992
|
-
});
|
|
8993
|
-
}
|
|
8994
|
-
}
|
|
8995
|
-
isOnline() {
|
|
8996
|
-
if (this.networkMonitor) {
|
|
8997
|
-
return this.currentOnlineState;
|
|
8998
|
-
}
|
|
8999
|
-
if (typeof navigator !== 'undefined' && 'onLine' in navigator) {
|
|
9000
|
-
return navigator.onLine;
|
|
9001
|
-
}
|
|
9002
|
-
return false;
|
|
9003
|
-
}
|
|
9004
|
-
async handleCachedRequest(url, requestFn, config) {
|
|
9005
|
-
if (!this.cache || config?.useCache === false) {
|
|
9006
|
-
const response = await requestFn();
|
|
9007
|
-
return response.data;
|
|
9008
|
-
}
|
|
9009
|
-
const cacheKey = this.generateCacheKey(url);
|
|
9010
|
-
const online = this.isOnline();
|
|
9011
|
-
log.debug('Request:', { url, cacheKey, online });
|
|
9012
|
-
if (online) {
|
|
9013
|
-
try {
|
|
9014
|
-
const response = await requestFn();
|
|
9015
|
-
if (this.cache) {
|
|
9016
|
-
await this.cache.set(cacheKey, response.data).catch((error) => {
|
|
9017
|
-
log.error('Failed to cache:', error instanceof Error ? error.message : error);
|
|
9018
|
-
});
|
|
9019
|
-
}
|
|
9020
|
-
return response.data;
|
|
9021
|
-
}
|
|
9022
|
-
catch (error) {
|
|
9023
|
-
const cached = await this.cache.get(cacheKey).catch(() => null);
|
|
9024
|
-
if (cached) {
|
|
9025
|
-
log.debug('Network failed, using cache fallback');
|
|
9026
|
-
return cached.data;
|
|
9027
|
-
}
|
|
9028
|
-
throw error;
|
|
9029
|
-
}
|
|
9030
|
-
}
|
|
9031
|
-
else {
|
|
9032
|
-
const cached = await this.cache.get(cacheKey).catch(() => null);
|
|
9033
|
-
if (cached) {
|
|
9034
|
-
log.debug('Offline, returning cached data');
|
|
9035
|
-
return cached.data;
|
|
9036
|
-
}
|
|
9037
|
-
throw new Error('Offline: No cached data available');
|
|
9038
|
-
}
|
|
9373
|
+
return this.appStateService.warning$;
|
|
9039
9374
|
}
|
|
9040
|
-
|
|
9041
|
-
|
|
9375
|
+
/**
|
|
9376
|
+
* Observable stream of telemetry state (data, isLoading, isCached, error)
|
|
9377
|
+
*/
|
|
9378
|
+
get telemetryState$() {
|
|
9379
|
+
this.ensureInitialized();
|
|
9380
|
+
return this.telemetryService.state$;
|
|
9042
9381
|
}
|
|
9043
|
-
|
|
9044
|
-
|
|
9045
|
-
|
|
9382
|
+
/**
|
|
9383
|
+
* Get the pemId from the installed certificate
|
|
9384
|
+
*/
|
|
9385
|
+
async getPemId() {
|
|
9386
|
+
this.ensureInitialized();
|
|
9046
9387
|
try {
|
|
9047
|
-
await this.
|
|
9388
|
+
const certInfo = await this.sdk.getCertificatesInfo();
|
|
9389
|
+
return certInfo?.pemId ?? null;
|
|
9048
9390
|
}
|
|
9049
|
-
catch
|
|
9050
|
-
|
|
9391
|
+
catch {
|
|
9392
|
+
return null;
|
|
9051
9393
|
}
|
|
9052
9394
|
}
|
|
9053
|
-
|
|
9054
|
-
|
|
9055
|
-
|
|
9056
|
-
|
|
9057
|
-
|
|
9058
|
-
|
|
9059
|
-
|
|
9060
|
-
|
|
9061
|
-
|
|
9062
|
-
}
|
|
9063
|
-
}
|
|
9064
|
-
|
|
9065
|
-
function transformError(error) {
|
|
9066
|
-
if (axios.isAxiosError(error)) {
|
|
9067
|
-
const response = error.response;
|
|
9068
|
-
if (!response) {
|
|
9069
|
-
return new ACubeSDKError('NETWORK_ERROR', 'Network error occurred', error);
|
|
9070
|
-
}
|
|
9071
|
-
const status = response.status;
|
|
9072
|
-
const data = response.data;
|
|
9073
|
-
const violations = data?.violations;
|
|
9074
|
-
let message = 'Unknown error occurred';
|
|
9075
|
-
if (data?.detail) {
|
|
9076
|
-
message = data.detail;
|
|
9077
|
-
}
|
|
9078
|
-
else if (data?.title) {
|
|
9079
|
-
message = data.title;
|
|
9080
|
-
}
|
|
9081
|
-
else if (error.message) {
|
|
9082
|
-
message = error.message;
|
|
9083
|
-
}
|
|
9084
|
-
switch (status) {
|
|
9085
|
-
case 400:
|
|
9086
|
-
return new ACubeSDKError('VALIDATION_ERROR', message, error, status, violations);
|
|
9087
|
-
case 401:
|
|
9088
|
-
return new ACubeSDKError('AUTH_ERROR', message, error, status, violations);
|
|
9089
|
-
case 403:
|
|
9090
|
-
return new ACubeSDKError('FORBIDDEN_ERROR', message, error, status, violations);
|
|
9091
|
-
case 404:
|
|
9092
|
-
return new ACubeSDKError('NOT_FOUND_ERROR', message, error, status, violations);
|
|
9093
|
-
case 422:
|
|
9094
|
-
return new ACubeSDKError('VALIDATION_ERROR', message, error, status, violations);
|
|
9095
|
-
default:
|
|
9096
|
-
return new ACubeSDKError('UNKNOWN_ERROR', message, error, status, violations);
|
|
9395
|
+
/**
|
|
9396
|
+
* Start polling telemetry using the pemId from installed certificate
|
|
9397
|
+
* Returns the pemId if successful, null if no certificate is installed
|
|
9398
|
+
*/
|
|
9399
|
+
async startTelemetryPollingAuto() {
|
|
9400
|
+
this.ensureInitialized();
|
|
9401
|
+
const pemId = await this.getPemId();
|
|
9402
|
+
if (pemId) {
|
|
9403
|
+
this.telemetryService.startPolling(pemId);
|
|
9097
9404
|
}
|
|
9405
|
+
return pemId;
|
|
9098
9406
|
}
|
|
9099
|
-
|
|
9100
|
-
|
|
9101
|
-
|
|
9102
|
-
|
|
9103
|
-
(
|
|
9104
|
-
|
|
9105
|
-
ErrorCategory["CLIENT_ERROR"] = "CLIENT_ERROR";
|
|
9106
|
-
ErrorCategory["AUTH_ERROR"] = "AUTH_ERROR";
|
|
9107
|
-
ErrorCategory["CERTIFICATE_ERROR"] = "CERTIFICATE_ERROR";
|
|
9108
|
-
ErrorCategory["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
9109
|
-
ErrorCategory["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
|
|
9110
|
-
})(ErrorCategory || (ErrorCategory = {}));
|
|
9111
|
-
function extractStatusCode(error) {
|
|
9112
|
-
if (error instanceof MTLSError && error.statusCode) {
|
|
9113
|
-
return error.statusCode;
|
|
9114
|
-
}
|
|
9115
|
-
const errorObj = error;
|
|
9116
|
-
if (errorObj?.response?.status) {
|
|
9117
|
-
return errorObj.response.status;
|
|
9118
|
-
}
|
|
9119
|
-
if (typeof errorObj?.statusCode === 'number') {
|
|
9120
|
-
return errorObj.statusCode;
|
|
9121
|
-
}
|
|
9122
|
-
return undefined;
|
|
9123
|
-
}
|
|
9124
|
-
function classifyError(error) {
|
|
9125
|
-
const statusCode = extractStatusCode(error);
|
|
9126
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
9127
|
-
if (statusCode && statusCode >= 500 && statusCode < 600) {
|
|
9128
|
-
return {
|
|
9129
|
-
category: ErrorCategory.SERVER_ERROR,
|
|
9130
|
-
statusCode,
|
|
9131
|
-
message: errorMessage,
|
|
9132
|
-
shouldRetry: false,
|
|
9133
|
-
userMessage: `Server error (${statusCode}): The server encountered an error.`,
|
|
9134
|
-
};
|
|
9135
|
-
}
|
|
9136
|
-
if (statusCode === 401 || statusCode === 403) {
|
|
9137
|
-
return {
|
|
9138
|
-
category: ErrorCategory.AUTH_ERROR,
|
|
9139
|
-
statusCode,
|
|
9140
|
-
message: errorMessage,
|
|
9141
|
-
shouldRetry: false,
|
|
9142
|
-
userMessage: `Authentication error (${statusCode}): Invalid credentials or insufficient permissions.`,
|
|
9143
|
-
};
|
|
9144
|
-
}
|
|
9145
|
-
if (statusCode && statusCode >= 400 && statusCode < 500) {
|
|
9146
|
-
return {
|
|
9147
|
-
category: ErrorCategory.CLIENT_ERROR,
|
|
9148
|
-
statusCode,
|
|
9149
|
-
message: errorMessage,
|
|
9150
|
-
shouldRetry: false,
|
|
9151
|
-
userMessage: `Request error (${statusCode}): ${errorMessage}`,
|
|
9152
|
-
};
|
|
9407
|
+
/**
|
|
9408
|
+
* Start polling telemetry for a specific PEM
|
|
9409
|
+
*/
|
|
9410
|
+
startTelemetryPolling(pemId) {
|
|
9411
|
+
this.ensureInitialized();
|
|
9412
|
+
this.telemetryService.startPolling(pemId);
|
|
9153
9413
|
}
|
|
9154
|
-
|
|
9155
|
-
|
|
9156
|
-
|
|
9157
|
-
|
|
9158
|
-
|
|
9159
|
-
|
|
9160
|
-
userMessage: 'Certificate error: Unable to establish secure connection.',
|
|
9161
|
-
};
|
|
9414
|
+
/**
|
|
9415
|
+
* Stop telemetry polling
|
|
9416
|
+
*/
|
|
9417
|
+
stopTelemetryPolling() {
|
|
9418
|
+
this.ensureInitialized();
|
|
9419
|
+
this.telemetryService.stopPolling();
|
|
9162
9420
|
}
|
|
9163
|
-
|
|
9164
|
-
|
|
9165
|
-
|
|
9166
|
-
|
|
9421
|
+
/**
|
|
9422
|
+
* Get simplified services for product use
|
|
9423
|
+
*/
|
|
9424
|
+
getServices() {
|
|
9425
|
+
this.ensureInitialized();
|
|
9426
|
+
const sdk = this.sdk;
|
|
9427
|
+
const telemetryService = this.telemetryService;
|
|
9167
9428
|
return {
|
|
9168
|
-
|
|
9169
|
-
|
|
9170
|
-
|
|
9171
|
-
|
|
9429
|
+
receipts: sdk.receipts,
|
|
9430
|
+
merchants: sdk.merchants,
|
|
9431
|
+
cashiers: sdk.cashiers,
|
|
9432
|
+
cashRegisters: sdk.cashRegisters,
|
|
9433
|
+
pointOfSales: sdk.pointOfSales,
|
|
9434
|
+
suppliers: sdk.suppliers,
|
|
9435
|
+
pems: sdk.pems,
|
|
9436
|
+
dailyReports: sdk.dailyReports,
|
|
9437
|
+
journals: sdk.journals,
|
|
9438
|
+
telemetry: {
|
|
9439
|
+
startPollingAuto: () => this.startTelemetryPollingAuto(),
|
|
9440
|
+
startPolling: (pemId) => telemetryService.startPolling(pemId),
|
|
9441
|
+
stopPolling: () => telemetryService.stopPolling(),
|
|
9442
|
+
getTelemetry: (pemId) => telemetryService.getTelemetry(pemId),
|
|
9443
|
+
refreshTelemetry: (pemId) => telemetryService.refreshTelemetry(pemId),
|
|
9444
|
+
triggerSync: () => telemetryService.triggerSync(),
|
|
9445
|
+
clearTelemetry: () => telemetryService.clearTelemetry(),
|
|
9446
|
+
getPemId: () => this.getPemId(),
|
|
9447
|
+
},
|
|
9448
|
+
login: (credentials) => sdk.login(credentials),
|
|
9449
|
+
logout: () => sdk.logout(),
|
|
9450
|
+
getCurrentUser: () => sdk.getCurrentUser(),
|
|
9451
|
+
isAuthenticated: () => sdk.isAuthenticated(),
|
|
9452
|
+
storeCertificate: (certificate, privateKey, options) => sdk.storeCertificate(certificate, privateKey, options),
|
|
9453
|
+
hasCertificate: () => sdk.hasCertificate(),
|
|
9454
|
+
clearCertificate: () => sdk.clearCertificate(),
|
|
9455
|
+
isOnline: () => sdk.isOnline(),
|
|
9172
9456
|
};
|
|
9173
9457
|
}
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
};
|
|
9181
|
-
}
|
|
9182
|
-
function shouldReconfigureCertificate(error) {
|
|
9183
|
-
const classification = classifyError(error);
|
|
9184
|
-
return classification.category === ErrorCategory.CERTIFICATE_ERROR;
|
|
9185
|
-
}
|
|
9186
|
-
function shouldRetryRequest(error, isRetryAttempt) {
|
|
9187
|
-
if (isRetryAttempt) {
|
|
9188
|
-
return false;
|
|
9458
|
+
/**
|
|
9459
|
+
* Manually trigger a notification sync
|
|
9460
|
+
*/
|
|
9461
|
+
async syncNotifications() {
|
|
9462
|
+
this.ensureInitialized();
|
|
9463
|
+
await this.notificationService.triggerSync();
|
|
9189
9464
|
}
|
|
9190
|
-
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
}
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
|
|
9204
|
-
|
|
9205
|
-
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
|
|
9209
|
-
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
|
|
9213
|
-
|
|
9214
|
-
|
|
9215
|
-
|
|
9216
|
-
|
|
9217
|
-
|
|
9218
|
-
|
|
9219
|
-
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
|
|
9227
|
-
|
|
9228
|
-
|
|
9229
|
-
// Receipt Item Schema
|
|
9230
|
-
const ReceiptItemSchema = z.object({
|
|
9231
|
-
type: GoodOrServiceSchema.optional(),
|
|
9232
|
-
quantity: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9233
|
-
description: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9234
|
-
unit_price: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9235
|
-
vat_rate_code: VatRateCodeSchema.optional(),
|
|
9236
|
-
simplified_vat_allocation: z.boolean().optional(),
|
|
9237
|
-
discount: z.string().nullable().optional(),
|
|
9238
|
-
is_down_payment_or_voucher_redemption: z.boolean().optional(),
|
|
9239
|
-
complimentary: z.boolean().optional(),
|
|
9240
|
-
});
|
|
9241
|
-
// Main Receipt Input Schema
|
|
9242
|
-
const ReceiptInputSchema = z
|
|
9243
|
-
.object({
|
|
9244
|
-
items: z.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
|
|
9245
|
-
customer_tax_code: z.string().optional(),
|
|
9246
|
-
customer_lottery_code: z.string().optional(),
|
|
9247
|
-
discount: z.string().nullable().optional(),
|
|
9248
|
-
invoice_issuing: z.boolean().optional(),
|
|
9249
|
-
uncollected_dcr_to_ssn: z.boolean().optional(),
|
|
9250
|
-
services_uncollected_amount: z.string().nullable().optional(),
|
|
9251
|
-
goods_uncollected_amount: z.string().nullable().optional(),
|
|
9252
|
-
cash_payment_amount: z.string().nullable().optional(),
|
|
9253
|
-
electronic_payment_amount: z.string().nullable().optional(),
|
|
9254
|
-
ticket_restaurant_payment_amount: z.string().nullable().optional(),
|
|
9255
|
-
ticket_restaurant_quantity: z.number().optional(),
|
|
9256
|
-
})
|
|
9257
|
-
.refine((data) => {
|
|
9258
|
-
// At least one payment method should be provided
|
|
9259
|
-
const hasCashPayment = data.cash_payment_amount && parseFloat(data.cash_payment_amount) > 0;
|
|
9260
|
-
const hasElectronicPayment = data.electronic_payment_amount && parseFloat(data.electronic_payment_amount) > 0;
|
|
9261
|
-
const hasTicketPayment = data.ticket_restaurant_payment_amount &&
|
|
9262
|
-
parseFloat(data.ticket_restaurant_payment_amount) > 0;
|
|
9263
|
-
return hasCashPayment || hasElectronicPayment || hasTicketPayment;
|
|
9264
|
-
}, {
|
|
9265
|
-
error: 'At least one payment method is required',
|
|
9266
|
-
path: ['payment_methods'],
|
|
9267
|
-
})
|
|
9268
|
-
.refine((data) => {
|
|
9269
|
-
// only one between customer_tax_code and customer_lottery_code can be provided
|
|
9270
|
-
return !data.customer_tax_code || !data.customer_lottery_code;
|
|
9271
|
-
}, {
|
|
9272
|
-
error: 'Only one between customer_tax_code and customer_lottery_code can be provided',
|
|
9273
|
-
path: ['customer_tax_code', 'customer_lottery_code'],
|
|
9274
|
-
});
|
|
9275
|
-
// Receipt Return or Void via PEM Schema
|
|
9276
|
-
const ReceiptReturnOrVoidViaPEMInputSchema = z.object({
|
|
9277
|
-
device_id: z.string().optional(),
|
|
9278
|
-
items: z.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
|
|
9279
|
-
document_number: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9280
|
-
document_datetime: z.string().optional(),
|
|
9281
|
-
lottery_code: z.string().optional(),
|
|
9282
|
-
});
|
|
9283
|
-
// Receipt Return or Void with Proof Schema
|
|
9284
|
-
const ReceiptReturnOrVoidWithProofInputSchema = z.object({
|
|
9285
|
-
items: z.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
|
|
9286
|
-
proof: ReceiptProofTypeSchema,
|
|
9287
|
-
document_datetime: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9288
|
-
});
|
|
9289
|
-
// Void Receipt Schema
|
|
9290
|
-
const VoidReceiptInputSchema = z.object({
|
|
9291
|
-
document_number: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9292
|
-
});
|
|
9293
|
-
const ReceiptReturnItemSchema = z
|
|
9294
|
-
.array(z.object({
|
|
9295
|
-
id: z.number(),
|
|
9296
|
-
quantity: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9297
|
-
}))
|
|
9298
|
-
.min(1, { error: 'arrayMin1' });
|
|
9299
|
-
// Receipt Return Schema
|
|
9300
|
-
const ReceiptReturnInputSchema = z.object({
|
|
9301
|
-
items: z.array(ReceiptReturnItemSchema).min(1, { error: 'arrayMin1' }),
|
|
9302
|
-
document_number: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9303
|
-
});
|
|
9304
|
-
|
|
9305
|
-
// Cashier Create Input Schema (MF1)
|
|
9306
|
-
const CashierCreateInputSchema = z.object({
|
|
9307
|
-
email: z
|
|
9308
|
-
.string()
|
|
9309
|
-
.min(1, { error: 'fieldIsRequired' })
|
|
9310
|
-
.max(255, { error: 'emailMaxLength' })
|
|
9311
|
-
.email({ error: 'invalidEmail' }),
|
|
9312
|
-
password: z
|
|
9313
|
-
.string()
|
|
9314
|
-
.min(8, { error: 'passwordMinLength' })
|
|
9315
|
-
.max(40, { error: 'passwordMaxLength' }),
|
|
9316
|
-
name: z.string().min(1, { error: 'fieldIsRequired' }).max(255, { error: 'nameMaxLength' }),
|
|
9317
|
-
display_name: z
|
|
9318
|
-
.string()
|
|
9319
|
-
.min(1, { error: 'fieldIsRequired' })
|
|
9320
|
-
.max(255, { error: 'displayNameMaxLength' }),
|
|
9321
|
-
});
|
|
9322
|
-
|
|
9323
|
-
// Enum options arrays
|
|
9324
|
-
const PEM_STATUS_OPTIONS = [
|
|
9325
|
-
'NEW',
|
|
9326
|
-
'REGISTERED',
|
|
9327
|
-
'ACTIVATED',
|
|
9328
|
-
'ONLINE',
|
|
9329
|
-
'OFFLINE',
|
|
9330
|
-
'DISCARDED',
|
|
9331
|
-
];
|
|
9332
|
-
// Address Schema (reusable)
|
|
9333
|
-
const AddressSchema = z.object({
|
|
9334
|
-
street_address: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9335
|
-
street_number: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9336
|
-
zip_code: z
|
|
9337
|
-
.string()
|
|
9338
|
-
.min(1, { error: 'fieldIsRequired' })
|
|
9339
|
-
.regex(/^\d{5}$/, { error: 'invalidZipCode' }),
|
|
9340
|
-
city: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9341
|
-
province: z
|
|
9342
|
-
.string()
|
|
9343
|
-
.min(2, { error: 'provinceMinLength' })
|
|
9344
|
-
.max(2, { error: 'provinceMaxLength' })
|
|
9345
|
-
.toUpperCase(),
|
|
9346
|
-
});
|
|
9347
|
-
// PEM Status Schema
|
|
9348
|
-
const PEMStatusSchema = z.enum(PEM_STATUS_OPTIONS);
|
|
9349
|
-
// Activation Request Schema
|
|
9350
|
-
const ActivationRequestSchema = z.object({
|
|
9351
|
-
registration_key: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9352
|
-
});
|
|
9353
|
-
// PEM Status Offline Request Schema
|
|
9354
|
-
const PEMStatusOfflineRequestSchema = z.object({
|
|
9355
|
-
timestamp: z
|
|
9356
|
-
.string()
|
|
9357
|
-
.min(1, { error: 'fieldIsRequired' })
|
|
9358
|
-
.refine((val) => !isNaN(Date.parse(val)), {
|
|
9359
|
-
error: 'invalidDateFormat',
|
|
9360
|
-
}),
|
|
9361
|
-
reason: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9362
|
-
});
|
|
9465
|
+
/**
|
|
9466
|
+
* Manually trigger a telemetry sync
|
|
9467
|
+
*/
|
|
9468
|
+
async syncTelemetry() {
|
|
9469
|
+
this.ensureInitialized();
|
|
9470
|
+
return this.telemetryService.triggerSync();
|
|
9471
|
+
}
|
|
9472
|
+
/**
|
|
9473
|
+
* Check if the manager is initialized
|
|
9474
|
+
*/
|
|
9475
|
+
getIsInitialized() {
|
|
9476
|
+
return this.isInitialized;
|
|
9477
|
+
}
|
|
9478
|
+
/**
|
|
9479
|
+
* Get the underlying SDK instance (for advanced use cases)
|
|
9480
|
+
*/
|
|
9481
|
+
getSDK() {
|
|
9482
|
+
this.ensureInitialized();
|
|
9483
|
+
return this.sdk;
|
|
9484
|
+
}
|
|
9485
|
+
cleanup() {
|
|
9486
|
+
this.notificationService?.destroy();
|
|
9487
|
+
this.telemetryService?.destroy();
|
|
9488
|
+
this.appStateService?.destroy();
|
|
9489
|
+
this.sdk?.destroy();
|
|
9490
|
+
this.notificationService = null;
|
|
9491
|
+
this.telemetryService = null;
|
|
9492
|
+
this.appStateService = null;
|
|
9493
|
+
this.sdk = null;
|
|
9494
|
+
this.isInitialized = false;
|
|
9495
|
+
this.isPollingActive = false;
|
|
9496
|
+
}
|
|
9497
|
+
ensureInitialized() {
|
|
9498
|
+
if (!this.isInitialized) {
|
|
9499
|
+
throw new ACubeSDKError('SDK_NOT_INITIALIZED', 'SDKManager not initialized. Call initialize() first.');
|
|
9500
|
+
}
|
|
9501
|
+
}
|
|
9502
|
+
}
|
|
9503
|
+
SDKManager.instance = null;
|
|
9363
9504
|
|
|
9364
|
-
|
|
9365
|
-
const
|
|
9366
|
-
|
|
9367
|
-
|
|
9368
|
-
});
|
|
9505
|
+
const RECEIPT_READY = 'ready';
|
|
9506
|
+
const RECEIPT_SENT = 'sent';
|
|
9507
|
+
const RECEIPT_SORT_DESCENDING = 'descending';
|
|
9508
|
+
const RECEIPT_SORT_ASCENDING = 'ascending';
|
|
9369
9509
|
|
|
9370
|
-
|
|
9371
|
-
|
|
9372
|
-
|
|
9373
|
-
|
|
9374
|
-
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
const MerchantCreateInputSchema = z
|
|
9378
|
-
.object({
|
|
9379
|
-
vat_number: z
|
|
9380
|
-
.string()
|
|
9381
|
-
.min(1, { error: 'fieldIsRequired' })
|
|
9382
|
-
.regex(VAT_NUMBER_REGEX, { error: 'invalidVatNumber' }),
|
|
9383
|
-
fiscal_code: z.string().regex(FISCAL_CODE_REGEX, { error: 'invalidFiscalCode' }).optional(),
|
|
9384
|
-
business_name: z.string().max(200, { error: 'businessNameMaxLength' }).optional().nullable(),
|
|
9385
|
-
first_name: z.string().max(100, { error: 'firstNameMaxLength' }).optional().nullable(),
|
|
9386
|
-
last_name: z.string().max(100, { error: 'lastNameMaxLength' }).optional().nullable(),
|
|
9387
|
-
email: z.string().min(1, { error: 'fieldIsRequired' }).email({ error: 'invalidEmail' }),
|
|
9388
|
-
password: z
|
|
9389
|
-
.string()
|
|
9390
|
-
.min(1, { error: 'fieldIsRequired' })
|
|
9391
|
-
.regex(PASSWORD_REGEX, { error: 'passwordComplexity' }),
|
|
9392
|
-
address: AddressSchema.optional(),
|
|
9393
|
-
})
|
|
9394
|
-
.refine((data) => {
|
|
9395
|
-
const hasBusinessName = data.business_name && data.business_name.trim() !== '';
|
|
9396
|
-
const hasPersonalNames = (data.first_name && data.first_name.trim() !== '') ||
|
|
9397
|
-
(data.last_name && data.last_name.trim() !== '');
|
|
9398
|
-
// If business name is set, first/last name must not be provided
|
|
9399
|
-
if (hasBusinessName && hasPersonalNames) {
|
|
9400
|
-
return false;
|
|
9510
|
+
const log = createPrefixedLogger('CACHE-HANDLER');
|
|
9511
|
+
class CacheHandler {
|
|
9512
|
+
constructor(cache, networkMonitor) {
|
|
9513
|
+
this.cache = cache;
|
|
9514
|
+
this.networkMonitor = networkMonitor;
|
|
9515
|
+
this.currentOnlineState = true;
|
|
9516
|
+
this.setupNetworkMonitoring();
|
|
9401
9517
|
}
|
|
9402
|
-
|
|
9403
|
-
|
|
9518
|
+
setupNetworkMonitoring() {
|
|
9519
|
+
if (this.networkMonitor) {
|
|
9520
|
+
this.networkSubscription = this.networkMonitor.online$.subscribe((online) => {
|
|
9521
|
+
this.currentOnlineState = online;
|
|
9522
|
+
});
|
|
9523
|
+
}
|
|
9524
|
+
}
|
|
9525
|
+
isOnline() {
|
|
9526
|
+
if (this.networkMonitor) {
|
|
9527
|
+
return this.currentOnlineState;
|
|
9528
|
+
}
|
|
9529
|
+
if (typeof navigator !== 'undefined' && 'onLine' in navigator) {
|
|
9530
|
+
return navigator.onLine;
|
|
9531
|
+
}
|
|
9404
9532
|
return false;
|
|
9405
9533
|
}
|
|
9406
|
-
|
|
9407
|
-
|
|
9408
|
-
|
|
9409
|
-
|
|
9410
|
-
}
|
|
9411
|
-
|
|
9412
|
-
const
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
|
|
9420
|
-
|
|
9421
|
-
|
|
9422
|
-
|
|
9423
|
-
|
|
9424
|
-
|
|
9425
|
-
|
|
9426
|
-
|
|
9427
|
-
|
|
9428
|
-
|
|
9429
|
-
|
|
9430
|
-
|
|
9431
|
-
|
|
9432
|
-
|
|
9433
|
-
|
|
9434
|
-
|
|
9435
|
-
|
|
9436
|
-
|
|
9437
|
-
|
|
9438
|
-
|
|
9439
|
-
|
|
9440
|
-
|
|
9441
|
-
|
|
9442
|
-
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
9453
|
-
|
|
9454
|
-
|
|
9455
|
-
|
|
9456
|
-
|
|
9457
|
-
|
|
9458
|
-
|
|
9459
|
-
|
|
9460
|
-
|
|
9461
|
-
|
|
9462
|
-
|
|
9463
|
-
|
|
9464
|
-
|
|
9465
|
-
|
|
9466
|
-
// Daily Report Status Schema
|
|
9467
|
-
const DailyReportStatusSchema = z.enum(DAILY_REPORT_STATUS_OPTIONS, {
|
|
9468
|
-
error: 'invalidDailyReportStatus',
|
|
9469
|
-
});
|
|
9470
|
-
// Daily Reports List Parameters Schema
|
|
9471
|
-
const DailyReportsParamsSchema = z.object({
|
|
9472
|
-
pem_serial_number: z.string().min(1, { error: 'fieldIsRequired' }).optional(),
|
|
9473
|
-
date_from: z
|
|
9474
|
-
.string()
|
|
9475
|
-
.refine((val) => !isNaN(Date.parse(val)), {
|
|
9476
|
-
error: 'invalidDateFormat',
|
|
9477
|
-
})
|
|
9478
|
-
.optional(),
|
|
9479
|
-
date_to: z
|
|
9480
|
-
.string()
|
|
9481
|
-
.refine((val) => !isNaN(Date.parse(val)), {
|
|
9482
|
-
error: 'invalidDateFormat',
|
|
9483
|
-
})
|
|
9484
|
-
.optional(),
|
|
9485
|
-
status: DailyReportStatusSchema.optional(),
|
|
9486
|
-
page: z.number().min(1, { error: 'pageMinValue' }).optional(),
|
|
9487
|
-
});
|
|
9488
|
-
|
|
9489
|
-
const NotificationScopeSchema = z.object({
|
|
9490
|
-
type: z.literal('global'),
|
|
9491
|
-
});
|
|
9492
|
-
const PemStatusSchema = z.enum(['ONLINE', 'OFFLINE']);
|
|
9493
|
-
const NotificationDataBlockAtSchema = z.object({
|
|
9494
|
-
block_at: z.string(),
|
|
9495
|
-
});
|
|
9496
|
-
const NotificationDataPemStatusSchema = z.object({
|
|
9497
|
-
from: PemStatusSchema,
|
|
9498
|
-
to: PemStatusSchema,
|
|
9499
|
-
});
|
|
9500
|
-
const NotificationBaseSchema = z.object({
|
|
9501
|
-
uuid: z.string().uuid({ error: 'invalidUuid' }),
|
|
9502
|
-
scope: NotificationScopeSchema,
|
|
9503
|
-
source: z.enum(['system', 'Italian Tax Authority']),
|
|
9504
|
-
level: z.enum(['info', 'warning', 'error', 'critical']),
|
|
9505
|
-
created_at: z.string(),
|
|
9506
|
-
});
|
|
9507
|
-
const NotificationMf2UnreachableSchema = NotificationBaseSchema.extend({
|
|
9508
|
-
type: z.literal('INTERNAL_COMMUNICATION_FAILURE'),
|
|
9509
|
-
code: z.literal('SYS-W-01'),
|
|
9510
|
-
data: NotificationDataBlockAtSchema,
|
|
9511
|
-
});
|
|
9512
|
-
const NotificationPemsBlockedSchema = NotificationBaseSchema.extend({
|
|
9513
|
-
type: z.literal('PEM_STATUS_CHANGED'),
|
|
9514
|
-
code: z.literal('SYS-C-01'),
|
|
9515
|
-
data: NotificationDataPemStatusSchema,
|
|
9516
|
-
});
|
|
9517
|
-
const NotificationPemBackOnlineSchema = NotificationBaseSchema.extend({
|
|
9518
|
-
type: z.literal('PEM_STATUS_CHANGED'),
|
|
9519
|
-
code: z.literal('SYS-I-01'),
|
|
9520
|
-
data: NotificationDataPemStatusSchema,
|
|
9521
|
-
});
|
|
9522
|
-
const NotificationSchema = z.discriminatedUnion('code', [
|
|
9523
|
-
NotificationMf2UnreachableSchema,
|
|
9524
|
-
NotificationPemsBlockedSchema,
|
|
9525
|
-
NotificationPemBackOnlineSchema,
|
|
9526
|
-
]);
|
|
9527
|
-
const NotificationListResponseSchema = z.object({
|
|
9528
|
-
members: z.array(NotificationSchema),
|
|
9529
|
-
});
|
|
9534
|
+
async handleCachedRequest(url, requestFn, config) {
|
|
9535
|
+
if (!this.cache || config?.useCache === false) {
|
|
9536
|
+
const response = await requestFn();
|
|
9537
|
+
return response.data;
|
|
9538
|
+
}
|
|
9539
|
+
const cacheKey = this.generateCacheKey(url);
|
|
9540
|
+
const online = this.isOnline();
|
|
9541
|
+
log.debug('Request:', { url, cacheKey, online });
|
|
9542
|
+
if (online) {
|
|
9543
|
+
try {
|
|
9544
|
+
const response = await requestFn();
|
|
9545
|
+
if (this.cache) {
|
|
9546
|
+
await this.cache.set(cacheKey, response.data).catch((error) => {
|
|
9547
|
+
log.error('Failed to cache:', error instanceof Error ? error.message : error);
|
|
9548
|
+
});
|
|
9549
|
+
}
|
|
9550
|
+
return response.data;
|
|
9551
|
+
}
|
|
9552
|
+
catch (error) {
|
|
9553
|
+
const cached = await this.cache.get(cacheKey).catch(() => null);
|
|
9554
|
+
if (cached) {
|
|
9555
|
+
log.debug('Network failed, using cache fallback');
|
|
9556
|
+
return cached.data;
|
|
9557
|
+
}
|
|
9558
|
+
throw error;
|
|
9559
|
+
}
|
|
9560
|
+
}
|
|
9561
|
+
else {
|
|
9562
|
+
const cached = await this.cache.get(cacheKey).catch(() => null);
|
|
9563
|
+
if (cached) {
|
|
9564
|
+
log.debug('Offline, returning cached data');
|
|
9565
|
+
return cached.data;
|
|
9566
|
+
}
|
|
9567
|
+
throw new Error('Offline: No cached data available');
|
|
9568
|
+
}
|
|
9569
|
+
}
|
|
9570
|
+
generateCacheKey(url) {
|
|
9571
|
+
return url;
|
|
9572
|
+
}
|
|
9573
|
+
async invalidateCache(pattern) {
|
|
9574
|
+
if (!this.cache)
|
|
9575
|
+
return;
|
|
9576
|
+
try {
|
|
9577
|
+
await this.cache.invalidate(pattern);
|
|
9578
|
+
}
|
|
9579
|
+
catch (error) {
|
|
9580
|
+
log.error('Invalidation failed:', error instanceof Error ? error.message : error);
|
|
9581
|
+
}
|
|
9582
|
+
}
|
|
9583
|
+
getCacheStatus() {
|
|
9584
|
+
return {
|
|
9585
|
+
available: !!this.cache,
|
|
9586
|
+
networkMonitorAvailable: !!this.networkMonitor,
|
|
9587
|
+
isOnline: this.isOnline(),
|
|
9588
|
+
};
|
|
9589
|
+
}
|
|
9590
|
+
destroy() {
|
|
9591
|
+
this.networkSubscription?.unsubscribe();
|
|
9592
|
+
}
|
|
9593
|
+
}
|
|
9530
9594
|
|
|
9531
|
-
|
|
9532
|
-
|
|
9533
|
-
|
|
9534
|
-
|
|
9535
|
-
|
|
9536
|
-
|
|
9537
|
-
|
|
9538
|
-
|
|
9539
|
-
|
|
9540
|
-
|
|
9541
|
-
|
|
9542
|
-
|
|
9543
|
-
|
|
9544
|
-
|
|
9545
|
-
|
|
9546
|
-
}
|
|
9547
|
-
|
|
9548
|
-
|
|
9549
|
-
|
|
9550
|
-
|
|
9551
|
-
|
|
9552
|
-
|
|
9553
|
-
|
|
9554
|
-
|
|
9555
|
-
|
|
9556
|
-
|
|
9557
|
-
|
|
9558
|
-
|
|
9559
|
-
|
|
9560
|
-
|
|
9561
|
-
|
|
9562
|
-
|
|
9563
|
-
|
|
9564
|
-
}
|
|
9565
|
-
|
|
9566
|
-
|
|
9567
|
-
outcome: z.enum(['success', 'failed', 'pending']),
|
|
9568
|
-
});
|
|
9569
|
-
const LotterySchema = z.object({
|
|
9570
|
-
last_transmission: TransmissionSchema,
|
|
9571
|
-
secret_request: LotterySecretRequestSchema,
|
|
9572
|
-
});
|
|
9573
|
-
const TelemetrySchema = z.object({
|
|
9574
|
-
pem_id: z.string(),
|
|
9575
|
-
pem_status: z.enum(['ONLINE', 'OFFLINE', 'ERROR']),
|
|
9576
|
-
pem_status_changed_at: z.string(),
|
|
9577
|
-
merchant: TelemetryMerchantSchema,
|
|
9578
|
-
supplier: TelemetrySupplierSchema,
|
|
9579
|
-
software: TelemetrySoftwareSchema,
|
|
9580
|
-
last_communication_at: z.string(),
|
|
9581
|
-
pending_receipts: PendingReceiptsSchema,
|
|
9582
|
-
last_receipt_transmission: TransmissionSchema,
|
|
9583
|
-
last_message_from_mf2: MessageSchema,
|
|
9584
|
-
ade_corrispettivi_transmission: TransmissionSchema,
|
|
9585
|
-
last_message_from_ade: MessageSchema,
|
|
9586
|
-
lottery: LotterySchema,
|
|
9587
|
-
});
|
|
9595
|
+
function transformError(error) {
|
|
9596
|
+
if (axios.isAxiosError(error)) {
|
|
9597
|
+
const response = error.response;
|
|
9598
|
+
if (!response) {
|
|
9599
|
+
return new ACubeSDKError('NETWORK_ERROR', 'Network error occurred', error);
|
|
9600
|
+
}
|
|
9601
|
+
const status = response.status;
|
|
9602
|
+
const data = response.data;
|
|
9603
|
+
const violations = data?.violations;
|
|
9604
|
+
let message = 'Unknown error occurred';
|
|
9605
|
+
if (data?.detail) {
|
|
9606
|
+
message = data.detail;
|
|
9607
|
+
}
|
|
9608
|
+
else if (data?.title) {
|
|
9609
|
+
message = data.title;
|
|
9610
|
+
}
|
|
9611
|
+
else if (error.message) {
|
|
9612
|
+
message = error.message;
|
|
9613
|
+
}
|
|
9614
|
+
switch (status) {
|
|
9615
|
+
case 400:
|
|
9616
|
+
return new ACubeSDKError('VALIDATION_ERROR', message, error, status, violations);
|
|
9617
|
+
case 401:
|
|
9618
|
+
return new ACubeSDKError('AUTH_ERROR', message, error, status, violations);
|
|
9619
|
+
case 403:
|
|
9620
|
+
return new ACubeSDKError('FORBIDDEN_ERROR', message, error, status, violations);
|
|
9621
|
+
case 404:
|
|
9622
|
+
return new ACubeSDKError('NOT_FOUND_ERROR', message, error, status, violations);
|
|
9623
|
+
case 422:
|
|
9624
|
+
return new ACubeSDKError('VALIDATION_ERROR', message, error, status, violations);
|
|
9625
|
+
default:
|
|
9626
|
+
return new ACubeSDKError('UNKNOWN_ERROR', message, error, status, violations);
|
|
9627
|
+
}
|
|
9628
|
+
}
|
|
9629
|
+
return new ACubeSDKError('UNKNOWN_ERROR', 'Unknown error occurred', error);
|
|
9630
|
+
}
|
|
9588
9631
|
|
|
9589
|
-
|
|
9590
|
-
|
|
9591
|
-
|
|
9592
|
-
|
|
9593
|
-
|
|
9594
|
-
|
|
9595
|
-
|
|
9596
|
-
|
|
9597
|
-
|
|
9598
|
-
|
|
9599
|
-
|
|
9600
|
-
|
|
9601
|
-
|
|
9602
|
-
|
|
9603
|
-
|
|
9604
|
-
|
|
9605
|
-
|
|
9606
|
-
|
|
9607
|
-
|
|
9608
|
-
|
|
9609
|
-
|
|
9610
|
-
|
|
9611
|
-
|
|
9612
|
-
|
|
9613
|
-
|
|
9614
|
-
|
|
9615
|
-
displayNameMaxLength: 'Display name is too long (max 255 characters)',
|
|
9616
|
-
};
|
|
9617
|
-
// Validation helper functions
|
|
9618
|
-
const validateInput = (schema, data) => {
|
|
9619
|
-
const result = schema.safeParse(data);
|
|
9620
|
-
if (!result.success) {
|
|
9621
|
-
const errors = result.error.issues.map((error) => ({
|
|
9622
|
-
field: error.path.join('.'),
|
|
9623
|
-
message: error.message,
|
|
9624
|
-
code: error.code,
|
|
9625
|
-
}));
|
|
9632
|
+
var ErrorCategory;
|
|
9633
|
+
(function (ErrorCategory) {
|
|
9634
|
+
ErrorCategory["SERVER_ERROR"] = "SERVER_ERROR";
|
|
9635
|
+
ErrorCategory["CLIENT_ERROR"] = "CLIENT_ERROR";
|
|
9636
|
+
ErrorCategory["AUTH_ERROR"] = "AUTH_ERROR";
|
|
9637
|
+
ErrorCategory["CERTIFICATE_ERROR"] = "CERTIFICATE_ERROR";
|
|
9638
|
+
ErrorCategory["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
9639
|
+
ErrorCategory["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
|
|
9640
|
+
})(ErrorCategory || (ErrorCategory = {}));
|
|
9641
|
+
function extractStatusCode(error) {
|
|
9642
|
+
if (error instanceof MTLSError && error.statusCode) {
|
|
9643
|
+
return error.statusCode;
|
|
9644
|
+
}
|
|
9645
|
+
const errorObj = error;
|
|
9646
|
+
if (errorObj?.response?.status) {
|
|
9647
|
+
return errorObj.response.status;
|
|
9648
|
+
}
|
|
9649
|
+
if (typeof errorObj?.statusCode === 'number') {
|
|
9650
|
+
return errorObj.statusCode;
|
|
9651
|
+
}
|
|
9652
|
+
return undefined;
|
|
9653
|
+
}
|
|
9654
|
+
function classifyError(error) {
|
|
9655
|
+
const statusCode = extractStatusCode(error);
|
|
9656
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
9657
|
+
if (statusCode && statusCode >= 500 && statusCode < 600) {
|
|
9626
9658
|
return {
|
|
9627
|
-
|
|
9628
|
-
|
|
9629
|
-
|
|
9659
|
+
category: ErrorCategory.SERVER_ERROR,
|
|
9660
|
+
statusCode,
|
|
9661
|
+
message: errorMessage,
|
|
9662
|
+
shouldRetry: false,
|
|
9663
|
+
userMessage: `Server error (${statusCode}): The server encountered an error.`,
|
|
9664
|
+
};
|
|
9665
|
+
}
|
|
9666
|
+
if (statusCode === 401 || statusCode === 403) {
|
|
9667
|
+
return {
|
|
9668
|
+
category: ErrorCategory.AUTH_ERROR,
|
|
9669
|
+
statusCode,
|
|
9670
|
+
message: errorMessage,
|
|
9671
|
+
shouldRetry: false,
|
|
9672
|
+
userMessage: `Authentication error (${statusCode}): Invalid credentials or insufficient permissions.`,
|
|
9673
|
+
};
|
|
9674
|
+
}
|
|
9675
|
+
if (statusCode && statusCode >= 400 && statusCode < 500) {
|
|
9676
|
+
return {
|
|
9677
|
+
category: ErrorCategory.CLIENT_ERROR,
|
|
9678
|
+
statusCode,
|
|
9679
|
+
message: errorMessage,
|
|
9680
|
+
shouldRetry: false,
|
|
9681
|
+
userMessage: `Request error (${statusCode}): ${errorMessage}`,
|
|
9682
|
+
};
|
|
9683
|
+
}
|
|
9684
|
+
if (error instanceof MTLSError) {
|
|
9685
|
+
return {
|
|
9686
|
+
category: ErrorCategory.CERTIFICATE_ERROR,
|
|
9687
|
+
statusCode,
|
|
9688
|
+
message: errorMessage,
|
|
9689
|
+
shouldRetry: true,
|
|
9690
|
+
userMessage: 'Certificate error: Unable to establish secure connection.',
|
|
9691
|
+
};
|
|
9692
|
+
}
|
|
9693
|
+
if (!statusCode &&
|
|
9694
|
+
(errorMessage.toLowerCase().includes('network') ||
|
|
9695
|
+
errorMessage.toLowerCase().includes('timeout') ||
|
|
9696
|
+
errorMessage.toLowerCase().includes('connection'))) {
|
|
9697
|
+
return {
|
|
9698
|
+
category: ErrorCategory.NETWORK_ERROR,
|
|
9699
|
+
message: errorMessage,
|
|
9700
|
+
shouldRetry: true,
|
|
9701
|
+
userMessage: 'Network error: Unable to connect to server.',
|
|
9630
9702
|
};
|
|
9631
9703
|
}
|
|
9632
9704
|
return {
|
|
9633
|
-
|
|
9634
|
-
|
|
9635
|
-
|
|
9705
|
+
category: ErrorCategory.UNKNOWN_ERROR,
|
|
9706
|
+
statusCode,
|
|
9707
|
+
message: errorMessage,
|
|
9708
|
+
shouldRetry: false,
|
|
9709
|
+
userMessage: `Unexpected error: ${errorMessage}`,
|
|
9636
9710
|
};
|
|
9637
|
-
}
|
|
9711
|
+
}
|
|
9712
|
+
function shouldReconfigureCertificate(error) {
|
|
9713
|
+
const classification = classifyError(error);
|
|
9714
|
+
return classification.category === ErrorCategory.CERTIFICATE_ERROR;
|
|
9715
|
+
}
|
|
9716
|
+
function shouldRetryRequest(error, isRetryAttempt) {
|
|
9717
|
+
if (isRetryAttempt) {
|
|
9718
|
+
return false;
|
|
9719
|
+
}
|
|
9720
|
+
const classification = classifyError(error);
|
|
9721
|
+
return classification.shouldRetry;
|
|
9722
|
+
}
|
|
9723
|
+
function getUserFriendlyMessage(error) {
|
|
9724
|
+
const classification = classifyError(error);
|
|
9725
|
+
return classification.userMessage;
|
|
9726
|
+
}
|
|
9638
9727
|
|
|
9639
9728
|
var MTLSErrorType;
|
|
9640
9729
|
(function (MTLSErrorType) {
|