@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.cjs.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var axios = require('axios');
|
|
6
5
|
var z = require('zod');
|
|
6
|
+
var axios = require('axios');
|
|
7
7
|
|
|
8
8
|
function _interopNamespaceDefault(e) {
|
|
9
9
|
var n = Object.create(null);
|
|
@@ -1861,135 +1861,440 @@ function extractRoles(jwtRoles) {
|
|
|
1861
1861
|
return allRoles;
|
|
1862
1862
|
}
|
|
1863
1863
|
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
return
|
|
1867
|
-
}
|
|
1868
|
-
get isAuthenticated$() {
|
|
1869
|
-
return this.userSubject.pipe(map((user) => user !== null), distinctUntilChanged(), takeUntil(this.destroy$));
|
|
1870
|
-
}
|
|
1871
|
-
get authState$() {
|
|
1872
|
-
return this.authStateSubject.asObservable();
|
|
1864
|
+
function clearObject(input) {
|
|
1865
|
+
if (input === null || input === undefined || input === '') {
|
|
1866
|
+
return undefined;
|
|
1873
1867
|
}
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
this.userSubject = new BehaviorSubject(null);
|
|
1880
|
-
this.authStateSubject = new BehaviorSubject('idle');
|
|
1881
|
-
this.destroy$ = new Subject();
|
|
1868
|
+
if (Array.isArray(input)) {
|
|
1869
|
+
const cleanedArray = input
|
|
1870
|
+
.map((item) => clearObject(item))
|
|
1871
|
+
.filter((item) => item !== undefined);
|
|
1872
|
+
return cleanedArray;
|
|
1882
1873
|
}
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
const
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
}
|
|
1890
|
-
const jwtPayload = parseJwt(response.data.token);
|
|
1891
|
-
const expiresAt = jwtPayload.exp * 1000;
|
|
1892
|
-
await this.tokenStorage.saveAccessToken(response.data.token, expiresAt);
|
|
1893
|
-
const user = this.createUserFromPayload(jwtPayload);
|
|
1894
|
-
await this.tokenStorage.saveUser(user);
|
|
1895
|
-
this.userSubject.next(user);
|
|
1896
|
-
this.authStateSubject.next('authenticated');
|
|
1897
|
-
this.events.onUserChanged?.(user);
|
|
1898
|
-
return user;
|
|
1899
|
-
}
|
|
1900
|
-
catch (error) {
|
|
1901
|
-
this.authStateSubject.next('error');
|
|
1902
|
-
throw error;
|
|
1874
|
+
if (typeof input === 'object' && input.constructor === Object) {
|
|
1875
|
+
const cleaned = {};
|
|
1876
|
+
for (const [key, value] of Object.entries(input)) {
|
|
1877
|
+
const cleanedValue = clearObject(value);
|
|
1878
|
+
if (cleanedValue !== undefined) {
|
|
1879
|
+
cleaned[key] = cleanedValue;
|
|
1880
|
+
}
|
|
1903
1881
|
}
|
|
1882
|
+
return cleaned;
|
|
1904
1883
|
}
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1884
|
+
return input;
|
|
1885
|
+
}
|
|
1886
|
+
function clearObjectShallow(obj) {
|
|
1887
|
+
if (!obj || typeof obj !== 'object') {
|
|
1888
|
+
return {};
|
|
1910
1889
|
}
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
if (
|
|
1914
|
-
|
|
1915
|
-
}
|
|
1916
|
-
const storedUser = await this.tokenStorage.getUser();
|
|
1917
|
-
if (storedUser) {
|
|
1918
|
-
this.userSubject.next(storedUser);
|
|
1919
|
-
this.authStateSubject.next('authenticated');
|
|
1920
|
-
return storedUser;
|
|
1921
|
-
}
|
|
1922
|
-
const token = await this.tokenStorage.getAccessToken();
|
|
1923
|
-
if (!token) {
|
|
1924
|
-
return null;
|
|
1925
|
-
}
|
|
1926
|
-
const jwtPayload = parseJwt(token);
|
|
1927
|
-
if (isTokenExpired(jwtPayload)) {
|
|
1928
|
-
await this.tokenStorage.clearTokens();
|
|
1929
|
-
return null;
|
|
1890
|
+
const cleaned = {};
|
|
1891
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
1892
|
+
if (value !== null && value !== undefined && value !== '') {
|
|
1893
|
+
cleaned[key] = value;
|
|
1930
1894
|
}
|
|
1931
|
-
const user = this.createUserFromPayload(jwtPayload);
|
|
1932
|
-
await this.tokenStorage.saveUser(user);
|
|
1933
|
-
this.userSubject.next(user);
|
|
1934
|
-
this.authStateSubject.next('authenticated');
|
|
1935
|
-
return user;
|
|
1936
1895
|
}
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1896
|
+
return cleaned;
|
|
1897
|
+
}
|
|
1898
|
+
function isEmpty(value) {
|
|
1899
|
+
return value === null || value === undefined || value === '';
|
|
1900
|
+
}
|
|
1901
|
+
function hasNonEmptyValues(obj) {
|
|
1902
|
+
if (!obj || typeof obj !== 'object') {
|
|
1903
|
+
return false;
|
|
1944
1904
|
}
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1905
|
+
return Object.values(obj).some((value) => !isEmpty(value));
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
/**
|
|
1909
|
+
* Platform detection utilities
|
|
1910
|
+
*/
|
|
1911
|
+
/**
|
|
1912
|
+
* Detect the current platform
|
|
1913
|
+
*/
|
|
1914
|
+
function detectPlatform() {
|
|
1915
|
+
// Check for React Native
|
|
1916
|
+
if (typeof global !== 'undefined' &&
|
|
1917
|
+
global.__DEV__ !== undefined &&
|
|
1918
|
+
typeof global.navigator !== 'undefined' &&
|
|
1919
|
+
global.navigator.product === 'ReactNative') {
|
|
1920
|
+
return {
|
|
1921
|
+
platform: 'react-native',
|
|
1922
|
+
isReactNative: true,
|
|
1923
|
+
isWeb: false,
|
|
1924
|
+
isNode: false,
|
|
1925
|
+
isExpo: checkExpo(),
|
|
1926
|
+
};
|
|
1959
1927
|
}
|
|
1960
|
-
|
|
1928
|
+
// Check for Web/Browser
|
|
1929
|
+
if (typeof window !== 'undefined' &&
|
|
1930
|
+
typeof window.document !== 'undefined' &&
|
|
1931
|
+
typeof window.navigator !== 'undefined') {
|
|
1961
1932
|
return {
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
pid: jwtPayload.pid,
|
|
1968
|
-
expiresAt: jwtPayload.exp * 1000,
|
|
1933
|
+
platform: 'web',
|
|
1934
|
+
isReactNative: false,
|
|
1935
|
+
isWeb: true,
|
|
1936
|
+
isNode: false,
|
|
1937
|
+
isExpo: false,
|
|
1969
1938
|
};
|
|
1970
1939
|
}
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1940
|
+
// Check for Node.js
|
|
1941
|
+
if (typeof process !== 'undefined' && process.versions && process.versions.node) {
|
|
1942
|
+
return {
|
|
1943
|
+
platform: 'node',
|
|
1944
|
+
isReactNative: false,
|
|
1945
|
+
isWeb: false,
|
|
1946
|
+
isNode: true,
|
|
1947
|
+
isExpo: false,
|
|
1948
|
+
};
|
|
1974
1949
|
}
|
|
1950
|
+
// Unknown platform
|
|
1951
|
+
return {
|
|
1952
|
+
platform: 'unknown',
|
|
1953
|
+
isReactNative: false,
|
|
1954
|
+
isWeb: false,
|
|
1955
|
+
isNode: false,
|
|
1956
|
+
isExpo: false,
|
|
1957
|
+
};
|
|
1975
1958
|
}
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1959
|
+
/**
|
|
1960
|
+
* Check if running in Expo
|
|
1961
|
+
*/
|
|
1962
|
+
function checkExpo() {
|
|
1963
|
+
try {
|
|
1964
|
+
return (typeof global !== 'undefined' &&
|
|
1965
|
+
(typeof global.Expo !== 'undefined' || typeof global.expo !== 'undefined'));
|
|
1981
1966
|
}
|
|
1982
|
-
|
|
1983
|
-
return
|
|
1967
|
+
catch {
|
|
1968
|
+
return false;
|
|
1984
1969
|
}
|
|
1985
|
-
|
|
1986
|
-
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
function getDefaultExportFromCjs (x) {
|
|
1973
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
var picocolors_browser = {exports: {}};
|
|
1977
|
+
|
|
1978
|
+
var x=String;
|
|
1979
|
+
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}};
|
|
1980
|
+
picocolors_browser.exports=create();
|
|
1981
|
+
picocolors_browser.exports.createColors = create;
|
|
1982
|
+
|
|
1983
|
+
var picocolors_browserExports = picocolors_browser.exports;
|
|
1984
|
+
var pc = /*@__PURE__*/getDefaultExportFromCjs(picocolors_browserExports);
|
|
1985
|
+
|
|
1986
|
+
const levelColors = {
|
|
1987
|
+
debug: {
|
|
1988
|
+
prefix: pc.gray,
|
|
1989
|
+
message: pc.gray,
|
|
1990
|
+
data: pc.dim,
|
|
1991
|
+
},
|
|
1992
|
+
info: {
|
|
1993
|
+
prefix: pc.cyan,
|
|
1994
|
+
message: pc.white,
|
|
1995
|
+
data: pc.dim,
|
|
1996
|
+
},
|
|
1997
|
+
warn: {
|
|
1998
|
+
prefix: pc.yellow,
|
|
1999
|
+
message: pc.yellow,
|
|
2000
|
+
data: pc.dim,
|
|
2001
|
+
},
|
|
2002
|
+
error: {
|
|
2003
|
+
prefix: pc.red,
|
|
2004
|
+
message: pc.red,
|
|
2005
|
+
data: pc.dim,
|
|
2006
|
+
},
|
|
2007
|
+
};
|
|
2008
|
+
const levelIcons = {
|
|
2009
|
+
debug: '🔍',
|
|
2010
|
+
info: 'ℹ️ ',
|
|
2011
|
+
warn: '⚠️ ',
|
|
2012
|
+
error: '❌',
|
|
2013
|
+
};
|
|
2014
|
+
/**
|
|
2015
|
+
* Format data for pretty display in logs
|
|
2016
|
+
*/
|
|
2017
|
+
function formatData(data, level) {
|
|
2018
|
+
if (data === undefined || data === null) {
|
|
2019
|
+
return String(data);
|
|
1987
2020
|
}
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
2021
|
+
// For primitive types, just return as string
|
|
2022
|
+
if (typeof data !== 'object') {
|
|
2023
|
+
return String(data);
|
|
2024
|
+
}
|
|
2025
|
+
// For Error objects, format nicely
|
|
2026
|
+
if (data instanceof Error) {
|
|
2027
|
+
return `${data.name}: ${data.message}${data.stack ? `\n${pc.dim(data.stack)}` : ''}`;
|
|
2028
|
+
}
|
|
2029
|
+
try {
|
|
2030
|
+
// Apply level-specific coloring
|
|
2031
|
+
const colors = levelColors[level];
|
|
2032
|
+
return colors.data(JSON.stringify(data, null, 2));
|
|
2033
|
+
}
|
|
2034
|
+
catch {
|
|
2035
|
+
// Fallback to JSON.stringify if pretty-format fails
|
|
2036
|
+
try {
|
|
2037
|
+
return JSON.stringify(data, null, 2);
|
|
2038
|
+
}
|
|
2039
|
+
catch {
|
|
2040
|
+
return String(data);
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
class Logger {
|
|
2045
|
+
constructor() {
|
|
2046
|
+
this.enabled = false;
|
|
2047
|
+
}
|
|
2048
|
+
setEnabled(enabled) {
|
|
2049
|
+
this.enabled = enabled;
|
|
2050
|
+
}
|
|
2051
|
+
isEnabled() {
|
|
2052
|
+
return this.enabled;
|
|
2053
|
+
}
|
|
2054
|
+
debug(prefix, message, data) {
|
|
2055
|
+
if (!this.enabled)
|
|
2056
|
+
return;
|
|
2057
|
+
this.log('debug', prefix, message, data);
|
|
2058
|
+
}
|
|
2059
|
+
info(prefix, message, data) {
|
|
2060
|
+
if (!this.enabled)
|
|
2061
|
+
return;
|
|
2062
|
+
this.log('info', prefix, message, data);
|
|
2063
|
+
}
|
|
2064
|
+
warn(prefix, message, data) {
|
|
2065
|
+
this.log('warn', prefix, message, data);
|
|
2066
|
+
}
|
|
2067
|
+
error(prefix, message, data) {
|
|
2068
|
+
this.log('error', prefix, message, data);
|
|
2069
|
+
}
|
|
2070
|
+
log(level, prefix, message, data) {
|
|
2071
|
+
const colors = levelColors[level];
|
|
2072
|
+
const icon = levelIcons[level];
|
|
2073
|
+
const isoTime = new Date().toISOString().split('T')[1] ?? '';
|
|
2074
|
+
const timestamp = pc.dim(isoTime.slice(0, 12));
|
|
2075
|
+
const formattedPrefix = colors.prefix(`[${prefix}]`);
|
|
2076
|
+
const formattedMessage = colors.message(message);
|
|
2077
|
+
const consoleMethod = level === 'debug'
|
|
2078
|
+
? console.debug
|
|
2079
|
+
: level === 'info'
|
|
2080
|
+
? console.info
|
|
2081
|
+
: level === 'warn'
|
|
2082
|
+
? console.warn
|
|
2083
|
+
: console.error;
|
|
2084
|
+
const header = `${timestamp} ${icon} ${formattedPrefix} ${formattedMessage}`;
|
|
2085
|
+
if (data !== undefined) {
|
|
2086
|
+
const formattedData = formatData(data, level);
|
|
2087
|
+
// Check if data is an object (multi-line) or primitive (single-line)
|
|
2088
|
+
const isMultiLine = typeof data === 'object' && data !== null;
|
|
2089
|
+
if (isMultiLine) {
|
|
2090
|
+
consoleMethod(`${header}\n${formattedData}`);
|
|
2091
|
+
}
|
|
2092
|
+
else {
|
|
2093
|
+
consoleMethod(`${header}`, formattedData);
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
else {
|
|
2097
|
+
consoleMethod(header);
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
const logger = new Logger();
|
|
2102
|
+
function createPrefixedLogger(prefix) {
|
|
2103
|
+
return {
|
|
2104
|
+
debug: (message, data) => logger.debug(prefix, message, data),
|
|
2105
|
+
info: (message, data) => logger.info(prefix, message, data),
|
|
2106
|
+
warn: (message, data) => logger.warn(prefix, message, data),
|
|
2107
|
+
error: (message, data) => logger.error(prefix, message, data),
|
|
2108
|
+
};
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
/**
|
|
2112
|
+
* Formats a numeric string value to have exactly the specified number of decimal places.
|
|
2113
|
+
* E.g., "1" → "1.00", "10" → "10.00", "1.5" → "1.50"
|
|
2114
|
+
* Returns undefined for undefined input (preserves optional fields).
|
|
2115
|
+
*
|
|
2116
|
+
* @param value - The string value to format
|
|
2117
|
+
* @param decimals - Number of decimal places (default: 2)
|
|
2118
|
+
* @returns Formatted string or undefined if input is undefined
|
|
2119
|
+
*/
|
|
2120
|
+
function formatDecimal(value, decimals = 2) {
|
|
2121
|
+
if (value === undefined)
|
|
2122
|
+
return undefined;
|
|
2123
|
+
const num = parseFloat(value);
|
|
2124
|
+
if (isNaN(num))
|
|
2125
|
+
return value;
|
|
2126
|
+
return num.toFixed(decimals);
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2129
|
+
const log$g = createPrefixedLogger('AUTH-SERVICE');
|
|
2130
|
+
class AuthenticationService {
|
|
2131
|
+
get user$() {
|
|
2132
|
+
return this.userSubject.asObservable();
|
|
2133
|
+
}
|
|
2134
|
+
get isAuthenticated$() {
|
|
2135
|
+
return this.userSubject.pipe(map((user) => user !== null), distinctUntilChanged(), takeUntil(this.destroy$));
|
|
2136
|
+
}
|
|
2137
|
+
get authState$() {
|
|
2138
|
+
return this.authStateSubject.asObservable();
|
|
2139
|
+
}
|
|
2140
|
+
constructor(httpPort, tokenStorage, config, events = {}) {
|
|
2141
|
+
this.httpPort = httpPort;
|
|
2142
|
+
this.tokenStorage = tokenStorage;
|
|
2143
|
+
this.config = config;
|
|
2144
|
+
this.events = events;
|
|
2145
|
+
this.userSubject = new BehaviorSubject(null);
|
|
2146
|
+
this.authStateSubject = new BehaviorSubject('idle');
|
|
2147
|
+
this.destroy$ = new Subject();
|
|
2148
|
+
}
|
|
2149
|
+
async login(credentials) {
|
|
2150
|
+
this.authStateSubject.next('authenticating');
|
|
2151
|
+
log$g.info('Login attempt', {
|
|
2152
|
+
authUrl: this.config.authUrl,
|
|
2153
|
+
email: credentials.email,
|
|
2154
|
+
});
|
|
2155
|
+
try {
|
|
2156
|
+
const response = await this.httpPort.post(`${this.config.authUrl}/login`, {
|
|
2157
|
+
email: credentials.email,
|
|
2158
|
+
password: credentials.password,
|
|
2159
|
+
});
|
|
2160
|
+
const jwtPayload = parseJwt(response.data.token);
|
|
2161
|
+
const expiresAt = jwtPayload.exp * 1000;
|
|
2162
|
+
log$g.info('Login successful', {
|
|
2163
|
+
authUrl: this.config.authUrl,
|
|
2164
|
+
tokenPrefix: response.data.token.substring(0, 30) + '...',
|
|
2165
|
+
expiresAt: new Date(expiresAt).toISOString(),
|
|
2166
|
+
});
|
|
2167
|
+
await this.tokenStorage.saveAccessToken(response.data.token, expiresAt);
|
|
2168
|
+
const user = this.createUserFromPayload(jwtPayload);
|
|
2169
|
+
await this.tokenStorage.saveUser(user);
|
|
2170
|
+
this.userSubject.next(user);
|
|
2171
|
+
this.authStateSubject.next('authenticated');
|
|
2172
|
+
this.events.onUserChanged?.(user);
|
|
2173
|
+
return user;
|
|
2174
|
+
}
|
|
2175
|
+
catch (error) {
|
|
2176
|
+
this.authStateSubject.next('error');
|
|
2177
|
+
throw error;
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
async logout() {
|
|
2181
|
+
await this.tokenStorage.clearTokens();
|
|
2182
|
+
this.userSubject.next(null);
|
|
2183
|
+
this.authStateSubject.next('idle');
|
|
2184
|
+
this.events.onUserChanged?.(null);
|
|
2185
|
+
}
|
|
2186
|
+
async getCurrentUser() {
|
|
2187
|
+
// Always verify token is valid before returning user
|
|
2188
|
+
const token = await this.tokenStorage.getAccessToken();
|
|
2189
|
+
if (!token) {
|
|
2190
|
+
// No token - clear any stale user state
|
|
2191
|
+
log$g.debug('getCurrentUser: No token in storage');
|
|
2192
|
+
if (this.userSubject.value) {
|
|
2193
|
+
this.userSubject.next(null);
|
|
2194
|
+
this.authStateSubject.next('idle');
|
|
2195
|
+
}
|
|
2196
|
+
return null;
|
|
2197
|
+
}
|
|
2198
|
+
log$g.debug('getCurrentUser: Token found', {
|
|
2199
|
+
tokenPrefix: token.substring(0, 30) + '...',
|
|
2200
|
+
tokenLength: token.length,
|
|
2201
|
+
});
|
|
2202
|
+
const jwtPayload = parseJwt(token);
|
|
2203
|
+
if (isTokenExpired(jwtPayload)) {
|
|
2204
|
+
// Token expired - clear everything
|
|
2205
|
+
log$g.warn('getCurrentUser: Token expired');
|
|
2206
|
+
await this.tokenStorage.clearTokens();
|
|
2207
|
+
this.userSubject.next(null);
|
|
2208
|
+
this.authStateSubject.next('idle');
|
|
2209
|
+
this.events.onUserChanged?.(null);
|
|
2210
|
+
return null;
|
|
2211
|
+
}
|
|
2212
|
+
// Token is valid - return cached user if available
|
|
2213
|
+
const currentUser = this.userSubject.value;
|
|
2214
|
+
if (currentUser) {
|
|
2215
|
+
log$g.debug('getCurrentUser: Returning cached user', {
|
|
2216
|
+
email: currentUser.email,
|
|
2217
|
+
roles: currentUser.roles,
|
|
2218
|
+
});
|
|
2219
|
+
return currentUser;
|
|
2220
|
+
}
|
|
2221
|
+
// Check stored user
|
|
2222
|
+
const storedUser = await this.tokenStorage.getUser();
|
|
2223
|
+
if (storedUser) {
|
|
2224
|
+
this.userSubject.next(storedUser);
|
|
2225
|
+
this.authStateSubject.next('authenticated');
|
|
2226
|
+
return storedUser;
|
|
2227
|
+
}
|
|
2228
|
+
// Create user from token
|
|
2229
|
+
const user = this.createUserFromPayload(jwtPayload);
|
|
2230
|
+
await this.tokenStorage.saveUser(user);
|
|
2231
|
+
this.userSubject.next(user);
|
|
2232
|
+
this.authStateSubject.next('authenticated');
|
|
2233
|
+
return user;
|
|
2234
|
+
}
|
|
2235
|
+
async isAuthenticated() {
|
|
2236
|
+
const token = await this.tokenStorage.getAccessToken();
|
|
2237
|
+
if (!token) {
|
|
2238
|
+
log$g.debug('isAuthenticated: No token in storage');
|
|
2239
|
+
return false;
|
|
2240
|
+
}
|
|
2241
|
+
const jwtPayload = parseJwt(token);
|
|
2242
|
+
const expired = isTokenExpired(jwtPayload);
|
|
2243
|
+
log$g.debug('isAuthenticated: Token check', {
|
|
2244
|
+
hasToken: true,
|
|
2245
|
+
expired,
|
|
2246
|
+
expiresAt: new Date(jwtPayload.exp * 1000).toISOString(),
|
|
2247
|
+
});
|
|
2248
|
+
return !expired;
|
|
2249
|
+
}
|
|
2250
|
+
async getAccessToken() {
|
|
2251
|
+
const token = await this.tokenStorage.getAccessToken();
|
|
2252
|
+
if (!token) {
|
|
2253
|
+
return null;
|
|
2254
|
+
}
|
|
2255
|
+
const jwtPayload = parseJwt(token);
|
|
2256
|
+
if (isTokenExpired(jwtPayload)) {
|
|
2257
|
+
await this.tokenStorage.clearTokens();
|
|
2258
|
+
this.userSubject.next(null);
|
|
2259
|
+
this.authStateSubject.next('idle');
|
|
2260
|
+
this.events.onUserChanged?.(null);
|
|
2261
|
+
return null;
|
|
2262
|
+
}
|
|
2263
|
+
return token;
|
|
2264
|
+
}
|
|
2265
|
+
createUserFromPayload(jwtPayload) {
|
|
2266
|
+
return {
|
|
2267
|
+
id: jwtPayload.uid.toString(),
|
|
2268
|
+
email: jwtPayload.username,
|
|
2269
|
+
username: jwtPayload.username,
|
|
2270
|
+
roles: jwtPayload.roles,
|
|
2271
|
+
fid: jwtPayload.fid,
|
|
2272
|
+
pid: jwtPayload.pid,
|
|
2273
|
+
expiresAt: jwtPayload.exp * 1000,
|
|
2274
|
+
};
|
|
2275
|
+
}
|
|
2276
|
+
destroy() {
|
|
2277
|
+
this.destroy$.next();
|
|
2278
|
+
this.destroy$.complete();
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
|
|
2282
|
+
const CERTIFICATE_KEY = 'acube_certificate';
|
|
2283
|
+
class CertificateService {
|
|
2284
|
+
get certificate$() {
|
|
2285
|
+
return this.certificateSubject.asObservable();
|
|
2286
|
+
}
|
|
2287
|
+
get hasCertificate$() {
|
|
2288
|
+
return this.certificateSubject.pipe(map((cert) => cert !== null), distinctUntilChanged());
|
|
2289
|
+
}
|
|
2290
|
+
get state$() {
|
|
2291
|
+
return this.stateSubject.asObservable();
|
|
2292
|
+
}
|
|
2293
|
+
constructor(secureStorage) {
|
|
2294
|
+
this.secureStorage = secureStorage;
|
|
2295
|
+
this.certificateSubject = new BehaviorSubject(null);
|
|
2296
|
+
this.stateSubject = new BehaviorSubject('idle');
|
|
2297
|
+
this.destroy$ = new Subject();
|
|
1993
2298
|
}
|
|
1994
2299
|
async hasCertificate() {
|
|
1995
2300
|
const cert = await this.getCertificate();
|
|
@@ -2080,409 +2385,680 @@ function hasAnyRole(userRoles, required) {
|
|
|
2080
2385
|
return Object.values(userRoles).some((roles) => required.some((role) => roles.includes(role)));
|
|
2081
2386
|
}
|
|
2082
2387
|
|
|
2083
|
-
class
|
|
2084
|
-
constructor(
|
|
2085
|
-
this.
|
|
2086
|
-
this.mtlsHandler = mtlsHandler;
|
|
2087
|
-
this.userProvider = userProvider;
|
|
2088
|
-
this.mtlsAdapter = mtlsAdapter;
|
|
2388
|
+
class ConfigManager {
|
|
2389
|
+
constructor(userConfig) {
|
|
2390
|
+
this.config = this.buildConfig(userConfig);
|
|
2089
2391
|
}
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
}
|
|
2100
|
-
if (userRole === 'CASHIER') {
|
|
2101
|
-
if (!isReceiptEndpoint) {
|
|
2102
|
-
return { mode: 'jwt', usePort444: false };
|
|
2103
|
-
}
|
|
2104
|
-
if (platform === 'mobile') {
|
|
2105
|
-
return { mode: 'mtls', usePort444: true };
|
|
2106
|
-
}
|
|
2107
|
-
return { mode: 'jwt', usePort444: true };
|
|
2108
|
-
}
|
|
2109
|
-
if (userRole === 'MERCHANT') {
|
|
2110
|
-
if (!isReceiptEndpoint) {
|
|
2111
|
-
return { mode: 'jwt', usePort444: false };
|
|
2112
|
-
}
|
|
2113
|
-
if (this.isReturnableItemsEndpoint(url)) {
|
|
2114
|
-
return { mode: 'mtls', usePort444: true };
|
|
2115
|
-
}
|
|
2116
|
-
if (method === 'GET') {
|
|
2117
|
-
if (this.isDetailedReceiptEndpoint(url)) {
|
|
2118
|
-
if (platform === 'mobile') {
|
|
2119
|
-
return { mode: 'mtls', usePort444: true };
|
|
2120
|
-
}
|
|
2121
|
-
return { mode: 'jwt', usePort444: true };
|
|
2122
|
-
}
|
|
2123
|
-
return { mode: 'jwt', usePort444: false };
|
|
2124
|
-
}
|
|
2125
|
-
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {
|
|
2126
|
-
if (platform === 'mobile') {
|
|
2127
|
-
return { mode: 'mtls', usePort444: true };
|
|
2128
|
-
}
|
|
2129
|
-
return { mode: 'jwt', usePort444: true };
|
|
2130
|
-
}
|
|
2131
|
-
return { mode: 'jwt', usePort444: false };
|
|
2132
|
-
}
|
|
2133
|
-
if (explicitMode) {
|
|
2134
|
-
if (userRole === 'SUPPLIER' && explicitMode === 'mtls') {
|
|
2135
|
-
return { mode: 'jwt', usePort444: false };
|
|
2136
|
-
}
|
|
2137
|
-
return {
|
|
2138
|
-
mode: explicitMode,
|
|
2139
|
-
usePort444: explicitMode === 'mtls' || (platform === 'web' && isReceiptEndpoint),
|
|
2140
|
-
};
|
|
2141
|
-
}
|
|
2142
|
-
if (platform === 'web') {
|
|
2143
|
-
return { mode: 'jwt', usePort444: isReceiptEndpoint };
|
|
2144
|
-
}
|
|
2145
|
-
if (isReceiptEndpoint && platform === 'mobile') {
|
|
2146
|
-
return { mode: 'mtls', usePort444: true };
|
|
2147
|
-
}
|
|
2148
|
-
return { mode: 'jwt', usePort444: false };
|
|
2149
|
-
}
|
|
2150
|
-
async getAuthHeaders() {
|
|
2151
|
-
return this.jwtHandler.getAuthHeaders();
|
|
2152
|
-
}
|
|
2153
|
-
getMtlsHandler() {
|
|
2154
|
-
return this.mtlsHandler;
|
|
2155
|
-
}
|
|
2156
|
-
getJwtHandler() {
|
|
2157
|
-
return this.jwtHandler;
|
|
2392
|
+
buildConfig(userConfig) {
|
|
2393
|
+
return {
|
|
2394
|
+
environment: userConfig.environment,
|
|
2395
|
+
apiUrl: this.getDefaultApiUrl(userConfig.environment),
|
|
2396
|
+
authUrl: this.getDefaultAuthUrl(userConfig.environment),
|
|
2397
|
+
timeout: 30000,
|
|
2398
|
+
retryAttempts: 3,
|
|
2399
|
+
debug: userConfig.debug ?? false,
|
|
2400
|
+
customHeaders: {},
|
|
2401
|
+
};
|
|
2158
2402
|
}
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2403
|
+
getDefaultApiUrl(environment) {
|
|
2404
|
+
switch (environment) {
|
|
2405
|
+
case 'production':
|
|
2406
|
+
return 'https://ereceipts-it.acubeapi.com';
|
|
2407
|
+
case 'development':
|
|
2408
|
+
return 'https://ereceipts-it.dev.acubeapi.com';
|
|
2409
|
+
case 'sandbox':
|
|
2410
|
+
default:
|
|
2411
|
+
return 'https://ereceipts-it-sandbox.acubeapi.com';
|
|
2162
2412
|
}
|
|
2163
|
-
const platformInfo = this.mtlsAdapter.getPlatformInfo();
|
|
2164
|
-
return platformInfo.platform === 'web' ? 'web' : 'mobile';
|
|
2165
2413
|
}
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
if (hasRole(user.roles, 'ROLE_SUPPLIER')) {
|
|
2175
|
-
return 'SUPPLIER';
|
|
2176
|
-
}
|
|
2177
|
-
if (hasRole(user.roles, 'ROLE_MERCHANT')) {
|
|
2178
|
-
return 'MERCHANT';
|
|
2179
|
-
}
|
|
2180
|
-
if (hasRole(user.roles, 'ROLE_CASHIER')) {
|
|
2181
|
-
return 'CASHIER';
|
|
2414
|
+
getDefaultAuthUrl(environment) {
|
|
2415
|
+
switch (environment) {
|
|
2416
|
+
case 'production':
|
|
2417
|
+
return 'https://common.api.acubeapi.com';
|
|
2418
|
+
case 'development':
|
|
2419
|
+
case 'sandbox':
|
|
2420
|
+
default:
|
|
2421
|
+
return 'https://common-sandbox.api.acubeapi.com';
|
|
2182
2422
|
}
|
|
2183
|
-
return null;
|
|
2184
2423
|
}
|
|
2185
|
-
|
|
2186
|
-
return
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
url.match(/\/mf1\/receipts\/[a-f0-9-]+\/returnable-items$/));
|
|
2191
|
-
}
|
|
2192
|
-
isDetailedReceiptEndpoint(url) {
|
|
2193
|
-
return !!(url.match(/\/receipts\/[a-f0-9-]+\/details$/) ||
|
|
2194
|
-
url.match(/\/mf1\/receipts\/[a-f0-9-]+\/details$/));
|
|
2424
|
+
getConfig() {
|
|
2425
|
+
return {
|
|
2426
|
+
environment: this.config.environment,
|
|
2427
|
+
debug: this.config.debug,
|
|
2428
|
+
};
|
|
2195
2429
|
}
|
|
2196
|
-
|
|
2197
|
-
return
|
|
2430
|
+
getApiUrl() {
|
|
2431
|
+
return this.config.apiUrl;
|
|
2198
2432
|
}
|
|
2199
|
-
|
|
2200
|
-
return
|
|
2433
|
+
getAuthUrl() {
|
|
2434
|
+
return this.config.authUrl;
|
|
2201
2435
|
}
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
class JwtAuthHandler {
|
|
2205
|
-
constructor(tokenStorage) {
|
|
2206
|
-
this.tokenStorage = tokenStorage;
|
|
2436
|
+
getEnvironment() {
|
|
2437
|
+
return this.config.environment;
|
|
2207
2438
|
}
|
|
2208
|
-
|
|
2209
|
-
return
|
|
2439
|
+
isDebugEnabled() {
|
|
2440
|
+
return this.config.debug;
|
|
2210
2441
|
}
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
if (!token) {
|
|
2214
|
-
return {};
|
|
2215
|
-
}
|
|
2216
|
-
return { Authorization: `Bearer ${token}` };
|
|
2442
|
+
getTimeout() {
|
|
2443
|
+
return this.config.timeout;
|
|
2217
2444
|
}
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
function clearObject(input) {
|
|
2221
|
-
if (input === null || input === undefined || input === '') {
|
|
2222
|
-
return undefined;
|
|
2445
|
+
getRetryAttempts() {
|
|
2446
|
+
return this.config.retryAttempts;
|
|
2223
2447
|
}
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
.map((item) => clearObject(item))
|
|
2227
|
-
.filter((item) => item !== undefined);
|
|
2228
|
-
return cleanedArray;
|
|
2448
|
+
getCustomHeaders() {
|
|
2449
|
+
return { ...this.config.customHeaders };
|
|
2229
2450
|
}
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
cleaned[key] = cleanedValue;
|
|
2236
|
-
}
|
|
2451
|
+
updateConfig(updates) {
|
|
2452
|
+
if (updates.environment) {
|
|
2453
|
+
this.config.environment = updates.environment;
|
|
2454
|
+
this.config.apiUrl = this.getDefaultApiUrl(updates.environment);
|
|
2455
|
+
this.config.authUrl = this.getDefaultAuthUrl(updates.environment);
|
|
2237
2456
|
}
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
return input;
|
|
2241
|
-
}
|
|
2242
|
-
function clearObjectShallow(obj) {
|
|
2243
|
-
if (!obj || typeof obj !== 'object') {
|
|
2244
|
-
return {};
|
|
2245
|
-
}
|
|
2246
|
-
const cleaned = {};
|
|
2247
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
2248
|
-
if (value !== null && value !== undefined && value !== '') {
|
|
2249
|
-
cleaned[key] = value;
|
|
2457
|
+
if (updates.debug !== undefined) {
|
|
2458
|
+
this.config.debug = updates.debug;
|
|
2250
2459
|
}
|
|
2251
2460
|
}
|
|
2252
|
-
return cleaned;
|
|
2253
|
-
}
|
|
2254
|
-
function isEmpty(value) {
|
|
2255
|
-
return value === null || value === undefined || value === '';
|
|
2256
|
-
}
|
|
2257
|
-
function hasNonEmptyValues(obj) {
|
|
2258
|
-
if (!obj || typeof obj !== 'object') {
|
|
2259
|
-
return false;
|
|
2260
|
-
}
|
|
2261
|
-
return Object.values(obj).some((value) => !isEmpty(value));
|
|
2262
2461
|
}
|
|
2263
2462
|
|
|
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
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2463
|
+
// Enum options arrays
|
|
2464
|
+
const VAT_RATE_CODE_OPTIONS = [
|
|
2465
|
+
'4.00',
|
|
2466
|
+
'5.00',
|
|
2467
|
+
'10.00',
|
|
2468
|
+
'22.00',
|
|
2469
|
+
'2.00',
|
|
2470
|
+
'6.40',
|
|
2471
|
+
'7.00',
|
|
2472
|
+
'7.30',
|
|
2473
|
+
'7.50',
|
|
2474
|
+
'7.65',
|
|
2475
|
+
'7.95',
|
|
2476
|
+
'8.30',
|
|
2477
|
+
'8.50',
|
|
2478
|
+
'8.80',
|
|
2479
|
+
'9.50',
|
|
2480
|
+
'12.30',
|
|
2481
|
+
'N1',
|
|
2482
|
+
'N2',
|
|
2483
|
+
'N3',
|
|
2484
|
+
'N4',
|
|
2485
|
+
'N5',
|
|
2486
|
+
'N6',
|
|
2487
|
+
];
|
|
2488
|
+
const GOOD_OR_SERVICE_OPTIONS = ['goods', 'service'];
|
|
2489
|
+
const RECEIPT_PROOF_TYPE_OPTIONS = ['POS', 'VR', 'ND'];
|
|
2490
|
+
// Enum types for receipt validation
|
|
2491
|
+
const VatRateCodeSchema = z__namespace.enum(VAT_RATE_CODE_OPTIONS);
|
|
2492
|
+
const GoodOrServiceSchema = z__namespace.enum(GOOD_OR_SERVICE_OPTIONS);
|
|
2493
|
+
const ReceiptProofTypeSchema = z__namespace.enum(RECEIPT_PROOF_TYPE_OPTIONS);
|
|
2494
|
+
// Receipt Item Schema
|
|
2495
|
+
const ReceiptItemSchema = z__namespace.object({
|
|
2496
|
+
type: GoodOrServiceSchema.optional(),
|
|
2497
|
+
quantity: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2498
|
+
description: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2499
|
+
unit_price: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2500
|
+
vat_rate_code: VatRateCodeSchema.optional(),
|
|
2501
|
+
simplified_vat_allocation: z__namespace.boolean().optional(),
|
|
2502
|
+
discount: z__namespace.string().nullable().optional(),
|
|
2503
|
+
is_down_payment_or_voucher_redemption: z__namespace.boolean().optional(),
|
|
2504
|
+
complimentary: z__namespace.boolean().optional(),
|
|
2505
|
+
});
|
|
2506
|
+
// Main Receipt Input Schema
|
|
2507
|
+
const ReceiptInputSchema = z__namespace
|
|
2508
|
+
.object({
|
|
2509
|
+
items: z__namespace.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
|
|
2510
|
+
customer_tax_code: z__namespace.string().optional(),
|
|
2511
|
+
customer_lottery_code: z__namespace.string().optional(),
|
|
2512
|
+
discount: z__namespace.string().nullable().optional(),
|
|
2513
|
+
invoice_issuing: z__namespace.boolean().optional(),
|
|
2514
|
+
uncollected_dcr_to_ssn: z__namespace.boolean().optional(),
|
|
2515
|
+
services_uncollected_amount: z__namespace.string().nullable().optional(),
|
|
2516
|
+
goods_uncollected_amount: z__namespace.string().nullable().optional(),
|
|
2517
|
+
cash_payment_amount: z__namespace.string().nullable().optional(),
|
|
2518
|
+
electronic_payment_amount: z__namespace.string().nullable().optional(),
|
|
2519
|
+
ticket_restaurant_payment_amount: z__namespace.string().nullable().optional(),
|
|
2520
|
+
ticket_restaurant_quantity: z__namespace.number().optional(),
|
|
2521
|
+
})
|
|
2522
|
+
.refine((data) => {
|
|
2523
|
+
// At least one payment method should be provided
|
|
2524
|
+
const hasCashPayment = data.cash_payment_amount && parseFloat(data.cash_payment_amount) > 0;
|
|
2525
|
+
const hasElectronicPayment = data.electronic_payment_amount && parseFloat(data.electronic_payment_amount) > 0;
|
|
2526
|
+
const hasTicketPayment = data.ticket_restaurant_payment_amount &&
|
|
2527
|
+
parseFloat(data.ticket_restaurant_payment_amount) > 0;
|
|
2528
|
+
return hasCashPayment || hasElectronicPayment || hasTicketPayment;
|
|
2529
|
+
}, {
|
|
2530
|
+
error: 'At least one payment method is required',
|
|
2531
|
+
path: ['payment_methods'],
|
|
2532
|
+
})
|
|
2533
|
+
.refine((data) => {
|
|
2534
|
+
// only one between customer_tax_code and customer_lottery_code can be provided
|
|
2535
|
+
return !data.customer_tax_code || !data.customer_lottery_code;
|
|
2536
|
+
}, {
|
|
2537
|
+
error: 'Only one between customer_tax_code and customer_lottery_code can be provided',
|
|
2538
|
+
path: ['customer_tax_code', 'customer_lottery_code'],
|
|
2539
|
+
});
|
|
2540
|
+
// Receipt Return or Void via PEM Schema
|
|
2541
|
+
const ReceiptReturnOrVoidViaPEMInputSchema = z__namespace.object({
|
|
2542
|
+
device_id: z__namespace.string().optional(),
|
|
2543
|
+
items: z__namespace.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
|
|
2544
|
+
document_number: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2545
|
+
document_datetime: z__namespace.string().optional(),
|
|
2546
|
+
lottery_code: z__namespace.string().optional(),
|
|
2547
|
+
});
|
|
2548
|
+
// Receipt Return or Void with Proof Schema
|
|
2549
|
+
const ReceiptReturnOrVoidWithProofInputSchema = z__namespace.object({
|
|
2550
|
+
items: z__namespace.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
|
|
2551
|
+
proof: ReceiptProofTypeSchema,
|
|
2552
|
+
document_datetime: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2553
|
+
});
|
|
2554
|
+
// Void Receipt Schema
|
|
2555
|
+
const VoidReceiptInputSchema = z__namespace.object({
|
|
2556
|
+
document_number: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2557
|
+
});
|
|
2558
|
+
const ReceiptReturnItemSchema = z__namespace
|
|
2559
|
+
.array(z__namespace.object({
|
|
2560
|
+
id: z__namespace.number(),
|
|
2561
|
+
quantity: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2562
|
+
}))
|
|
2563
|
+
.min(1, { error: 'arrayMin1' });
|
|
2564
|
+
// Receipt Return Schema
|
|
2565
|
+
const ReceiptReturnInputSchema = z__namespace.object({
|
|
2566
|
+
items: z__namespace.array(ReceiptReturnItemSchema).min(1, { error: 'arrayMin1' }),
|
|
2567
|
+
document_number: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2568
|
+
});
|
|
2569
|
+
|
|
2570
|
+
// Cashier Create Input Schema (MF1)
|
|
2571
|
+
const CashierCreateInputSchema = z__namespace.object({
|
|
2572
|
+
email: z__namespace
|
|
2573
|
+
.string()
|
|
2574
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2575
|
+
.max(255, { error: 'emailMaxLength' })
|
|
2576
|
+
.email({ error: 'invalidEmail' }),
|
|
2577
|
+
password: z__namespace
|
|
2578
|
+
.string()
|
|
2579
|
+
.min(8, { error: 'passwordMinLength' })
|
|
2580
|
+
.max(40, { error: 'passwordMaxLength' }),
|
|
2581
|
+
name: z__namespace.string().min(1, { error: 'fieldIsRequired' }).max(255, { error: 'nameMaxLength' }),
|
|
2582
|
+
display_name: z__namespace
|
|
2583
|
+
.string()
|
|
2584
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2585
|
+
.max(255, { error: 'displayNameMaxLength' }),
|
|
2586
|
+
});
|
|
2587
|
+
|
|
2588
|
+
// Enum options arrays
|
|
2589
|
+
const PEM_STATUS_OPTIONS = [
|
|
2590
|
+
'NEW',
|
|
2591
|
+
'REGISTERED',
|
|
2592
|
+
'ACTIVATED',
|
|
2593
|
+
'ONLINE',
|
|
2594
|
+
'OFFLINE',
|
|
2595
|
+
'DISCARDED',
|
|
2596
|
+
];
|
|
2597
|
+
// Address Schema (reusable)
|
|
2598
|
+
const AddressSchema = z__namespace.object({
|
|
2599
|
+
street_address: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2600
|
+
street_number: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2601
|
+
zip_code: z__namespace
|
|
2602
|
+
.string()
|
|
2603
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2604
|
+
.regex(/^\d{5}$/, { error: 'invalidZipCode' }),
|
|
2605
|
+
city: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2606
|
+
province: z__namespace
|
|
2607
|
+
.string()
|
|
2608
|
+
.min(2, { error: 'provinceMinLength' })
|
|
2609
|
+
.max(2, { error: 'provinceMaxLength' })
|
|
2610
|
+
.toUpperCase(),
|
|
2611
|
+
});
|
|
2612
|
+
// PEM Status Schema
|
|
2613
|
+
const PEMStatusSchema = z__namespace.enum(PEM_STATUS_OPTIONS);
|
|
2614
|
+
// Activation Request Schema
|
|
2615
|
+
const ActivationRequestSchema = z__namespace.object({
|
|
2616
|
+
registration_key: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2617
|
+
});
|
|
2618
|
+
// PEM Status Offline Request Schema
|
|
2619
|
+
const PEMStatusOfflineRequestSchema = z__namespace.object({
|
|
2620
|
+
timestamp: z__namespace
|
|
2621
|
+
.string()
|
|
2622
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2623
|
+
.refine((val) => !isNaN(Date.parse(val)), {
|
|
2624
|
+
error: 'invalidDateFormat',
|
|
2625
|
+
}),
|
|
2626
|
+
reason: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2627
|
+
});
|
|
2628
|
+
|
|
2629
|
+
// Cash Register Create Schema
|
|
2630
|
+
const CashRegisterCreateSchema = z__namespace.object({
|
|
2631
|
+
pem_serial_number: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2632
|
+
name: z__namespace.string().min(1, { error: 'fieldIsRequired' }).max(100, { error: 'nameMaxLength' }),
|
|
2633
|
+
});
|
|
2634
|
+
|
|
2635
|
+
// VAT number validation regex (Partita IVA - 11 digits)
|
|
2636
|
+
const VAT_NUMBER_REGEX = /^\d{11}$/;
|
|
2637
|
+
// Fiscal code validation regex (Codice Fiscale - 11 digits only for merchants)
|
|
2638
|
+
const FISCAL_CODE_REGEX = /^\d{11}$/;
|
|
2639
|
+
// Password validation regex (from OpenAPI spec)
|
|
2640
|
+
const PASSWORD_REGEX = /^((?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{10,}).*)$/;
|
|
2641
|
+
// Merchant Create Input Schema
|
|
2642
|
+
const MerchantCreateInputSchema = z__namespace
|
|
2643
|
+
.object({
|
|
2644
|
+
vat_number: z__namespace
|
|
2645
|
+
.string()
|
|
2646
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2647
|
+
.regex(VAT_NUMBER_REGEX, { error: 'invalidVatNumber' }),
|
|
2648
|
+
fiscal_code: z__namespace.string().regex(FISCAL_CODE_REGEX, { error: 'invalidFiscalCode' }).optional(),
|
|
2649
|
+
business_name: z__namespace.string().max(200, { error: 'businessNameMaxLength' }).optional().nullable(),
|
|
2650
|
+
first_name: z__namespace.string().max(100, { error: 'firstNameMaxLength' }).optional().nullable(),
|
|
2651
|
+
last_name: z__namespace.string().max(100, { error: 'lastNameMaxLength' }).optional().nullable(),
|
|
2652
|
+
email: z__namespace.string().min(1, { error: 'fieldIsRequired' }).email({ error: 'invalidEmail' }),
|
|
2653
|
+
password: z__namespace
|
|
2654
|
+
.string()
|
|
2655
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2656
|
+
.regex(PASSWORD_REGEX, { error: 'passwordComplexity' }),
|
|
2657
|
+
address: AddressSchema.optional(),
|
|
2658
|
+
})
|
|
2659
|
+
.refine((data) => {
|
|
2660
|
+
const hasBusinessName = data.business_name && data.business_name.trim() !== '';
|
|
2661
|
+
const hasPersonalNames = (data.first_name && data.first_name.trim() !== '') ||
|
|
2662
|
+
(data.last_name && data.last_name.trim() !== '');
|
|
2663
|
+
// If business name is set, first/last name must not be provided
|
|
2664
|
+
if (hasBusinessName && hasPersonalNames) {
|
|
2665
|
+
return false;
|
|
2322
2666
|
}
|
|
2323
|
-
|
|
2667
|
+
// At least one naming method must be provided
|
|
2668
|
+
if (!hasBusinessName && !hasPersonalNames) {
|
|
2324
2669
|
return false;
|
|
2325
2670
|
}
|
|
2326
|
-
|
|
2671
|
+
return true;
|
|
2672
|
+
}, {
|
|
2673
|
+
error: 'businessNameOrPersonalNamesRequired',
|
|
2674
|
+
path: ['business_name'],
|
|
2675
|
+
});
|
|
2676
|
+
// Merchant Update Input Schema
|
|
2677
|
+
const MerchantUpdateInputSchema = z__namespace.object({
|
|
2678
|
+
business_name: z__namespace.string().max(200, { error: 'businessNameMaxLength' }).optional().nullable(),
|
|
2679
|
+
first_name: z__namespace.string().max(100, { error: 'firstNameMaxLength' }).optional().nullable(),
|
|
2680
|
+
last_name: z__namespace.string().max(100, { error: 'lastNameMaxLength' }).optional().nullable(),
|
|
2681
|
+
address: AddressSchema.optional().nullable(),
|
|
2682
|
+
});
|
|
2683
|
+
|
|
2684
|
+
// Enum options arrays
|
|
2685
|
+
const PEM_TYPE_OPTIONS = ['AP', 'SP', 'TM', 'PV'];
|
|
2686
|
+
// PEM Data Schema
|
|
2687
|
+
const PemDataSchema = z__namespace.object({
|
|
2688
|
+
version: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2689
|
+
type: z__namespace.enum(PEM_TYPE_OPTIONS, {
|
|
2690
|
+
error: 'invalidPemType',
|
|
2691
|
+
}),
|
|
2692
|
+
});
|
|
2693
|
+
// PEM Create Input Schema
|
|
2694
|
+
const PemCreateInputSchema = z__namespace.object({
|
|
2695
|
+
merchant_uuid: z__namespace.string().min(1, { error: 'fieldIsRequired' }).uuid({ error: 'invalidUuid' }),
|
|
2696
|
+
address: AddressSchema.optional(),
|
|
2697
|
+
/* external_pem_data: PemDataSchema.optional(), */
|
|
2698
|
+
});
|
|
2699
|
+
|
|
2700
|
+
// Italian Fiscal ID validation regex (Codice Fiscale for individuals or Partita IVA for companies)
|
|
2701
|
+
const FISCAL_ID_REGEX = /^([A-Z]{6}[0-9LMNPQRSTUV]{2}[ABCDEHLMPRST][0-9LMNPQRSTUV]{2}[A-Z][0-9LMNPQRSTUV]{3}[A-Z]|[0-9]{11})$/;
|
|
2702
|
+
// Supplier Create Input Schema
|
|
2703
|
+
const SupplierCreateInputSchema = z__namespace.object({
|
|
2704
|
+
fiscal_id: z__namespace
|
|
2705
|
+
.string()
|
|
2706
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2707
|
+
.regex(FISCAL_ID_REGEX, { error: 'invalidFiscalId' })
|
|
2708
|
+
.toUpperCase(),
|
|
2709
|
+
name: z__namespace.string().min(1, { error: 'fieldIsRequired' }).max(200, { error: 'nameMaxLength' }),
|
|
2710
|
+
address: AddressSchema.optional(),
|
|
2711
|
+
});
|
|
2712
|
+
// Supplier Update Input Schema
|
|
2713
|
+
const SupplierUpdateInputSchema = z__namespace.object({
|
|
2714
|
+
name: z__namespace.string().min(1, { error: 'fieldIsRequired' }).max(200, { error: 'nameMaxLength' }),
|
|
2715
|
+
address: AddressSchema.optional(),
|
|
2716
|
+
});
|
|
2717
|
+
|
|
2718
|
+
// Journal Close Input Schema
|
|
2719
|
+
const JournalCloseInputSchema = z__namespace.object({
|
|
2720
|
+
closing_timestamp: z__namespace
|
|
2721
|
+
.string()
|
|
2722
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2723
|
+
.refine((val) => !isNaN(Date.parse(val)), {
|
|
2724
|
+
error: 'invalidDateFormat',
|
|
2725
|
+
}),
|
|
2726
|
+
reason: z__namespace.string().max(255, { error: 'reasonMaxLength' }).optional(),
|
|
2727
|
+
});
|
|
2728
|
+
|
|
2729
|
+
// Daily Report Status Options
|
|
2730
|
+
const DAILY_REPORT_STATUS_OPTIONS = ['pending', 'sent', 'error'];
|
|
2731
|
+
// Daily Report Status Schema
|
|
2732
|
+
const DailyReportStatusSchema = z__namespace.enum(DAILY_REPORT_STATUS_OPTIONS, {
|
|
2733
|
+
error: 'invalidDailyReportStatus',
|
|
2734
|
+
});
|
|
2735
|
+
// Daily Reports List Parameters Schema
|
|
2736
|
+
const DailyReportsParamsSchema = z__namespace.object({
|
|
2737
|
+
pem_serial_number: z__namespace.string().min(1, { error: 'fieldIsRequired' }).optional(),
|
|
2738
|
+
date_from: z__namespace
|
|
2739
|
+
.string()
|
|
2740
|
+
.refine((val) => !isNaN(Date.parse(val)), {
|
|
2741
|
+
error: 'invalidDateFormat',
|
|
2742
|
+
})
|
|
2743
|
+
.optional(),
|
|
2744
|
+
date_to: z__namespace
|
|
2745
|
+
.string()
|
|
2746
|
+
.refine((val) => !isNaN(Date.parse(val)), {
|
|
2747
|
+
error: 'invalidDateFormat',
|
|
2748
|
+
})
|
|
2749
|
+
.optional(),
|
|
2750
|
+
status: DailyReportStatusSchema.optional(),
|
|
2751
|
+
page: z__namespace.number().min(1, { error: 'pageMinValue' }).optional(),
|
|
2752
|
+
});
|
|
2753
|
+
|
|
2754
|
+
const NotificationScopeSchema = z__namespace.object({
|
|
2755
|
+
type: z__namespace.literal('global'),
|
|
2756
|
+
});
|
|
2757
|
+
const PemStatusSchema = z__namespace.enum(['ONLINE', 'OFFLINE']);
|
|
2758
|
+
const NotificationDataBlockAtSchema = z__namespace.object({
|
|
2759
|
+
block_at: z__namespace.string(),
|
|
2760
|
+
});
|
|
2761
|
+
const NotificationDataPemStatusSchema = z__namespace.object({
|
|
2762
|
+
from: PemStatusSchema,
|
|
2763
|
+
to: PemStatusSchema,
|
|
2764
|
+
});
|
|
2765
|
+
const NotificationBaseSchema = z__namespace.object({
|
|
2766
|
+
uuid: z__namespace.string().uuid({ error: 'invalidUuid' }),
|
|
2767
|
+
scope: NotificationScopeSchema,
|
|
2768
|
+
source: z__namespace.enum(['system', 'Italian Tax Authority']),
|
|
2769
|
+
level: z__namespace.enum(['info', 'warning', 'error', 'critical']),
|
|
2770
|
+
created_at: z__namespace.string(),
|
|
2771
|
+
});
|
|
2772
|
+
const NotificationMf2UnreachableSchema = NotificationBaseSchema.extend({
|
|
2773
|
+
type: z__namespace.literal('INTERNAL_COMMUNICATION_FAILURE'),
|
|
2774
|
+
code: z__namespace.literal('SYS-W-01'),
|
|
2775
|
+
data: NotificationDataBlockAtSchema,
|
|
2776
|
+
});
|
|
2777
|
+
const NotificationPemsBlockedSchema = NotificationBaseSchema.extend({
|
|
2778
|
+
type: z__namespace.literal('PEM_STATUS_CHANGED'),
|
|
2779
|
+
code: z__namespace.literal('SYS-C-01'),
|
|
2780
|
+
data: NotificationDataPemStatusSchema,
|
|
2781
|
+
});
|
|
2782
|
+
const NotificationPemBackOnlineSchema = NotificationBaseSchema.extend({
|
|
2783
|
+
type: z__namespace.literal('PEM_STATUS_CHANGED'),
|
|
2784
|
+
code: z__namespace.literal('SYS-I-01'),
|
|
2785
|
+
data: NotificationDataPemStatusSchema,
|
|
2786
|
+
});
|
|
2787
|
+
const NotificationSchema = z__namespace.discriminatedUnion('code', [
|
|
2788
|
+
NotificationMf2UnreachableSchema,
|
|
2789
|
+
NotificationPemsBlockedSchema,
|
|
2790
|
+
NotificationPemBackOnlineSchema,
|
|
2791
|
+
]);
|
|
2792
|
+
const NotificationListResponseSchema = z__namespace.object({
|
|
2793
|
+
members: z__namespace.array(NotificationSchema),
|
|
2794
|
+
});
|
|
2795
|
+
|
|
2796
|
+
const TelemetryMerchantSchema = z__namespace.object({
|
|
2797
|
+
vat_number: z__namespace.string(),
|
|
2798
|
+
fiscal_code: z__namespace.string().nullable(),
|
|
2799
|
+
business_name: z__namespace.string(),
|
|
2800
|
+
});
|
|
2801
|
+
const TelemetrySupplierSchema = z__namespace.object({
|
|
2802
|
+
vat_number: z__namespace.string(),
|
|
2803
|
+
fiscal_code: z__namespace.string().nullable(),
|
|
2804
|
+
business_name: z__namespace.string(),
|
|
2805
|
+
});
|
|
2806
|
+
const TelemetrySoftwareVersionSchema = z__namespace.object({
|
|
2807
|
+
version: z__namespace.string(),
|
|
2808
|
+
swid: z__namespace.string(),
|
|
2809
|
+
installed_at: z__namespace.string(),
|
|
2810
|
+
status: z__namespace.enum(['active', 'inactive', 'archived']),
|
|
2811
|
+
});
|
|
2812
|
+
const TelemetrySoftwareSchema = z__namespace.object({
|
|
2813
|
+
code: z__namespace.string(),
|
|
2814
|
+
name: z__namespace.string(),
|
|
2815
|
+
approval_reference: z__namespace.string(),
|
|
2816
|
+
version_info: TelemetrySoftwareVersionSchema,
|
|
2817
|
+
});
|
|
2818
|
+
const PendingReceiptsSchema = z__namespace.object({
|
|
2819
|
+
count: z__namespace.number().int().nonnegative(),
|
|
2820
|
+
total_amount: z__namespace.string(),
|
|
2821
|
+
});
|
|
2822
|
+
const TransmissionSchema = z__namespace.object({
|
|
2823
|
+
attempted_at: z__namespace.string(),
|
|
2824
|
+
outcome: z__namespace.enum(['success', 'failed', 'pending']),
|
|
2825
|
+
});
|
|
2826
|
+
const MessageSchema = z__namespace.object({
|
|
2827
|
+
received_at: z__namespace.string(),
|
|
2828
|
+
content: z__namespace.string(),
|
|
2829
|
+
});
|
|
2830
|
+
const LotterySecretRequestSchema = z__namespace.object({
|
|
2831
|
+
requested_at: z__namespace.string(),
|
|
2832
|
+
outcome: z__namespace.enum(['success', 'failed', 'pending']),
|
|
2833
|
+
});
|
|
2834
|
+
const LotterySchema = z__namespace.object({
|
|
2835
|
+
last_transmission: TransmissionSchema,
|
|
2836
|
+
secret_request: LotterySecretRequestSchema,
|
|
2837
|
+
});
|
|
2838
|
+
const TelemetrySchema = z__namespace.object({
|
|
2839
|
+
pem_id: z__namespace.string(),
|
|
2840
|
+
pem_status: z__namespace.enum(['ONLINE', 'OFFLINE', 'ERROR']),
|
|
2841
|
+
pem_status_changed_at: z__namespace.string(),
|
|
2842
|
+
merchant: TelemetryMerchantSchema,
|
|
2843
|
+
supplier: TelemetrySupplierSchema,
|
|
2844
|
+
software: TelemetrySoftwareSchema,
|
|
2845
|
+
last_communication_at: z__namespace.string(),
|
|
2846
|
+
pending_receipts: PendingReceiptsSchema,
|
|
2847
|
+
last_receipt_transmission: TransmissionSchema,
|
|
2848
|
+
last_message_from_mf2: MessageSchema,
|
|
2849
|
+
ade_corrispettivi_transmission: TransmissionSchema,
|
|
2850
|
+
last_message_from_ade: MessageSchema,
|
|
2851
|
+
lottery: LotterySchema,
|
|
2852
|
+
});
|
|
2853
|
+
|
|
2854
|
+
// Receipt schemas and types
|
|
2855
|
+
// Common validation utilities
|
|
2856
|
+
const ValidationMessages = {
|
|
2857
|
+
fieldIsRequired: 'This field is required',
|
|
2858
|
+
arrayMin1: 'At least one item is required',
|
|
2859
|
+
paymentMethodRequired: 'At least one payment method is required',
|
|
2860
|
+
invalidEmail: 'Please enter a valid email address',
|
|
2861
|
+
passwordMinLength: 'Password must be at least 8 characters long',
|
|
2862
|
+
passwordComplexity: 'Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character',
|
|
2863
|
+
invalidZipCode: 'Please enter a valid 5-digit zip code',
|
|
2864
|
+
provinceMinLength: 'Province code must be 2 characters',
|
|
2865
|
+
provinceMaxLength: 'Province code must be 2 characters',
|
|
2866
|
+
invalidDateFormat: 'Please enter a valid date',
|
|
2867
|
+
nameMaxLength: 'Name is too long',
|
|
2868
|
+
invalidFiscalId: 'Please enter a valid Italian fiscal ID (Codice Fiscale or Partita IVA)',
|
|
2869
|
+
invalidVatNumber: 'Please enter a valid VAT number (11 digits)',
|
|
2870
|
+
invalidFiscalCode: 'Please enter a valid fiscal code (11 digits)',
|
|
2871
|
+
businessNameMaxLength: 'Business name is too long (max 200 characters)',
|
|
2872
|
+
businessNameOrPersonalNamesRequired: 'Please provide either a business name or first/last name, but not both',
|
|
2873
|
+
firstNameMaxLength: 'First name is too long (max 100 characters)',
|
|
2874
|
+
lastNameMaxLength: 'Last name is too long (max 100 characters)',
|
|
2875
|
+
invalidUuid: 'Please enter a valid UUID',
|
|
2876
|
+
invalidPemType: 'PEM type must be one of: AP, SP, TM, PV',
|
|
2877
|
+
reasonMaxLength: 'Reason is too long (max 255 characters)',
|
|
2878
|
+
pageMinValue: 'Page number must be at least 1',
|
|
2879
|
+
invalidDailyReportStatus: 'Daily report status must be one of: pending, sent, error',
|
|
2880
|
+
displayNameMaxLength: 'Display name is too long (max 255 characters)',
|
|
2881
|
+
};
|
|
2882
|
+
// Validation helper functions
|
|
2883
|
+
const validateInput = (schema, data) => {
|
|
2884
|
+
const result = schema.safeParse(data);
|
|
2885
|
+
if (!result.success) {
|
|
2886
|
+
const errors = result.error.issues.map((error) => ({
|
|
2887
|
+
field: error.path.join('.'),
|
|
2888
|
+
message: error.message,
|
|
2889
|
+
code: error.code,
|
|
2890
|
+
}));
|
|
2891
|
+
return {
|
|
2892
|
+
success: false,
|
|
2893
|
+
errors,
|
|
2894
|
+
data: null,
|
|
2895
|
+
};
|
|
2896
|
+
}
|
|
2897
|
+
return {
|
|
2898
|
+
success: true,
|
|
2899
|
+
errors: [],
|
|
2900
|
+
data: result.data,
|
|
2901
|
+
};
|
|
2902
|
+
};
|
|
2327
2903
|
|
|
2328
|
-
|
|
2329
|
-
|
|
2904
|
+
class ACubeSDKError extends Error {
|
|
2905
|
+
constructor(type, message, originalError, statusCode, violations) {
|
|
2906
|
+
super(message);
|
|
2907
|
+
this.type = type;
|
|
2908
|
+
this.originalError = originalError;
|
|
2909
|
+
this.statusCode = statusCode;
|
|
2910
|
+
this.name = 'ACubeSDKError';
|
|
2911
|
+
this.violations = violations;
|
|
2912
|
+
}
|
|
2330
2913
|
}
|
|
2331
2914
|
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
var picocolors_browserExports = picocolors_browser.exports;
|
|
2340
|
-
var pc = /*@__PURE__*/getDefaultExportFromCjs(picocolors_browserExports);
|
|
2341
|
-
|
|
2342
|
-
const levelColors = {
|
|
2343
|
-
debug: {
|
|
2344
|
-
prefix: pc.gray,
|
|
2345
|
-
message: pc.gray,
|
|
2346
|
-
data: pc.dim,
|
|
2347
|
-
},
|
|
2348
|
-
info: {
|
|
2349
|
-
prefix: pc.cyan,
|
|
2350
|
-
message: pc.white,
|
|
2351
|
-
data: pc.dim,
|
|
2352
|
-
},
|
|
2353
|
-
warn: {
|
|
2354
|
-
prefix: pc.yellow,
|
|
2355
|
-
message: pc.yellow,
|
|
2356
|
-
data: pc.dim,
|
|
2357
|
-
},
|
|
2358
|
-
error: {
|
|
2359
|
-
prefix: pc.red,
|
|
2360
|
-
message: pc.red,
|
|
2361
|
-
data: pc.dim,
|
|
2362
|
-
},
|
|
2363
|
-
};
|
|
2364
|
-
const levelIcons = {
|
|
2365
|
-
debug: '🔍',
|
|
2366
|
-
info: 'ℹ️ ',
|
|
2367
|
-
warn: '⚠️ ',
|
|
2368
|
-
error: '❌',
|
|
2369
|
-
};
|
|
2370
|
-
/**
|
|
2371
|
-
* Format data for pretty display in logs
|
|
2372
|
-
*/
|
|
2373
|
-
function formatData(data, level) {
|
|
2374
|
-
if (data === undefined || data === null) {
|
|
2375
|
-
return String(data);
|
|
2915
|
+
const log$f = createPrefixedLogger('AUTH-STRATEGY');
|
|
2916
|
+
class AuthStrategy {
|
|
2917
|
+
constructor(jwtHandler, mtlsHandler, userProvider, mtlsAdapter) {
|
|
2918
|
+
this.jwtHandler = jwtHandler;
|
|
2919
|
+
this.mtlsHandler = mtlsHandler;
|
|
2920
|
+
this.userProvider = userProvider;
|
|
2921
|
+
this.mtlsAdapter = mtlsAdapter;
|
|
2376
2922
|
}
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2923
|
+
async determineAuthConfig(url, method, explicitMode) {
|
|
2924
|
+
if (this.isNotificationEndpoint(url) || this.isTelemetryEndpoint(url)) {
|
|
2925
|
+
return { mode: 'mtls', usePort444: true };
|
|
2926
|
+
}
|
|
2927
|
+
const platform = this.detectPlatform();
|
|
2928
|
+
const userRole = await this.getUserRole();
|
|
2929
|
+
const isReceiptEndpoint = this.isReceiptEndpoint(url);
|
|
2930
|
+
log$f.debug('Determining auth config', {
|
|
2931
|
+
url,
|
|
2932
|
+
method,
|
|
2933
|
+
platform,
|
|
2934
|
+
userRole,
|
|
2935
|
+
isReceiptEndpoint,
|
|
2936
|
+
explicitMode,
|
|
2937
|
+
});
|
|
2938
|
+
if (userRole === 'SUPPLIER') {
|
|
2939
|
+
return { mode: 'jwt', usePort444: false };
|
|
2940
|
+
}
|
|
2941
|
+
if (userRole === 'CASHIER') {
|
|
2942
|
+
if (!isReceiptEndpoint) {
|
|
2943
|
+
return { mode: 'jwt', usePort444: false };
|
|
2944
|
+
}
|
|
2945
|
+
if (platform === 'mobile') {
|
|
2946
|
+
return { mode: 'mtls', usePort444: true };
|
|
2947
|
+
}
|
|
2948
|
+
return { mode: 'jwt', usePort444: true };
|
|
2949
|
+
}
|
|
2950
|
+
if (userRole === 'MERCHANT') {
|
|
2951
|
+
if (!isReceiptEndpoint) {
|
|
2952
|
+
return { mode: 'jwt', usePort444: false };
|
|
2953
|
+
}
|
|
2954
|
+
if (this.isReturnableItemsEndpoint(url)) {
|
|
2955
|
+
return { mode: 'mtls', usePort444: true };
|
|
2956
|
+
}
|
|
2957
|
+
if (method === 'GET') {
|
|
2958
|
+
if (this.isDetailedReceiptEndpoint(url)) {
|
|
2959
|
+
if (platform === 'mobile') {
|
|
2960
|
+
return { mode: 'mtls', usePort444: true };
|
|
2961
|
+
}
|
|
2962
|
+
return { mode: 'jwt', usePort444: true };
|
|
2963
|
+
}
|
|
2964
|
+
return { mode: 'jwt', usePort444: false };
|
|
2965
|
+
}
|
|
2966
|
+
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {
|
|
2967
|
+
if (platform === 'mobile') {
|
|
2968
|
+
return { mode: 'mtls', usePort444: true };
|
|
2969
|
+
}
|
|
2970
|
+
return { mode: 'jwt', usePort444: true };
|
|
2971
|
+
}
|
|
2972
|
+
return { mode: 'jwt', usePort444: false };
|
|
2973
|
+
}
|
|
2974
|
+
if (explicitMode) {
|
|
2975
|
+
if (userRole === 'SUPPLIER' && explicitMode === 'mtls') {
|
|
2976
|
+
return { mode: 'jwt', usePort444: false };
|
|
2977
|
+
}
|
|
2978
|
+
return {
|
|
2979
|
+
mode: explicitMode,
|
|
2980
|
+
usePort444: explicitMode === 'mtls' || (platform === 'web' && isReceiptEndpoint),
|
|
2981
|
+
};
|
|
2982
|
+
}
|
|
2983
|
+
if (platform === 'web') {
|
|
2984
|
+
return { mode: 'jwt', usePort444: isReceiptEndpoint };
|
|
2985
|
+
}
|
|
2986
|
+
if (isReceiptEndpoint && platform === 'mobile') {
|
|
2987
|
+
return { mode: 'mtls', usePort444: true };
|
|
2988
|
+
}
|
|
2989
|
+
return { mode: 'jwt', usePort444: false };
|
|
2380
2990
|
}
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
return `${data.name}: ${data.message}${data.stack ? `\n${pc.dim(data.stack)}` : ''}`;
|
|
2991
|
+
async getAuthHeaders() {
|
|
2992
|
+
return this.jwtHandler.getAuthHeaders();
|
|
2384
2993
|
}
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
const colors = levelColors[level];
|
|
2388
|
-
return colors.data(JSON.stringify(data, null, 2));
|
|
2994
|
+
getMtlsHandler() {
|
|
2995
|
+
return this.mtlsHandler;
|
|
2389
2996
|
}
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2997
|
+
getJwtHandler() {
|
|
2998
|
+
return this.jwtHandler;
|
|
2999
|
+
}
|
|
3000
|
+
detectPlatform() {
|
|
3001
|
+
if (!this.mtlsAdapter) {
|
|
3002
|
+
return 'web';
|
|
2394
3003
|
}
|
|
2395
|
-
|
|
2396
|
-
|
|
3004
|
+
const platformInfo = this.mtlsAdapter.getPlatformInfo();
|
|
3005
|
+
return platformInfo.platform === 'web' ? 'web' : 'mobile';
|
|
3006
|
+
}
|
|
3007
|
+
async getUserRole() {
|
|
3008
|
+
if (!this.userProvider) {
|
|
3009
|
+
return null;
|
|
3010
|
+
}
|
|
3011
|
+
const user = await this.userProvider.getCurrentUser();
|
|
3012
|
+
if (!user || !user.roles) {
|
|
3013
|
+
return null;
|
|
3014
|
+
}
|
|
3015
|
+
if (hasRole(user.roles, 'ROLE_SUPPLIER')) {
|
|
3016
|
+
return 'SUPPLIER';
|
|
3017
|
+
}
|
|
3018
|
+
if (hasRole(user.roles, 'ROLE_MERCHANT')) {
|
|
3019
|
+
return 'MERCHANT';
|
|
2397
3020
|
}
|
|
3021
|
+
if (hasRole(user.roles, 'ROLE_CASHIER')) {
|
|
3022
|
+
return 'CASHIER';
|
|
3023
|
+
}
|
|
3024
|
+
return null;
|
|
2398
3025
|
}
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
constructor() {
|
|
2402
|
-
this.enabled = false;
|
|
3026
|
+
isReceiptEndpoint(url) {
|
|
3027
|
+
return url.includes('/receipts') || url.includes('/mf1/receipts');
|
|
2403
3028
|
}
|
|
2404
|
-
|
|
2405
|
-
|
|
3029
|
+
isReturnableItemsEndpoint(url) {
|
|
3030
|
+
return !!(url.match(/\/receipts\/[a-f0-9-]+\/returnable-items$/) ||
|
|
3031
|
+
url.match(/\/mf1\/receipts\/[a-f0-9-]+\/returnable-items$/));
|
|
2406
3032
|
}
|
|
2407
|
-
|
|
2408
|
-
return
|
|
3033
|
+
isDetailedReceiptEndpoint(url) {
|
|
3034
|
+
return !!(url.match(/\/receipts\/[a-f0-9-]+\/details$/) ||
|
|
3035
|
+
url.match(/\/mf1\/receipts\/[a-f0-9-]+\/details$/));
|
|
2409
3036
|
}
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
return;
|
|
2413
|
-
this.log('debug', prefix, message, data);
|
|
3037
|
+
isNotificationEndpoint(url) {
|
|
3038
|
+
return url.includes('/mf1/notifications');
|
|
2414
3039
|
}
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
return;
|
|
2418
|
-
this.log('info', prefix, message, data);
|
|
3040
|
+
isTelemetryEndpoint(url) {
|
|
3041
|
+
return !!url.match(/\/mf1\/pems\/[^/]+\/telemetry/);
|
|
2419
3042
|
}
|
|
2420
|
-
|
|
2421
|
-
|
|
3043
|
+
}
|
|
3044
|
+
|
|
3045
|
+
class JwtAuthHandler {
|
|
3046
|
+
constructor(tokenStorage) {
|
|
3047
|
+
this.tokenStorage = tokenStorage;
|
|
2422
3048
|
}
|
|
2423
|
-
|
|
2424
|
-
|
|
3049
|
+
async getAuthConfig(_url, _method) {
|
|
3050
|
+
return { mode: 'jwt', usePort444: false };
|
|
2425
3051
|
}
|
|
2426
|
-
|
|
2427
|
-
const
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
const timestamp = pc.dim(isoTime.slice(0, 12));
|
|
2431
|
-
const formattedPrefix = colors.prefix(`[${prefix}]`);
|
|
2432
|
-
const formattedMessage = colors.message(message);
|
|
2433
|
-
const consoleMethod = level === 'debug'
|
|
2434
|
-
? console.debug
|
|
2435
|
-
: level === 'info'
|
|
2436
|
-
? console.info
|
|
2437
|
-
: level === 'warn'
|
|
2438
|
-
? console.warn
|
|
2439
|
-
: console.error;
|
|
2440
|
-
const header = `${timestamp} ${icon} ${formattedPrefix} ${formattedMessage}`;
|
|
2441
|
-
if (data !== undefined) {
|
|
2442
|
-
const formattedData = formatData(data, level);
|
|
2443
|
-
// Check if data is an object (multi-line) or primitive (single-line)
|
|
2444
|
-
const isMultiLine = typeof data === 'object' && data !== null;
|
|
2445
|
-
if (isMultiLine) {
|
|
2446
|
-
consoleMethod(`${header}\n${formattedData}`);
|
|
2447
|
-
}
|
|
2448
|
-
else {
|
|
2449
|
-
consoleMethod(`${header}`, formattedData);
|
|
2450
|
-
}
|
|
2451
|
-
}
|
|
2452
|
-
else {
|
|
2453
|
-
consoleMethod(header);
|
|
3052
|
+
async getAuthHeaders() {
|
|
3053
|
+
const token = await this.tokenStorage.getAccessToken();
|
|
3054
|
+
if (!token) {
|
|
3055
|
+
return {};
|
|
2454
3056
|
}
|
|
3057
|
+
return { Authorization: `Bearer ${token}` };
|
|
2455
3058
|
}
|
|
2456
3059
|
}
|
|
2457
|
-
const logger = new Logger();
|
|
2458
|
-
function createPrefixedLogger(prefix) {
|
|
2459
|
-
return {
|
|
2460
|
-
debug: (message, data) => logger.debug(prefix, message, data),
|
|
2461
|
-
info: (message, data) => logger.info(prefix, message, data),
|
|
2462
|
-
warn: (message, data) => logger.warn(prefix, message, data),
|
|
2463
|
-
error: (message, data) => logger.error(prefix, message, data),
|
|
2464
|
-
};
|
|
2465
|
-
}
|
|
2466
|
-
|
|
2467
|
-
/**
|
|
2468
|
-
* Formats a numeric string value to have exactly the specified number of decimal places.
|
|
2469
|
-
* E.g., "1" → "1.00", "10" → "10.00", "1.5" → "1.50"
|
|
2470
|
-
* Returns undefined for undefined input (preserves optional fields).
|
|
2471
|
-
*
|
|
2472
|
-
* @param value - The string value to format
|
|
2473
|
-
* @param decimals - Number of decimal places (default: 2)
|
|
2474
|
-
* @returns Formatted string or undefined if input is undefined
|
|
2475
|
-
*/
|
|
2476
|
-
function formatDecimal(value, decimals = 2) {
|
|
2477
|
-
if (value === undefined)
|
|
2478
|
-
return undefined;
|
|
2479
|
-
const num = parseFloat(value);
|
|
2480
|
-
if (isNaN(num))
|
|
2481
|
-
return value;
|
|
2482
|
-
return num.toFixed(decimals);
|
|
2483
|
-
}
|
|
2484
3060
|
|
|
2485
|
-
const log$
|
|
3061
|
+
const log$e = createPrefixedLogger('MTLS-HANDLER');
|
|
2486
3062
|
class MtlsAuthHandler {
|
|
2487
3063
|
constructor(mtlsAdapter, certificatePort) {
|
|
2488
3064
|
this.mtlsAdapter = mtlsAdapter;
|
|
@@ -2524,7 +3100,7 @@ class MtlsAuthHandler {
|
|
|
2524
3100
|
async makeRequest(url, config, jwtToken) {
|
|
2525
3101
|
const requestKey = this.generateRequestKey(url, config, jwtToken);
|
|
2526
3102
|
if (this.pendingRequests.has(requestKey)) {
|
|
2527
|
-
log$
|
|
3103
|
+
log$e.debug('Deduplicating concurrent request:', url);
|
|
2528
3104
|
return this.pendingRequests.get(requestKey);
|
|
2529
3105
|
}
|
|
2530
3106
|
const requestPromise = this.executeRequest(url, config, jwtToken, false);
|
|
@@ -2548,10 +3124,10 @@ class MtlsAuthHandler {
|
|
|
2548
3124
|
};
|
|
2549
3125
|
if (jwtToken) {
|
|
2550
3126
|
headers['Authorization'] = `Bearer ${jwtToken}`;
|
|
2551
|
-
log$
|
|
3127
|
+
log$e.debug('JWT token present:', jwtToken.substring(0, 20) + '...');
|
|
2552
3128
|
}
|
|
2553
3129
|
else {
|
|
2554
|
-
log$
|
|
3130
|
+
log$e.warn('No JWT token provided for mTLS request');
|
|
2555
3131
|
}
|
|
2556
3132
|
const fullUrl = this.constructMtlsUrl(url);
|
|
2557
3133
|
const mtlsConfig = {
|
|
@@ -2562,25 +3138,25 @@ class MtlsAuthHandler {
|
|
|
2562
3138
|
timeout: config.timeout,
|
|
2563
3139
|
responseType: config.responseType,
|
|
2564
3140
|
};
|
|
2565
|
-
log$
|
|
2566
|
-
log$
|
|
3141
|
+
log$e.debug('header-mtls', headers);
|
|
3142
|
+
log$e.debug(`${config.method} ${fullUrl}`);
|
|
2567
3143
|
if (config.data) {
|
|
2568
|
-
log$
|
|
3144
|
+
log$e.debug('Request body:', config.data);
|
|
2569
3145
|
}
|
|
2570
3146
|
try {
|
|
2571
3147
|
const response = await this.mtlsAdapter.request(mtlsConfig);
|
|
2572
|
-
log$
|
|
3148
|
+
log$e.debug(`Response ${response.status} from ${fullUrl}`);
|
|
2573
3149
|
if (response.data) {
|
|
2574
|
-
log$
|
|
3150
|
+
log$e.debug('Response body:', response.data);
|
|
2575
3151
|
}
|
|
2576
3152
|
return response.data;
|
|
2577
3153
|
}
|
|
2578
3154
|
catch (error) {
|
|
2579
|
-
log$
|
|
3155
|
+
log$e.error(`Response error from ${fullUrl}:`, error);
|
|
2580
3156
|
if (error && typeof error === 'object' && 'response' in error) {
|
|
2581
3157
|
const axiosError = error;
|
|
2582
3158
|
if (axiosError.response?.data) {
|
|
2583
|
-
log$
|
|
3159
|
+
log$e.error('Response body:', axiosError.response.data);
|
|
2584
3160
|
}
|
|
2585
3161
|
}
|
|
2586
3162
|
if (isRetryAttempt) {
|
|
@@ -2590,7 +3166,7 @@ class MtlsAuthHandler {
|
|
|
2590
3166
|
if (!shouldRetry) {
|
|
2591
3167
|
throw error;
|
|
2592
3168
|
}
|
|
2593
|
-
log$
|
|
3169
|
+
log$e.debug('Request failed, reconfiguring certificate and retrying...');
|
|
2594
3170
|
try {
|
|
2595
3171
|
await this.configureCertificate(certificate);
|
|
2596
3172
|
return await this.executeRequest(url, config, jwtToken, true);
|
|
@@ -3317,7 +3893,7 @@ function decompressData(data, compressed) {
|
|
|
3317
3893
|
return new CompressionAdapter().decompress(data, compressed);
|
|
3318
3894
|
}
|
|
3319
3895
|
|
|
3320
|
-
const log$
|
|
3896
|
+
const log$d = createPrefixedLogger('CACHE-RN');
|
|
3321
3897
|
/**
|
|
3322
3898
|
* React Native cache adapter using SQLite (Expo or react-native-sqlite-storage)
|
|
3323
3899
|
* Cache never expires - data persists until explicitly invalidated
|
|
@@ -3401,26 +3977,26 @@ class ReactNativeCacheAdapter {
|
|
|
3401
3977
|
await this.runMigrations();
|
|
3402
3978
|
}
|
|
3403
3979
|
async runMigrations() {
|
|
3404
|
-
log$
|
|
3980
|
+
log$d.debug('Running database migrations...');
|
|
3405
3981
|
try {
|
|
3406
3982
|
// Check if compressed column exists
|
|
3407
3983
|
this.hasCompressedColumn = await this.checkColumnExists('compressed');
|
|
3408
3984
|
if (!this.hasCompressedColumn) {
|
|
3409
|
-
log$
|
|
3985
|
+
log$d.debug('Adding compressed column to cache table');
|
|
3410
3986
|
const addColumnSQL = `ALTER TABLE ${ReactNativeCacheAdapter.TABLE_NAME} ADD COLUMN compressed INTEGER DEFAULT 0`;
|
|
3411
3987
|
await this.executeSql(addColumnSQL);
|
|
3412
3988
|
this.hasCompressedColumn = true;
|
|
3413
|
-
log$
|
|
3989
|
+
log$d.debug('Successfully added compressed column');
|
|
3414
3990
|
}
|
|
3415
3991
|
else {
|
|
3416
|
-
log$
|
|
3992
|
+
log$d.debug('Compressed column already exists');
|
|
3417
3993
|
}
|
|
3418
|
-
log$
|
|
3994
|
+
log$d.debug('Database migrations completed', {
|
|
3419
3995
|
hasCompressedColumn: this.hasCompressedColumn,
|
|
3420
3996
|
});
|
|
3421
3997
|
}
|
|
3422
3998
|
catch (error) {
|
|
3423
|
-
log$
|
|
3999
|
+
log$d.debug('Migration failed, disabling compression features', error);
|
|
3424
4000
|
this.hasCompressedColumn = false;
|
|
3425
4001
|
// Don't throw - allow the app to continue even if migration fails
|
|
3426
4002
|
// The compressed feature will just be disabled
|
|
@@ -3431,20 +4007,20 @@ class ReactNativeCacheAdapter {
|
|
|
3431
4007
|
const pragmaSQL = `PRAGMA table_info(${ReactNativeCacheAdapter.TABLE_NAME})`;
|
|
3432
4008
|
const results = await this.executeSql(pragmaSQL);
|
|
3433
4009
|
const columns = this.normalizeResults(results);
|
|
3434
|
-
log$
|
|
4010
|
+
log$d.debug('Table columns found', { columns: columns.map((c) => c.name) });
|
|
3435
4011
|
return columns.some((column) => column.name === columnName);
|
|
3436
4012
|
}
|
|
3437
4013
|
catch (error) {
|
|
3438
|
-
log$
|
|
4014
|
+
log$d.debug('Error checking column existence', error);
|
|
3439
4015
|
return false;
|
|
3440
4016
|
}
|
|
3441
4017
|
}
|
|
3442
4018
|
async get(key) {
|
|
3443
4019
|
await this.ensureInitialized();
|
|
3444
4020
|
const sql = `SELECT * FROM ${ReactNativeCacheAdapter.TABLE_NAME} WHERE cache_key = ?`;
|
|
3445
|
-
log$
|
|
4021
|
+
log$d.debug('Executing get query', { sql, key });
|
|
3446
4022
|
const results = await this.executeSql(sql, [key]);
|
|
3447
|
-
log$
|
|
4023
|
+
log$d.debug('Get query results', { key, hasResults: !!results });
|
|
3448
4024
|
// Normalize results from different SQLite implementations
|
|
3449
4025
|
const rows = this.normalizeResults(results);
|
|
3450
4026
|
if (!rows || rows.length === 0) {
|
|
@@ -3467,7 +4043,7 @@ class ReactNativeCacheAdapter {
|
|
|
3467
4043
|
data,
|
|
3468
4044
|
timestamp: Date.now(),
|
|
3469
4045
|
};
|
|
3470
|
-
log$
|
|
4046
|
+
log$d.debug('Setting cache item', { key });
|
|
3471
4047
|
return this.setItem(key, item);
|
|
3472
4048
|
}
|
|
3473
4049
|
async setItem(key, item) {
|
|
@@ -3480,7 +4056,7 @@ class ReactNativeCacheAdapter {
|
|
|
3480
4056
|
const compressionResult = compressData(serializedData, this.options.compressionThreshold);
|
|
3481
4057
|
finalData = compressionResult.data;
|
|
3482
4058
|
isCompressed = compressionResult.compressed;
|
|
3483
|
-
log$
|
|
4059
|
+
log$d.debug('Compression result', {
|
|
3484
4060
|
key,
|
|
3485
4061
|
originalSize: compressionResult.originalSize,
|
|
3486
4062
|
compressedSize: compressionResult.compressedSize,
|
|
@@ -3488,7 +4064,7 @@ class ReactNativeCacheAdapter {
|
|
|
3488
4064
|
savings: compressionResult.originalSize - compressionResult.compressedSize,
|
|
3489
4065
|
});
|
|
3490
4066
|
}
|
|
3491
|
-
log$
|
|
4067
|
+
log$d.debug('Setting item with metadata', {
|
|
3492
4068
|
key,
|
|
3493
4069
|
timestamp: item.timestamp,
|
|
3494
4070
|
compressed: isCompressed,
|
|
@@ -3514,14 +4090,14 @@ class ReactNativeCacheAdapter {
|
|
|
3514
4090
|
`;
|
|
3515
4091
|
params = [key, finalData, item.timestamp];
|
|
3516
4092
|
}
|
|
3517
|
-
log$
|
|
4093
|
+
log$d.debug('Executing setItem SQL', { key, paramsCount: params.length });
|
|
3518
4094
|
await this.executeSql(sql, params);
|
|
3519
4095
|
}
|
|
3520
4096
|
async setBatch(items) {
|
|
3521
4097
|
if (items.length === 0)
|
|
3522
4098
|
return;
|
|
3523
4099
|
await this.ensureInitialized();
|
|
3524
|
-
log$
|
|
4100
|
+
log$d.debug('Batch setting items', { count: items.length });
|
|
3525
4101
|
if (this.isExpo) {
|
|
3526
4102
|
await this.db.withTransactionAsync(async () => {
|
|
3527
4103
|
for (const [key, item] of items) {
|
|
@@ -3539,7 +4115,7 @@ class ReactNativeCacheAdapter {
|
|
|
3539
4115
|
}, reject, () => resolve());
|
|
3540
4116
|
});
|
|
3541
4117
|
}
|
|
3542
|
-
log$
|
|
4118
|
+
log$d.debug('Batch operation completed', { count: items.length });
|
|
3543
4119
|
}
|
|
3544
4120
|
async setBatchItem(key, item) {
|
|
3545
4121
|
// Handle compression if enabled and compressed column is available
|
|
@@ -3713,10 +4289,10 @@ class MemoryCacheAdapter {
|
|
|
3713
4289
|
return keySize + itemSize;
|
|
3714
4290
|
}
|
|
3715
4291
|
async get(key) {
|
|
3716
|
-
log$
|
|
4292
|
+
log$d.debug('Getting cache item', { key });
|
|
3717
4293
|
const item = this.cache.get(key);
|
|
3718
4294
|
if (!item) {
|
|
3719
|
-
log$
|
|
4295
|
+
log$d.debug('Cache miss', { key });
|
|
3720
4296
|
return null;
|
|
3721
4297
|
}
|
|
3722
4298
|
// Handle decompression if needed
|
|
@@ -3726,7 +4302,7 @@ class MemoryCacheAdapter {
|
|
|
3726
4302
|
const decompressed = decompressData(item.data, true);
|
|
3727
4303
|
finalData = JSON.parse(decompressed.data);
|
|
3728
4304
|
}
|
|
3729
|
-
log$
|
|
4305
|
+
log$d.debug('Cache hit', { key, compressed: isCompressed });
|
|
3730
4306
|
return {
|
|
3731
4307
|
...item,
|
|
3732
4308
|
data: finalData,
|
|
@@ -3734,7 +4310,7 @@ class MemoryCacheAdapter {
|
|
|
3734
4310
|
};
|
|
3735
4311
|
}
|
|
3736
4312
|
async set(key, data) {
|
|
3737
|
-
log$
|
|
4313
|
+
log$d.debug('Setting cache item', { key });
|
|
3738
4314
|
// Handle compression if enabled
|
|
3739
4315
|
let finalData = data;
|
|
3740
4316
|
let isCompressed = false;
|
|
@@ -3744,7 +4320,7 @@ class MemoryCacheAdapter {
|
|
|
3744
4320
|
if (compressionResult.compressed) {
|
|
3745
4321
|
finalData = compressionResult.data;
|
|
3746
4322
|
isCompressed = true;
|
|
3747
|
-
log$
|
|
4323
|
+
log$d.debug('Compression result', {
|
|
3748
4324
|
key,
|
|
3749
4325
|
originalSize: compressionResult.originalSize,
|
|
3750
4326
|
compressedSize: compressionResult.compressedSize,
|
|
@@ -3776,13 +4352,13 @@ class MemoryCacheAdapter {
|
|
|
3776
4352
|
const oldestItemSize = this.calculateItemSize(oldestKey, oldestItem);
|
|
3777
4353
|
this.totalBytes -= oldestItemSize;
|
|
3778
4354
|
this.cache.delete(oldestKey);
|
|
3779
|
-
log$
|
|
4355
|
+
log$d.debug('Removed oldest item for capacity', { oldestKey, freedBytes: oldestItemSize });
|
|
3780
4356
|
}
|
|
3781
4357
|
}
|
|
3782
4358
|
// Set new item and update total size
|
|
3783
4359
|
this.cache.set(key, item);
|
|
3784
4360
|
this.totalBytes += newItemSize;
|
|
3785
|
-
log$
|
|
4361
|
+
log$d.debug('Updated cache size', {
|
|
3786
4362
|
entries: this.cache.size,
|
|
3787
4363
|
totalBytes: this.totalBytes,
|
|
3788
4364
|
newItemSize,
|
|
@@ -3791,7 +4367,7 @@ class MemoryCacheAdapter {
|
|
|
3791
4367
|
async setBatch(items) {
|
|
3792
4368
|
if (items.length === 0)
|
|
3793
4369
|
return;
|
|
3794
|
-
log$
|
|
4370
|
+
log$d.debug('Batch setting items', { count: items.length });
|
|
3795
4371
|
let totalNewBytes = 0;
|
|
3796
4372
|
let totalOldBytes = 0;
|
|
3797
4373
|
const itemsToRemove = [];
|
|
@@ -3820,7 +4396,7 @@ class MemoryCacheAdapter {
|
|
|
3820
4396
|
itemsToRemove.push(oldKey);
|
|
3821
4397
|
}
|
|
3822
4398
|
if (itemsToRemove.length > 0) {
|
|
3823
|
-
log$
|
|
4399
|
+
log$d.debug('Removed items for batch capacity', {
|
|
3824
4400
|
removedCount: itemsToRemove.length,
|
|
3825
4401
|
removedKeys: itemsToRemove,
|
|
3826
4402
|
});
|
|
@@ -3832,7 +4408,7 @@ class MemoryCacheAdapter {
|
|
|
3832
4408
|
for (const [key, item] of items) {
|
|
3833
4409
|
this.cache.set(key, item);
|
|
3834
4410
|
}
|
|
3835
|
-
log$
|
|
4411
|
+
log$d.debug('Batch operation completed', {
|
|
3836
4412
|
count: items.length,
|
|
3837
4413
|
totalBytes: this.totalBytes,
|
|
3838
4414
|
entries: this.cache.size,
|
|
@@ -3854,7 +4430,7 @@ class MemoryCacheAdapter {
|
|
|
3854
4430
|
}
|
|
3855
4431
|
}
|
|
3856
4432
|
if (removed > 0) {
|
|
3857
|
-
log$
|
|
4433
|
+
log$d.debug('Invalidation completed', {
|
|
3858
4434
|
pattern,
|
|
3859
4435
|
entriesRemoved: removed,
|
|
3860
4436
|
bytesFreed,
|
|
@@ -3866,7 +4442,7 @@ class MemoryCacheAdapter {
|
|
|
3866
4442
|
async clear() {
|
|
3867
4443
|
this.cache.clear();
|
|
3868
4444
|
this.totalBytes = 0;
|
|
3869
|
-
log$
|
|
4445
|
+
log$d.debug('Cache cleared', { entries: 0, bytes: 0 });
|
|
3870
4446
|
}
|
|
3871
4447
|
async getSize() {
|
|
3872
4448
|
return {
|
|
@@ -4197,7 +4773,7 @@ replaceTraps((oldTraps) => ({
|
|
|
4197
4773
|
},
|
|
4198
4774
|
}));
|
|
4199
4775
|
|
|
4200
|
-
const log$
|
|
4776
|
+
const log$c = createPrefixedLogger('CACHE-WEB');
|
|
4201
4777
|
/**
|
|
4202
4778
|
* Web cache adapter using IndexedDB with automatic error recovery
|
|
4203
4779
|
* Cache never expires - data persists until explicitly invalidated
|
|
@@ -4220,18 +4796,18 @@ class WebCacheAdapter {
|
|
|
4220
4796
|
async initialize() {
|
|
4221
4797
|
if (this.db)
|
|
4222
4798
|
return;
|
|
4223
|
-
log$
|
|
4799
|
+
log$c.debug('Initializing IndexedDB cache', {
|
|
4224
4800
|
dbName: WebCacheAdapter.DB_NAME,
|
|
4225
4801
|
version: WebCacheAdapter.DB_VERSION,
|
|
4226
4802
|
});
|
|
4227
4803
|
try {
|
|
4228
4804
|
this.db = await this.openDatabase();
|
|
4229
|
-
log$
|
|
4805
|
+
log$c.debug('IndexedDB cache initialized successfully');
|
|
4230
4806
|
this.retryCount = 0; // Reset retry count on success
|
|
4231
4807
|
}
|
|
4232
4808
|
catch (error) {
|
|
4233
4809
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
4234
|
-
log$
|
|
4810
|
+
log$c.debug('Failed to initialize IndexedDB', { error: errorMessage });
|
|
4235
4811
|
// Check if this is a version conflict error
|
|
4236
4812
|
if (this.isVersionConflictError(errorMessage)) {
|
|
4237
4813
|
await this.handleVersionConflict();
|
|
@@ -4244,32 +4820,32 @@ class WebCacheAdapter {
|
|
|
4244
4820
|
async openDatabase() {
|
|
4245
4821
|
return await openDB(WebCacheAdapter.DB_NAME, WebCacheAdapter.DB_VERSION, {
|
|
4246
4822
|
upgrade: (db, oldVersion, newVersion, transaction) => {
|
|
4247
|
-
log$
|
|
4823
|
+
log$c.debug('Database upgrade needed', { oldVersion, newVersion });
|
|
4248
4824
|
this.handleUpgrade(db, oldVersion, newVersion, transaction);
|
|
4249
4825
|
},
|
|
4250
4826
|
blocked: () => {
|
|
4251
|
-
log$
|
|
4827
|
+
log$c.debug('Database blocked - another tab may be open');
|
|
4252
4828
|
},
|
|
4253
4829
|
blocking: () => {
|
|
4254
|
-
log$
|
|
4830
|
+
log$c.debug('Database blocking - will close connection');
|
|
4255
4831
|
if (this.db) {
|
|
4256
4832
|
this.db.close();
|
|
4257
4833
|
this.db = null;
|
|
4258
4834
|
}
|
|
4259
4835
|
},
|
|
4260
4836
|
terminated: () => {
|
|
4261
|
-
log$
|
|
4837
|
+
log$c.debug('Database connection terminated unexpectedly');
|
|
4262
4838
|
this.db = null;
|
|
4263
4839
|
},
|
|
4264
4840
|
});
|
|
4265
4841
|
}
|
|
4266
4842
|
handleUpgrade(db, oldVersion, newVersion, transaction) {
|
|
4267
|
-
log$
|
|
4843
|
+
log$c.debug('Handling database upgrade', { oldVersion, newVersion });
|
|
4268
4844
|
// Create cache store if it doesn't exist (initial setup)
|
|
4269
4845
|
if (!db.objectStoreNames.contains(WebCacheAdapter.STORE_NAME)) {
|
|
4270
4846
|
const store = db.createObjectStore(WebCacheAdapter.STORE_NAME, { keyPath: 'key' });
|
|
4271
4847
|
store.createIndex('timestamp', 'timestamp', { unique: false });
|
|
4272
|
-
log$
|
|
4848
|
+
log$c.debug('Created cache store and timestamp index');
|
|
4273
4849
|
}
|
|
4274
4850
|
// Handle migration from version 1 to 2
|
|
4275
4851
|
if (oldVersion < 2) {
|
|
@@ -4280,16 +4856,16 @@ class WebCacheAdapter {
|
|
|
4280
4856
|
try {
|
|
4281
4857
|
if (store.indexNames.contains(indexName)) {
|
|
4282
4858
|
store.deleteIndex(indexName);
|
|
4283
|
-
log$
|
|
4859
|
+
log$c.debug(`Removed unused index: ${indexName}`);
|
|
4284
4860
|
}
|
|
4285
4861
|
}
|
|
4286
4862
|
catch (error) {
|
|
4287
4863
|
// Ignore errors if indexes don't exist
|
|
4288
|
-
log$
|
|
4864
|
+
log$c.debug(`Warning: Could not remove index ${indexName}`, error);
|
|
4289
4865
|
}
|
|
4290
4866
|
});
|
|
4291
4867
|
}
|
|
4292
|
-
log$
|
|
4868
|
+
log$c.debug('Database upgrade completed');
|
|
4293
4869
|
}
|
|
4294
4870
|
isVersionConflictError(errorMessage) {
|
|
4295
4871
|
return (errorMessage.includes('less than the existing version') ||
|
|
@@ -4297,7 +4873,7 @@ class WebCacheAdapter {
|
|
|
4297
4873
|
errorMessage.includes('VersionError'));
|
|
4298
4874
|
}
|
|
4299
4875
|
async handleVersionConflict() {
|
|
4300
|
-
log$
|
|
4876
|
+
log$c.debug('Handling version conflict, attempting recovery...');
|
|
4301
4877
|
if (this.retryCount >= this.maxRetries) {
|
|
4302
4878
|
throw new Error('Failed to resolve IndexedDB version conflict after multiple attempts');
|
|
4303
4879
|
}
|
|
@@ -4309,19 +4885,19 @@ class WebCacheAdapter {
|
|
|
4309
4885
|
this.db = null;
|
|
4310
4886
|
}
|
|
4311
4887
|
// Delete the problematic database
|
|
4312
|
-
log$
|
|
4888
|
+
log$c.debug('Deleting problematic database to resolve version conflict');
|
|
4313
4889
|
await deleteDB(WebCacheAdapter.DB_NAME);
|
|
4314
4890
|
// Wait a bit for the deletion to complete
|
|
4315
4891
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
4316
4892
|
// Try to open the database again
|
|
4317
|
-
log$
|
|
4893
|
+
log$c.debug(`Retrying database initialization (attempt ${this.retryCount}/${this.maxRetries})`);
|
|
4318
4894
|
this.db = await this.openDatabase();
|
|
4319
|
-
log$
|
|
4895
|
+
log$c.debug('Successfully recovered from version conflict');
|
|
4320
4896
|
this.retryCount = 0; // Reset retry count on success
|
|
4321
4897
|
}
|
|
4322
4898
|
catch (retryError) {
|
|
4323
4899
|
const retryErrorMessage = retryError instanceof Error ? retryError.message : 'Unknown error';
|
|
4324
|
-
log$
|
|
4900
|
+
log$c.debug('Recovery attempt failed', { attempt: this.retryCount, error: retryErrorMessage });
|
|
4325
4901
|
if (this.retryCount < this.maxRetries) {
|
|
4326
4902
|
// Try again
|
|
4327
4903
|
await this.handleVersionConflict();
|
|
@@ -4333,7 +4909,7 @@ class WebCacheAdapter {
|
|
|
4333
4909
|
}
|
|
4334
4910
|
async get(key) {
|
|
4335
4911
|
await this.ensureInitialized();
|
|
4336
|
-
log$
|
|
4912
|
+
log$c.debug('Getting cache item', { key });
|
|
4337
4913
|
try {
|
|
4338
4914
|
const transaction = this.db.transaction([WebCacheAdapter.STORE_NAME], 'readonly');
|
|
4339
4915
|
const store = transaction.objectStore(WebCacheAdapter.STORE_NAME);
|
|
@@ -4359,7 +4935,7 @@ class WebCacheAdapter {
|
|
|
4359
4935
|
};
|
|
4360
4936
|
}
|
|
4361
4937
|
catch (error) {
|
|
4362
|
-
log$
|
|
4938
|
+
log$c.debug('Error getting cache item', { key, error });
|
|
4363
4939
|
return null; // Return null on error instead of throwing
|
|
4364
4940
|
}
|
|
4365
4941
|
}
|
|
@@ -4381,7 +4957,7 @@ class WebCacheAdapter {
|
|
|
4381
4957
|
if (compressionResult.compressed) {
|
|
4382
4958
|
finalData = compressionResult.data;
|
|
4383
4959
|
isCompressed = true;
|
|
4384
|
-
log$
|
|
4960
|
+
log$c.debug('Compression result', {
|
|
4385
4961
|
key,
|
|
4386
4962
|
originalSize: compressionResult.originalSize,
|
|
4387
4963
|
compressedSize: compressionResult.compressedSize,
|
|
@@ -4390,7 +4966,7 @@ class WebCacheAdapter {
|
|
|
4390
4966
|
});
|
|
4391
4967
|
}
|
|
4392
4968
|
}
|
|
4393
|
-
log$
|
|
4969
|
+
log$c.debug('Setting cache item', { key, timestamp: item.timestamp, compressed: isCompressed });
|
|
4394
4970
|
const storedItem = {
|
|
4395
4971
|
key,
|
|
4396
4972
|
data: finalData,
|
|
@@ -4403,7 +4979,7 @@ class WebCacheAdapter {
|
|
|
4403
4979
|
await store.put(storedItem);
|
|
4404
4980
|
}
|
|
4405
4981
|
catch (error) {
|
|
4406
|
-
log$
|
|
4982
|
+
log$c.debug('Error setting cache item', { key, error });
|
|
4407
4983
|
// Silently fail for cache writes
|
|
4408
4984
|
}
|
|
4409
4985
|
}
|
|
@@ -4411,7 +4987,7 @@ class WebCacheAdapter {
|
|
|
4411
4987
|
if (items.length === 0)
|
|
4412
4988
|
return;
|
|
4413
4989
|
await this.ensureInitialized();
|
|
4414
|
-
log$
|
|
4990
|
+
log$c.debug('Batch setting items', { count: items.length });
|
|
4415
4991
|
try {
|
|
4416
4992
|
const transaction = this.db.transaction([WebCacheAdapter.STORE_NAME], 'readwrite');
|
|
4417
4993
|
const store = transaction.objectStore(WebCacheAdapter.STORE_NAME);
|
|
@@ -4421,10 +4997,10 @@ class WebCacheAdapter {
|
|
|
4421
4997
|
return store.put(storedItem);
|
|
4422
4998
|
});
|
|
4423
4999
|
await Promise.all(promises);
|
|
4424
|
-
log$
|
|
5000
|
+
log$c.debug('Batch operation completed', { count: items.length });
|
|
4425
5001
|
}
|
|
4426
5002
|
catch (error) {
|
|
4427
|
-
log$
|
|
5003
|
+
log$c.debug('Error in batch operation', { count: items.length, error });
|
|
4428
5004
|
// Silently fail for batch writes
|
|
4429
5005
|
}
|
|
4430
5006
|
}
|
|
@@ -4459,10 +5035,10 @@ class WebCacheAdapter {
|
|
|
4459
5035
|
const transaction = this.db.transaction([WebCacheAdapter.STORE_NAME], 'readwrite');
|
|
4460
5036
|
const store = transaction.objectStore(WebCacheAdapter.STORE_NAME);
|
|
4461
5037
|
await store.clear();
|
|
4462
|
-
log$
|
|
5038
|
+
log$c.debug('Cache cleared successfully');
|
|
4463
5039
|
}
|
|
4464
5040
|
catch (error) {
|
|
4465
|
-
log$
|
|
5041
|
+
log$c.debug('Error clearing cache', error);
|
|
4466
5042
|
// Silently fail for cache clear
|
|
4467
5043
|
}
|
|
4468
5044
|
}
|
|
@@ -4488,7 +5064,7 @@ class WebCacheAdapter {
|
|
|
4488
5064
|
};
|
|
4489
5065
|
}
|
|
4490
5066
|
catch (error) {
|
|
4491
|
-
log$
|
|
5067
|
+
log$c.debug('Error getting cache size', error);
|
|
4492
5068
|
return {
|
|
4493
5069
|
entries: 0,
|
|
4494
5070
|
bytes: 0,
|
|
@@ -4513,7 +5089,7 @@ class WebCacheAdapter {
|
|
|
4513
5089
|
return allKeys.filter((key) => regex.test(key));
|
|
4514
5090
|
}
|
|
4515
5091
|
catch (error) {
|
|
4516
|
-
log$
|
|
5092
|
+
log$c.debug('Error getting cache keys', error);
|
|
4517
5093
|
return [];
|
|
4518
5094
|
}
|
|
4519
5095
|
}
|
|
@@ -4526,7 +5102,7 @@ class WebCacheAdapter {
|
|
|
4526
5102
|
return true;
|
|
4527
5103
|
}
|
|
4528
5104
|
catch (error) {
|
|
4529
|
-
log$
|
|
5105
|
+
log$c.debug('Error deleting cache item', { key, error });
|
|
4530
5106
|
return false;
|
|
4531
5107
|
}
|
|
4532
5108
|
}
|
|
@@ -4538,7 +5114,7 @@ class WebCacheAdapter {
|
|
|
4538
5114
|
await this.initPromise;
|
|
4539
5115
|
}
|
|
4540
5116
|
catch (error) {
|
|
4541
|
-
log$
|
|
5117
|
+
log$c.debug('Failed to ensure initialization', error);
|
|
4542
5118
|
// Reset and try once more
|
|
4543
5119
|
this.initPromise = null;
|
|
4544
5120
|
this.db = null;
|
|
@@ -4557,7 +5133,7 @@ WebCacheAdapter.DB_NAME = 'acube_cache';
|
|
|
4557
5133
|
WebCacheAdapter.DB_VERSION = 2;
|
|
4558
5134
|
WebCacheAdapter.STORE_NAME = 'cache_entries';
|
|
4559
5135
|
|
|
4560
|
-
const log$
|
|
5136
|
+
const log$b = createPrefixedLogger('CACHE-LOADER');
|
|
4561
5137
|
function loadCacheAdapter(platform) {
|
|
4562
5138
|
try {
|
|
4563
5139
|
switch (platform) {
|
|
@@ -4589,7 +5165,7 @@ function loadCacheAdapter(platform) {
|
|
|
4589
5165
|
}
|
|
4590
5166
|
}
|
|
4591
5167
|
catch (error) {
|
|
4592
|
-
log$
|
|
5168
|
+
log$b.warn(`Cache adapter not available for platform ${platform}:`, error);
|
|
4593
5169
|
return undefined;
|
|
4594
5170
|
}
|
|
4595
5171
|
}
|
|
@@ -4646,7 +5222,7 @@ class BaseSecureStorageAdapter {
|
|
|
4646
5222
|
}
|
|
4647
5223
|
}
|
|
4648
5224
|
|
|
4649
|
-
const log$
|
|
5225
|
+
const log$a = createPrefixedLogger('NETWORK-BASE');
|
|
4650
5226
|
class NetworkBase {
|
|
4651
5227
|
constructor(initialOnline = true, debounceMs = 300) {
|
|
4652
5228
|
this.destroy$ = new Subject();
|
|
@@ -4666,14 +5242,14 @@ class NetworkBase {
|
|
|
4666
5242
|
const current = this.statusSubject.getValue();
|
|
4667
5243
|
if (current.online !== online) {
|
|
4668
5244
|
this.statusSubject.next({ online, timestamp: Date.now() });
|
|
4669
|
-
log$
|
|
5245
|
+
log$a.debug(`Network status changed: ${online ? 'online' : 'offline'}`);
|
|
4670
5246
|
}
|
|
4671
5247
|
}
|
|
4672
5248
|
destroy() {
|
|
4673
5249
|
this.destroy$.next();
|
|
4674
5250
|
this.destroy$.complete();
|
|
4675
5251
|
this.statusSubject.complete();
|
|
4676
|
-
log$
|
|
5252
|
+
log$a.debug('Network monitor destroyed');
|
|
4677
5253
|
}
|
|
4678
5254
|
}
|
|
4679
5255
|
|
|
@@ -4733,7 +5309,7 @@ class NodeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
4733
5309
|
}
|
|
4734
5310
|
}
|
|
4735
5311
|
|
|
4736
|
-
const log$
|
|
5312
|
+
const log$9 = createPrefixedLogger('RN-STORAGE');
|
|
4737
5313
|
/**
|
|
4738
5314
|
* React Native storage adapter using AsyncStorage
|
|
4739
5315
|
* Note: Uses native batch operations for better performance (not base class)
|
|
@@ -4765,7 +5341,7 @@ class ReactNativeStorageAdapter {
|
|
|
4765
5341
|
return await this.AsyncStorage.getItem(key);
|
|
4766
5342
|
}
|
|
4767
5343
|
catch (error) {
|
|
4768
|
-
log$
|
|
5344
|
+
log$9.error('Failed to get item from AsyncStorage:', error);
|
|
4769
5345
|
return null;
|
|
4770
5346
|
}
|
|
4771
5347
|
}
|
|
@@ -4806,7 +5382,7 @@ class ReactNativeStorageAdapter {
|
|
|
4806
5382
|
return await this.AsyncStorage.getAllKeys();
|
|
4807
5383
|
}
|
|
4808
5384
|
catch (error) {
|
|
4809
|
-
log$
|
|
5385
|
+
log$9.error('Failed to get all keys:', error);
|
|
4810
5386
|
return [];
|
|
4811
5387
|
}
|
|
4812
5388
|
}
|
|
@@ -4823,7 +5399,7 @@ class ReactNativeStorageAdapter {
|
|
|
4823
5399
|
return result;
|
|
4824
5400
|
}
|
|
4825
5401
|
catch (error) {
|
|
4826
|
-
log$
|
|
5402
|
+
log$9.error('Failed to get multiple items:', error);
|
|
4827
5403
|
const result = {};
|
|
4828
5404
|
keys.forEach((key) => {
|
|
4829
5405
|
result[key] = null;
|
|
@@ -4873,7 +5449,7 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
4873
5449
|
return;
|
|
4874
5450
|
}
|
|
4875
5451
|
catch {
|
|
4876
|
-
log$
|
|
5452
|
+
log$9.debug('expo-secure-store not available, trying react-native-keychain');
|
|
4877
5453
|
}
|
|
4878
5454
|
try {
|
|
4879
5455
|
const Keychain = require('react-native-keychain');
|
|
@@ -4882,7 +5458,7 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
4882
5458
|
return;
|
|
4883
5459
|
}
|
|
4884
5460
|
catch {
|
|
4885
|
-
log$
|
|
5461
|
+
log$9.error('react-native-keychain not available');
|
|
4886
5462
|
}
|
|
4887
5463
|
throw new Error('No secure storage available. Please install expo-secure-store or react-native-keychain');
|
|
4888
5464
|
}
|
|
@@ -4900,7 +5476,7 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
4900
5476
|
}
|
|
4901
5477
|
}
|
|
4902
5478
|
catch (error) {
|
|
4903
|
-
log$
|
|
5479
|
+
log$9.error('Failed to get secure item:', error);
|
|
4904
5480
|
}
|
|
4905
5481
|
return null;
|
|
4906
5482
|
}
|
|
@@ -4937,10 +5513,10 @@ class ReactNativeSecureStorageAdapter extends BaseSecureStorageAdapter {
|
|
|
4937
5513
|
}
|
|
4938
5514
|
}
|
|
4939
5515
|
async clear() {
|
|
4940
|
-
log$
|
|
5516
|
+
log$9.warn('Clear all secure items not fully implemented for React Native');
|
|
4941
5517
|
}
|
|
4942
5518
|
async getAllKeys() {
|
|
4943
|
-
log$
|
|
5519
|
+
log$9.warn('Get all secure keys not implemented for React Native');
|
|
4944
5520
|
return [];
|
|
4945
5521
|
}
|
|
4946
5522
|
async isAvailable() {
|
|
@@ -5158,7 +5734,7 @@ class NodeNetworkMonitor extends NetworkBase {
|
|
|
5158
5734
|
}
|
|
5159
5735
|
}
|
|
5160
5736
|
|
|
5161
|
-
const log$
|
|
5737
|
+
const log$8 = createPrefixedLogger('RN-NETWORK');
|
|
5162
5738
|
/**
|
|
5163
5739
|
* React Native network monitor using RxJS
|
|
5164
5740
|
* Supports both @react-native-community/netinfo and expo-network
|
|
@@ -5171,7 +5747,7 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5171
5747
|
this.moduleReady$ = new Subject();
|
|
5172
5748
|
this.isExpo = detectPlatform().isExpo;
|
|
5173
5749
|
this.init().catch((error) => {
|
|
5174
|
-
log$
|
|
5750
|
+
log$8.error('Network monitor initialization failed:', error);
|
|
5175
5751
|
});
|
|
5176
5752
|
}
|
|
5177
5753
|
async init() {
|
|
@@ -5190,10 +5766,10 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5190
5766
|
try {
|
|
5191
5767
|
const module = require('@react-native-community/netinfo');
|
|
5192
5768
|
this.netInfoModule = module.default || module;
|
|
5193
|
-
log$
|
|
5769
|
+
log$8.debug('Loaded @react-native-community/netinfo module');
|
|
5194
5770
|
}
|
|
5195
5771
|
catch (error) {
|
|
5196
|
-
log$
|
|
5772
|
+
log$8.error('Failed to load React Native NetInfo module:', error);
|
|
5197
5773
|
this.netInfoModule = null;
|
|
5198
5774
|
}
|
|
5199
5775
|
}
|
|
@@ -5201,10 +5777,10 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5201
5777
|
try {
|
|
5202
5778
|
const module = require('expo-network');
|
|
5203
5779
|
this.netInfoModule = module.default || module;
|
|
5204
|
-
log$
|
|
5780
|
+
log$8.debug('Loaded expo-network module');
|
|
5205
5781
|
}
|
|
5206
5782
|
catch (error) {
|
|
5207
|
-
log$
|
|
5783
|
+
log$8.error('Failed to load Expo Network module:', error);
|
|
5208
5784
|
this.netInfoModule = null;
|
|
5209
5785
|
}
|
|
5210
5786
|
}
|
|
@@ -5221,16 +5797,16 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5221
5797
|
}
|
|
5222
5798
|
const online = !!(state.isConnected && state.isInternetReachable !== false);
|
|
5223
5799
|
this.updateStatus(online);
|
|
5224
|
-
log$
|
|
5800
|
+
log$8.debug('Initial network state:', online ? 'online' : 'offline');
|
|
5225
5801
|
}
|
|
5226
5802
|
catch (error) {
|
|
5227
|
-
log$
|
|
5803
|
+
log$8.warn('Could not fetch initial network state:', error);
|
|
5228
5804
|
}
|
|
5229
5805
|
}
|
|
5230
5806
|
subscribeToStateChanges() {
|
|
5231
5807
|
if (!this.netInfoModule)
|
|
5232
5808
|
return;
|
|
5233
|
-
log$
|
|
5809
|
+
log$8.debug('Subscribing to network state changes');
|
|
5234
5810
|
const handleState = (state) => {
|
|
5235
5811
|
const online = !!(state.isConnected && (state.isInternetReachable ?? true));
|
|
5236
5812
|
this.updateStatus(online);
|
|
@@ -5272,7 +5848,7 @@ class ReactNativeNetworkMonitor extends NetworkBase {
|
|
|
5272
5848
|
};
|
|
5273
5849
|
}
|
|
5274
5850
|
catch (error) {
|
|
5275
|
-
log$
|
|
5851
|
+
log$8.error('Failed to fetch detailed network info:', error);
|
|
5276
5852
|
return null;
|
|
5277
5853
|
}
|
|
5278
5854
|
}
|
|
@@ -5408,7 +5984,7 @@ class CertificateValidator {
|
|
|
5408
5984
|
}
|
|
5409
5985
|
}
|
|
5410
5986
|
|
|
5411
|
-
const log$
|
|
5987
|
+
const log$7 = createPrefixedLogger('RN-MTLS');
|
|
5412
5988
|
/**
|
|
5413
5989
|
* React Native mTLS Adapter using @a-cube-io/expo-mutual-tls
|
|
5414
5990
|
*/
|
|
@@ -5430,7 +6006,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5430
6006
|
this.expoMTLS = ExpoMutualTls;
|
|
5431
6007
|
// Set up debug logging with the correct event signature
|
|
5432
6008
|
const debugListener = ExpoMutualTls.onDebugLog((event) => {
|
|
5433
|
-
log$
|
|
6009
|
+
log$7.debug(`${event.type}: ${event.message}`, {
|
|
5434
6010
|
method: event.method,
|
|
5435
6011
|
url: event.url,
|
|
5436
6012
|
statusCode: event.statusCode,
|
|
@@ -5440,28 +6016,28 @@ class ReactNativeMTLSAdapter {
|
|
|
5440
6016
|
this.eventListeners.push(debugListener);
|
|
5441
6017
|
// Set up error logging with the correct event signature
|
|
5442
6018
|
const errorListener = ExpoMutualTls.onError((event) => {
|
|
5443
|
-
log$
|
|
6019
|
+
log$7.error(event.message, {
|
|
5444
6020
|
code: event.code,
|
|
5445
6021
|
});
|
|
5446
6022
|
});
|
|
5447
6023
|
this.eventListeners.push(errorListener);
|
|
5448
6024
|
// Set up certificate expiry monitoring with the correct event signature
|
|
5449
6025
|
const expiryListener = ExpoMutualTls.onCertificateExpiry((event) => {
|
|
5450
|
-
log$
|
|
6026
|
+
log$7.warn(`Certificate ${event.subject} expires at ${new Date(event.expiry)}`, {
|
|
5451
6027
|
alias: event.alias,
|
|
5452
6028
|
warning: event.warning,
|
|
5453
6029
|
});
|
|
5454
6030
|
});
|
|
5455
6031
|
this.eventListeners.push(expiryListener);
|
|
5456
|
-
log$
|
|
6032
|
+
log$7.debug('Expo mTLS module loaded successfully');
|
|
5457
6033
|
}
|
|
5458
6034
|
catch (error) {
|
|
5459
|
-
log$
|
|
6035
|
+
log$7.warn('@a-cube-io/expo-mutual-tls not available:', error);
|
|
5460
6036
|
}
|
|
5461
6037
|
}
|
|
5462
6038
|
async isMTLSSupported() {
|
|
5463
6039
|
const supported = this.expoMTLS !== null;
|
|
5464
|
-
log$
|
|
6040
|
+
log$7.debug('mTLS support check:', {
|
|
5465
6041
|
supported,
|
|
5466
6042
|
platform: this.getPlatformInfo().platform,
|
|
5467
6043
|
moduleAvailable: !!this.expoMTLS,
|
|
@@ -5473,7 +6049,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5473
6049
|
throw new MTLSError(exports.MTLSErrorType.NOT_SUPPORTED, 'Expo mTLS module not available');
|
|
5474
6050
|
}
|
|
5475
6051
|
this.config = config;
|
|
5476
|
-
log$
|
|
6052
|
+
log$7.debug('Initialized with config:', {
|
|
5477
6053
|
baseUrl: config.baseUrl,
|
|
5478
6054
|
port: config.port,
|
|
5479
6055
|
timeout: config.timeout,
|
|
@@ -5487,7 +6063,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5487
6063
|
if (!this.config) {
|
|
5488
6064
|
throw new MTLSError(exports.MTLSErrorType.CONFIGURATION_ERROR, 'Adapter not initialized. Call initialize() first.');
|
|
5489
6065
|
}
|
|
5490
|
-
log$
|
|
6066
|
+
log$7.debug('Configuring certificate:', {
|
|
5491
6067
|
format: certificateData.format,
|
|
5492
6068
|
hasPassword: !!certificateData.password,
|
|
5493
6069
|
certificateLength: certificateData.certificate.length,
|
|
@@ -5504,14 +6080,14 @@ class ReactNativeMTLSAdapter {
|
|
|
5504
6080
|
'client-key-service', // keyService
|
|
5505
6081
|
true // enableLogging - let the native module handle its own debug logging
|
|
5506
6082
|
);
|
|
5507
|
-
log$
|
|
6083
|
+
log$7.debug('PEM services configured:', configResult);
|
|
5508
6084
|
if (!configResult.success) {
|
|
5509
6085
|
throw new MTLSError(exports.MTLSErrorType.CONFIGURATION_ERROR, `PEM configuration failed: ${configResult.state}`);
|
|
5510
6086
|
}
|
|
5511
6087
|
// Step 2: Store the actual PEM certificate and private key
|
|
5512
6088
|
const storeResult = await this.expoMTLS.storePEM(certificateData.certificate, certificateData.privateKey, certificateData.password // passphrase (optional)
|
|
5513
6089
|
);
|
|
5514
|
-
log$
|
|
6090
|
+
log$7.debug('PEM certificate store result:', storeResult);
|
|
5515
6091
|
if (!storeResult) {
|
|
5516
6092
|
throw new MTLSError(exports.MTLSErrorType.CERTIFICATE_INVALID, 'Failed to store PEM certificate');
|
|
5517
6093
|
}
|
|
@@ -5524,14 +6100,14 @@ class ReactNativeMTLSAdapter {
|
|
|
5524
6100
|
const configResult = await this.expoMTLS.configureP12('client-p12-service', // keychainService
|
|
5525
6101
|
true // enableLogging - let the native module handle its own debug logging
|
|
5526
6102
|
);
|
|
5527
|
-
log$
|
|
6103
|
+
log$7.debug('P12 service configured:', configResult);
|
|
5528
6104
|
if (!configResult.success) {
|
|
5529
6105
|
throw new MTLSError(exports.MTLSErrorType.CONFIGURATION_ERROR, `P12 configuration failed: ${configResult.state}`);
|
|
5530
6106
|
}
|
|
5531
6107
|
// Step 2: Store the P12 certificate data
|
|
5532
6108
|
const storeResult = await this.expoMTLS.storeP12(certificateData.certificate, // P12 data in certificate field
|
|
5533
6109
|
certificateData.password);
|
|
5534
|
-
log$
|
|
6110
|
+
log$7.debug('P12 certificate store result:', storeResult);
|
|
5535
6111
|
if (!storeResult) {
|
|
5536
6112
|
throw new MTLSError(exports.MTLSErrorType.CERTIFICATE_INVALID, 'Failed to store P12 certificate');
|
|
5537
6113
|
}
|
|
@@ -5545,7 +6121,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5545
6121
|
if (error instanceof MTLSError) {
|
|
5546
6122
|
throw error;
|
|
5547
6123
|
}
|
|
5548
|
-
log$
|
|
6124
|
+
log$7.error('Certificate configuration failed:', error);
|
|
5549
6125
|
throw new MTLSError(exports.MTLSErrorType.CONFIGURATION_ERROR, 'Failed to configure certificate', error);
|
|
5550
6126
|
}
|
|
5551
6127
|
}
|
|
@@ -5556,38 +6132,38 @@ class ReactNativeMTLSAdapter {
|
|
|
5556
6132
|
try {
|
|
5557
6133
|
// Use static method call
|
|
5558
6134
|
const hasCert = await this.expoMTLS.hasCertificate();
|
|
5559
|
-
log$
|
|
6135
|
+
log$7.debug('Certificate availability check:', hasCert);
|
|
5560
6136
|
return hasCert;
|
|
5561
6137
|
}
|
|
5562
6138
|
catch (error) {
|
|
5563
|
-
log$
|
|
6139
|
+
log$7.error('Certificate check failed:', error);
|
|
5564
6140
|
return false;
|
|
5565
6141
|
}
|
|
5566
6142
|
}
|
|
5567
6143
|
async getCertificateInfo() {
|
|
5568
6144
|
if (!this.expoMTLS) {
|
|
5569
|
-
log$
|
|
6145
|
+
log$7.debug('Certificate info requested but module not available');
|
|
5570
6146
|
return null;
|
|
5571
6147
|
}
|
|
5572
6148
|
try {
|
|
5573
6149
|
const hasCert = await this.hasCertificate();
|
|
5574
6150
|
if (!hasCert) {
|
|
5575
|
-
log$
|
|
6151
|
+
log$7.debug('No certificate stored');
|
|
5576
6152
|
return null;
|
|
5577
6153
|
}
|
|
5578
6154
|
// Use getCertificatesInfo to retrieve information about stored certificates
|
|
5579
6155
|
const result = await this.expoMTLS.getCertificatesInfo();
|
|
5580
6156
|
if (!result || !result.certificates || result.certificates.length === 0) {
|
|
5581
|
-
log$
|
|
6157
|
+
log$7.debug('No certificate information available');
|
|
5582
6158
|
return null;
|
|
5583
6159
|
}
|
|
5584
6160
|
// Get the first certificate (primary client certificate)
|
|
5585
6161
|
const cert = result.certificates[0];
|
|
5586
6162
|
if (!cert) {
|
|
5587
|
-
log$
|
|
6163
|
+
log$7.debug('Certificate data is empty');
|
|
5588
6164
|
return null;
|
|
5589
6165
|
}
|
|
5590
|
-
log$
|
|
6166
|
+
log$7.debug('Retrieved certificate info:', {
|
|
5591
6167
|
subject: cert.subject.commonName,
|
|
5592
6168
|
issuer: cert.issuer.commonName,
|
|
5593
6169
|
validFrom: new Date(cert.validFrom),
|
|
@@ -5606,7 +6182,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5606
6182
|
};
|
|
5607
6183
|
}
|
|
5608
6184
|
catch (error) {
|
|
5609
|
-
log$
|
|
6185
|
+
log$7.error('Failed to get certificate info:', error);
|
|
5610
6186
|
return null;
|
|
5611
6187
|
}
|
|
5612
6188
|
}
|
|
@@ -5617,7 +6193,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5617
6193
|
*/
|
|
5618
6194
|
async parseCertificateData(certificateData) {
|
|
5619
6195
|
if (!this.expoMTLS) {
|
|
5620
|
-
log$
|
|
6196
|
+
log$7.debug('Parse certificate: Module not available');
|
|
5621
6197
|
return null;
|
|
5622
6198
|
}
|
|
5623
6199
|
try {
|
|
@@ -5634,14 +6210,14 @@ class ReactNativeMTLSAdapter {
|
|
|
5634
6210
|
else {
|
|
5635
6211
|
throw new MTLSError(exports.MTLSErrorType.CERTIFICATE_INVALID, `Unsupported certificate format: ${certificateData.format}`);
|
|
5636
6212
|
}
|
|
5637
|
-
log$
|
|
6213
|
+
log$7.debug('Certificate parsed successfully:', {
|
|
5638
6214
|
certificateCount: result.certificates.length,
|
|
5639
6215
|
subjects: result.certificates.map((cert) => cert.subject.commonName),
|
|
5640
6216
|
});
|
|
5641
6217
|
return result;
|
|
5642
6218
|
}
|
|
5643
6219
|
catch (error) {
|
|
5644
|
-
log$
|
|
6220
|
+
log$7.error('Failed to parse certificate:', error);
|
|
5645
6221
|
if (error instanceof MTLSError) {
|
|
5646
6222
|
throw error;
|
|
5647
6223
|
}
|
|
@@ -5656,7 +6232,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5656
6232
|
if (!hasCert) {
|
|
5657
6233
|
throw new MTLSError(exports.MTLSErrorType.CERTIFICATE_NOT_FOUND, 'No certificate configured');
|
|
5658
6234
|
}
|
|
5659
|
-
log$
|
|
6235
|
+
log$7.debug('Making mTLS request:', {
|
|
5660
6236
|
method: requestConfig.method || 'GET',
|
|
5661
6237
|
url: requestConfig.url,
|
|
5662
6238
|
headers: requestConfig.headers,
|
|
@@ -5664,7 +6240,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5664
6240
|
responseType: requestConfig.responseType,
|
|
5665
6241
|
});
|
|
5666
6242
|
if (requestConfig.data) {
|
|
5667
|
-
log$
|
|
6243
|
+
log$7.debug('mTLS request body:', requestConfig.data);
|
|
5668
6244
|
}
|
|
5669
6245
|
try {
|
|
5670
6246
|
const response = await this.expoMTLS.request(requestConfig.url, {
|
|
@@ -5673,7 +6249,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5673
6249
|
body: requestConfig.data ? JSON.stringify(requestConfig.data) : undefined,
|
|
5674
6250
|
responseType: requestConfig.responseType,
|
|
5675
6251
|
});
|
|
5676
|
-
log$
|
|
6252
|
+
log$7.debug('mTLS request successful:', response);
|
|
5677
6253
|
if (!response.success) {
|
|
5678
6254
|
throw new MTLSError(exports.MTLSErrorType.CONNECTION_FAILED, `mTLS request failed: ${response.statusMessage} (${response.statusCode})`, undefined, response.statusCode);
|
|
5679
6255
|
}
|
|
@@ -5685,7 +6261,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5685
6261
|
data = JSON.parse(response.body);
|
|
5686
6262
|
}
|
|
5687
6263
|
catch (parseError) {
|
|
5688
|
-
log$
|
|
6264
|
+
log$7.warn('Failed to parse JSON response:', parseError);
|
|
5689
6265
|
// If parsing fails, keep raw body
|
|
5690
6266
|
}
|
|
5691
6267
|
}
|
|
@@ -5702,7 +6278,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5702
6278
|
};
|
|
5703
6279
|
}
|
|
5704
6280
|
catch (error) {
|
|
5705
|
-
log$
|
|
6281
|
+
log$7.error('mTLS request failed:', error);
|
|
5706
6282
|
throw new MTLSError(exports.MTLSErrorType.CONNECTION_FAILED, 'mTLS request failed', error);
|
|
5707
6283
|
}
|
|
5708
6284
|
}
|
|
@@ -5715,18 +6291,18 @@ class ReactNativeMTLSAdapter {
|
|
|
5715
6291
|
*/
|
|
5716
6292
|
async testConnection() {
|
|
5717
6293
|
if (!this.expoMTLS || !this.config) {
|
|
5718
|
-
log$
|
|
6294
|
+
log$7.debug('Diagnostic test: No mTLS module or config available');
|
|
5719
6295
|
return false;
|
|
5720
6296
|
}
|
|
5721
6297
|
try {
|
|
5722
6298
|
const hasCert = await this.hasCertificate();
|
|
5723
6299
|
if (!hasCert) {
|
|
5724
|
-
log$
|
|
6300
|
+
log$7.debug('Diagnostic test: No certificate configured');
|
|
5725
6301
|
return false;
|
|
5726
6302
|
}
|
|
5727
|
-
log$
|
|
6303
|
+
log$7.debug('Running diagnostic test (may fail even if mTLS works):', this.config.baseUrl);
|
|
5728
6304
|
const result = await this.expoMTLS.testConnection(this.config.baseUrl);
|
|
5729
|
-
log$
|
|
6305
|
+
log$7.debug('Diagnostic test result (NOT validation):', {
|
|
5730
6306
|
success: result.success,
|
|
5731
6307
|
statusCode: result.statusCode,
|
|
5732
6308
|
statusMessage: result.statusMessage,
|
|
@@ -5737,13 +6313,13 @@ class ReactNativeMTLSAdapter {
|
|
|
5737
6313
|
return result.success;
|
|
5738
6314
|
}
|
|
5739
6315
|
catch (error) {
|
|
5740
|
-
log$
|
|
6316
|
+
log$7.warn('Diagnostic test failed (this is expected):', error);
|
|
5741
6317
|
return false;
|
|
5742
6318
|
}
|
|
5743
6319
|
}
|
|
5744
6320
|
async removeCertificate() {
|
|
5745
6321
|
if (!this.expoMTLS) {
|
|
5746
|
-
log$
|
|
6322
|
+
log$7.debug('Remove certificate: Module not available');
|
|
5747
6323
|
return;
|
|
5748
6324
|
}
|
|
5749
6325
|
try {
|
|
@@ -5752,10 +6328,10 @@ class ReactNativeMTLSAdapter {
|
|
|
5752
6328
|
this.isConfigured = false;
|
|
5753
6329
|
// Cleanup event listeners
|
|
5754
6330
|
this.cleanupEventListeners();
|
|
5755
|
-
log$
|
|
6331
|
+
log$7.debug('Certificate removed successfully');
|
|
5756
6332
|
}
|
|
5757
6333
|
catch (error) {
|
|
5758
|
-
log$
|
|
6334
|
+
log$7.error('Failed to remove certificate:', error);
|
|
5759
6335
|
throw new MTLSError(exports.MTLSErrorType.CONFIGURATION_ERROR, 'Failed to remove certificate', error);
|
|
5760
6336
|
}
|
|
5761
6337
|
}
|
|
@@ -5764,7 +6340,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5764
6340
|
*/
|
|
5765
6341
|
cleanupEventListeners() {
|
|
5766
6342
|
if (this.eventListeners.length > 0) {
|
|
5767
|
-
log$
|
|
6343
|
+
log$7.debug(`Cleaning up ${this.eventListeners.length} event listeners`);
|
|
5768
6344
|
}
|
|
5769
6345
|
// Remove individual listeners if they have remove methods
|
|
5770
6346
|
this.eventListeners.forEach((listener) => {
|
|
@@ -5801,7 +6377,7 @@ class ReactNativeMTLSAdapter {
|
|
|
5801
6377
|
}
|
|
5802
6378
|
}
|
|
5803
6379
|
|
|
5804
|
-
const log$
|
|
6380
|
+
const log$6 = createPrefixedLogger('WEB-MTLS');
|
|
5805
6381
|
/**
|
|
5806
6382
|
* Web mTLS Adapter - Graceful fallback for web browsers
|
|
5807
6383
|
*
|
|
@@ -5815,13 +6391,13 @@ const log$7 = createPrefixedLogger('WEB-MTLS');
|
|
|
5815
6391
|
*/
|
|
5816
6392
|
class WebMTLSAdapter {
|
|
5817
6393
|
constructor() {
|
|
5818
|
-
log$
|
|
5819
|
-
log$
|
|
6394
|
+
log$6.warn('Web browsers do not support programmatic mTLS configuration');
|
|
6395
|
+
log$6.info('Use JWT authentication or configure client certificates in browser settings');
|
|
5820
6396
|
}
|
|
5821
6397
|
async isMTLSSupported() {
|
|
5822
6398
|
// mTLS is not supported programmatically in web browsers
|
|
5823
6399
|
const supported = false;
|
|
5824
|
-
log$
|
|
6400
|
+
log$6.debug('mTLS support check:', {
|
|
5825
6401
|
supported,
|
|
5826
6402
|
platform: this.getPlatformInfo().platform,
|
|
5827
6403
|
reason: 'Browser security model prevents programmatic certificate configuration',
|
|
@@ -5830,14 +6406,14 @@ class WebMTLSAdapter {
|
|
|
5830
6406
|
return supported;
|
|
5831
6407
|
}
|
|
5832
6408
|
async initialize(config) {
|
|
5833
|
-
log$
|
|
6409
|
+
log$6.warn('Initialized but mTLS not available in web browsers:', {
|
|
5834
6410
|
baseUrl: config.baseUrl,
|
|
5835
6411
|
port: config.port,
|
|
5836
6412
|
recommendation: 'Use standard HTTPS with JWT authentication',
|
|
5837
6413
|
});
|
|
5838
6414
|
}
|
|
5839
6415
|
async configureCertificate(certificateData) {
|
|
5840
|
-
log$
|
|
6416
|
+
log$6.error('Certificate configuration attempted:', {
|
|
5841
6417
|
format: certificateData.format,
|
|
5842
6418
|
reason: 'Not supported in web browsers',
|
|
5843
6419
|
alternatives: [
|
|
@@ -5852,15 +6428,15 @@ class WebMTLSAdapter {
|
|
|
5852
6428
|
}
|
|
5853
6429
|
async hasCertificate() {
|
|
5854
6430
|
// We cannot detect if the browser has certificates configured
|
|
5855
|
-
log$
|
|
6431
|
+
log$6.debug('Certificate availability check: Cannot detect browser certificates programmatically');
|
|
5856
6432
|
return false;
|
|
5857
6433
|
}
|
|
5858
6434
|
async getCertificateInfo() {
|
|
5859
|
-
log$
|
|
6435
|
+
log$6.debug('Certificate info requested: Not accessible in web browsers');
|
|
5860
6436
|
return null;
|
|
5861
6437
|
}
|
|
5862
6438
|
async request(requestConfig) {
|
|
5863
|
-
log$
|
|
6439
|
+
log$6.error('mTLS request attempted:', {
|
|
5864
6440
|
method: requestConfig.method,
|
|
5865
6441
|
url: requestConfig.url,
|
|
5866
6442
|
reason: 'Not supported in web browsers',
|
|
@@ -5875,11 +6451,11 @@ class WebMTLSAdapter {
|
|
|
5875
6451
|
'are properly configured in the browser certificate store.');
|
|
5876
6452
|
}
|
|
5877
6453
|
async testConnection() {
|
|
5878
|
-
log$
|
|
6454
|
+
log$6.debug('Connection test: mTLS not available in web browsers');
|
|
5879
6455
|
return false;
|
|
5880
6456
|
}
|
|
5881
6457
|
async removeCertificate() {
|
|
5882
|
-
log$
|
|
6458
|
+
log$6.debug('Remove certificate: No certificates to remove (not supported in web browsers)');
|
|
5883
6459
|
// No-op - cannot remove certificates programmatically in browsers
|
|
5884
6460
|
}
|
|
5885
6461
|
/**
|
|
@@ -5887,7 +6463,7 @@ class WebMTLSAdapter {
|
|
|
5887
6463
|
* Always returns null for web browsers as mTLS is not supported
|
|
5888
6464
|
*/
|
|
5889
6465
|
getBaseUrl() {
|
|
5890
|
-
log$
|
|
6466
|
+
log$6.debug('Base URL requested: Not supported in web browsers');
|
|
5891
6467
|
return null;
|
|
5892
6468
|
}
|
|
5893
6469
|
getPlatformInfo() {
|
|
@@ -5920,7 +6496,7 @@ class WebMTLSAdapter {
|
|
|
5920
6496
|
}
|
|
5921
6497
|
}
|
|
5922
6498
|
|
|
5923
|
-
const log$
|
|
6499
|
+
const log$5 = createPrefixedLogger('MTLS-LOADER');
|
|
5924
6500
|
function loadMTLSAdapter(platform, config) {
|
|
5925
6501
|
try {
|
|
5926
6502
|
let adapter;
|
|
@@ -5954,7 +6530,7 @@ function loadMTLSAdapter(platform, config) {
|
|
|
5954
6530
|
return adapter;
|
|
5955
6531
|
}
|
|
5956
6532
|
catch (error) {
|
|
5957
|
-
log$
|
|
6533
|
+
log$5.warn(`mTLS adapter not available for platform ${platform}:`, error);
|
|
5958
6534
|
return null;
|
|
5959
6535
|
}
|
|
5960
6536
|
}
|
|
@@ -5964,7 +6540,7 @@ async function initializeAdapterAsync(adapter, config) {
|
|
|
5964
6540
|
if (isSupported) {
|
|
5965
6541
|
await adapter.initialize(config);
|
|
5966
6542
|
const platformInfo = adapter.getPlatformInfo();
|
|
5967
|
-
log$
|
|
6543
|
+
log$5.debug('mTLS adapter initialized:', {
|
|
5968
6544
|
platform: platformInfo.platform,
|
|
5969
6545
|
mtlsSupported: platformInfo.mtlsSupported,
|
|
5970
6546
|
certificateStorage: platformInfo.certificateStorage,
|
|
@@ -5973,20 +6549,20 @@ async function initializeAdapterAsync(adapter, config) {
|
|
|
5973
6549
|
}
|
|
5974
6550
|
}
|
|
5975
6551
|
catch (error) {
|
|
5976
|
-
log$
|
|
6552
|
+
log$5.warn('Failed to initialize mTLS adapter:', error);
|
|
5977
6553
|
}
|
|
5978
6554
|
}
|
|
5979
6555
|
|
|
5980
|
-
const log$
|
|
6556
|
+
const log$4 = createPrefixedLogger('ADAPTER-LOADER');
|
|
5981
6557
|
function loadPlatformAdapters(options = {}) {
|
|
5982
6558
|
const { mtlsConfig } = options;
|
|
5983
6559
|
const { platform } = detectPlatform();
|
|
5984
|
-
log$
|
|
6560
|
+
log$4.debug('Loading adapters for platform:', platform);
|
|
5985
6561
|
const storageAdapters = loadStorageAdapters(platform);
|
|
5986
6562
|
const networkMonitor = loadNetworkMonitor(platform);
|
|
5987
6563
|
const cache = loadCacheAdapter(platform);
|
|
5988
6564
|
const mtls = loadMTLSAdapter(platform, mtlsConfig);
|
|
5989
|
-
log$
|
|
6565
|
+
log$4.debug('Adapters loaded:', {
|
|
5990
6566
|
platform,
|
|
5991
6567
|
hasStorage: !!storageAdapters.storage,
|
|
5992
6568
|
hasSecureStorage: !!storageAdapters.secureStorage,
|
|
@@ -6012,92 +6588,6 @@ function createACubeMTLSConfig(baseUrl, timeout, autoInitialize = true, forcePor
|
|
|
6012
6588
|
};
|
|
6013
6589
|
}
|
|
6014
6590
|
|
|
6015
|
-
class ConfigManager {
|
|
6016
|
-
constructor(userConfig) {
|
|
6017
|
-
this.config = this.buildConfig(userConfig);
|
|
6018
|
-
}
|
|
6019
|
-
buildConfig(userConfig) {
|
|
6020
|
-
return {
|
|
6021
|
-
environment: userConfig.environment,
|
|
6022
|
-
apiUrl: this.getDefaultApiUrl(userConfig.environment),
|
|
6023
|
-
authUrl: this.getDefaultAuthUrl(userConfig.environment),
|
|
6024
|
-
timeout: 30000,
|
|
6025
|
-
retryAttempts: 3,
|
|
6026
|
-
debug: userConfig.debug ?? false,
|
|
6027
|
-
customHeaders: {},
|
|
6028
|
-
};
|
|
6029
|
-
}
|
|
6030
|
-
getDefaultApiUrl(environment) {
|
|
6031
|
-
switch (environment) {
|
|
6032
|
-
case 'production':
|
|
6033
|
-
return 'https://ereceipts-it.acubeapi.com';
|
|
6034
|
-
case 'development':
|
|
6035
|
-
return 'https://ereceipts-it.dev.acubeapi.com';
|
|
6036
|
-
case 'sandbox':
|
|
6037
|
-
default:
|
|
6038
|
-
return 'https://ereceipts-it-sandbox.acubeapi.com';
|
|
6039
|
-
}
|
|
6040
|
-
}
|
|
6041
|
-
getDefaultAuthUrl(environment) {
|
|
6042
|
-
switch (environment) {
|
|
6043
|
-
case 'production':
|
|
6044
|
-
return 'https://common.api.acubeapi.com';
|
|
6045
|
-
case 'development':
|
|
6046
|
-
case 'sandbox':
|
|
6047
|
-
default:
|
|
6048
|
-
return 'https://common-sandbox.api.acubeapi.com';
|
|
6049
|
-
}
|
|
6050
|
-
}
|
|
6051
|
-
getConfig() {
|
|
6052
|
-
return {
|
|
6053
|
-
environment: this.config.environment,
|
|
6054
|
-
debug: this.config.debug,
|
|
6055
|
-
};
|
|
6056
|
-
}
|
|
6057
|
-
getApiUrl() {
|
|
6058
|
-
return this.config.apiUrl;
|
|
6059
|
-
}
|
|
6060
|
-
getAuthUrl() {
|
|
6061
|
-
return this.config.authUrl;
|
|
6062
|
-
}
|
|
6063
|
-
getEnvironment() {
|
|
6064
|
-
return this.config.environment;
|
|
6065
|
-
}
|
|
6066
|
-
isDebugEnabled() {
|
|
6067
|
-
return this.config.debug;
|
|
6068
|
-
}
|
|
6069
|
-
getTimeout() {
|
|
6070
|
-
return this.config.timeout;
|
|
6071
|
-
}
|
|
6072
|
-
getRetryAttempts() {
|
|
6073
|
-
return this.config.retryAttempts;
|
|
6074
|
-
}
|
|
6075
|
-
getCustomHeaders() {
|
|
6076
|
-
return { ...this.config.customHeaders };
|
|
6077
|
-
}
|
|
6078
|
-
updateConfig(updates) {
|
|
6079
|
-
if (updates.environment) {
|
|
6080
|
-
this.config.environment = updates.environment;
|
|
6081
|
-
this.config.apiUrl = this.getDefaultApiUrl(updates.environment);
|
|
6082
|
-
this.config.authUrl = this.getDefaultAuthUrl(updates.environment);
|
|
6083
|
-
}
|
|
6084
|
-
if (updates.debug !== undefined) {
|
|
6085
|
-
this.config.debug = updates.debug;
|
|
6086
|
-
}
|
|
6087
|
-
}
|
|
6088
|
-
}
|
|
6089
|
-
|
|
6090
|
-
class ACubeSDKError extends Error {
|
|
6091
|
-
constructor(type, message, originalError, statusCode, violations) {
|
|
6092
|
-
super(message);
|
|
6093
|
-
this.type = type;
|
|
6094
|
-
this.originalError = originalError;
|
|
6095
|
-
this.statusCode = statusCode;
|
|
6096
|
-
this.name = 'ACubeSDKError';
|
|
6097
|
-
this.violations = violations;
|
|
6098
|
-
}
|
|
6099
|
-
}
|
|
6100
|
-
|
|
6101
6591
|
const DI_TOKENS = {
|
|
6102
6592
|
HTTP_PORT: Symbol('HTTP_PORT'),
|
|
6103
6593
|
BASE_HTTP_PORT: Symbol('BASE_HTTP_PORT'),
|
|
@@ -7098,7 +7588,7 @@ class TelemetryRepositoryImpl {
|
|
|
7098
7588
|
}
|
|
7099
7589
|
}
|
|
7100
7590
|
|
|
7101
|
-
const log$
|
|
7591
|
+
const log$3 = createPrefixedLogger('CACHE-KEY');
|
|
7102
7592
|
const URL_PATTERNS = [
|
|
7103
7593
|
// Receipt (mf1) - specific patterns first
|
|
7104
7594
|
{
|
|
@@ -7191,7 +7681,7 @@ const DEFAULT_TTL = 5 * 60 * 1000; // 5 minutes
|
|
|
7191
7681
|
class CacheKeyGenerator {
|
|
7192
7682
|
constructor(customConfig) {
|
|
7193
7683
|
this.config = { ...DEFAULT_TTL_CONFIG, ...customConfig };
|
|
7194
|
-
log$
|
|
7684
|
+
log$3.info('CacheKeyGenerator initialized with config:', {
|
|
7195
7685
|
resources: Object.keys(this.config),
|
|
7196
7686
|
});
|
|
7197
7687
|
}
|
|
@@ -7201,7 +7691,7 @@ class CacheKeyGenerator {
|
|
|
7201
7691
|
// Fallback: use URL as key
|
|
7202
7692
|
const paramStr = params ? this.serializeParams(params) : '';
|
|
7203
7693
|
const key = paramStr ? `${url}?${paramStr}` : url;
|
|
7204
|
-
log$
|
|
7694
|
+
log$3.debug('URL not matched, using fallback key:', { url, key });
|
|
7205
7695
|
return key;
|
|
7206
7696
|
}
|
|
7207
7697
|
const { resource, ids, action, isList, parent } = parsed;
|
|
@@ -7209,21 +7699,21 @@ class CacheKeyGenerator {
|
|
|
7209
7699
|
const paramStr = params ? this.serializeParams(params) : '';
|
|
7210
7700
|
const parentPart = parent && ids.length > 0 ? `${parent}=${ids[0]}&` : '';
|
|
7211
7701
|
const key = `${resource}:list:${parentPart}${paramStr}`;
|
|
7212
|
-
log$
|
|
7702
|
+
log$3.debug('Generated list cache key:', { url, key, resource });
|
|
7213
7703
|
return key;
|
|
7214
7704
|
}
|
|
7215
7705
|
// Single item
|
|
7216
7706
|
if (ids.length === 0 && action) {
|
|
7217
7707
|
// Special case for endpoints like /cashiers/me
|
|
7218
7708
|
const key = `${resource}:${action}`;
|
|
7219
|
-
log$
|
|
7709
|
+
log$3.debug('Generated special action cache key:', { url, key, resource, action });
|
|
7220
7710
|
return key;
|
|
7221
7711
|
}
|
|
7222
7712
|
let key = `${resource}:${ids.join(':')}`;
|
|
7223
7713
|
if (action) {
|
|
7224
7714
|
key += `:${action}`;
|
|
7225
7715
|
}
|
|
7226
|
-
log$
|
|
7716
|
+
log$3.debug('Generated item cache key:', { url, key, resource, ids, action });
|
|
7227
7717
|
return key;
|
|
7228
7718
|
}
|
|
7229
7719
|
parseResource(url) {
|
|
@@ -7233,23 +7723,23 @@ class CacheKeyGenerator {
|
|
|
7233
7723
|
getTTL(url) {
|
|
7234
7724
|
const resource = this.parseResource(url);
|
|
7235
7725
|
if (!resource) {
|
|
7236
|
-
log$
|
|
7726
|
+
log$3.debug('No resource found for URL, using default TTL:', { url, ttl: DEFAULT_TTL });
|
|
7237
7727
|
return DEFAULT_TTL;
|
|
7238
7728
|
}
|
|
7239
7729
|
const ttl = this.config[resource].ttlMs;
|
|
7240
|
-
log$
|
|
7730
|
+
log$3.debug('TTL for resource:', { url, resource, ttlMs: ttl, ttlMin: ttl / 60000 });
|
|
7241
7731
|
return ttl;
|
|
7242
7732
|
}
|
|
7243
7733
|
shouldCache(url) {
|
|
7244
7734
|
const parsed = this.parseUrl(url);
|
|
7245
7735
|
if (!parsed) {
|
|
7246
|
-
log$
|
|
7736
|
+
log$3.debug('URL not recognized, should not cache:', { url });
|
|
7247
7737
|
return false;
|
|
7248
7738
|
}
|
|
7249
7739
|
const { resource, isList } = parsed;
|
|
7250
7740
|
const config = this.config[resource];
|
|
7251
7741
|
if (isList) {
|
|
7252
|
-
log$
|
|
7742
|
+
log$3.debug('List endpoint cache decision:', {
|
|
7253
7743
|
url,
|
|
7254
7744
|
resource,
|
|
7255
7745
|
isList: true,
|
|
@@ -7257,7 +7747,7 @@ class CacheKeyGenerator {
|
|
|
7257
7747
|
});
|
|
7258
7748
|
return config.cacheList;
|
|
7259
7749
|
}
|
|
7260
|
-
log$
|
|
7750
|
+
log$3.debug('Item endpoint cache decision:', {
|
|
7261
7751
|
url,
|
|
7262
7752
|
resource,
|
|
7263
7753
|
isList: false,
|
|
@@ -7268,7 +7758,7 @@ class CacheKeyGenerator {
|
|
|
7268
7758
|
getInvalidationPatterns(url, method) {
|
|
7269
7759
|
const parsed = this.parseUrl(url);
|
|
7270
7760
|
if (!parsed) {
|
|
7271
|
-
log$
|
|
7761
|
+
log$3.debug('No patterns to invalidate for URL:', { url, method });
|
|
7272
7762
|
return [];
|
|
7273
7763
|
}
|
|
7274
7764
|
const { resource, ids, parent } = parsed;
|
|
@@ -7290,7 +7780,7 @@ class CacheKeyGenerator {
|
|
|
7290
7780
|
if (resource === 'cashier' && (method === 'PUT' || method === 'DELETE')) {
|
|
7291
7781
|
patterns.push('cashier:me');
|
|
7292
7782
|
}
|
|
7293
|
-
log$
|
|
7783
|
+
log$3.debug('Invalidation patterns:', { url, method, patterns });
|
|
7294
7784
|
return patterns;
|
|
7295
7785
|
}
|
|
7296
7786
|
parseUrl(url) {
|
|
@@ -7325,7 +7815,7 @@ class CacheKeyGenerator {
|
|
|
7325
7815
|
}
|
|
7326
7816
|
}
|
|
7327
7817
|
|
|
7328
|
-
const log$
|
|
7818
|
+
const log$2 = createPrefixedLogger('CACHE');
|
|
7329
7819
|
class CachingHttpDecorator {
|
|
7330
7820
|
constructor(http, cache, keyGenerator, networkMonitor, config = {}) {
|
|
7331
7821
|
this.http = http;
|
|
@@ -7335,7 +7825,7 @@ class CachingHttpDecorator {
|
|
|
7335
7825
|
this.config = config;
|
|
7336
7826
|
this.currentOnlineState = true;
|
|
7337
7827
|
this.authToken = null;
|
|
7338
|
-
log$
|
|
7828
|
+
log$2.info('CachingHttpDecorator initialized', {
|
|
7339
7829
|
enabled: config.enabled !== false,
|
|
7340
7830
|
hasNetworkMonitor: !!networkMonitor,
|
|
7341
7831
|
});
|
|
@@ -7345,7 +7835,7 @@ class CachingHttpDecorator {
|
|
|
7345
7835
|
if (this.networkMonitor) {
|
|
7346
7836
|
this.networkSubscription = this.networkMonitor.online$.subscribe((online) => {
|
|
7347
7837
|
if (this.currentOnlineState !== online) {
|
|
7348
|
-
log$
|
|
7838
|
+
log$2.info('Network state changed:', { online });
|
|
7349
7839
|
}
|
|
7350
7840
|
this.currentOnlineState = online;
|
|
7351
7841
|
});
|
|
@@ -7364,19 +7854,19 @@ class CachingHttpDecorator {
|
|
|
7364
7854
|
const startTime = Date.now();
|
|
7365
7855
|
// Check if caching is disabled globally
|
|
7366
7856
|
if (this.config.enabled === false) {
|
|
7367
|
-
log$
|
|
7857
|
+
log$2.debug('GET (cache disabled globally):', { url });
|
|
7368
7858
|
return this.http.get(url, config);
|
|
7369
7859
|
}
|
|
7370
7860
|
// Check if this URL should be cached
|
|
7371
7861
|
const shouldCache = this.keyGenerator.shouldCache(url);
|
|
7372
7862
|
if (!shouldCache) {
|
|
7373
|
-
log$
|
|
7863
|
+
log$2.debug('GET (not cacheable - likely a list endpoint):', { url });
|
|
7374
7864
|
return this.http.get(url, config);
|
|
7375
7865
|
}
|
|
7376
7866
|
const cacheKey = this.keyGenerator.generate(url, config?.params);
|
|
7377
7867
|
const ttl = this.keyGenerator.getTTL(url);
|
|
7378
7868
|
const resource = this.keyGenerator.parseResource(url);
|
|
7379
|
-
log$
|
|
7869
|
+
log$2.info('GET request starting:', {
|
|
7380
7870
|
url,
|
|
7381
7871
|
resource,
|
|
7382
7872
|
cacheKey,
|
|
@@ -7389,18 +7879,18 @@ class CachingHttpDecorator {
|
|
|
7389
7879
|
try {
|
|
7390
7880
|
cached = await this.cache.get(cacheKey);
|
|
7391
7881
|
if (cached) {
|
|
7392
|
-
log$
|
|
7882
|
+
log$2.debug('Cache entry found:', {
|
|
7393
7883
|
cacheKey,
|
|
7394
7884
|
timestamp: new Date(cached.timestamp).toISOString(),
|
|
7395
7885
|
ageMs: Date.now() - cached.timestamp,
|
|
7396
7886
|
});
|
|
7397
7887
|
}
|
|
7398
7888
|
else {
|
|
7399
|
-
log$
|
|
7889
|
+
log$2.debug('Cache entry not found:', { cacheKey });
|
|
7400
7890
|
}
|
|
7401
7891
|
}
|
|
7402
7892
|
catch (error) {
|
|
7403
|
-
log$
|
|
7893
|
+
log$2.warn('Cache lookup failed:', {
|
|
7404
7894
|
cacheKey,
|
|
7405
7895
|
error: error instanceof Error ? error.message : error,
|
|
7406
7896
|
});
|
|
@@ -7408,7 +7898,7 @@ class CachingHttpDecorator {
|
|
|
7408
7898
|
if (cached) {
|
|
7409
7899
|
const age = Date.now() - cached.timestamp;
|
|
7410
7900
|
const isExpired = age >= ttl;
|
|
7411
|
-
log$
|
|
7901
|
+
log$2.debug('Cache analysis:', {
|
|
7412
7902
|
cacheKey,
|
|
7413
7903
|
ageMs: age,
|
|
7414
7904
|
ageSec: Math.round(age / 1000),
|
|
@@ -7419,7 +7909,7 @@ class CachingHttpDecorator {
|
|
|
7419
7909
|
// If within TTL, return cached data
|
|
7420
7910
|
if (!isExpired) {
|
|
7421
7911
|
const duration = Date.now() - startTime;
|
|
7422
|
-
log$
|
|
7912
|
+
log$2.info('CACHE HIT:', {
|
|
7423
7913
|
url,
|
|
7424
7914
|
cacheKey,
|
|
7425
7915
|
ageMs: age,
|
|
@@ -7434,7 +7924,7 @@ class CachingHttpDecorator {
|
|
|
7434
7924
|
// If offline and cache is stale, return stale data
|
|
7435
7925
|
if (!this.isOnline()) {
|
|
7436
7926
|
const duration = Date.now() - startTime;
|
|
7437
|
-
log$
|
|
7927
|
+
log$2.info('CACHE STALE (offline):', {
|
|
7438
7928
|
url,
|
|
7439
7929
|
cacheKey,
|
|
7440
7930
|
ageMs: age,
|
|
@@ -7446,25 +7936,25 @@ class CachingHttpDecorator {
|
|
|
7446
7936
|
headers: { 'x-cache': 'STALE' },
|
|
7447
7937
|
};
|
|
7448
7938
|
}
|
|
7449
|
-
log$
|
|
7939
|
+
log$2.debug('Cache expired, fetching fresh data:', { cacheKey, ageMs: age, ttlMs: ttl });
|
|
7450
7940
|
}
|
|
7451
7941
|
// Fetch fresh data
|
|
7452
7942
|
try {
|
|
7453
|
-
log$
|
|
7943
|
+
log$2.debug('Fetching from network:', { url });
|
|
7454
7944
|
const response = await this.http.get(url, config);
|
|
7455
7945
|
// Cache the response
|
|
7456
7946
|
try {
|
|
7457
7947
|
await this.cache.set(cacheKey, response.data);
|
|
7458
|
-
log$
|
|
7948
|
+
log$2.debug('Response cached successfully:', { cacheKey });
|
|
7459
7949
|
}
|
|
7460
7950
|
catch (error) {
|
|
7461
|
-
log$
|
|
7951
|
+
log$2.error('Failed to cache response:', {
|
|
7462
7952
|
cacheKey,
|
|
7463
7953
|
error: error instanceof Error ? error.message : error,
|
|
7464
7954
|
});
|
|
7465
7955
|
}
|
|
7466
7956
|
const duration = Date.now() - startTime;
|
|
7467
|
-
log$
|
|
7957
|
+
log$2.info('CACHE MISS (fetched fresh):', {
|
|
7468
7958
|
url,
|
|
7469
7959
|
cacheKey,
|
|
7470
7960
|
status: response.status,
|
|
@@ -7479,7 +7969,7 @@ class CachingHttpDecorator {
|
|
|
7479
7969
|
// On error, return stale cache if available
|
|
7480
7970
|
if (cached) {
|
|
7481
7971
|
const duration = Date.now() - startTime;
|
|
7482
|
-
log$
|
|
7972
|
+
log$2.warn('CACHE STALE (network error):', {
|
|
7483
7973
|
url,
|
|
7484
7974
|
cacheKey,
|
|
7485
7975
|
error: error instanceof Error ? error.message : 'Unknown error',
|
|
@@ -7491,7 +7981,7 @@ class CachingHttpDecorator {
|
|
|
7491
7981
|
headers: { 'x-cache': 'STALE' },
|
|
7492
7982
|
};
|
|
7493
7983
|
}
|
|
7494
|
-
log$
|
|
7984
|
+
log$2.error('Network error with no cache fallback:', {
|
|
7495
7985
|
url,
|
|
7496
7986
|
error: error instanceof Error ? error.message : error,
|
|
7497
7987
|
});
|
|
@@ -7499,31 +7989,35 @@ class CachingHttpDecorator {
|
|
|
7499
7989
|
}
|
|
7500
7990
|
}
|
|
7501
7991
|
async post(url, data, config) {
|
|
7502
|
-
log$
|
|
7992
|
+
log$2.info('POST request:', { url });
|
|
7503
7993
|
const response = await this.http.post(url, data, config);
|
|
7504
7994
|
await this.invalidateRelated(url, 'POST');
|
|
7505
7995
|
return response;
|
|
7506
7996
|
}
|
|
7507
7997
|
async put(url, data, config) {
|
|
7508
|
-
log$
|
|
7998
|
+
log$2.info('PUT request:', { url });
|
|
7509
7999
|
const response = await this.http.put(url, data, config);
|
|
7510
8000
|
await this.invalidateRelated(url, 'PUT');
|
|
7511
8001
|
return response;
|
|
7512
8002
|
}
|
|
7513
8003
|
async patch(url, data, config) {
|
|
7514
|
-
log$
|
|
8004
|
+
log$2.info('PATCH request:', { url });
|
|
7515
8005
|
const response = await this.http.patch(url, data, config);
|
|
7516
8006
|
await this.invalidateRelated(url, 'PATCH');
|
|
7517
8007
|
return response;
|
|
7518
8008
|
}
|
|
7519
8009
|
async delete(url, config) {
|
|
7520
|
-
log$
|
|
8010
|
+
log$2.info('DELETE request:', { url });
|
|
7521
8011
|
const response = await this.http.delete(url, config);
|
|
7522
8012
|
await this.invalidateRelated(url, 'DELETE');
|
|
7523
8013
|
return response;
|
|
7524
8014
|
}
|
|
7525
8015
|
setAuthToken(token) {
|
|
7526
|
-
log$
|
|
8016
|
+
log$2.debug('CachingHttpDecorator.setAuthToken called:', {
|
|
8017
|
+
hasToken: !!token,
|
|
8018
|
+
tokenPrefix: token?.substring(0, 20),
|
|
8019
|
+
underlyingHttpType: this.http.constructor.name,
|
|
8020
|
+
});
|
|
7527
8021
|
this.authToken = token;
|
|
7528
8022
|
this.http.setAuthToken(token);
|
|
7529
8023
|
}
|
|
@@ -7533,17 +8027,17 @@ class CachingHttpDecorator {
|
|
|
7533
8027
|
async invalidateRelated(url, method) {
|
|
7534
8028
|
const patterns = this.keyGenerator.getInvalidationPatterns(url, method);
|
|
7535
8029
|
if (patterns.length === 0) {
|
|
7536
|
-
log$
|
|
8030
|
+
log$2.debug('No cache patterns to invalidate:', { url, method });
|
|
7537
8031
|
return;
|
|
7538
8032
|
}
|
|
7539
|
-
log$
|
|
8033
|
+
log$2.info('Invalidating cache patterns:', { url, method, patterns });
|
|
7540
8034
|
for (const pattern of patterns) {
|
|
7541
8035
|
try {
|
|
7542
8036
|
await this.cache.invalidate(pattern);
|
|
7543
|
-
log$
|
|
8037
|
+
log$2.debug('Cache pattern invalidated:', { pattern });
|
|
7544
8038
|
}
|
|
7545
8039
|
catch (error) {
|
|
7546
|
-
log$
|
|
8040
|
+
log$2.error('Failed to invalidate pattern:', {
|
|
7547
8041
|
pattern,
|
|
7548
8042
|
error: error instanceof Error ? error.message : error,
|
|
7549
8043
|
});
|
|
@@ -7551,12 +8045,13 @@ class CachingHttpDecorator {
|
|
|
7551
8045
|
}
|
|
7552
8046
|
}
|
|
7553
8047
|
destroy() {
|
|
7554
|
-
log$
|
|
8048
|
+
log$2.debug('CachingHttpDecorator destroyed');
|
|
7555
8049
|
this.networkSubscription?.unsubscribe();
|
|
7556
8050
|
}
|
|
7557
8051
|
}
|
|
7558
8052
|
|
|
7559
|
-
const
|
|
8053
|
+
const logJwt = createPrefixedLogger('HTTP-JWT');
|
|
8054
|
+
const logMtls = createPrefixedLogger('HTTP-MTLS');
|
|
7560
8055
|
class AxiosHttpAdapter {
|
|
7561
8056
|
constructor(config) {
|
|
7562
8057
|
this.authToken = null;
|
|
@@ -7574,23 +8069,30 @@ class AxiosHttpAdapter {
|
|
|
7574
8069
|
}
|
|
7575
8070
|
setMTLSAdapter(adapter) {
|
|
7576
8071
|
this.mtlsAdapter = adapter;
|
|
7577
|
-
|
|
8072
|
+
logMtls.debug('mTLS adapter configured:', !!adapter);
|
|
7578
8073
|
}
|
|
7579
8074
|
setAuthStrategy(strategy) {
|
|
7580
8075
|
this.authStrategy = strategy;
|
|
7581
|
-
|
|
8076
|
+
logJwt.debug('Auth strategy configured:', !!strategy);
|
|
7582
8077
|
}
|
|
7583
8078
|
async shouldUseMTLS(url, method) {
|
|
7584
8079
|
if (!this.mtlsAdapter) {
|
|
8080
|
+
logJwt.debug(`No mTLS adapter, using JWT for ${method} ${url}`);
|
|
7585
8081
|
return false;
|
|
7586
8082
|
}
|
|
7587
8083
|
if (this.authStrategy) {
|
|
7588
8084
|
const config = await this.authStrategy.determineAuthConfig(url, method);
|
|
7589
|
-
|
|
8085
|
+
const logger = config.mode === 'mtls' ? logMtls : logJwt;
|
|
8086
|
+
logger.debug(`Auth config for ${method} ${url}:`, config);
|
|
7590
8087
|
return config.mode === 'mtls';
|
|
7591
8088
|
}
|
|
7592
8089
|
// Fallback: use mTLS for mf1/mf2 endpoints if no strategy
|
|
7593
|
-
|
|
8090
|
+
// This should rarely happen - only before SDK is fully initialized
|
|
8091
|
+
const useMtls = url.startsWith('/mf1') || url.startsWith('/mf2');
|
|
8092
|
+
if (useMtls) {
|
|
8093
|
+
logMtls.warn(`No auth strategy set, falling back to mTLS for ${method} ${url}`);
|
|
8094
|
+
}
|
|
8095
|
+
return useMtls;
|
|
7594
8096
|
}
|
|
7595
8097
|
async makeMTLSRequest(url, method, data, config) {
|
|
7596
8098
|
if (!this.mtlsAdapter) {
|
|
@@ -7603,10 +8105,10 @@ class AxiosHttpAdapter {
|
|
|
7603
8105
|
};
|
|
7604
8106
|
if (this.authToken) {
|
|
7605
8107
|
headers['Authorization'] = `Bearer ${this.authToken}`;
|
|
7606
|
-
|
|
8108
|
+
logMtls.debug('JWT token present for mTLS request');
|
|
7607
8109
|
}
|
|
7608
8110
|
else {
|
|
7609
|
-
|
|
8111
|
+
logMtls.warn('No JWT token for mTLS request');
|
|
7610
8112
|
}
|
|
7611
8113
|
const mtlsConfig = {
|
|
7612
8114
|
url: fullUrl,
|
|
@@ -7615,15 +8117,15 @@ class AxiosHttpAdapter {
|
|
|
7615
8117
|
data,
|
|
7616
8118
|
timeout: config?.timeout,
|
|
7617
8119
|
};
|
|
7618
|
-
|
|
8120
|
+
logMtls.debug(`mTLS ${method} ${fullUrl}`);
|
|
7619
8121
|
if (data) {
|
|
7620
|
-
|
|
8122
|
+
logMtls.debug('Request body:', data);
|
|
7621
8123
|
}
|
|
7622
8124
|
try {
|
|
7623
8125
|
const response = await this.mtlsAdapter.request(mtlsConfig);
|
|
7624
|
-
|
|
8126
|
+
logMtls.debug(`mTLS Response ${response.status} from ${fullUrl}`);
|
|
7625
8127
|
if (response.data) {
|
|
7626
|
-
|
|
8128
|
+
logMtls.debug('Response body:', response.data);
|
|
7627
8129
|
}
|
|
7628
8130
|
return {
|
|
7629
8131
|
data: response.data,
|
|
@@ -7632,11 +8134,11 @@ class AxiosHttpAdapter {
|
|
|
7632
8134
|
};
|
|
7633
8135
|
}
|
|
7634
8136
|
catch (error) {
|
|
7635
|
-
|
|
8137
|
+
logMtls.error(`mTLS Response error from ${fullUrl}:`, error);
|
|
7636
8138
|
if (error && typeof error === 'object' && 'response' in error) {
|
|
7637
8139
|
const axiosError = error;
|
|
7638
8140
|
if (axiosError.response?.data) {
|
|
7639
|
-
|
|
8141
|
+
logMtls.error('Response body:', axiosError.response.data);
|
|
7640
8142
|
}
|
|
7641
8143
|
}
|
|
7642
8144
|
throw error;
|
|
@@ -7651,36 +8153,46 @@ class AxiosHttpAdapter {
|
|
|
7651
8153
|
this.client.interceptors.request.use((config) => {
|
|
7652
8154
|
if (this.authToken) {
|
|
7653
8155
|
config.headers.Authorization = `Bearer ${this.authToken}`;
|
|
7654
|
-
|
|
8156
|
+
logJwt.debug('Adding JWT token to request', {
|
|
8157
|
+
tokenPrefix: this.authToken.substring(0, 30) + '...',
|
|
8158
|
+
tokenLength: this.authToken.length,
|
|
8159
|
+
});
|
|
7655
8160
|
}
|
|
7656
8161
|
else {
|
|
7657
|
-
|
|
8162
|
+
logJwt.warn('No JWT token available for request:', { url: config.url });
|
|
7658
8163
|
}
|
|
7659
8164
|
const method = config.method?.toUpperCase() ?? 'UNKNOWN';
|
|
7660
|
-
|
|
8165
|
+
const authHeader = config.headers.Authorization;
|
|
8166
|
+
// Log full request details for debugging
|
|
8167
|
+
logJwt.info(`→ ${method} ${config.url}`, {
|
|
8168
|
+
baseURL: config.baseURL,
|
|
8169
|
+
fullURL: `${config.baseURL}${config.url}`,
|
|
8170
|
+
hasAuthHeader: !!authHeader,
|
|
8171
|
+
authHeaderValue: authHeader ? `${String(authHeader).substring(0, 50)}...` : 'MISSING',
|
|
8172
|
+
});
|
|
7661
8173
|
if (config.params && Object.keys(config.params).length > 0) {
|
|
7662
|
-
|
|
8174
|
+
logJwt.debug('Request params:', config.params);
|
|
7663
8175
|
}
|
|
7664
8176
|
if (config.data) {
|
|
7665
|
-
|
|
8177
|
+
logJwt.debug('Request body:', config.data);
|
|
7666
8178
|
}
|
|
7667
8179
|
return config;
|
|
7668
8180
|
}, (error) => {
|
|
7669
|
-
|
|
8181
|
+
logJwt.error('Request error:', error);
|
|
7670
8182
|
return Promise.reject(error);
|
|
7671
8183
|
});
|
|
7672
8184
|
this.client.interceptors.response.use((response) => {
|
|
7673
8185
|
const method = response.config.method?.toUpperCase() ?? 'UNKNOWN';
|
|
7674
|
-
|
|
8186
|
+
logJwt.debug(`← ${method} ${response.status} ${response.config.url}`);
|
|
7675
8187
|
if (response.data) {
|
|
7676
|
-
|
|
8188
|
+
logJwt.debug('Response body:', response.data);
|
|
7677
8189
|
}
|
|
7678
8190
|
return response;
|
|
7679
8191
|
}, (error) => {
|
|
7680
8192
|
const method = error.config?.method?.toUpperCase() ?? 'UNKNOWN';
|
|
7681
|
-
|
|
8193
|
+
logJwt.error(`← ${method} ${error.response?.status ?? 'ERR'} ${error.config?.url ?? 'unknown'}`);
|
|
7682
8194
|
if (error.response?.data) {
|
|
7683
|
-
|
|
8195
|
+
logJwt.error('Response body:', error.response.data);
|
|
7684
8196
|
}
|
|
7685
8197
|
return Promise.reject(error);
|
|
7686
8198
|
});
|
|
@@ -7750,7 +8262,10 @@ class AxiosHttpAdapter {
|
|
|
7750
8262
|
return this.mapResponse(response);
|
|
7751
8263
|
}
|
|
7752
8264
|
setAuthToken(token) {
|
|
7753
|
-
|
|
8265
|
+
logJwt.info('setAuthToken called:', {
|
|
8266
|
+
hasToken: !!token,
|
|
8267
|
+
tokenPrefix: token?.substring(0, 20),
|
|
8268
|
+
});
|
|
7754
8269
|
this.authToken = token;
|
|
7755
8270
|
}
|
|
7756
8271
|
getAuthToken() {
|
|
@@ -7966,6 +8481,11 @@ class ACubeSDK {
|
|
|
7966
8481
|
const tokenStorage = this.container.get(DI_TOKENS.TOKEN_STORAGE_PORT);
|
|
7967
8482
|
const httpPort = this.container.get(DI_TOKENS.HTTP_PORT);
|
|
7968
8483
|
const baseHttpPort = this.container.get(DI_TOKENS.BASE_HTTP_PORT);
|
|
8484
|
+
log$1.debug('HTTP ports initialized', {
|
|
8485
|
+
httpPortType: httpPort.constructor.name,
|
|
8486
|
+
baseHttpPortType: baseHttpPort.constructor.name,
|
|
8487
|
+
areSameInstance: httpPort === baseHttpPort,
|
|
8488
|
+
});
|
|
7969
8489
|
log$1.debug('Initializing authentication service');
|
|
7970
8490
|
this.authService = new AuthenticationService(httpPort, tokenStorage, {
|
|
7971
8491
|
authUrl: this.config.getAuthUrl(),
|
|
@@ -7998,12 +8518,22 @@ class ACubeSDK {
|
|
|
7998
8518
|
this.offlineManager.sync().catch(() => { });
|
|
7999
8519
|
}
|
|
8000
8520
|
});
|
|
8001
|
-
|
|
8521
|
+
const isAuth = await this.authService.isAuthenticated();
|
|
8522
|
+
log$1.debug('Checking authentication status during init', { isAuthenticated: isAuth });
|
|
8523
|
+
if (isAuth) {
|
|
8002
8524
|
const token = await this.authService.getAccessToken();
|
|
8525
|
+
log$1.debug('Token retrieved during init', {
|
|
8526
|
+
hasToken: !!token,
|
|
8527
|
+
tokenPrefix: token?.substring(0, 20),
|
|
8528
|
+
});
|
|
8003
8529
|
if (token) {
|
|
8004
8530
|
httpPort.setAuthToken(token);
|
|
8531
|
+
log$1.info('Auth token set on HTTP port during initialization');
|
|
8005
8532
|
}
|
|
8006
8533
|
}
|
|
8534
|
+
else {
|
|
8535
|
+
log$1.warn('User not authenticated during SDK init - token will be set after login');
|
|
8536
|
+
}
|
|
8007
8537
|
if (this.adapters?.mtls && 'setMTLSAdapter' in baseHttpPort) {
|
|
8008
8538
|
log$1.debug('Connecting mTLS adapter to HTTP port');
|
|
8009
8539
|
const httpWithMtls = baseHttpPort;
|
|
@@ -8822,842 +9352,401 @@ class SDKManager {
|
|
|
8822
9352
|
this.appStateService.state$.subscribe(this.events.onAppStateChanged);
|
|
8823
9353
|
}
|
|
8824
9354
|
if (this.events?.onTelemetryStateChanged) {
|
|
8825
|
-
this.telemetryService.state$.subscribe(this.events.onTelemetryStateChanged);
|
|
8826
|
-
}
|
|
8827
|
-
this.isInitialized = true;
|
|
8828
|
-
// Only start polling for MERCHANT/CASHIER users (SUPPLIER gets 401 on these endpoints)
|
|
8829
|
-
const user = await this.sdk.getCurrentUser();
|
|
8830
|
-
const canPoll = user && hasAnyRole(user.roles, ['ROLE_MERCHANT', 'ROLE_CASHIER']);
|
|
8831
|
-
if (canPoll) {
|
|
8832
|
-
this.notificationService.startPolling();
|
|
8833
|
-
await this.startTelemetryPollingAuto();
|
|
8834
|
-
this.isPollingActive = true;
|
|
8835
|
-
}
|
|
8836
|
-
// AppStateService remains active for all users (handles OFFLINE network state)
|
|
8837
|
-
}
|
|
8838
|
-
/**
|
|
8839
|
-
* Observable stream of app state
|
|
8840
|
-
* Emits AppState with mode (NORMAL, WARNING, BLOCKED, OFFLINE)
|
|
8841
|
-
*/
|
|
8842
|
-
get appState$() {
|
|
8843
|
-
this.ensureInitialized();
|
|
8844
|
-
return this.appStateService.state$;
|
|
8845
|
-
}
|
|
8846
|
-
/**
|
|
8847
|
-
* Observable stream of app mode only
|
|
8848
|
-
* Emits AppMode with distinctUntilChanged
|
|
8849
|
-
*/
|
|
8850
|
-
get mode$() {
|
|
8851
|
-
this.ensureInitialized();
|
|
8852
|
-
return this.appStateService.mode$;
|
|
8853
|
-
}
|
|
8854
|
-
/**
|
|
8855
|
-
* Observable stream indicating if app is blocked
|
|
8856
|
-
*/
|
|
8857
|
-
get isBlocked$() {
|
|
8858
|
-
this.ensureInitialized();
|
|
8859
|
-
return this.appStateService.isBlocked$;
|
|
8860
|
-
}
|
|
8861
|
-
/**
|
|
8862
|
-
* Observable stream of warning state with countdown
|
|
8863
|
-
*/
|
|
8864
|
-
get warning$() {
|
|
8865
|
-
this.ensureInitialized();
|
|
8866
|
-
return this.appStateService.warning$;
|
|
8867
|
-
}
|
|
8868
|
-
/**
|
|
8869
|
-
* Observable stream of telemetry state (data, isLoading, isCached, error)
|
|
8870
|
-
*/
|
|
8871
|
-
get telemetryState$() {
|
|
8872
|
-
this.ensureInitialized();
|
|
8873
|
-
return this.telemetryService.state$;
|
|
8874
|
-
}
|
|
8875
|
-
/**
|
|
8876
|
-
* Get the pemId from the installed certificate
|
|
8877
|
-
*/
|
|
8878
|
-
async getPemId() {
|
|
8879
|
-
this.ensureInitialized();
|
|
8880
|
-
try {
|
|
8881
|
-
const certInfo = await this.sdk.getCertificatesInfo();
|
|
8882
|
-
return certInfo?.pemId ?? null;
|
|
8883
|
-
}
|
|
8884
|
-
catch {
|
|
8885
|
-
return null;
|
|
8886
|
-
}
|
|
8887
|
-
}
|
|
8888
|
-
/**
|
|
8889
|
-
* Start polling telemetry using the pemId from installed certificate
|
|
8890
|
-
* Returns the pemId if successful, null if no certificate is installed
|
|
8891
|
-
*/
|
|
8892
|
-
async startTelemetryPollingAuto() {
|
|
8893
|
-
this.ensureInitialized();
|
|
8894
|
-
const pemId = await this.getPemId();
|
|
8895
|
-
if (pemId) {
|
|
8896
|
-
this.telemetryService.startPolling(pemId);
|
|
8897
|
-
}
|
|
8898
|
-
return pemId;
|
|
8899
|
-
}
|
|
8900
|
-
/**
|
|
8901
|
-
* Start polling telemetry for a specific PEM
|
|
8902
|
-
*/
|
|
8903
|
-
startTelemetryPolling(pemId) {
|
|
8904
|
-
this.ensureInitialized();
|
|
8905
|
-
this.telemetryService.startPolling(pemId);
|
|
8906
|
-
}
|
|
8907
|
-
/**
|
|
8908
|
-
* Stop telemetry polling
|
|
8909
|
-
*/
|
|
8910
|
-
stopTelemetryPolling() {
|
|
8911
|
-
this.ensureInitialized();
|
|
8912
|
-
this.telemetryService.stopPolling();
|
|
8913
|
-
}
|
|
8914
|
-
/**
|
|
8915
|
-
* Get simplified services for product use
|
|
8916
|
-
*/
|
|
8917
|
-
getServices() {
|
|
8918
|
-
this.ensureInitialized();
|
|
8919
|
-
const sdk = this.sdk;
|
|
8920
|
-
const telemetryService = this.telemetryService;
|
|
8921
|
-
return {
|
|
8922
|
-
receipts: sdk.receipts,
|
|
8923
|
-
merchants: sdk.merchants,
|
|
8924
|
-
cashiers: sdk.cashiers,
|
|
8925
|
-
cashRegisters: sdk.cashRegisters,
|
|
8926
|
-
pointOfSales: sdk.pointOfSales,
|
|
8927
|
-
suppliers: sdk.suppliers,
|
|
8928
|
-
pems: sdk.pems,
|
|
8929
|
-
dailyReports: sdk.dailyReports,
|
|
8930
|
-
journals: sdk.journals,
|
|
8931
|
-
telemetry: {
|
|
8932
|
-
startPollingAuto: () => this.startTelemetryPollingAuto(),
|
|
8933
|
-
startPolling: (pemId) => telemetryService.startPolling(pemId),
|
|
8934
|
-
stopPolling: () => telemetryService.stopPolling(),
|
|
8935
|
-
getTelemetry: (pemId) => telemetryService.getTelemetry(pemId),
|
|
8936
|
-
refreshTelemetry: (pemId) => telemetryService.refreshTelemetry(pemId),
|
|
8937
|
-
triggerSync: () => telemetryService.triggerSync(),
|
|
8938
|
-
clearTelemetry: () => telemetryService.clearTelemetry(),
|
|
8939
|
-
getPemId: () => this.getPemId(),
|
|
8940
|
-
},
|
|
8941
|
-
login: (credentials) => sdk.login(credentials),
|
|
8942
|
-
logout: () => sdk.logout(),
|
|
8943
|
-
getCurrentUser: () => sdk.getCurrentUser(),
|
|
8944
|
-
isAuthenticated: () => sdk.isAuthenticated(),
|
|
8945
|
-
storeCertificate: (certificate, privateKey, options) => sdk.storeCertificate(certificate, privateKey, options),
|
|
8946
|
-
hasCertificate: () => sdk.hasCertificate(),
|
|
8947
|
-
clearCertificate: () => sdk.clearCertificate(),
|
|
8948
|
-
isOnline: () => sdk.isOnline(),
|
|
8949
|
-
};
|
|
9355
|
+
this.telemetryService.state$.subscribe(this.events.onTelemetryStateChanged);
|
|
9356
|
+
}
|
|
9357
|
+
this.isInitialized = true;
|
|
9358
|
+
// Only start polling for MERCHANT/CASHIER users (SUPPLIER gets 401 on these endpoints)
|
|
9359
|
+
const user = await this.sdk.getCurrentUser();
|
|
9360
|
+
const canPoll = user && hasAnyRole(user.roles, ['ROLE_MERCHANT', 'ROLE_CASHIER']);
|
|
9361
|
+
if (canPoll) {
|
|
9362
|
+
this.notificationService.startPolling();
|
|
9363
|
+
await this.startTelemetryPollingAuto();
|
|
9364
|
+
this.isPollingActive = true;
|
|
9365
|
+
}
|
|
9366
|
+
// AppStateService remains active for all users (handles OFFLINE network state)
|
|
8950
9367
|
}
|
|
8951
9368
|
/**
|
|
8952
|
-
*
|
|
9369
|
+
* Observable stream of app state
|
|
9370
|
+
* Emits AppState with mode (NORMAL, WARNING, BLOCKED, OFFLINE)
|
|
8953
9371
|
*/
|
|
8954
|
-
|
|
9372
|
+
get appState$() {
|
|
8955
9373
|
this.ensureInitialized();
|
|
8956
|
-
|
|
9374
|
+
return this.appStateService.state$;
|
|
8957
9375
|
}
|
|
8958
9376
|
/**
|
|
8959
|
-
*
|
|
9377
|
+
* Observable stream of app mode only
|
|
9378
|
+
* Emits AppMode with distinctUntilChanged
|
|
8960
9379
|
*/
|
|
8961
|
-
|
|
9380
|
+
get mode$() {
|
|
8962
9381
|
this.ensureInitialized();
|
|
8963
|
-
return this.
|
|
9382
|
+
return this.appStateService.mode$;
|
|
8964
9383
|
}
|
|
8965
9384
|
/**
|
|
8966
|
-
*
|
|
9385
|
+
* Observable stream indicating if app is blocked
|
|
8967
9386
|
*/
|
|
8968
|
-
|
|
8969
|
-
|
|
9387
|
+
get isBlocked$() {
|
|
9388
|
+
this.ensureInitialized();
|
|
9389
|
+
return this.appStateService.isBlocked$;
|
|
8970
9390
|
}
|
|
8971
9391
|
/**
|
|
8972
|
-
*
|
|
9392
|
+
* Observable stream of warning state with countdown
|
|
8973
9393
|
*/
|
|
8974
|
-
|
|
9394
|
+
get warning$() {
|
|
8975
9395
|
this.ensureInitialized();
|
|
8976
|
-
return this.
|
|
8977
|
-
}
|
|
8978
|
-
cleanup() {
|
|
8979
|
-
this.notificationService?.destroy();
|
|
8980
|
-
this.telemetryService?.destroy();
|
|
8981
|
-
this.appStateService?.destroy();
|
|
8982
|
-
this.sdk?.destroy();
|
|
8983
|
-
this.notificationService = null;
|
|
8984
|
-
this.telemetryService = null;
|
|
8985
|
-
this.appStateService = null;
|
|
8986
|
-
this.sdk = null;
|
|
8987
|
-
this.isInitialized = false;
|
|
8988
|
-
this.isPollingActive = false;
|
|
8989
|
-
}
|
|
8990
|
-
ensureInitialized() {
|
|
8991
|
-
if (!this.isInitialized) {
|
|
8992
|
-
throw new ACubeSDKError('SDK_NOT_INITIALIZED', 'SDKManager not initialized. Call initialize() first.');
|
|
8993
|
-
}
|
|
8994
|
-
}
|
|
8995
|
-
}
|
|
8996
|
-
SDKManager.instance = null;
|
|
8997
|
-
|
|
8998
|
-
const RECEIPT_READY = 'ready';
|
|
8999
|
-
const RECEIPT_SENT = 'sent';
|
|
9000
|
-
const RECEIPT_SORT_DESCENDING = 'descending';
|
|
9001
|
-
const RECEIPT_SORT_ASCENDING = 'ascending';
|
|
9002
|
-
|
|
9003
|
-
const log = createPrefixedLogger('CACHE-HANDLER');
|
|
9004
|
-
class CacheHandler {
|
|
9005
|
-
constructor(cache, networkMonitor) {
|
|
9006
|
-
this.cache = cache;
|
|
9007
|
-
this.networkMonitor = networkMonitor;
|
|
9008
|
-
this.currentOnlineState = true;
|
|
9009
|
-
this.setupNetworkMonitoring();
|
|
9010
|
-
}
|
|
9011
|
-
setupNetworkMonitoring() {
|
|
9012
|
-
if (this.networkMonitor) {
|
|
9013
|
-
this.networkSubscription = this.networkMonitor.online$.subscribe((online) => {
|
|
9014
|
-
this.currentOnlineState = online;
|
|
9015
|
-
});
|
|
9016
|
-
}
|
|
9017
|
-
}
|
|
9018
|
-
isOnline() {
|
|
9019
|
-
if (this.networkMonitor) {
|
|
9020
|
-
return this.currentOnlineState;
|
|
9021
|
-
}
|
|
9022
|
-
if (typeof navigator !== 'undefined' && 'onLine' in navigator) {
|
|
9023
|
-
return navigator.onLine;
|
|
9024
|
-
}
|
|
9025
|
-
return false;
|
|
9026
|
-
}
|
|
9027
|
-
async handleCachedRequest(url, requestFn, config) {
|
|
9028
|
-
if (!this.cache || config?.useCache === false) {
|
|
9029
|
-
const response = await requestFn();
|
|
9030
|
-
return response.data;
|
|
9031
|
-
}
|
|
9032
|
-
const cacheKey = this.generateCacheKey(url);
|
|
9033
|
-
const online = this.isOnline();
|
|
9034
|
-
log.debug('Request:', { url, cacheKey, online });
|
|
9035
|
-
if (online) {
|
|
9036
|
-
try {
|
|
9037
|
-
const response = await requestFn();
|
|
9038
|
-
if (this.cache) {
|
|
9039
|
-
await this.cache.set(cacheKey, response.data).catch((error) => {
|
|
9040
|
-
log.error('Failed to cache:', error instanceof Error ? error.message : error);
|
|
9041
|
-
});
|
|
9042
|
-
}
|
|
9043
|
-
return response.data;
|
|
9044
|
-
}
|
|
9045
|
-
catch (error) {
|
|
9046
|
-
const cached = await this.cache.get(cacheKey).catch(() => null);
|
|
9047
|
-
if (cached) {
|
|
9048
|
-
log.debug('Network failed, using cache fallback');
|
|
9049
|
-
return cached.data;
|
|
9050
|
-
}
|
|
9051
|
-
throw error;
|
|
9052
|
-
}
|
|
9053
|
-
}
|
|
9054
|
-
else {
|
|
9055
|
-
const cached = await this.cache.get(cacheKey).catch(() => null);
|
|
9056
|
-
if (cached) {
|
|
9057
|
-
log.debug('Offline, returning cached data');
|
|
9058
|
-
return cached.data;
|
|
9059
|
-
}
|
|
9060
|
-
throw new Error('Offline: No cached data available');
|
|
9061
|
-
}
|
|
9396
|
+
return this.appStateService.warning$;
|
|
9062
9397
|
}
|
|
9063
|
-
|
|
9064
|
-
|
|
9398
|
+
/**
|
|
9399
|
+
* Observable stream of telemetry state (data, isLoading, isCached, error)
|
|
9400
|
+
*/
|
|
9401
|
+
get telemetryState$() {
|
|
9402
|
+
this.ensureInitialized();
|
|
9403
|
+
return this.telemetryService.state$;
|
|
9065
9404
|
}
|
|
9066
|
-
|
|
9067
|
-
|
|
9068
|
-
|
|
9405
|
+
/**
|
|
9406
|
+
* Get the pemId from the installed certificate
|
|
9407
|
+
*/
|
|
9408
|
+
async getPemId() {
|
|
9409
|
+
this.ensureInitialized();
|
|
9069
9410
|
try {
|
|
9070
|
-
await this.
|
|
9411
|
+
const certInfo = await this.sdk.getCertificatesInfo();
|
|
9412
|
+
return certInfo?.pemId ?? null;
|
|
9071
9413
|
}
|
|
9072
|
-
catch
|
|
9073
|
-
|
|
9414
|
+
catch {
|
|
9415
|
+
return null;
|
|
9074
9416
|
}
|
|
9075
9417
|
}
|
|
9076
|
-
|
|
9077
|
-
|
|
9078
|
-
|
|
9079
|
-
|
|
9080
|
-
|
|
9081
|
-
|
|
9082
|
-
|
|
9083
|
-
|
|
9084
|
-
|
|
9085
|
-
}
|
|
9086
|
-
}
|
|
9087
|
-
|
|
9088
|
-
function transformError(error) {
|
|
9089
|
-
if (axios.isAxiosError(error)) {
|
|
9090
|
-
const response = error.response;
|
|
9091
|
-
if (!response) {
|
|
9092
|
-
return new ACubeSDKError('NETWORK_ERROR', 'Network error occurred', error);
|
|
9093
|
-
}
|
|
9094
|
-
const status = response.status;
|
|
9095
|
-
const data = response.data;
|
|
9096
|
-
const violations = data?.violations;
|
|
9097
|
-
let message = 'Unknown error occurred';
|
|
9098
|
-
if (data?.detail) {
|
|
9099
|
-
message = data.detail;
|
|
9100
|
-
}
|
|
9101
|
-
else if (data?.title) {
|
|
9102
|
-
message = data.title;
|
|
9103
|
-
}
|
|
9104
|
-
else if (error.message) {
|
|
9105
|
-
message = error.message;
|
|
9106
|
-
}
|
|
9107
|
-
switch (status) {
|
|
9108
|
-
case 400:
|
|
9109
|
-
return new ACubeSDKError('VALIDATION_ERROR', message, error, status, violations);
|
|
9110
|
-
case 401:
|
|
9111
|
-
return new ACubeSDKError('AUTH_ERROR', message, error, status, violations);
|
|
9112
|
-
case 403:
|
|
9113
|
-
return new ACubeSDKError('FORBIDDEN_ERROR', message, error, status, violations);
|
|
9114
|
-
case 404:
|
|
9115
|
-
return new ACubeSDKError('NOT_FOUND_ERROR', message, error, status, violations);
|
|
9116
|
-
case 422:
|
|
9117
|
-
return new ACubeSDKError('VALIDATION_ERROR', message, error, status, violations);
|
|
9118
|
-
default:
|
|
9119
|
-
return new ACubeSDKError('UNKNOWN_ERROR', message, error, status, violations);
|
|
9418
|
+
/**
|
|
9419
|
+
* Start polling telemetry using the pemId from installed certificate
|
|
9420
|
+
* Returns the pemId if successful, null if no certificate is installed
|
|
9421
|
+
*/
|
|
9422
|
+
async startTelemetryPollingAuto() {
|
|
9423
|
+
this.ensureInitialized();
|
|
9424
|
+
const pemId = await this.getPemId();
|
|
9425
|
+
if (pemId) {
|
|
9426
|
+
this.telemetryService.startPolling(pemId);
|
|
9120
9427
|
}
|
|
9428
|
+
return pemId;
|
|
9121
9429
|
}
|
|
9122
|
-
|
|
9123
|
-
|
|
9124
|
-
|
|
9125
|
-
|
|
9126
|
-
(
|
|
9127
|
-
|
|
9128
|
-
ErrorCategory["CLIENT_ERROR"] = "CLIENT_ERROR";
|
|
9129
|
-
ErrorCategory["AUTH_ERROR"] = "AUTH_ERROR";
|
|
9130
|
-
ErrorCategory["CERTIFICATE_ERROR"] = "CERTIFICATE_ERROR";
|
|
9131
|
-
ErrorCategory["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
9132
|
-
ErrorCategory["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
|
|
9133
|
-
})(exports.ErrorCategory || (exports.ErrorCategory = {}));
|
|
9134
|
-
function extractStatusCode(error) {
|
|
9135
|
-
if (error instanceof MTLSError && error.statusCode) {
|
|
9136
|
-
return error.statusCode;
|
|
9137
|
-
}
|
|
9138
|
-
const errorObj = error;
|
|
9139
|
-
if (errorObj?.response?.status) {
|
|
9140
|
-
return errorObj.response.status;
|
|
9141
|
-
}
|
|
9142
|
-
if (typeof errorObj?.statusCode === 'number') {
|
|
9143
|
-
return errorObj.statusCode;
|
|
9144
|
-
}
|
|
9145
|
-
return undefined;
|
|
9146
|
-
}
|
|
9147
|
-
function classifyError(error) {
|
|
9148
|
-
const statusCode = extractStatusCode(error);
|
|
9149
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
9150
|
-
if (statusCode && statusCode >= 500 && statusCode < 600) {
|
|
9151
|
-
return {
|
|
9152
|
-
category: exports.ErrorCategory.SERVER_ERROR,
|
|
9153
|
-
statusCode,
|
|
9154
|
-
message: errorMessage,
|
|
9155
|
-
shouldRetry: false,
|
|
9156
|
-
userMessage: `Server error (${statusCode}): The server encountered an error.`,
|
|
9157
|
-
};
|
|
9158
|
-
}
|
|
9159
|
-
if (statusCode === 401 || statusCode === 403) {
|
|
9160
|
-
return {
|
|
9161
|
-
category: exports.ErrorCategory.AUTH_ERROR,
|
|
9162
|
-
statusCode,
|
|
9163
|
-
message: errorMessage,
|
|
9164
|
-
shouldRetry: false,
|
|
9165
|
-
userMessage: `Authentication error (${statusCode}): Invalid credentials or insufficient permissions.`,
|
|
9166
|
-
};
|
|
9167
|
-
}
|
|
9168
|
-
if (statusCode && statusCode >= 400 && statusCode < 500) {
|
|
9169
|
-
return {
|
|
9170
|
-
category: exports.ErrorCategory.CLIENT_ERROR,
|
|
9171
|
-
statusCode,
|
|
9172
|
-
message: errorMessage,
|
|
9173
|
-
shouldRetry: false,
|
|
9174
|
-
userMessage: `Request error (${statusCode}): ${errorMessage}`,
|
|
9175
|
-
};
|
|
9430
|
+
/**
|
|
9431
|
+
* Start polling telemetry for a specific PEM
|
|
9432
|
+
*/
|
|
9433
|
+
startTelemetryPolling(pemId) {
|
|
9434
|
+
this.ensureInitialized();
|
|
9435
|
+
this.telemetryService.startPolling(pemId);
|
|
9176
9436
|
}
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
9181
|
-
|
|
9182
|
-
|
|
9183
|
-
userMessage: 'Certificate error: Unable to establish secure connection.',
|
|
9184
|
-
};
|
|
9437
|
+
/**
|
|
9438
|
+
* Stop telemetry polling
|
|
9439
|
+
*/
|
|
9440
|
+
stopTelemetryPolling() {
|
|
9441
|
+
this.ensureInitialized();
|
|
9442
|
+
this.telemetryService.stopPolling();
|
|
9185
9443
|
}
|
|
9186
|
-
|
|
9187
|
-
|
|
9188
|
-
|
|
9189
|
-
|
|
9444
|
+
/**
|
|
9445
|
+
* Get simplified services for product use
|
|
9446
|
+
*/
|
|
9447
|
+
getServices() {
|
|
9448
|
+
this.ensureInitialized();
|
|
9449
|
+
const sdk = this.sdk;
|
|
9450
|
+
const telemetryService = this.telemetryService;
|
|
9190
9451
|
return {
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
9194
|
-
|
|
9452
|
+
receipts: sdk.receipts,
|
|
9453
|
+
merchants: sdk.merchants,
|
|
9454
|
+
cashiers: sdk.cashiers,
|
|
9455
|
+
cashRegisters: sdk.cashRegisters,
|
|
9456
|
+
pointOfSales: sdk.pointOfSales,
|
|
9457
|
+
suppliers: sdk.suppliers,
|
|
9458
|
+
pems: sdk.pems,
|
|
9459
|
+
dailyReports: sdk.dailyReports,
|
|
9460
|
+
journals: sdk.journals,
|
|
9461
|
+
telemetry: {
|
|
9462
|
+
startPollingAuto: () => this.startTelemetryPollingAuto(),
|
|
9463
|
+
startPolling: (pemId) => telemetryService.startPolling(pemId),
|
|
9464
|
+
stopPolling: () => telemetryService.stopPolling(),
|
|
9465
|
+
getTelemetry: (pemId) => telemetryService.getTelemetry(pemId),
|
|
9466
|
+
refreshTelemetry: (pemId) => telemetryService.refreshTelemetry(pemId),
|
|
9467
|
+
triggerSync: () => telemetryService.triggerSync(),
|
|
9468
|
+
clearTelemetry: () => telemetryService.clearTelemetry(),
|
|
9469
|
+
getPemId: () => this.getPemId(),
|
|
9470
|
+
},
|
|
9471
|
+
login: (credentials) => sdk.login(credentials),
|
|
9472
|
+
logout: () => sdk.logout(),
|
|
9473
|
+
getCurrentUser: () => sdk.getCurrentUser(),
|
|
9474
|
+
isAuthenticated: () => sdk.isAuthenticated(),
|
|
9475
|
+
storeCertificate: (certificate, privateKey, options) => sdk.storeCertificate(certificate, privateKey, options),
|
|
9476
|
+
hasCertificate: () => sdk.hasCertificate(),
|
|
9477
|
+
clearCertificate: () => sdk.clearCertificate(),
|
|
9478
|
+
isOnline: () => sdk.isOnline(),
|
|
9195
9479
|
};
|
|
9196
9480
|
}
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
};
|
|
9204
|
-
}
|
|
9205
|
-
function shouldReconfigureCertificate(error) {
|
|
9206
|
-
const classification = classifyError(error);
|
|
9207
|
-
return classification.category === exports.ErrorCategory.CERTIFICATE_ERROR;
|
|
9208
|
-
}
|
|
9209
|
-
function shouldRetryRequest(error, isRetryAttempt) {
|
|
9210
|
-
if (isRetryAttempt) {
|
|
9211
|
-
return false;
|
|
9481
|
+
/**
|
|
9482
|
+
* Manually trigger a notification sync
|
|
9483
|
+
*/
|
|
9484
|
+
async syncNotifications() {
|
|
9485
|
+
this.ensureInitialized();
|
|
9486
|
+
await this.notificationService.triggerSync();
|
|
9212
9487
|
}
|
|
9213
|
-
|
|
9214
|
-
|
|
9215
|
-
|
|
9216
|
-
|
|
9217
|
-
|
|
9218
|
-
|
|
9219
|
-
}
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
|
|
9227
|
-
|
|
9228
|
-
|
|
9229
|
-
|
|
9230
|
-
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9234
|
-
|
|
9235
|
-
|
|
9236
|
-
|
|
9237
|
-
|
|
9238
|
-
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
|
|
9245
|
-
|
|
9246
|
-
|
|
9247
|
-
|
|
9248
|
-
|
|
9249
|
-
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
// Receipt Item Schema
|
|
9253
|
-
const ReceiptItemSchema = z__namespace.object({
|
|
9254
|
-
type: GoodOrServiceSchema.optional(),
|
|
9255
|
-
quantity: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
9256
|
-
description: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
9257
|
-
unit_price: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
9258
|
-
vat_rate_code: VatRateCodeSchema.optional(),
|
|
9259
|
-
simplified_vat_allocation: z__namespace.boolean().optional(),
|
|
9260
|
-
discount: z__namespace.string().nullable().optional(),
|
|
9261
|
-
is_down_payment_or_voucher_redemption: z__namespace.boolean().optional(),
|
|
9262
|
-
complimentary: z__namespace.boolean().optional(),
|
|
9263
|
-
});
|
|
9264
|
-
// Main Receipt Input Schema
|
|
9265
|
-
const ReceiptInputSchema = z__namespace
|
|
9266
|
-
.object({
|
|
9267
|
-
items: z__namespace.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
|
|
9268
|
-
customer_tax_code: z__namespace.string().optional(),
|
|
9269
|
-
customer_lottery_code: z__namespace.string().optional(),
|
|
9270
|
-
discount: z__namespace.string().nullable().optional(),
|
|
9271
|
-
invoice_issuing: z__namespace.boolean().optional(),
|
|
9272
|
-
uncollected_dcr_to_ssn: z__namespace.boolean().optional(),
|
|
9273
|
-
services_uncollected_amount: z__namespace.string().nullable().optional(),
|
|
9274
|
-
goods_uncollected_amount: z__namespace.string().nullable().optional(),
|
|
9275
|
-
cash_payment_amount: z__namespace.string().nullable().optional(),
|
|
9276
|
-
electronic_payment_amount: z__namespace.string().nullable().optional(),
|
|
9277
|
-
ticket_restaurant_payment_amount: z__namespace.string().nullable().optional(),
|
|
9278
|
-
ticket_restaurant_quantity: z__namespace.number().optional(),
|
|
9279
|
-
})
|
|
9280
|
-
.refine((data) => {
|
|
9281
|
-
// At least one payment method should be provided
|
|
9282
|
-
const hasCashPayment = data.cash_payment_amount && parseFloat(data.cash_payment_amount) > 0;
|
|
9283
|
-
const hasElectronicPayment = data.electronic_payment_amount && parseFloat(data.electronic_payment_amount) > 0;
|
|
9284
|
-
const hasTicketPayment = data.ticket_restaurant_payment_amount &&
|
|
9285
|
-
parseFloat(data.ticket_restaurant_payment_amount) > 0;
|
|
9286
|
-
return hasCashPayment || hasElectronicPayment || hasTicketPayment;
|
|
9287
|
-
}, {
|
|
9288
|
-
error: 'At least one payment method is required',
|
|
9289
|
-
path: ['payment_methods'],
|
|
9290
|
-
})
|
|
9291
|
-
.refine((data) => {
|
|
9292
|
-
// only one between customer_tax_code and customer_lottery_code can be provided
|
|
9293
|
-
return !data.customer_tax_code || !data.customer_lottery_code;
|
|
9294
|
-
}, {
|
|
9295
|
-
error: 'Only one between customer_tax_code and customer_lottery_code can be provided',
|
|
9296
|
-
path: ['customer_tax_code', 'customer_lottery_code'],
|
|
9297
|
-
});
|
|
9298
|
-
// Receipt Return or Void via PEM Schema
|
|
9299
|
-
const ReceiptReturnOrVoidViaPEMInputSchema = z__namespace.object({
|
|
9300
|
-
device_id: z__namespace.string().optional(),
|
|
9301
|
-
items: z__namespace.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
|
|
9302
|
-
document_number: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
9303
|
-
document_datetime: z__namespace.string().optional(),
|
|
9304
|
-
lottery_code: z__namespace.string().optional(),
|
|
9305
|
-
});
|
|
9306
|
-
// Receipt Return or Void with Proof Schema
|
|
9307
|
-
const ReceiptReturnOrVoidWithProofInputSchema = z__namespace.object({
|
|
9308
|
-
items: z__namespace.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
|
|
9309
|
-
proof: ReceiptProofTypeSchema,
|
|
9310
|
-
document_datetime: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
9311
|
-
});
|
|
9312
|
-
// Void Receipt Schema
|
|
9313
|
-
const VoidReceiptInputSchema = z__namespace.object({
|
|
9314
|
-
document_number: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
9315
|
-
});
|
|
9316
|
-
const ReceiptReturnItemSchema = z__namespace
|
|
9317
|
-
.array(z__namespace.object({
|
|
9318
|
-
id: z__namespace.number(),
|
|
9319
|
-
quantity: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
9320
|
-
}))
|
|
9321
|
-
.min(1, { error: 'arrayMin1' });
|
|
9322
|
-
// Receipt Return Schema
|
|
9323
|
-
const ReceiptReturnInputSchema = z__namespace.object({
|
|
9324
|
-
items: z__namespace.array(ReceiptReturnItemSchema).min(1, { error: 'arrayMin1' }),
|
|
9325
|
-
document_number: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
9326
|
-
});
|
|
9327
|
-
|
|
9328
|
-
// Cashier Create Input Schema (MF1)
|
|
9329
|
-
const CashierCreateInputSchema = z__namespace.object({
|
|
9330
|
-
email: z__namespace
|
|
9331
|
-
.string()
|
|
9332
|
-
.min(1, { error: 'fieldIsRequired' })
|
|
9333
|
-
.max(255, { error: 'emailMaxLength' })
|
|
9334
|
-
.email({ error: 'invalidEmail' }),
|
|
9335
|
-
password: z__namespace
|
|
9336
|
-
.string()
|
|
9337
|
-
.min(8, { error: 'passwordMinLength' })
|
|
9338
|
-
.max(40, { error: 'passwordMaxLength' }),
|
|
9339
|
-
name: z__namespace.string().min(1, { error: 'fieldIsRequired' }).max(255, { error: 'nameMaxLength' }),
|
|
9340
|
-
display_name: z__namespace
|
|
9341
|
-
.string()
|
|
9342
|
-
.min(1, { error: 'fieldIsRequired' })
|
|
9343
|
-
.max(255, { error: 'displayNameMaxLength' }),
|
|
9344
|
-
});
|
|
9345
|
-
|
|
9346
|
-
// Enum options arrays
|
|
9347
|
-
const PEM_STATUS_OPTIONS = [
|
|
9348
|
-
'NEW',
|
|
9349
|
-
'REGISTERED',
|
|
9350
|
-
'ACTIVATED',
|
|
9351
|
-
'ONLINE',
|
|
9352
|
-
'OFFLINE',
|
|
9353
|
-
'DISCARDED',
|
|
9354
|
-
];
|
|
9355
|
-
// Address Schema (reusable)
|
|
9356
|
-
const AddressSchema = z__namespace.object({
|
|
9357
|
-
street_address: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
9358
|
-
street_number: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
9359
|
-
zip_code: z__namespace
|
|
9360
|
-
.string()
|
|
9361
|
-
.min(1, { error: 'fieldIsRequired' })
|
|
9362
|
-
.regex(/^\d{5}$/, { error: 'invalidZipCode' }),
|
|
9363
|
-
city: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
9364
|
-
province: z__namespace
|
|
9365
|
-
.string()
|
|
9366
|
-
.min(2, { error: 'provinceMinLength' })
|
|
9367
|
-
.max(2, { error: 'provinceMaxLength' })
|
|
9368
|
-
.toUpperCase(),
|
|
9369
|
-
});
|
|
9370
|
-
// PEM Status Schema
|
|
9371
|
-
const PEMStatusSchema = z__namespace.enum(PEM_STATUS_OPTIONS);
|
|
9372
|
-
// Activation Request Schema
|
|
9373
|
-
const ActivationRequestSchema = z__namespace.object({
|
|
9374
|
-
registration_key: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
9375
|
-
});
|
|
9376
|
-
// PEM Status Offline Request Schema
|
|
9377
|
-
const PEMStatusOfflineRequestSchema = z__namespace.object({
|
|
9378
|
-
timestamp: z__namespace
|
|
9379
|
-
.string()
|
|
9380
|
-
.min(1, { error: 'fieldIsRequired' })
|
|
9381
|
-
.refine((val) => !isNaN(Date.parse(val)), {
|
|
9382
|
-
error: 'invalidDateFormat',
|
|
9383
|
-
}),
|
|
9384
|
-
reason: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
9385
|
-
});
|
|
9488
|
+
/**
|
|
9489
|
+
* Manually trigger a telemetry sync
|
|
9490
|
+
*/
|
|
9491
|
+
async syncTelemetry() {
|
|
9492
|
+
this.ensureInitialized();
|
|
9493
|
+
return this.telemetryService.triggerSync();
|
|
9494
|
+
}
|
|
9495
|
+
/**
|
|
9496
|
+
* Check if the manager is initialized
|
|
9497
|
+
*/
|
|
9498
|
+
getIsInitialized() {
|
|
9499
|
+
return this.isInitialized;
|
|
9500
|
+
}
|
|
9501
|
+
/**
|
|
9502
|
+
* Get the underlying SDK instance (for advanced use cases)
|
|
9503
|
+
*/
|
|
9504
|
+
getSDK() {
|
|
9505
|
+
this.ensureInitialized();
|
|
9506
|
+
return this.sdk;
|
|
9507
|
+
}
|
|
9508
|
+
cleanup() {
|
|
9509
|
+
this.notificationService?.destroy();
|
|
9510
|
+
this.telemetryService?.destroy();
|
|
9511
|
+
this.appStateService?.destroy();
|
|
9512
|
+
this.sdk?.destroy();
|
|
9513
|
+
this.notificationService = null;
|
|
9514
|
+
this.telemetryService = null;
|
|
9515
|
+
this.appStateService = null;
|
|
9516
|
+
this.sdk = null;
|
|
9517
|
+
this.isInitialized = false;
|
|
9518
|
+
this.isPollingActive = false;
|
|
9519
|
+
}
|
|
9520
|
+
ensureInitialized() {
|
|
9521
|
+
if (!this.isInitialized) {
|
|
9522
|
+
throw new ACubeSDKError('SDK_NOT_INITIALIZED', 'SDKManager not initialized. Call initialize() first.');
|
|
9523
|
+
}
|
|
9524
|
+
}
|
|
9525
|
+
}
|
|
9526
|
+
SDKManager.instance = null;
|
|
9386
9527
|
|
|
9387
|
-
|
|
9388
|
-
const
|
|
9389
|
-
|
|
9390
|
-
|
|
9391
|
-
});
|
|
9528
|
+
const RECEIPT_READY = 'ready';
|
|
9529
|
+
const RECEIPT_SENT = 'sent';
|
|
9530
|
+
const RECEIPT_SORT_DESCENDING = 'descending';
|
|
9531
|
+
const RECEIPT_SORT_ASCENDING = 'ascending';
|
|
9392
9532
|
|
|
9393
|
-
|
|
9394
|
-
|
|
9395
|
-
|
|
9396
|
-
|
|
9397
|
-
|
|
9398
|
-
|
|
9399
|
-
|
|
9400
|
-
const MerchantCreateInputSchema = z__namespace
|
|
9401
|
-
.object({
|
|
9402
|
-
vat_number: z__namespace
|
|
9403
|
-
.string()
|
|
9404
|
-
.min(1, { error: 'fieldIsRequired' })
|
|
9405
|
-
.regex(VAT_NUMBER_REGEX, { error: 'invalidVatNumber' }),
|
|
9406
|
-
fiscal_code: z__namespace.string().regex(FISCAL_CODE_REGEX, { error: 'invalidFiscalCode' }).optional(),
|
|
9407
|
-
business_name: z__namespace.string().max(200, { error: 'businessNameMaxLength' }).optional().nullable(),
|
|
9408
|
-
first_name: z__namespace.string().max(100, { error: 'firstNameMaxLength' }).optional().nullable(),
|
|
9409
|
-
last_name: z__namespace.string().max(100, { error: 'lastNameMaxLength' }).optional().nullable(),
|
|
9410
|
-
email: z__namespace.string().min(1, { error: 'fieldIsRequired' }).email({ error: 'invalidEmail' }),
|
|
9411
|
-
password: z__namespace
|
|
9412
|
-
.string()
|
|
9413
|
-
.min(1, { error: 'fieldIsRequired' })
|
|
9414
|
-
.regex(PASSWORD_REGEX, { error: 'passwordComplexity' }),
|
|
9415
|
-
address: AddressSchema.optional(),
|
|
9416
|
-
})
|
|
9417
|
-
.refine((data) => {
|
|
9418
|
-
const hasBusinessName = data.business_name && data.business_name.trim() !== '';
|
|
9419
|
-
const hasPersonalNames = (data.first_name && data.first_name.trim() !== '') ||
|
|
9420
|
-
(data.last_name && data.last_name.trim() !== '');
|
|
9421
|
-
// If business name is set, first/last name must not be provided
|
|
9422
|
-
if (hasBusinessName && hasPersonalNames) {
|
|
9423
|
-
return false;
|
|
9533
|
+
const log = createPrefixedLogger('CACHE-HANDLER');
|
|
9534
|
+
class CacheHandler {
|
|
9535
|
+
constructor(cache, networkMonitor) {
|
|
9536
|
+
this.cache = cache;
|
|
9537
|
+
this.networkMonitor = networkMonitor;
|
|
9538
|
+
this.currentOnlineState = true;
|
|
9539
|
+
this.setupNetworkMonitoring();
|
|
9424
9540
|
}
|
|
9425
|
-
|
|
9426
|
-
|
|
9541
|
+
setupNetworkMonitoring() {
|
|
9542
|
+
if (this.networkMonitor) {
|
|
9543
|
+
this.networkSubscription = this.networkMonitor.online$.subscribe((online) => {
|
|
9544
|
+
this.currentOnlineState = online;
|
|
9545
|
+
});
|
|
9546
|
+
}
|
|
9547
|
+
}
|
|
9548
|
+
isOnline() {
|
|
9549
|
+
if (this.networkMonitor) {
|
|
9550
|
+
return this.currentOnlineState;
|
|
9551
|
+
}
|
|
9552
|
+
if (typeof navigator !== 'undefined' && 'onLine' in navigator) {
|
|
9553
|
+
return navigator.onLine;
|
|
9554
|
+
}
|
|
9427
9555
|
return false;
|
|
9428
9556
|
}
|
|
9429
|
-
|
|
9430
|
-
|
|
9431
|
-
|
|
9432
|
-
|
|
9433
|
-
}
|
|
9434
|
-
|
|
9435
|
-
const
|
|
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
|
-
|
|
9467
|
-
|
|
9468
|
-
|
|
9469
|
-
|
|
9470
|
-
|
|
9471
|
-
|
|
9472
|
-
|
|
9473
|
-
|
|
9474
|
-
|
|
9475
|
-
|
|
9476
|
-
|
|
9477
|
-
|
|
9478
|
-
|
|
9479
|
-
|
|
9480
|
-
|
|
9481
|
-
|
|
9482
|
-
|
|
9483
|
-
|
|
9484
|
-
|
|
9485
|
-
|
|
9486
|
-
|
|
9487
|
-
|
|
9488
|
-
|
|
9489
|
-
// Daily Report Status Schema
|
|
9490
|
-
const DailyReportStatusSchema = z__namespace.enum(DAILY_REPORT_STATUS_OPTIONS, {
|
|
9491
|
-
error: 'invalidDailyReportStatus',
|
|
9492
|
-
});
|
|
9493
|
-
// Daily Reports List Parameters Schema
|
|
9494
|
-
const DailyReportsParamsSchema = z__namespace.object({
|
|
9495
|
-
pem_serial_number: z__namespace.string().min(1, { error: 'fieldIsRequired' }).optional(),
|
|
9496
|
-
date_from: z__namespace
|
|
9497
|
-
.string()
|
|
9498
|
-
.refine((val) => !isNaN(Date.parse(val)), {
|
|
9499
|
-
error: 'invalidDateFormat',
|
|
9500
|
-
})
|
|
9501
|
-
.optional(),
|
|
9502
|
-
date_to: z__namespace
|
|
9503
|
-
.string()
|
|
9504
|
-
.refine((val) => !isNaN(Date.parse(val)), {
|
|
9505
|
-
error: 'invalidDateFormat',
|
|
9506
|
-
})
|
|
9507
|
-
.optional(),
|
|
9508
|
-
status: DailyReportStatusSchema.optional(),
|
|
9509
|
-
page: z__namespace.number().min(1, { error: 'pageMinValue' }).optional(),
|
|
9510
|
-
});
|
|
9511
|
-
|
|
9512
|
-
const NotificationScopeSchema = z__namespace.object({
|
|
9513
|
-
type: z__namespace.literal('global'),
|
|
9514
|
-
});
|
|
9515
|
-
const PemStatusSchema = z__namespace.enum(['ONLINE', 'OFFLINE']);
|
|
9516
|
-
const NotificationDataBlockAtSchema = z__namespace.object({
|
|
9517
|
-
block_at: z__namespace.string(),
|
|
9518
|
-
});
|
|
9519
|
-
const NotificationDataPemStatusSchema = z__namespace.object({
|
|
9520
|
-
from: PemStatusSchema,
|
|
9521
|
-
to: PemStatusSchema,
|
|
9522
|
-
});
|
|
9523
|
-
const NotificationBaseSchema = z__namespace.object({
|
|
9524
|
-
uuid: z__namespace.string().uuid({ error: 'invalidUuid' }),
|
|
9525
|
-
scope: NotificationScopeSchema,
|
|
9526
|
-
source: z__namespace.enum(['system', 'Italian Tax Authority']),
|
|
9527
|
-
level: z__namespace.enum(['info', 'warning', 'error', 'critical']),
|
|
9528
|
-
created_at: z__namespace.string(),
|
|
9529
|
-
});
|
|
9530
|
-
const NotificationMf2UnreachableSchema = NotificationBaseSchema.extend({
|
|
9531
|
-
type: z__namespace.literal('INTERNAL_COMMUNICATION_FAILURE'),
|
|
9532
|
-
code: z__namespace.literal('SYS-W-01'),
|
|
9533
|
-
data: NotificationDataBlockAtSchema,
|
|
9534
|
-
});
|
|
9535
|
-
const NotificationPemsBlockedSchema = NotificationBaseSchema.extend({
|
|
9536
|
-
type: z__namespace.literal('PEM_STATUS_CHANGED'),
|
|
9537
|
-
code: z__namespace.literal('SYS-C-01'),
|
|
9538
|
-
data: NotificationDataPemStatusSchema,
|
|
9539
|
-
});
|
|
9540
|
-
const NotificationPemBackOnlineSchema = NotificationBaseSchema.extend({
|
|
9541
|
-
type: z__namespace.literal('PEM_STATUS_CHANGED'),
|
|
9542
|
-
code: z__namespace.literal('SYS-I-01'),
|
|
9543
|
-
data: NotificationDataPemStatusSchema,
|
|
9544
|
-
});
|
|
9545
|
-
const NotificationSchema = z__namespace.discriminatedUnion('code', [
|
|
9546
|
-
NotificationMf2UnreachableSchema,
|
|
9547
|
-
NotificationPemsBlockedSchema,
|
|
9548
|
-
NotificationPemBackOnlineSchema,
|
|
9549
|
-
]);
|
|
9550
|
-
const NotificationListResponseSchema = z__namespace.object({
|
|
9551
|
-
members: z__namespace.array(NotificationSchema),
|
|
9552
|
-
});
|
|
9557
|
+
async handleCachedRequest(url, requestFn, config) {
|
|
9558
|
+
if (!this.cache || config?.useCache === false) {
|
|
9559
|
+
const response = await requestFn();
|
|
9560
|
+
return response.data;
|
|
9561
|
+
}
|
|
9562
|
+
const cacheKey = this.generateCacheKey(url);
|
|
9563
|
+
const online = this.isOnline();
|
|
9564
|
+
log.debug('Request:', { url, cacheKey, online });
|
|
9565
|
+
if (online) {
|
|
9566
|
+
try {
|
|
9567
|
+
const response = await requestFn();
|
|
9568
|
+
if (this.cache) {
|
|
9569
|
+
await this.cache.set(cacheKey, response.data).catch((error) => {
|
|
9570
|
+
log.error('Failed to cache:', error instanceof Error ? error.message : error);
|
|
9571
|
+
});
|
|
9572
|
+
}
|
|
9573
|
+
return response.data;
|
|
9574
|
+
}
|
|
9575
|
+
catch (error) {
|
|
9576
|
+
const cached = await this.cache.get(cacheKey).catch(() => null);
|
|
9577
|
+
if (cached) {
|
|
9578
|
+
log.debug('Network failed, using cache fallback');
|
|
9579
|
+
return cached.data;
|
|
9580
|
+
}
|
|
9581
|
+
throw error;
|
|
9582
|
+
}
|
|
9583
|
+
}
|
|
9584
|
+
else {
|
|
9585
|
+
const cached = await this.cache.get(cacheKey).catch(() => null);
|
|
9586
|
+
if (cached) {
|
|
9587
|
+
log.debug('Offline, returning cached data');
|
|
9588
|
+
return cached.data;
|
|
9589
|
+
}
|
|
9590
|
+
throw new Error('Offline: No cached data available');
|
|
9591
|
+
}
|
|
9592
|
+
}
|
|
9593
|
+
generateCacheKey(url) {
|
|
9594
|
+
return url;
|
|
9595
|
+
}
|
|
9596
|
+
async invalidateCache(pattern) {
|
|
9597
|
+
if (!this.cache)
|
|
9598
|
+
return;
|
|
9599
|
+
try {
|
|
9600
|
+
await this.cache.invalidate(pattern);
|
|
9601
|
+
}
|
|
9602
|
+
catch (error) {
|
|
9603
|
+
log.error('Invalidation failed:', error instanceof Error ? error.message : error);
|
|
9604
|
+
}
|
|
9605
|
+
}
|
|
9606
|
+
getCacheStatus() {
|
|
9607
|
+
return {
|
|
9608
|
+
available: !!this.cache,
|
|
9609
|
+
networkMonitorAvailable: !!this.networkMonitor,
|
|
9610
|
+
isOnline: this.isOnline(),
|
|
9611
|
+
};
|
|
9612
|
+
}
|
|
9613
|
+
destroy() {
|
|
9614
|
+
this.networkSubscription?.unsubscribe();
|
|
9615
|
+
}
|
|
9616
|
+
}
|
|
9553
9617
|
|
|
9554
|
-
|
|
9555
|
-
|
|
9556
|
-
|
|
9557
|
-
|
|
9558
|
-
|
|
9559
|
-
|
|
9560
|
-
|
|
9561
|
-
|
|
9562
|
-
|
|
9563
|
-
|
|
9564
|
-
|
|
9565
|
-
|
|
9566
|
-
|
|
9567
|
-
|
|
9568
|
-
|
|
9569
|
-
}
|
|
9570
|
-
|
|
9571
|
-
|
|
9572
|
-
|
|
9573
|
-
|
|
9574
|
-
|
|
9575
|
-
|
|
9576
|
-
|
|
9577
|
-
|
|
9578
|
-
|
|
9579
|
-
|
|
9580
|
-
|
|
9581
|
-
|
|
9582
|
-
|
|
9583
|
-
|
|
9584
|
-
|
|
9585
|
-
|
|
9586
|
-
|
|
9587
|
-
}
|
|
9588
|
-
|
|
9589
|
-
|
|
9590
|
-
outcome: z__namespace.enum(['success', 'failed', 'pending']),
|
|
9591
|
-
});
|
|
9592
|
-
const LotterySchema = z__namespace.object({
|
|
9593
|
-
last_transmission: TransmissionSchema,
|
|
9594
|
-
secret_request: LotterySecretRequestSchema,
|
|
9595
|
-
});
|
|
9596
|
-
const TelemetrySchema = z__namespace.object({
|
|
9597
|
-
pem_id: z__namespace.string(),
|
|
9598
|
-
pem_status: z__namespace.enum(['ONLINE', 'OFFLINE', 'ERROR']),
|
|
9599
|
-
pem_status_changed_at: z__namespace.string(),
|
|
9600
|
-
merchant: TelemetryMerchantSchema,
|
|
9601
|
-
supplier: TelemetrySupplierSchema,
|
|
9602
|
-
software: TelemetrySoftwareSchema,
|
|
9603
|
-
last_communication_at: z__namespace.string(),
|
|
9604
|
-
pending_receipts: PendingReceiptsSchema,
|
|
9605
|
-
last_receipt_transmission: TransmissionSchema,
|
|
9606
|
-
last_message_from_mf2: MessageSchema,
|
|
9607
|
-
ade_corrispettivi_transmission: TransmissionSchema,
|
|
9608
|
-
last_message_from_ade: MessageSchema,
|
|
9609
|
-
lottery: LotterySchema,
|
|
9610
|
-
});
|
|
9618
|
+
function transformError(error) {
|
|
9619
|
+
if (axios.isAxiosError(error)) {
|
|
9620
|
+
const response = error.response;
|
|
9621
|
+
if (!response) {
|
|
9622
|
+
return new ACubeSDKError('NETWORK_ERROR', 'Network error occurred', error);
|
|
9623
|
+
}
|
|
9624
|
+
const status = response.status;
|
|
9625
|
+
const data = response.data;
|
|
9626
|
+
const violations = data?.violations;
|
|
9627
|
+
let message = 'Unknown error occurred';
|
|
9628
|
+
if (data?.detail) {
|
|
9629
|
+
message = data.detail;
|
|
9630
|
+
}
|
|
9631
|
+
else if (data?.title) {
|
|
9632
|
+
message = data.title;
|
|
9633
|
+
}
|
|
9634
|
+
else if (error.message) {
|
|
9635
|
+
message = error.message;
|
|
9636
|
+
}
|
|
9637
|
+
switch (status) {
|
|
9638
|
+
case 400:
|
|
9639
|
+
return new ACubeSDKError('VALIDATION_ERROR', message, error, status, violations);
|
|
9640
|
+
case 401:
|
|
9641
|
+
return new ACubeSDKError('AUTH_ERROR', message, error, status, violations);
|
|
9642
|
+
case 403:
|
|
9643
|
+
return new ACubeSDKError('FORBIDDEN_ERROR', message, error, status, violations);
|
|
9644
|
+
case 404:
|
|
9645
|
+
return new ACubeSDKError('NOT_FOUND_ERROR', message, error, status, violations);
|
|
9646
|
+
case 422:
|
|
9647
|
+
return new ACubeSDKError('VALIDATION_ERROR', message, error, status, violations);
|
|
9648
|
+
default:
|
|
9649
|
+
return new ACubeSDKError('UNKNOWN_ERROR', message, error, status, violations);
|
|
9650
|
+
}
|
|
9651
|
+
}
|
|
9652
|
+
return new ACubeSDKError('UNKNOWN_ERROR', 'Unknown error occurred', error);
|
|
9653
|
+
}
|
|
9611
9654
|
|
|
9612
|
-
|
|
9613
|
-
|
|
9614
|
-
|
|
9615
|
-
|
|
9616
|
-
|
|
9617
|
-
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
|
|
9621
|
-
|
|
9622
|
-
|
|
9623
|
-
|
|
9624
|
-
|
|
9625
|
-
|
|
9626
|
-
|
|
9627
|
-
|
|
9628
|
-
|
|
9629
|
-
|
|
9630
|
-
|
|
9631
|
-
|
|
9632
|
-
|
|
9633
|
-
|
|
9634
|
-
|
|
9635
|
-
|
|
9636
|
-
|
|
9637
|
-
|
|
9638
|
-
displayNameMaxLength: 'Display name is too long (max 255 characters)',
|
|
9639
|
-
};
|
|
9640
|
-
// Validation helper functions
|
|
9641
|
-
const validateInput = (schema, data) => {
|
|
9642
|
-
const result = schema.safeParse(data);
|
|
9643
|
-
if (!result.success) {
|
|
9644
|
-
const errors = result.error.issues.map((error) => ({
|
|
9645
|
-
field: error.path.join('.'),
|
|
9646
|
-
message: error.message,
|
|
9647
|
-
code: error.code,
|
|
9648
|
-
}));
|
|
9655
|
+
exports.ErrorCategory = void 0;
|
|
9656
|
+
(function (ErrorCategory) {
|
|
9657
|
+
ErrorCategory["SERVER_ERROR"] = "SERVER_ERROR";
|
|
9658
|
+
ErrorCategory["CLIENT_ERROR"] = "CLIENT_ERROR";
|
|
9659
|
+
ErrorCategory["AUTH_ERROR"] = "AUTH_ERROR";
|
|
9660
|
+
ErrorCategory["CERTIFICATE_ERROR"] = "CERTIFICATE_ERROR";
|
|
9661
|
+
ErrorCategory["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
9662
|
+
ErrorCategory["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
|
|
9663
|
+
})(exports.ErrorCategory || (exports.ErrorCategory = {}));
|
|
9664
|
+
function extractStatusCode(error) {
|
|
9665
|
+
if (error instanceof MTLSError && error.statusCode) {
|
|
9666
|
+
return error.statusCode;
|
|
9667
|
+
}
|
|
9668
|
+
const errorObj = error;
|
|
9669
|
+
if (errorObj?.response?.status) {
|
|
9670
|
+
return errorObj.response.status;
|
|
9671
|
+
}
|
|
9672
|
+
if (typeof errorObj?.statusCode === 'number') {
|
|
9673
|
+
return errorObj.statusCode;
|
|
9674
|
+
}
|
|
9675
|
+
return undefined;
|
|
9676
|
+
}
|
|
9677
|
+
function classifyError(error) {
|
|
9678
|
+
const statusCode = extractStatusCode(error);
|
|
9679
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
9680
|
+
if (statusCode && statusCode >= 500 && statusCode < 600) {
|
|
9649
9681
|
return {
|
|
9650
|
-
|
|
9651
|
-
|
|
9652
|
-
|
|
9682
|
+
category: exports.ErrorCategory.SERVER_ERROR,
|
|
9683
|
+
statusCode,
|
|
9684
|
+
message: errorMessage,
|
|
9685
|
+
shouldRetry: false,
|
|
9686
|
+
userMessage: `Server error (${statusCode}): The server encountered an error.`,
|
|
9687
|
+
};
|
|
9688
|
+
}
|
|
9689
|
+
if (statusCode === 401 || statusCode === 403) {
|
|
9690
|
+
return {
|
|
9691
|
+
category: exports.ErrorCategory.AUTH_ERROR,
|
|
9692
|
+
statusCode,
|
|
9693
|
+
message: errorMessage,
|
|
9694
|
+
shouldRetry: false,
|
|
9695
|
+
userMessage: `Authentication error (${statusCode}): Invalid credentials or insufficient permissions.`,
|
|
9696
|
+
};
|
|
9697
|
+
}
|
|
9698
|
+
if (statusCode && statusCode >= 400 && statusCode < 500) {
|
|
9699
|
+
return {
|
|
9700
|
+
category: exports.ErrorCategory.CLIENT_ERROR,
|
|
9701
|
+
statusCode,
|
|
9702
|
+
message: errorMessage,
|
|
9703
|
+
shouldRetry: false,
|
|
9704
|
+
userMessage: `Request error (${statusCode}): ${errorMessage}`,
|
|
9705
|
+
};
|
|
9706
|
+
}
|
|
9707
|
+
if (error instanceof MTLSError) {
|
|
9708
|
+
return {
|
|
9709
|
+
category: exports.ErrorCategory.CERTIFICATE_ERROR,
|
|
9710
|
+
statusCode,
|
|
9711
|
+
message: errorMessage,
|
|
9712
|
+
shouldRetry: true,
|
|
9713
|
+
userMessage: 'Certificate error: Unable to establish secure connection.',
|
|
9714
|
+
};
|
|
9715
|
+
}
|
|
9716
|
+
if (!statusCode &&
|
|
9717
|
+
(errorMessage.toLowerCase().includes('network') ||
|
|
9718
|
+
errorMessage.toLowerCase().includes('timeout') ||
|
|
9719
|
+
errorMessage.toLowerCase().includes('connection'))) {
|
|
9720
|
+
return {
|
|
9721
|
+
category: exports.ErrorCategory.NETWORK_ERROR,
|
|
9722
|
+
message: errorMessage,
|
|
9723
|
+
shouldRetry: true,
|
|
9724
|
+
userMessage: 'Network error: Unable to connect to server.',
|
|
9653
9725
|
};
|
|
9654
9726
|
}
|
|
9655
9727
|
return {
|
|
9656
|
-
|
|
9657
|
-
|
|
9658
|
-
|
|
9728
|
+
category: exports.ErrorCategory.UNKNOWN_ERROR,
|
|
9729
|
+
statusCode,
|
|
9730
|
+
message: errorMessage,
|
|
9731
|
+
shouldRetry: false,
|
|
9732
|
+
userMessage: `Unexpected error: ${errorMessage}`,
|
|
9659
9733
|
};
|
|
9660
|
-
}
|
|
9734
|
+
}
|
|
9735
|
+
function shouldReconfigureCertificate(error) {
|
|
9736
|
+
const classification = classifyError(error);
|
|
9737
|
+
return classification.category === exports.ErrorCategory.CERTIFICATE_ERROR;
|
|
9738
|
+
}
|
|
9739
|
+
function shouldRetryRequest(error, isRetryAttempt) {
|
|
9740
|
+
if (isRetryAttempt) {
|
|
9741
|
+
return false;
|
|
9742
|
+
}
|
|
9743
|
+
const classification = classifyError(error);
|
|
9744
|
+
return classification.shouldRetry;
|
|
9745
|
+
}
|
|
9746
|
+
function getUserFriendlyMessage(error) {
|
|
9747
|
+
const classification = classifyError(error);
|
|
9748
|
+
return classification.userMessage;
|
|
9749
|
+
}
|
|
9661
9750
|
|
|
9662
9751
|
var MTLSErrorType;
|
|
9663
9752
|
(function (MTLSErrorType) {
|