@a-cube-io/ereceipts-js-sdk 2.0.7 → 2.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.js +1552 -1383
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +20 -3
- package/dist/index.esm.js +1552 -1383
- package/dist/index.esm.js.map +1 -1
- package/dist/index.native.js +1552 -1383
- 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);
|
|
@@ -1415,6 +1415,15 @@ function from(input, scheduler) {
|
|
|
1415
1415
|
return scheduler ? scheduled(input, scheduler) : innerFrom(input);
|
|
1416
1416
|
}
|
|
1417
1417
|
|
|
1418
|
+
function of() {
|
|
1419
|
+
var args = [];
|
|
1420
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
1421
|
+
args[_i] = arguments[_i];
|
|
1422
|
+
}
|
|
1423
|
+
var scheduler = popScheduler(args);
|
|
1424
|
+
return from(args, scheduler);
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1418
1427
|
function isValidDate(value) {
|
|
1419
1428
|
return value instanceof Date && !isNaN(value);
|
|
1420
1429
|
}
|
|
@@ -1861,131 +1870,436 @@ function extractRoles(jwtRoles) {
|
|
|
1861
1870
|
return allRoles;
|
|
1862
1871
|
}
|
|
1863
1872
|
|
|
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();
|
|
1873
|
+
function clearObject(input) {
|
|
1874
|
+
if (input === null || input === undefined || input === '') {
|
|
1875
|
+
return undefined;
|
|
1873
1876
|
}
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
this.userSubject = new BehaviorSubject(null);
|
|
1880
|
-
this.authStateSubject = new BehaviorSubject('idle');
|
|
1881
|
-
this.destroy$ = new Subject();
|
|
1877
|
+
if (Array.isArray(input)) {
|
|
1878
|
+
const cleanedArray = input
|
|
1879
|
+
.map((item) => clearObject(item))
|
|
1880
|
+
.filter((item) => item !== undefined);
|
|
1881
|
+
return cleanedArray;
|
|
1882
1882
|
}
|
|
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;
|
|
1883
|
+
if (typeof input === 'object' && input.constructor === Object) {
|
|
1884
|
+
const cleaned = {};
|
|
1885
|
+
for (const [key, value] of Object.entries(input)) {
|
|
1886
|
+
const cleanedValue = clearObject(value);
|
|
1887
|
+
if (cleanedValue !== undefined) {
|
|
1888
|
+
cleaned[key] = cleanedValue;
|
|
1889
|
+
}
|
|
1903
1890
|
}
|
|
1891
|
+
return cleaned;
|
|
1904
1892
|
}
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1893
|
+
return input;
|
|
1894
|
+
}
|
|
1895
|
+
function clearObjectShallow(obj) {
|
|
1896
|
+
if (!obj || typeof obj !== 'object') {
|
|
1897
|
+
return {};
|
|
1910
1898
|
}
|
|
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;
|
|
1899
|
+
const cleaned = {};
|
|
1900
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
1901
|
+
if (value !== null && value !== undefined && value !== '') {
|
|
1902
|
+
cleaned[key] = value;
|
|
1930
1903
|
}
|
|
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
1904
|
}
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1905
|
+
return cleaned;
|
|
1906
|
+
}
|
|
1907
|
+
function isEmpty(value) {
|
|
1908
|
+
return value === null || value === undefined || value === '';
|
|
1909
|
+
}
|
|
1910
|
+
function hasNonEmptyValues(obj) {
|
|
1911
|
+
if (!obj || typeof obj !== 'object') {
|
|
1912
|
+
return false;
|
|
1944
1913
|
}
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1914
|
+
return Object.values(obj).some((value) => !isEmpty(value));
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
/**
|
|
1918
|
+
* Platform detection utilities
|
|
1919
|
+
*/
|
|
1920
|
+
/**
|
|
1921
|
+
* Detect the current platform
|
|
1922
|
+
*/
|
|
1923
|
+
function detectPlatform() {
|
|
1924
|
+
// Check for React Native
|
|
1925
|
+
if (typeof global !== 'undefined' &&
|
|
1926
|
+
global.__DEV__ !== undefined &&
|
|
1927
|
+
typeof global.navigator !== 'undefined' &&
|
|
1928
|
+
global.navigator.product === 'ReactNative') {
|
|
1929
|
+
return {
|
|
1930
|
+
platform: 'react-native',
|
|
1931
|
+
isReactNative: true,
|
|
1932
|
+
isWeb: false,
|
|
1933
|
+
isNode: false,
|
|
1934
|
+
isExpo: checkExpo(),
|
|
1935
|
+
};
|
|
1959
1936
|
}
|
|
1960
|
-
|
|
1937
|
+
// Check for Web/Browser
|
|
1938
|
+
if (typeof window !== 'undefined' &&
|
|
1939
|
+
typeof window.document !== 'undefined' &&
|
|
1940
|
+
typeof window.navigator !== 'undefined') {
|
|
1961
1941
|
return {
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
pid: jwtPayload.pid,
|
|
1968
|
-
expiresAt: jwtPayload.exp * 1000,
|
|
1942
|
+
platform: 'web',
|
|
1943
|
+
isReactNative: false,
|
|
1944
|
+
isWeb: true,
|
|
1945
|
+
isNode: false,
|
|
1946
|
+
isExpo: false,
|
|
1969
1947
|
};
|
|
1970
1948
|
}
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1949
|
+
// Check for Node.js
|
|
1950
|
+
if (typeof process !== 'undefined' && process.versions && process.versions.node) {
|
|
1951
|
+
return {
|
|
1952
|
+
platform: 'node',
|
|
1953
|
+
isReactNative: false,
|
|
1954
|
+
isWeb: false,
|
|
1955
|
+
isNode: true,
|
|
1956
|
+
isExpo: false,
|
|
1957
|
+
};
|
|
1974
1958
|
}
|
|
1959
|
+
// Unknown platform
|
|
1960
|
+
return {
|
|
1961
|
+
platform: 'unknown',
|
|
1962
|
+
isReactNative: false,
|
|
1963
|
+
isWeb: false,
|
|
1964
|
+
isNode: false,
|
|
1965
|
+
isExpo: false,
|
|
1966
|
+
};
|
|
1975
1967
|
}
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1968
|
+
/**
|
|
1969
|
+
* Check if running in Expo
|
|
1970
|
+
*/
|
|
1971
|
+
function checkExpo() {
|
|
1972
|
+
try {
|
|
1973
|
+
return (typeof global !== 'undefined' &&
|
|
1974
|
+
(typeof global.Expo !== 'undefined' || typeof global.expo !== 'undefined'));
|
|
1981
1975
|
}
|
|
1982
|
-
|
|
1983
|
-
return
|
|
1976
|
+
catch {
|
|
1977
|
+
return false;
|
|
1984
1978
|
}
|
|
1985
|
-
|
|
1986
|
-
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
function getDefaultExportFromCjs (x) {
|
|
1982
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
var picocolors_browser = {exports: {}};
|
|
1986
|
+
|
|
1987
|
+
var x=String;
|
|
1988
|
+
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}};
|
|
1989
|
+
picocolors_browser.exports=create();
|
|
1990
|
+
picocolors_browser.exports.createColors = create;
|
|
1991
|
+
|
|
1992
|
+
var picocolors_browserExports = picocolors_browser.exports;
|
|
1993
|
+
var pc = /*@__PURE__*/getDefaultExportFromCjs(picocolors_browserExports);
|
|
1994
|
+
|
|
1995
|
+
const levelColors = {
|
|
1996
|
+
debug: {
|
|
1997
|
+
prefix: pc.gray,
|
|
1998
|
+
message: pc.gray,
|
|
1999
|
+
data: pc.dim,
|
|
2000
|
+
},
|
|
2001
|
+
info: {
|
|
2002
|
+
prefix: pc.cyan,
|
|
2003
|
+
message: pc.white,
|
|
2004
|
+
data: pc.dim,
|
|
2005
|
+
},
|
|
2006
|
+
warn: {
|
|
2007
|
+
prefix: pc.yellow,
|
|
2008
|
+
message: pc.yellow,
|
|
2009
|
+
data: pc.dim,
|
|
2010
|
+
},
|
|
2011
|
+
error: {
|
|
2012
|
+
prefix: pc.red,
|
|
2013
|
+
message: pc.red,
|
|
2014
|
+
data: pc.dim,
|
|
2015
|
+
},
|
|
2016
|
+
};
|
|
2017
|
+
const levelIcons = {
|
|
2018
|
+
debug: '🔍',
|
|
2019
|
+
info: 'ℹ️ ',
|
|
2020
|
+
warn: '⚠️ ',
|
|
2021
|
+
error: '❌',
|
|
2022
|
+
};
|
|
2023
|
+
/**
|
|
2024
|
+
* Format data for pretty display in logs
|
|
2025
|
+
*/
|
|
2026
|
+
function formatData(data, level) {
|
|
2027
|
+
if (data === undefined || data === null) {
|
|
2028
|
+
return String(data);
|
|
1987
2029
|
}
|
|
1988
|
-
|
|
2030
|
+
// For primitive types, just return as string
|
|
2031
|
+
if (typeof data !== 'object') {
|
|
2032
|
+
return String(data);
|
|
2033
|
+
}
|
|
2034
|
+
// For Error objects, format nicely
|
|
2035
|
+
if (data instanceof Error) {
|
|
2036
|
+
return `${data.name}: ${data.message}${data.stack ? `\n${pc.dim(data.stack)}` : ''}`;
|
|
2037
|
+
}
|
|
2038
|
+
try {
|
|
2039
|
+
// Apply level-specific coloring
|
|
2040
|
+
const colors = levelColors[level];
|
|
2041
|
+
return colors.data(JSON.stringify(data, null, 2));
|
|
2042
|
+
}
|
|
2043
|
+
catch {
|
|
2044
|
+
// Fallback to JSON.stringify if pretty-format fails
|
|
2045
|
+
try {
|
|
2046
|
+
return JSON.stringify(data, null, 2);
|
|
2047
|
+
}
|
|
2048
|
+
catch {
|
|
2049
|
+
return String(data);
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
class Logger {
|
|
2054
|
+
constructor() {
|
|
2055
|
+
this.enabled = false;
|
|
2056
|
+
}
|
|
2057
|
+
setEnabled(enabled) {
|
|
2058
|
+
this.enabled = enabled;
|
|
2059
|
+
}
|
|
2060
|
+
isEnabled() {
|
|
2061
|
+
return this.enabled;
|
|
2062
|
+
}
|
|
2063
|
+
debug(prefix, message, data) {
|
|
2064
|
+
if (!this.enabled)
|
|
2065
|
+
return;
|
|
2066
|
+
this.log('debug', prefix, message, data);
|
|
2067
|
+
}
|
|
2068
|
+
info(prefix, message, data) {
|
|
2069
|
+
if (!this.enabled)
|
|
2070
|
+
return;
|
|
2071
|
+
this.log('info', prefix, message, data);
|
|
2072
|
+
}
|
|
2073
|
+
warn(prefix, message, data) {
|
|
2074
|
+
this.log('warn', prefix, message, data);
|
|
2075
|
+
}
|
|
2076
|
+
error(prefix, message, data) {
|
|
2077
|
+
this.log('error', prefix, message, data);
|
|
2078
|
+
}
|
|
2079
|
+
log(level, prefix, message, data) {
|
|
2080
|
+
const colors = levelColors[level];
|
|
2081
|
+
const icon = levelIcons[level];
|
|
2082
|
+
const isoTime = new Date().toISOString().split('T')[1] ?? '';
|
|
2083
|
+
const timestamp = pc.dim(isoTime.slice(0, 12));
|
|
2084
|
+
const formattedPrefix = colors.prefix(`[${prefix}]`);
|
|
2085
|
+
const formattedMessage = colors.message(message);
|
|
2086
|
+
const consoleMethod = level === 'debug'
|
|
2087
|
+
? console.debug
|
|
2088
|
+
: level === 'info'
|
|
2089
|
+
? console.info
|
|
2090
|
+
: level === 'warn'
|
|
2091
|
+
? console.warn
|
|
2092
|
+
: console.error;
|
|
2093
|
+
const header = `${timestamp} ${icon} ${formattedPrefix} ${formattedMessage}`;
|
|
2094
|
+
if (data !== undefined) {
|
|
2095
|
+
const formattedData = formatData(data, level);
|
|
2096
|
+
// Check if data is an object (multi-line) or primitive (single-line)
|
|
2097
|
+
const isMultiLine = typeof data === 'object' && data !== null;
|
|
2098
|
+
if (isMultiLine) {
|
|
2099
|
+
consoleMethod(`${header}\n${formattedData}`);
|
|
2100
|
+
}
|
|
2101
|
+
else {
|
|
2102
|
+
consoleMethod(`${header}`, formattedData);
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
else {
|
|
2106
|
+
consoleMethod(header);
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
const logger = new Logger();
|
|
2111
|
+
function createPrefixedLogger(prefix) {
|
|
2112
|
+
return {
|
|
2113
|
+
debug: (message, data) => logger.debug(prefix, message, data),
|
|
2114
|
+
info: (message, data) => logger.info(prefix, message, data),
|
|
2115
|
+
warn: (message, data) => logger.warn(prefix, message, data),
|
|
2116
|
+
error: (message, data) => logger.error(prefix, message, data),
|
|
2117
|
+
};
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
/**
|
|
2121
|
+
* Formats a numeric string value to have exactly the specified number of decimal places.
|
|
2122
|
+
* E.g., "1" → "1.00", "10" → "10.00", "1.5" → "1.50"
|
|
2123
|
+
* Returns undefined for undefined input (preserves optional fields).
|
|
2124
|
+
*
|
|
2125
|
+
* @param value - The string value to format
|
|
2126
|
+
* @param decimals - Number of decimal places (default: 2)
|
|
2127
|
+
* @returns Formatted string or undefined if input is undefined
|
|
2128
|
+
*/
|
|
2129
|
+
function formatDecimal(value, decimals = 2) {
|
|
2130
|
+
if (value === undefined)
|
|
2131
|
+
return undefined;
|
|
2132
|
+
const num = parseFloat(value);
|
|
2133
|
+
if (isNaN(num))
|
|
2134
|
+
return value;
|
|
2135
|
+
return num.toFixed(decimals);
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
const log$h = createPrefixedLogger('AUTH-SERVICE');
|
|
2139
|
+
class AuthenticationService {
|
|
2140
|
+
get user$() {
|
|
2141
|
+
return this.userSubject.asObservable();
|
|
2142
|
+
}
|
|
2143
|
+
get isAuthenticated$() {
|
|
2144
|
+
return this.userSubject.pipe(map((user) => user !== null), distinctUntilChanged(), takeUntil(this.destroy$));
|
|
2145
|
+
}
|
|
2146
|
+
get authState$() {
|
|
2147
|
+
return this.authStateSubject.asObservable();
|
|
2148
|
+
}
|
|
2149
|
+
constructor(httpPort, tokenStorage, config, events = {}) {
|
|
2150
|
+
this.httpPort = httpPort;
|
|
2151
|
+
this.tokenStorage = tokenStorage;
|
|
2152
|
+
this.config = config;
|
|
2153
|
+
this.events = events;
|
|
2154
|
+
this.userSubject = new BehaviorSubject(null);
|
|
2155
|
+
this.authStateSubject = new BehaviorSubject('idle');
|
|
2156
|
+
this.destroy$ = new Subject();
|
|
2157
|
+
}
|
|
2158
|
+
async login(credentials) {
|
|
2159
|
+
this.authStateSubject.next('authenticating');
|
|
2160
|
+
log$h.info('Login attempt', {
|
|
2161
|
+
authUrl: this.config.authUrl,
|
|
2162
|
+
email: credentials.email,
|
|
2163
|
+
});
|
|
2164
|
+
try {
|
|
2165
|
+
const response = await this.httpPort.post(`${this.config.authUrl}/login`, {
|
|
2166
|
+
email: credentials.email,
|
|
2167
|
+
password: credentials.password,
|
|
2168
|
+
});
|
|
2169
|
+
const jwtPayload = parseJwt(response.data.token);
|
|
2170
|
+
const expiresAt = jwtPayload.exp * 1000;
|
|
2171
|
+
log$h.info('Login successful', {
|
|
2172
|
+
authUrl: this.config.authUrl,
|
|
2173
|
+
tokenPrefix: response.data.token.substring(0, 30) + '...',
|
|
2174
|
+
expiresAt: new Date(expiresAt).toISOString(),
|
|
2175
|
+
});
|
|
2176
|
+
await this.tokenStorage.saveAccessToken(response.data.token, expiresAt);
|
|
2177
|
+
const user = this.createUserFromPayload(jwtPayload);
|
|
2178
|
+
await this.tokenStorage.saveUser(user);
|
|
2179
|
+
this.userSubject.next(user);
|
|
2180
|
+
this.authStateSubject.next('authenticated');
|
|
2181
|
+
this.events.onUserChanged?.(user);
|
|
2182
|
+
return user;
|
|
2183
|
+
}
|
|
2184
|
+
catch (error) {
|
|
2185
|
+
this.authStateSubject.next('error');
|
|
2186
|
+
throw error;
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
async logout() {
|
|
2190
|
+
await this.tokenStorage.clearTokens();
|
|
2191
|
+
this.userSubject.next(null);
|
|
2192
|
+
this.authStateSubject.next('idle');
|
|
2193
|
+
this.events.onUserChanged?.(null);
|
|
2194
|
+
}
|
|
2195
|
+
async getCurrentUser() {
|
|
2196
|
+
// Always verify token is valid before returning user
|
|
2197
|
+
const token = await this.tokenStorage.getAccessToken();
|
|
2198
|
+
if (!token) {
|
|
2199
|
+
// No token - clear any stale user state
|
|
2200
|
+
log$h.debug('getCurrentUser: No token in storage');
|
|
2201
|
+
if (this.userSubject.value) {
|
|
2202
|
+
this.userSubject.next(null);
|
|
2203
|
+
this.authStateSubject.next('idle');
|
|
2204
|
+
}
|
|
2205
|
+
return null;
|
|
2206
|
+
}
|
|
2207
|
+
log$h.debug('getCurrentUser: Token found', {
|
|
2208
|
+
tokenPrefix: token.substring(0, 30) + '...',
|
|
2209
|
+
tokenLength: token.length,
|
|
2210
|
+
});
|
|
2211
|
+
const jwtPayload = parseJwt(token);
|
|
2212
|
+
if (isTokenExpired(jwtPayload)) {
|
|
2213
|
+
// Token expired - clear everything
|
|
2214
|
+
log$h.warn('getCurrentUser: Token expired');
|
|
2215
|
+
await this.tokenStorage.clearTokens();
|
|
2216
|
+
this.userSubject.next(null);
|
|
2217
|
+
this.authStateSubject.next('idle');
|
|
2218
|
+
this.events.onUserChanged?.(null);
|
|
2219
|
+
return null;
|
|
2220
|
+
}
|
|
2221
|
+
// Token is valid - return cached user if available
|
|
2222
|
+
const currentUser = this.userSubject.value;
|
|
2223
|
+
if (currentUser) {
|
|
2224
|
+
log$h.debug('getCurrentUser: Returning cached user', {
|
|
2225
|
+
email: currentUser.email,
|
|
2226
|
+
roles: currentUser.roles,
|
|
2227
|
+
});
|
|
2228
|
+
return currentUser;
|
|
2229
|
+
}
|
|
2230
|
+
// Check stored user
|
|
2231
|
+
const storedUser = await this.tokenStorage.getUser();
|
|
2232
|
+
if (storedUser) {
|
|
2233
|
+
this.userSubject.next(storedUser);
|
|
2234
|
+
this.authStateSubject.next('authenticated');
|
|
2235
|
+
return storedUser;
|
|
2236
|
+
}
|
|
2237
|
+
// Create user from token
|
|
2238
|
+
const user = this.createUserFromPayload(jwtPayload);
|
|
2239
|
+
await this.tokenStorage.saveUser(user);
|
|
2240
|
+
this.userSubject.next(user);
|
|
2241
|
+
this.authStateSubject.next('authenticated');
|
|
2242
|
+
return user;
|
|
2243
|
+
}
|
|
2244
|
+
async isAuthenticated() {
|
|
2245
|
+
const token = await this.tokenStorage.getAccessToken();
|
|
2246
|
+
if (!token) {
|
|
2247
|
+
log$h.debug('isAuthenticated: No token in storage');
|
|
2248
|
+
return false;
|
|
2249
|
+
}
|
|
2250
|
+
const jwtPayload = parseJwt(token);
|
|
2251
|
+
const expired = isTokenExpired(jwtPayload);
|
|
2252
|
+
log$h.debug('isAuthenticated: Token check', {
|
|
2253
|
+
hasToken: true,
|
|
2254
|
+
expired,
|
|
2255
|
+
expiresAt: new Date(jwtPayload.exp * 1000).toISOString(),
|
|
2256
|
+
});
|
|
2257
|
+
return !expired;
|
|
2258
|
+
}
|
|
2259
|
+
async getAccessToken() {
|
|
2260
|
+
const token = await this.tokenStorage.getAccessToken();
|
|
2261
|
+
if (!token) {
|
|
2262
|
+
return null;
|
|
2263
|
+
}
|
|
2264
|
+
const jwtPayload = parseJwt(token);
|
|
2265
|
+
if (isTokenExpired(jwtPayload)) {
|
|
2266
|
+
await this.tokenStorage.clearTokens();
|
|
2267
|
+
this.userSubject.next(null);
|
|
2268
|
+
this.authStateSubject.next('idle');
|
|
2269
|
+
this.events.onUserChanged?.(null);
|
|
2270
|
+
return null;
|
|
2271
|
+
}
|
|
2272
|
+
return token;
|
|
2273
|
+
}
|
|
2274
|
+
createUserFromPayload(jwtPayload) {
|
|
2275
|
+
return {
|
|
2276
|
+
id: jwtPayload.uid.toString(),
|
|
2277
|
+
email: jwtPayload.username,
|
|
2278
|
+
username: jwtPayload.username,
|
|
2279
|
+
roles: jwtPayload.roles,
|
|
2280
|
+
fid: jwtPayload.fid,
|
|
2281
|
+
pid: jwtPayload.pid,
|
|
2282
|
+
expiresAt: jwtPayload.exp * 1000,
|
|
2283
|
+
};
|
|
2284
|
+
}
|
|
2285
|
+
destroy() {
|
|
2286
|
+
this.destroy$.next();
|
|
2287
|
+
this.destroy$.complete();
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2291
|
+
const CERTIFICATE_KEY = 'acube_certificate';
|
|
2292
|
+
class CertificateService {
|
|
2293
|
+
get certificate$() {
|
|
2294
|
+
return this.certificateSubject.asObservable();
|
|
2295
|
+
}
|
|
2296
|
+
get hasCertificate$() {
|
|
2297
|
+
return this.certificateSubject.pipe(map((cert) => cert !== null), distinctUntilChanged());
|
|
2298
|
+
}
|
|
2299
|
+
get state$() {
|
|
2300
|
+
return this.stateSubject.asObservable();
|
|
2301
|
+
}
|
|
2302
|
+
constructor(secureStorage) {
|
|
1989
2303
|
this.secureStorage = secureStorage;
|
|
1990
2304
|
this.certificateSubject = new BehaviorSubject(null);
|
|
1991
2305
|
this.stateSubject = new BehaviorSubject('idle');
|
|
@@ -2080,406 +2394,677 @@ function hasAnyRole(userRoles, required) {
|
|
|
2080
2394
|
return Object.values(userRoles).some((roles) => required.some((role) => roles.includes(role)));
|
|
2081
2395
|
}
|
|
2082
2396
|
|
|
2083
|
-
class
|
|
2084
|
-
constructor(
|
|
2085
|
-
this.
|
|
2086
|
-
this.mtlsHandler = mtlsHandler;
|
|
2087
|
-
this.userProvider = userProvider;
|
|
2088
|
-
this.mtlsAdapter = mtlsAdapter;
|
|
2397
|
+
class ConfigManager {
|
|
2398
|
+
constructor(userConfig) {
|
|
2399
|
+
this.config = this.buildConfig(userConfig);
|
|
2089
2400
|
}
|
|
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;
|
|
2401
|
+
buildConfig(userConfig) {
|
|
2402
|
+
return {
|
|
2403
|
+
environment: userConfig.environment,
|
|
2404
|
+
apiUrl: this.getDefaultApiUrl(userConfig.environment),
|
|
2405
|
+
authUrl: this.getDefaultAuthUrl(userConfig.environment),
|
|
2406
|
+
timeout: 30000,
|
|
2407
|
+
retryAttempts: 3,
|
|
2408
|
+
debug: userConfig.debug ?? false,
|
|
2409
|
+
customHeaders: {},
|
|
2410
|
+
};
|
|
2158
2411
|
}
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2412
|
+
getDefaultApiUrl(environment) {
|
|
2413
|
+
switch (environment) {
|
|
2414
|
+
case 'production':
|
|
2415
|
+
return 'https://ereceipts-it.acubeapi.com';
|
|
2416
|
+
case 'development':
|
|
2417
|
+
return 'https://ereceipts-it.dev.acubeapi.com';
|
|
2418
|
+
case 'sandbox':
|
|
2419
|
+
default:
|
|
2420
|
+
return 'https://ereceipts-it-sandbox.acubeapi.com';
|
|
2162
2421
|
}
|
|
2163
|
-
const platformInfo = this.mtlsAdapter.getPlatformInfo();
|
|
2164
|
-
return platformInfo.platform === 'web' ? 'web' : 'mobile';
|
|
2165
2422
|
}
|
|
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';
|
|
2423
|
+
getDefaultAuthUrl(environment) {
|
|
2424
|
+
switch (environment) {
|
|
2425
|
+
case 'production':
|
|
2426
|
+
return 'https://common.api.acubeapi.com';
|
|
2427
|
+
case 'development':
|
|
2428
|
+
case 'sandbox':
|
|
2429
|
+
default:
|
|
2430
|
+
return 'https://common-sandbox.api.acubeapi.com';
|
|
2182
2431
|
}
|
|
2183
|
-
return null;
|
|
2184
|
-
}
|
|
2185
|
-
isReceiptEndpoint(url) {
|
|
2186
|
-
return url.includes('/receipts') || url.includes('/mf1/receipts');
|
|
2187
|
-
}
|
|
2188
|
-
isReturnableItemsEndpoint(url) {
|
|
2189
|
-
return !!(url.match(/\/receipts\/[a-f0-9-]+\/returnable-items$/) ||
|
|
2190
|
-
url.match(/\/mf1\/receipts\/[a-f0-9-]+\/returnable-items$/));
|
|
2191
2432
|
}
|
|
2192
|
-
|
|
2193
|
-
return
|
|
2194
|
-
|
|
2433
|
+
getConfig() {
|
|
2434
|
+
return {
|
|
2435
|
+
environment: this.config.environment,
|
|
2436
|
+
debug: this.config.debug,
|
|
2437
|
+
};
|
|
2195
2438
|
}
|
|
2196
|
-
|
|
2197
|
-
return
|
|
2439
|
+
getApiUrl() {
|
|
2440
|
+
return this.config.apiUrl;
|
|
2198
2441
|
}
|
|
2199
|
-
|
|
2200
|
-
return
|
|
2442
|
+
getAuthUrl() {
|
|
2443
|
+
return this.config.authUrl;
|
|
2201
2444
|
}
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
class JwtAuthHandler {
|
|
2205
|
-
constructor(tokenStorage) {
|
|
2206
|
-
this.tokenStorage = tokenStorage;
|
|
2445
|
+
getEnvironment() {
|
|
2446
|
+
return this.config.environment;
|
|
2207
2447
|
}
|
|
2208
|
-
|
|
2209
|
-
return
|
|
2448
|
+
isDebugEnabled() {
|
|
2449
|
+
return this.config.debug;
|
|
2210
2450
|
}
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
if (!token) {
|
|
2214
|
-
return {};
|
|
2215
|
-
}
|
|
2216
|
-
return { Authorization: `Bearer ${token}` };
|
|
2451
|
+
getTimeout() {
|
|
2452
|
+
return this.config.timeout;
|
|
2217
2453
|
}
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
function clearObject(input) {
|
|
2221
|
-
if (input === null || input === undefined || input === '') {
|
|
2222
|
-
return undefined;
|
|
2454
|
+
getRetryAttempts() {
|
|
2455
|
+
return this.config.retryAttempts;
|
|
2223
2456
|
}
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
.map((item) => clearObject(item))
|
|
2227
|
-
.filter((item) => item !== undefined);
|
|
2228
|
-
return cleanedArray;
|
|
2457
|
+
getCustomHeaders() {
|
|
2458
|
+
return { ...this.config.customHeaders };
|
|
2229
2459
|
}
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
cleaned[key] = cleanedValue;
|
|
2236
|
-
}
|
|
2460
|
+
updateConfig(updates) {
|
|
2461
|
+
if (updates.environment) {
|
|
2462
|
+
this.config.environment = updates.environment;
|
|
2463
|
+
this.config.apiUrl = this.getDefaultApiUrl(updates.environment);
|
|
2464
|
+
this.config.authUrl = this.getDefaultAuthUrl(updates.environment);
|
|
2237
2465
|
}
|
|
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;
|
|
2466
|
+
if (updates.debug !== undefined) {
|
|
2467
|
+
this.config.debug = updates.debug;
|
|
2250
2468
|
}
|
|
2251
2469
|
}
|
|
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
2470
|
}
|
|
2263
2471
|
|
|
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
|
-
|
|
2472
|
+
// Enum options arrays
|
|
2473
|
+
const VAT_RATE_CODE_OPTIONS = [
|
|
2474
|
+
'4.00',
|
|
2475
|
+
'5.00',
|
|
2476
|
+
'10.00',
|
|
2477
|
+
'22.00',
|
|
2478
|
+
'2.00',
|
|
2479
|
+
'6.40',
|
|
2480
|
+
'7.00',
|
|
2481
|
+
'7.30',
|
|
2482
|
+
'7.50',
|
|
2483
|
+
'7.65',
|
|
2484
|
+
'7.95',
|
|
2485
|
+
'8.30',
|
|
2486
|
+
'8.50',
|
|
2487
|
+
'8.80',
|
|
2488
|
+
'9.50',
|
|
2489
|
+
'12.30',
|
|
2490
|
+
'N1',
|
|
2491
|
+
'N2',
|
|
2492
|
+
'N3',
|
|
2493
|
+
'N4',
|
|
2494
|
+
'N5',
|
|
2495
|
+
'N6',
|
|
2496
|
+
];
|
|
2497
|
+
const GOOD_OR_SERVICE_OPTIONS = ['goods', 'service'];
|
|
2498
|
+
const RECEIPT_PROOF_TYPE_OPTIONS = ['POS', 'VR', 'ND'];
|
|
2499
|
+
// Enum types for receipt validation
|
|
2500
|
+
const VatRateCodeSchema = z__namespace.enum(VAT_RATE_CODE_OPTIONS);
|
|
2501
|
+
const GoodOrServiceSchema = z__namespace.enum(GOOD_OR_SERVICE_OPTIONS);
|
|
2502
|
+
const ReceiptProofTypeSchema = z__namespace.enum(RECEIPT_PROOF_TYPE_OPTIONS);
|
|
2503
|
+
// Receipt Item Schema
|
|
2504
|
+
const ReceiptItemSchema = z__namespace.object({
|
|
2505
|
+
type: GoodOrServiceSchema.optional(),
|
|
2506
|
+
quantity: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2507
|
+
description: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2508
|
+
unit_price: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2509
|
+
vat_rate_code: VatRateCodeSchema.optional(),
|
|
2510
|
+
simplified_vat_allocation: z__namespace.boolean().optional(),
|
|
2511
|
+
discount: z__namespace.string().nullable().optional(),
|
|
2512
|
+
is_down_payment_or_voucher_redemption: z__namespace.boolean().optional(),
|
|
2513
|
+
complimentary: z__namespace.boolean().optional(),
|
|
2514
|
+
});
|
|
2515
|
+
// Main Receipt Input Schema
|
|
2516
|
+
const ReceiptInputSchema = z__namespace
|
|
2517
|
+
.object({
|
|
2518
|
+
items: z__namespace.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
|
|
2519
|
+
customer_tax_code: z__namespace.string().optional(),
|
|
2520
|
+
customer_lottery_code: z__namespace.string().optional(),
|
|
2521
|
+
discount: z__namespace.string().nullable().optional(),
|
|
2522
|
+
invoice_issuing: z__namespace.boolean().optional(),
|
|
2523
|
+
uncollected_dcr_to_ssn: z__namespace.boolean().optional(),
|
|
2524
|
+
services_uncollected_amount: z__namespace.string().nullable().optional(),
|
|
2525
|
+
goods_uncollected_amount: z__namespace.string().nullable().optional(),
|
|
2526
|
+
cash_payment_amount: z__namespace.string().nullable().optional(),
|
|
2527
|
+
electronic_payment_amount: z__namespace.string().nullable().optional(),
|
|
2528
|
+
ticket_restaurant_payment_amount: z__namespace.string().nullable().optional(),
|
|
2529
|
+
ticket_restaurant_quantity: z__namespace.number().optional(),
|
|
2530
|
+
})
|
|
2531
|
+
.refine((data) => {
|
|
2532
|
+
// At least one payment method should be provided
|
|
2533
|
+
const hasCashPayment = data.cash_payment_amount && parseFloat(data.cash_payment_amount) > 0;
|
|
2534
|
+
const hasElectronicPayment = data.electronic_payment_amount && parseFloat(data.electronic_payment_amount) > 0;
|
|
2535
|
+
const hasTicketPayment = data.ticket_restaurant_payment_amount &&
|
|
2536
|
+
parseFloat(data.ticket_restaurant_payment_amount) > 0;
|
|
2537
|
+
return hasCashPayment || hasElectronicPayment || hasTicketPayment;
|
|
2538
|
+
}, {
|
|
2539
|
+
error: 'At least one payment method is required',
|
|
2540
|
+
path: ['payment_methods'],
|
|
2541
|
+
})
|
|
2542
|
+
.refine((data) => {
|
|
2543
|
+
// only one between customer_tax_code and customer_lottery_code can be provided
|
|
2544
|
+
return !data.customer_tax_code || !data.customer_lottery_code;
|
|
2545
|
+
}, {
|
|
2546
|
+
error: 'Only one between customer_tax_code and customer_lottery_code can be provided',
|
|
2547
|
+
path: ['customer_tax_code', 'customer_lottery_code'],
|
|
2548
|
+
});
|
|
2549
|
+
// Receipt Return or Void via PEM Schema
|
|
2550
|
+
const ReceiptReturnOrVoidViaPEMInputSchema = z__namespace.object({
|
|
2551
|
+
device_id: z__namespace.string().optional(),
|
|
2552
|
+
items: z__namespace.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
|
|
2553
|
+
document_number: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2554
|
+
document_datetime: z__namespace.string().optional(),
|
|
2555
|
+
lottery_code: z__namespace.string().optional(),
|
|
2556
|
+
});
|
|
2557
|
+
// Receipt Return or Void with Proof Schema
|
|
2558
|
+
const ReceiptReturnOrVoidWithProofInputSchema = z__namespace.object({
|
|
2559
|
+
items: z__namespace.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
|
|
2560
|
+
proof: ReceiptProofTypeSchema,
|
|
2561
|
+
document_datetime: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2562
|
+
});
|
|
2563
|
+
// Void Receipt Schema
|
|
2564
|
+
const VoidReceiptInputSchema = z__namespace.object({
|
|
2565
|
+
document_number: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2566
|
+
});
|
|
2567
|
+
const ReceiptReturnItemSchema = z__namespace
|
|
2568
|
+
.array(z__namespace.object({
|
|
2569
|
+
id: z__namespace.number(),
|
|
2570
|
+
quantity: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2571
|
+
}))
|
|
2572
|
+
.min(1, { error: 'arrayMin1' });
|
|
2573
|
+
// Receipt Return Schema
|
|
2574
|
+
const ReceiptReturnInputSchema = z__namespace.object({
|
|
2575
|
+
items: z__namespace.array(ReceiptReturnItemSchema).min(1, { error: 'arrayMin1' }),
|
|
2576
|
+
document_number: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2577
|
+
});
|
|
2578
|
+
|
|
2579
|
+
// Cashier Create Input Schema (MF1)
|
|
2580
|
+
const CashierCreateInputSchema = z__namespace.object({
|
|
2581
|
+
email: z__namespace
|
|
2582
|
+
.string()
|
|
2583
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2584
|
+
.max(255, { error: 'emailMaxLength' })
|
|
2585
|
+
.email({ error: 'invalidEmail' }),
|
|
2586
|
+
password: z__namespace
|
|
2587
|
+
.string()
|
|
2588
|
+
.min(8, { error: 'passwordMinLength' })
|
|
2589
|
+
.max(40, { error: 'passwordMaxLength' }),
|
|
2590
|
+
name: z__namespace.string().min(1, { error: 'fieldIsRequired' }).max(255, { error: 'nameMaxLength' }),
|
|
2591
|
+
display_name: z__namespace
|
|
2592
|
+
.string()
|
|
2593
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2594
|
+
.max(255, { error: 'displayNameMaxLength' }),
|
|
2595
|
+
});
|
|
2596
|
+
|
|
2597
|
+
// Enum options arrays
|
|
2598
|
+
const PEM_STATUS_OPTIONS = [
|
|
2599
|
+
'NEW',
|
|
2600
|
+
'REGISTERED',
|
|
2601
|
+
'ACTIVATED',
|
|
2602
|
+
'ONLINE',
|
|
2603
|
+
'OFFLINE',
|
|
2604
|
+
'DISCARDED',
|
|
2605
|
+
];
|
|
2606
|
+
// Address Schema (reusable)
|
|
2607
|
+
const AddressSchema = z__namespace.object({
|
|
2608
|
+
street_address: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2609
|
+
street_number: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2610
|
+
zip_code: z__namespace
|
|
2611
|
+
.string()
|
|
2612
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2613
|
+
.regex(/^\d{5}$/, { error: 'invalidZipCode' }),
|
|
2614
|
+
city: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2615
|
+
province: z__namespace
|
|
2616
|
+
.string()
|
|
2617
|
+
.min(2, { error: 'provinceMinLength' })
|
|
2618
|
+
.max(2, { error: 'provinceMaxLength' })
|
|
2619
|
+
.toUpperCase(),
|
|
2620
|
+
});
|
|
2621
|
+
// PEM Status Schema
|
|
2622
|
+
const PEMStatusSchema = z__namespace.enum(PEM_STATUS_OPTIONS);
|
|
2623
|
+
// Activation Request Schema
|
|
2624
|
+
const ActivationRequestSchema = z__namespace.object({
|
|
2625
|
+
registration_key: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2626
|
+
});
|
|
2627
|
+
// PEM Status Offline Request Schema
|
|
2628
|
+
const PEMStatusOfflineRequestSchema = z__namespace.object({
|
|
2629
|
+
timestamp: z__namespace
|
|
2630
|
+
.string()
|
|
2631
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2632
|
+
.refine((val) => !isNaN(Date.parse(val)), {
|
|
2633
|
+
error: 'invalidDateFormat',
|
|
2634
|
+
}),
|
|
2635
|
+
reason: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2636
|
+
});
|
|
2637
|
+
|
|
2638
|
+
// Cash Register Create Schema
|
|
2639
|
+
const CashRegisterCreateSchema = z__namespace.object({
|
|
2640
|
+
pem_serial_number: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2641
|
+
name: z__namespace.string().min(1, { error: 'fieldIsRequired' }).max(100, { error: 'nameMaxLength' }),
|
|
2642
|
+
});
|
|
2643
|
+
|
|
2644
|
+
// VAT number validation regex (Partita IVA - 11 digits)
|
|
2645
|
+
const VAT_NUMBER_REGEX = /^\d{11}$/;
|
|
2646
|
+
// Fiscal code validation regex (Codice Fiscale - 11 digits only for merchants)
|
|
2647
|
+
const FISCAL_CODE_REGEX = /^\d{11}$/;
|
|
2648
|
+
// Password validation regex (from OpenAPI spec)
|
|
2649
|
+
const PASSWORD_REGEX = /^((?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{10,}).*)$/;
|
|
2650
|
+
// Merchant Create Input Schema
|
|
2651
|
+
const MerchantCreateInputSchema = z__namespace
|
|
2652
|
+
.object({
|
|
2653
|
+
vat_number: z__namespace
|
|
2654
|
+
.string()
|
|
2655
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2656
|
+
.regex(VAT_NUMBER_REGEX, { error: 'invalidVatNumber' }),
|
|
2657
|
+
fiscal_code: z__namespace.string().regex(FISCAL_CODE_REGEX, { error: 'invalidFiscalCode' }).optional(),
|
|
2658
|
+
business_name: z__namespace.string().max(200, { error: 'businessNameMaxLength' }).optional().nullable(),
|
|
2659
|
+
first_name: z__namespace.string().max(100, { error: 'firstNameMaxLength' }).optional().nullable(),
|
|
2660
|
+
last_name: z__namespace.string().max(100, { error: 'lastNameMaxLength' }).optional().nullable(),
|
|
2661
|
+
email: z__namespace.string().min(1, { error: 'fieldIsRequired' }).email({ error: 'invalidEmail' }),
|
|
2662
|
+
password: z__namespace
|
|
2663
|
+
.string()
|
|
2664
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2665
|
+
.regex(PASSWORD_REGEX, { error: 'passwordComplexity' }),
|
|
2666
|
+
address: AddressSchema.optional(),
|
|
2667
|
+
})
|
|
2668
|
+
.refine((data) => {
|
|
2669
|
+
const hasBusinessName = data.business_name && data.business_name.trim() !== '';
|
|
2670
|
+
const hasPersonalNames = (data.first_name && data.first_name.trim() !== '') ||
|
|
2671
|
+
(data.last_name && data.last_name.trim() !== '');
|
|
2672
|
+
// If business name is set, first/last name must not be provided
|
|
2673
|
+
if (hasBusinessName && hasPersonalNames) {
|
|
2674
|
+
return false;
|
|
2322
2675
|
}
|
|
2323
|
-
|
|
2676
|
+
// At least one naming method must be provided
|
|
2677
|
+
if (!hasBusinessName && !hasPersonalNames) {
|
|
2324
2678
|
return false;
|
|
2325
2679
|
}
|
|
2326
|
-
|
|
2680
|
+
return true;
|
|
2681
|
+
}, {
|
|
2682
|
+
error: 'businessNameOrPersonalNamesRequired',
|
|
2683
|
+
path: ['business_name'],
|
|
2684
|
+
});
|
|
2685
|
+
// Merchant Update Input Schema
|
|
2686
|
+
const MerchantUpdateInputSchema = z__namespace.object({
|
|
2687
|
+
business_name: z__namespace.string().max(200, { error: 'businessNameMaxLength' }).optional().nullable(),
|
|
2688
|
+
first_name: z__namespace.string().max(100, { error: 'firstNameMaxLength' }).optional().nullable(),
|
|
2689
|
+
last_name: z__namespace.string().max(100, { error: 'lastNameMaxLength' }).optional().nullable(),
|
|
2690
|
+
address: AddressSchema.optional().nullable(),
|
|
2691
|
+
});
|
|
2327
2692
|
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2693
|
+
// Enum options arrays
|
|
2694
|
+
const PEM_TYPE_OPTIONS = ['AP', 'SP', 'TM', 'PV'];
|
|
2695
|
+
// PEM Data Schema
|
|
2696
|
+
const PemDataSchema = z__namespace.object({
|
|
2697
|
+
version: z__namespace.string().min(1, { error: 'fieldIsRequired' }),
|
|
2698
|
+
type: z__namespace.enum(PEM_TYPE_OPTIONS, {
|
|
2699
|
+
error: 'invalidPemType',
|
|
2700
|
+
}),
|
|
2701
|
+
});
|
|
2702
|
+
// PEM Create Input Schema
|
|
2703
|
+
const PemCreateInputSchema = z__namespace.object({
|
|
2704
|
+
merchant_uuid: z__namespace.string().min(1, { error: 'fieldIsRequired' }).uuid({ error: 'invalidUuid' }),
|
|
2705
|
+
address: AddressSchema.optional(),
|
|
2706
|
+
/* external_pem_data: PemDataSchema.optional(), */
|
|
2707
|
+
});
|
|
2331
2708
|
|
|
2332
|
-
|
|
2709
|
+
// Italian Fiscal ID validation regex (Codice Fiscale for individuals or Partita IVA for companies)
|
|
2710
|
+
const FISCAL_ID_REGEX = /^([A-Z]{6}[0-9LMNPQRSTUV]{2}[ABCDEHLMPRST][0-9LMNPQRSTUV]{2}[A-Z][0-9LMNPQRSTUV]{3}[A-Z]|[0-9]{11})$/;
|
|
2711
|
+
// Supplier Create Input Schema
|
|
2712
|
+
const SupplierCreateInputSchema = z__namespace.object({
|
|
2713
|
+
fiscal_id: z__namespace
|
|
2714
|
+
.string()
|
|
2715
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2716
|
+
.regex(FISCAL_ID_REGEX, { error: 'invalidFiscalId' })
|
|
2717
|
+
.toUpperCase(),
|
|
2718
|
+
name: z__namespace.string().min(1, { error: 'fieldIsRequired' }).max(200, { error: 'nameMaxLength' }),
|
|
2719
|
+
address: AddressSchema.optional(),
|
|
2720
|
+
});
|
|
2721
|
+
// Supplier Update Input Schema
|
|
2722
|
+
const SupplierUpdateInputSchema = z__namespace.object({
|
|
2723
|
+
name: z__namespace.string().min(1, { error: 'fieldIsRequired' }).max(200, { error: 'nameMaxLength' }),
|
|
2724
|
+
address: AddressSchema.optional(),
|
|
2725
|
+
});
|
|
2333
2726
|
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2727
|
+
// Journal Close Input Schema
|
|
2728
|
+
const JournalCloseInputSchema = z__namespace.object({
|
|
2729
|
+
closing_timestamp: z__namespace
|
|
2730
|
+
.string()
|
|
2731
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2732
|
+
.refine((val) => !isNaN(Date.parse(val)), {
|
|
2733
|
+
error: 'invalidDateFormat',
|
|
2734
|
+
}),
|
|
2735
|
+
reason: z__namespace.string().max(255, { error: 'reasonMaxLength' }).optional(),
|
|
2736
|
+
});
|
|
2338
2737
|
|
|
2339
|
-
|
|
2340
|
-
|
|
2738
|
+
// Daily Report Status Options
|
|
2739
|
+
const DAILY_REPORT_STATUS_OPTIONS = ['pending', 'sent', 'error'];
|
|
2740
|
+
// Daily Report Status Schema
|
|
2741
|
+
const DailyReportStatusSchema = z__namespace.enum(DAILY_REPORT_STATUS_OPTIONS, {
|
|
2742
|
+
error: 'invalidDailyReportStatus',
|
|
2743
|
+
});
|
|
2744
|
+
// Daily Reports List Parameters Schema
|
|
2745
|
+
const DailyReportsParamsSchema = z__namespace.object({
|
|
2746
|
+
pem_serial_number: z__namespace.string().min(1, { error: 'fieldIsRequired' }).optional(),
|
|
2747
|
+
date_from: z__namespace
|
|
2748
|
+
.string()
|
|
2749
|
+
.refine((val) => !isNaN(Date.parse(val)), {
|
|
2750
|
+
error: 'invalidDateFormat',
|
|
2751
|
+
})
|
|
2752
|
+
.optional(),
|
|
2753
|
+
date_to: z__namespace
|
|
2754
|
+
.string()
|
|
2755
|
+
.refine((val) => !isNaN(Date.parse(val)), {
|
|
2756
|
+
error: 'invalidDateFormat',
|
|
2757
|
+
})
|
|
2758
|
+
.optional(),
|
|
2759
|
+
status: DailyReportStatusSchema.optional(),
|
|
2760
|
+
page: z__namespace.number().min(1, { error: 'pageMinValue' }).optional(),
|
|
2761
|
+
});
|
|
2762
|
+
|
|
2763
|
+
const NotificationScopeSchema = z__namespace.object({
|
|
2764
|
+
type: z__namespace.literal('global'),
|
|
2765
|
+
});
|
|
2766
|
+
const PemStatusSchema = z__namespace.enum(['ONLINE', 'OFFLINE']);
|
|
2767
|
+
const NotificationDataBlockAtSchema = z__namespace.object({
|
|
2768
|
+
block_at: z__namespace.string(),
|
|
2769
|
+
});
|
|
2770
|
+
const NotificationDataPemStatusSchema = z__namespace.object({
|
|
2771
|
+
from: PemStatusSchema,
|
|
2772
|
+
to: PemStatusSchema,
|
|
2773
|
+
});
|
|
2774
|
+
const NotificationBaseSchema = z__namespace.object({
|
|
2775
|
+
uuid: z__namespace.string().uuid({ error: 'invalidUuid' }),
|
|
2776
|
+
scope: NotificationScopeSchema,
|
|
2777
|
+
source: z__namespace.enum(['system', 'Italian Tax Authority']),
|
|
2778
|
+
level: z__namespace.enum(['info', 'warning', 'error', 'critical']),
|
|
2779
|
+
created_at: z__namespace.string(),
|
|
2780
|
+
});
|
|
2781
|
+
const NotificationMf2UnreachableSchema = NotificationBaseSchema.extend({
|
|
2782
|
+
type: z__namespace.literal('INTERNAL_COMMUNICATION_FAILURE'),
|
|
2783
|
+
code: z__namespace.literal('SYS-W-01'),
|
|
2784
|
+
data: NotificationDataBlockAtSchema,
|
|
2785
|
+
});
|
|
2786
|
+
const NotificationPemsBlockedSchema = NotificationBaseSchema.extend({
|
|
2787
|
+
type: z__namespace.literal('PEM_STATUS_CHANGED'),
|
|
2788
|
+
code: z__namespace.literal('SYS-C-01'),
|
|
2789
|
+
data: NotificationDataPemStatusSchema,
|
|
2790
|
+
});
|
|
2791
|
+
const NotificationPemBackOnlineSchema = NotificationBaseSchema.extend({
|
|
2792
|
+
type: z__namespace.literal('PEM_STATUS_CHANGED'),
|
|
2793
|
+
code: z__namespace.literal('SYS-I-01'),
|
|
2794
|
+
data: NotificationDataPemStatusSchema,
|
|
2795
|
+
});
|
|
2796
|
+
const NotificationSchema = z__namespace.discriminatedUnion('code', [
|
|
2797
|
+
NotificationMf2UnreachableSchema,
|
|
2798
|
+
NotificationPemsBlockedSchema,
|
|
2799
|
+
NotificationPemBackOnlineSchema,
|
|
2800
|
+
]);
|
|
2801
|
+
const NotificationListResponseSchema = z__namespace.object({
|
|
2802
|
+
members: z__namespace.array(NotificationSchema),
|
|
2803
|
+
});
|
|
2804
|
+
|
|
2805
|
+
const TelemetryMerchantSchema = z__namespace.object({
|
|
2806
|
+
vat_number: z__namespace.string(),
|
|
2807
|
+
fiscal_code: z__namespace.string().nullable(),
|
|
2808
|
+
business_name: z__namespace.string(),
|
|
2809
|
+
});
|
|
2810
|
+
const TelemetrySupplierSchema = z__namespace.object({
|
|
2811
|
+
vat_number: z__namespace.string(),
|
|
2812
|
+
fiscal_code: z__namespace.string().nullable(),
|
|
2813
|
+
business_name: z__namespace.string(),
|
|
2814
|
+
});
|
|
2815
|
+
const TelemetrySoftwareVersionSchema = z__namespace.object({
|
|
2816
|
+
version: z__namespace.string(),
|
|
2817
|
+
swid: z__namespace.string(),
|
|
2818
|
+
installed_at: z__namespace.string(),
|
|
2819
|
+
status: z__namespace.enum(['active', 'inactive', 'archived']),
|
|
2820
|
+
});
|
|
2821
|
+
const TelemetrySoftwareSchema = z__namespace.object({
|
|
2822
|
+
code: z__namespace.string(),
|
|
2823
|
+
name: z__namespace.string(),
|
|
2824
|
+
approval_reference: z__namespace.string(),
|
|
2825
|
+
version_info: TelemetrySoftwareVersionSchema,
|
|
2826
|
+
});
|
|
2827
|
+
const PendingReceiptsSchema = z__namespace.object({
|
|
2828
|
+
count: z__namespace.number().int().nonnegative(),
|
|
2829
|
+
total_amount: z__namespace.string(),
|
|
2830
|
+
});
|
|
2831
|
+
const TransmissionSchema = z__namespace.object({
|
|
2832
|
+
attempted_at: z__namespace.string(),
|
|
2833
|
+
outcome: z__namespace.enum(['success', 'failed', 'pending']),
|
|
2834
|
+
});
|
|
2835
|
+
const MessageSchema = z__namespace.object({
|
|
2836
|
+
received_at: z__namespace.string(),
|
|
2837
|
+
content: z__namespace.string(),
|
|
2838
|
+
});
|
|
2839
|
+
const LotterySecretRequestSchema = z__namespace.object({
|
|
2840
|
+
requested_at: z__namespace.string(),
|
|
2841
|
+
outcome: z__namespace.enum(['success', 'failed', 'pending']),
|
|
2842
|
+
});
|
|
2843
|
+
const LotterySchema = z__namespace.object({
|
|
2844
|
+
last_transmission: TransmissionSchema,
|
|
2845
|
+
secret_request: LotterySecretRequestSchema,
|
|
2846
|
+
});
|
|
2847
|
+
const TelemetrySchema = z__namespace.object({
|
|
2848
|
+
pem_id: z__namespace.string(),
|
|
2849
|
+
pem_status: z__namespace.enum(['ONLINE', 'OFFLINE', 'ERROR']),
|
|
2850
|
+
pem_status_changed_at: z__namespace.string(),
|
|
2851
|
+
merchant: TelemetryMerchantSchema,
|
|
2852
|
+
supplier: TelemetrySupplierSchema,
|
|
2853
|
+
software: TelemetrySoftwareSchema,
|
|
2854
|
+
last_communication_at: z__namespace.string(),
|
|
2855
|
+
pending_receipts: PendingReceiptsSchema,
|
|
2856
|
+
last_receipt_transmission: TransmissionSchema,
|
|
2857
|
+
last_message_from_mf2: MessageSchema,
|
|
2858
|
+
ade_corrispettivi_transmission: TransmissionSchema,
|
|
2859
|
+
last_message_from_ade: MessageSchema,
|
|
2860
|
+
lottery: LotterySchema,
|
|
2861
|
+
});
|
|
2341
2862
|
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2863
|
+
// Receipt schemas and types
|
|
2864
|
+
// Common validation utilities
|
|
2865
|
+
const ValidationMessages = {
|
|
2866
|
+
fieldIsRequired: 'This field is required',
|
|
2867
|
+
arrayMin1: 'At least one item is required',
|
|
2868
|
+
paymentMethodRequired: 'At least one payment method is required',
|
|
2869
|
+
invalidEmail: 'Please enter a valid email address',
|
|
2870
|
+
passwordMinLength: 'Password must be at least 8 characters long',
|
|
2871
|
+
passwordComplexity: 'Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character',
|
|
2872
|
+
invalidZipCode: 'Please enter a valid 5-digit zip code',
|
|
2873
|
+
provinceMinLength: 'Province code must be 2 characters',
|
|
2874
|
+
provinceMaxLength: 'Province code must be 2 characters',
|
|
2875
|
+
invalidDateFormat: 'Please enter a valid date',
|
|
2876
|
+
nameMaxLength: 'Name is too long',
|
|
2877
|
+
invalidFiscalId: 'Please enter a valid Italian fiscal ID (Codice Fiscale or Partita IVA)',
|
|
2878
|
+
invalidVatNumber: 'Please enter a valid VAT number (11 digits)',
|
|
2879
|
+
invalidFiscalCode: 'Please enter a valid fiscal code (11 digits)',
|
|
2880
|
+
businessNameMaxLength: 'Business name is too long (max 200 characters)',
|
|
2881
|
+
businessNameOrPersonalNamesRequired: 'Please provide either a business name or first/last name, but not both',
|
|
2882
|
+
firstNameMaxLength: 'First name is too long (max 100 characters)',
|
|
2883
|
+
lastNameMaxLength: 'Last name is too long (max 100 characters)',
|
|
2884
|
+
invalidUuid: 'Please enter a valid UUID',
|
|
2885
|
+
invalidPemType: 'PEM type must be one of: AP, SP, TM, PV',
|
|
2886
|
+
reasonMaxLength: 'Reason is too long (max 255 characters)',
|
|
2887
|
+
pageMinValue: 'Page number must be at least 1',
|
|
2888
|
+
invalidDailyReportStatus: 'Daily report status must be one of: pending, sent, error',
|
|
2889
|
+
displayNameMaxLength: 'Display name is too long (max 255 characters)',
|
|
2369
2890
|
};
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
return
|
|
2891
|
+
// Validation helper functions
|
|
2892
|
+
const validateInput = (schema, data) => {
|
|
2893
|
+
const result = schema.safeParse(data);
|
|
2894
|
+
if (!result.success) {
|
|
2895
|
+
const errors = result.error.issues.map((error) => ({
|
|
2896
|
+
field: error.path.join('.'),
|
|
2897
|
+
message: error.message,
|
|
2898
|
+
code: error.code,
|
|
2899
|
+
}));
|
|
2900
|
+
return {
|
|
2901
|
+
success: false,
|
|
2902
|
+
errors,
|
|
2903
|
+
data: null,
|
|
2904
|
+
};
|
|
2380
2905
|
}
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2906
|
+
return {
|
|
2907
|
+
success: true,
|
|
2908
|
+
errors: [],
|
|
2909
|
+
data: result.data,
|
|
2910
|
+
};
|
|
2911
|
+
};
|
|
2912
|
+
|
|
2913
|
+
class ACubeSDKError extends Error {
|
|
2914
|
+
constructor(type, message, originalError, statusCode, violations) {
|
|
2915
|
+
super(message);
|
|
2916
|
+
this.type = type;
|
|
2917
|
+
this.originalError = originalError;
|
|
2918
|
+
this.statusCode = statusCode;
|
|
2919
|
+
this.name = 'ACubeSDKError';
|
|
2920
|
+
this.violations = violations;
|
|
2384
2921
|
}
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2922
|
+
}
|
|
2923
|
+
|
|
2924
|
+
const log$g = createPrefixedLogger('AUTH-STRATEGY');
|
|
2925
|
+
class AuthStrategy {
|
|
2926
|
+
constructor(jwtHandler, mtlsHandler, userProvider, mtlsAdapter) {
|
|
2927
|
+
this.jwtHandler = jwtHandler;
|
|
2928
|
+
this.mtlsHandler = mtlsHandler;
|
|
2929
|
+
this.userProvider = userProvider;
|
|
2930
|
+
this.mtlsAdapter = mtlsAdapter;
|
|
2389
2931
|
}
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
return JSON.stringify(data, null, 2);
|
|
2932
|
+
async determineAuthConfig(url, method, explicitMode) {
|
|
2933
|
+
if (this.isNotificationEndpoint(url) || this.isTelemetryEndpoint(url)) {
|
|
2934
|
+
return { mode: 'mtls', usePort444: true };
|
|
2394
2935
|
}
|
|
2395
|
-
|
|
2396
|
-
|
|
2936
|
+
const platform = this.detectPlatform();
|
|
2937
|
+
const userRole = await this.getUserRole();
|
|
2938
|
+
const isReceiptEndpoint = this.isReceiptEndpoint(url);
|
|
2939
|
+
log$g.debug('Determining auth config', {
|
|
2940
|
+
url,
|
|
2941
|
+
method,
|
|
2942
|
+
platform,
|
|
2943
|
+
userRole,
|
|
2944
|
+
isReceiptEndpoint,
|
|
2945
|
+
explicitMode,
|
|
2946
|
+
});
|
|
2947
|
+
if (userRole === 'SUPPLIER') {
|
|
2948
|
+
return { mode: 'jwt', usePort444: false };
|
|
2397
2949
|
}
|
|
2950
|
+
if (userRole === 'CASHIER') {
|
|
2951
|
+
if (!isReceiptEndpoint) {
|
|
2952
|
+
return { mode: 'jwt', usePort444: false };
|
|
2953
|
+
}
|
|
2954
|
+
if (platform === 'mobile') {
|
|
2955
|
+
return { mode: 'mtls', usePort444: true };
|
|
2956
|
+
}
|
|
2957
|
+
return { mode: 'jwt', usePort444: true };
|
|
2958
|
+
}
|
|
2959
|
+
if (userRole === 'MERCHANT') {
|
|
2960
|
+
if (!isReceiptEndpoint) {
|
|
2961
|
+
return { mode: 'jwt', usePort444: false };
|
|
2962
|
+
}
|
|
2963
|
+
if (this.isReturnableItemsEndpoint(url)) {
|
|
2964
|
+
return { mode: 'mtls', usePort444: true };
|
|
2965
|
+
}
|
|
2966
|
+
if (method === 'GET') {
|
|
2967
|
+
if (this.isDetailedReceiptEndpoint(url)) {
|
|
2968
|
+
if (platform === 'mobile') {
|
|
2969
|
+
return { mode: 'mtls', usePort444: true };
|
|
2970
|
+
}
|
|
2971
|
+
return { mode: 'jwt', usePort444: true };
|
|
2972
|
+
}
|
|
2973
|
+
return { mode: 'jwt', usePort444: false };
|
|
2974
|
+
}
|
|
2975
|
+
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {
|
|
2976
|
+
if (platform === 'mobile') {
|
|
2977
|
+
return { mode: 'mtls', usePort444: true };
|
|
2978
|
+
}
|
|
2979
|
+
return { mode: 'jwt', usePort444: true };
|
|
2980
|
+
}
|
|
2981
|
+
return { mode: 'jwt', usePort444: false };
|
|
2982
|
+
}
|
|
2983
|
+
if (explicitMode) {
|
|
2984
|
+
if (userRole === 'SUPPLIER' && explicitMode === 'mtls') {
|
|
2985
|
+
return { mode: 'jwt', usePort444: false };
|
|
2986
|
+
}
|
|
2987
|
+
return {
|
|
2988
|
+
mode: explicitMode,
|
|
2989
|
+
usePort444: explicitMode === 'mtls' || (platform === 'web' && isReceiptEndpoint),
|
|
2990
|
+
};
|
|
2991
|
+
}
|
|
2992
|
+
if (platform === 'web') {
|
|
2993
|
+
return { mode: 'jwt', usePort444: isReceiptEndpoint };
|
|
2994
|
+
}
|
|
2995
|
+
if (isReceiptEndpoint && platform === 'mobile') {
|
|
2996
|
+
return { mode: 'mtls', usePort444: true };
|
|
2997
|
+
}
|
|
2998
|
+
return { mode: 'jwt', usePort444: false };
|
|
2398
2999
|
}
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
constructor() {
|
|
2402
|
-
this.enabled = false;
|
|
2403
|
-
}
|
|
2404
|
-
setEnabled(enabled) {
|
|
2405
|
-
this.enabled = enabled;
|
|
2406
|
-
}
|
|
2407
|
-
isEnabled() {
|
|
2408
|
-
return this.enabled;
|
|
2409
|
-
}
|
|
2410
|
-
debug(prefix, message, data) {
|
|
2411
|
-
if (!this.enabled)
|
|
2412
|
-
return;
|
|
2413
|
-
this.log('debug', prefix, message, data);
|
|
3000
|
+
async getAuthHeaders() {
|
|
3001
|
+
return this.jwtHandler.getAuthHeaders();
|
|
2414
3002
|
}
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
return;
|
|
2418
|
-
this.log('info', prefix, message, data);
|
|
3003
|
+
getMtlsHandler() {
|
|
3004
|
+
return this.mtlsHandler;
|
|
2419
3005
|
}
|
|
2420
|
-
|
|
2421
|
-
this.
|
|
3006
|
+
getJwtHandler() {
|
|
3007
|
+
return this.jwtHandler;
|
|
2422
3008
|
}
|
|
2423
|
-
|
|
2424
|
-
this.
|
|
3009
|
+
detectPlatform() {
|
|
3010
|
+
if (!this.mtlsAdapter) {
|
|
3011
|
+
return 'web';
|
|
3012
|
+
}
|
|
3013
|
+
const platformInfo = this.mtlsAdapter.getPlatformInfo();
|
|
3014
|
+
return platformInfo.platform === 'web' ? 'web' : 'mobile';
|
|
2425
3015
|
}
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
const isoTime = new Date().toISOString().split('T')[1] ?? '';
|
|
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
|
-
}
|
|
3016
|
+
async getUserRole() {
|
|
3017
|
+
if (!this.userProvider) {
|
|
3018
|
+
return null;
|
|
2451
3019
|
}
|
|
2452
|
-
|
|
2453
|
-
|
|
3020
|
+
const user = await this.userProvider.getCurrentUser();
|
|
3021
|
+
if (!user || !user.roles) {
|
|
3022
|
+
return null;
|
|
3023
|
+
}
|
|
3024
|
+
if (hasRole(user.roles, 'ROLE_SUPPLIER')) {
|
|
3025
|
+
return 'SUPPLIER';
|
|
3026
|
+
}
|
|
3027
|
+
if (hasRole(user.roles, 'ROLE_MERCHANT')) {
|
|
3028
|
+
return 'MERCHANT';
|
|
3029
|
+
}
|
|
3030
|
+
if (hasRole(user.roles, 'ROLE_CASHIER')) {
|
|
3031
|
+
return 'CASHIER';
|
|
2454
3032
|
}
|
|
3033
|
+
return null;
|
|
3034
|
+
}
|
|
3035
|
+
isReceiptEndpoint(url) {
|
|
3036
|
+
return url.includes('/receipts') || url.includes('/mf1/receipts');
|
|
3037
|
+
}
|
|
3038
|
+
isReturnableItemsEndpoint(url) {
|
|
3039
|
+
return !!(url.match(/\/receipts\/[a-f0-9-]+\/returnable-items$/) ||
|
|
3040
|
+
url.match(/\/mf1\/receipts\/[a-f0-9-]+\/returnable-items$/));
|
|
3041
|
+
}
|
|
3042
|
+
isDetailedReceiptEndpoint(url) {
|
|
3043
|
+
return !!(url.match(/\/receipts\/[a-f0-9-]+\/details$/) ||
|
|
3044
|
+
url.match(/\/mf1\/receipts\/[a-f0-9-]+\/details$/));
|
|
3045
|
+
}
|
|
3046
|
+
isNotificationEndpoint(url) {
|
|
3047
|
+
return url.includes('/mf1/notifications');
|
|
3048
|
+
}
|
|
3049
|
+
isTelemetryEndpoint(url) {
|
|
3050
|
+
return !!url.match(/\/mf1\/pems\/[^/]+\/telemetry/);
|
|
2455
3051
|
}
|
|
2456
|
-
}
|
|
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
3052
|
}
|
|
2466
3053
|
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
return value;
|
|
2482
|
-
return num.toFixed(decimals);
|
|
3054
|
+
class JwtAuthHandler {
|
|
3055
|
+
constructor(tokenStorage) {
|
|
3056
|
+
this.tokenStorage = tokenStorage;
|
|
3057
|
+
}
|
|
3058
|
+
async getAuthConfig(_url, _method) {
|
|
3059
|
+
return { mode: 'jwt', usePort444: false };
|
|
3060
|
+
}
|
|
3061
|
+
async getAuthHeaders() {
|
|
3062
|
+
const token = await this.tokenStorage.getAccessToken();
|
|
3063
|
+
if (!token) {
|
|
3064
|
+
return {};
|
|
3065
|
+
}
|
|
3066
|
+
return { Authorization: `Bearer ${token}` };
|
|
3067
|
+
}
|
|
2483
3068
|
}
|
|
2484
3069
|
|
|
2485
3070
|
const log$f = createPrefixedLogger('MTLS-HANDLER');
|
|
@@ -6012,92 +6597,6 @@ function createACubeMTLSConfig(baseUrl, timeout, autoInitialize = true, forcePor
|
|
|
6012
6597
|
};
|
|
6013
6598
|
}
|
|
6014
6599
|
|
|
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
6600
|
const DI_TOKENS = {
|
|
6102
6601
|
HTTP_PORT: Symbol('HTTP_PORT'),
|
|
6103
6602
|
BASE_HTTP_PORT: Symbol('BASE_HTTP_PORT'),
|
|
@@ -7523,7 +8022,11 @@ class CachingHttpDecorator {
|
|
|
7523
8022
|
return response;
|
|
7524
8023
|
}
|
|
7525
8024
|
setAuthToken(token) {
|
|
7526
|
-
log$3.debug('
|
|
8025
|
+
log$3.debug('CachingHttpDecorator.setAuthToken called:', {
|
|
8026
|
+
hasToken: !!token,
|
|
8027
|
+
tokenPrefix: token?.substring(0, 20),
|
|
8028
|
+
underlyingHttpType: this.http.constructor.name,
|
|
8029
|
+
});
|
|
7527
8030
|
this.authToken = token;
|
|
7528
8031
|
this.http.setAuthToken(token);
|
|
7529
8032
|
}
|
|
@@ -7556,7 +8059,8 @@ class CachingHttpDecorator {
|
|
|
7556
8059
|
}
|
|
7557
8060
|
}
|
|
7558
8061
|
|
|
7559
|
-
const
|
|
8062
|
+
const logJwt = createPrefixedLogger('HTTP-JWT');
|
|
8063
|
+
const logMtls = createPrefixedLogger('HTTP-MTLS');
|
|
7560
8064
|
class AxiosHttpAdapter {
|
|
7561
8065
|
constructor(config) {
|
|
7562
8066
|
this.authToken = null;
|
|
@@ -7574,23 +8078,30 @@ class AxiosHttpAdapter {
|
|
|
7574
8078
|
}
|
|
7575
8079
|
setMTLSAdapter(adapter) {
|
|
7576
8080
|
this.mtlsAdapter = adapter;
|
|
7577
|
-
|
|
8081
|
+
logMtls.debug('mTLS adapter configured:', !!adapter);
|
|
7578
8082
|
}
|
|
7579
8083
|
setAuthStrategy(strategy) {
|
|
7580
8084
|
this.authStrategy = strategy;
|
|
7581
|
-
|
|
8085
|
+
logJwt.debug('Auth strategy configured:', !!strategy);
|
|
7582
8086
|
}
|
|
7583
8087
|
async shouldUseMTLS(url, method) {
|
|
7584
8088
|
if (!this.mtlsAdapter) {
|
|
8089
|
+
logJwt.debug(`No mTLS adapter, using JWT for ${method} ${url}`);
|
|
7585
8090
|
return false;
|
|
7586
8091
|
}
|
|
7587
8092
|
if (this.authStrategy) {
|
|
7588
8093
|
const config = await this.authStrategy.determineAuthConfig(url, method);
|
|
7589
|
-
|
|
8094
|
+
const logger = config.mode === 'mtls' ? logMtls : logJwt;
|
|
8095
|
+
logger.debug(`Auth config for ${method} ${url}:`, config);
|
|
7590
8096
|
return config.mode === 'mtls';
|
|
7591
8097
|
}
|
|
7592
8098
|
// Fallback: use mTLS for mf1/mf2 endpoints if no strategy
|
|
7593
|
-
|
|
8099
|
+
// This should rarely happen - only before SDK is fully initialized
|
|
8100
|
+
const useMtls = url.startsWith('/mf1') || url.startsWith('/mf2');
|
|
8101
|
+
if (useMtls) {
|
|
8102
|
+
logMtls.warn(`No auth strategy set, falling back to mTLS for ${method} ${url}`);
|
|
8103
|
+
}
|
|
8104
|
+
return useMtls;
|
|
7594
8105
|
}
|
|
7595
8106
|
async makeMTLSRequest(url, method, data, config) {
|
|
7596
8107
|
if (!this.mtlsAdapter) {
|
|
@@ -7603,10 +8114,10 @@ class AxiosHttpAdapter {
|
|
|
7603
8114
|
};
|
|
7604
8115
|
if (this.authToken) {
|
|
7605
8116
|
headers['Authorization'] = `Bearer ${this.authToken}`;
|
|
7606
|
-
|
|
8117
|
+
logMtls.debug('JWT token present for mTLS request');
|
|
7607
8118
|
}
|
|
7608
8119
|
else {
|
|
7609
|
-
|
|
8120
|
+
logMtls.warn('No JWT token for mTLS request');
|
|
7610
8121
|
}
|
|
7611
8122
|
const mtlsConfig = {
|
|
7612
8123
|
url: fullUrl,
|
|
@@ -7615,15 +8126,15 @@ class AxiosHttpAdapter {
|
|
|
7615
8126
|
data,
|
|
7616
8127
|
timeout: config?.timeout,
|
|
7617
8128
|
};
|
|
7618
|
-
|
|
8129
|
+
logMtls.debug(`mTLS ${method} ${fullUrl}`);
|
|
7619
8130
|
if (data) {
|
|
7620
|
-
|
|
8131
|
+
logMtls.debug('Request body:', data);
|
|
7621
8132
|
}
|
|
7622
8133
|
try {
|
|
7623
8134
|
const response = await this.mtlsAdapter.request(mtlsConfig);
|
|
7624
|
-
|
|
8135
|
+
logMtls.debug(`mTLS Response ${response.status} from ${fullUrl}`);
|
|
7625
8136
|
if (response.data) {
|
|
7626
|
-
|
|
8137
|
+
logMtls.debug('Response body:', response.data);
|
|
7627
8138
|
}
|
|
7628
8139
|
return {
|
|
7629
8140
|
data: response.data,
|
|
@@ -7632,11 +8143,11 @@ class AxiosHttpAdapter {
|
|
|
7632
8143
|
};
|
|
7633
8144
|
}
|
|
7634
8145
|
catch (error) {
|
|
7635
|
-
|
|
8146
|
+
logMtls.error(`mTLS Response error from ${fullUrl}:`, error);
|
|
7636
8147
|
if (error && typeof error === 'object' && 'response' in error) {
|
|
7637
8148
|
const axiosError = error;
|
|
7638
8149
|
if (axiosError.response?.data) {
|
|
7639
|
-
|
|
8150
|
+
logMtls.error('Response body:', axiosError.response.data);
|
|
7640
8151
|
}
|
|
7641
8152
|
}
|
|
7642
8153
|
throw error;
|
|
@@ -7651,36 +8162,46 @@ class AxiosHttpAdapter {
|
|
|
7651
8162
|
this.client.interceptors.request.use((config) => {
|
|
7652
8163
|
if (this.authToken) {
|
|
7653
8164
|
config.headers.Authorization = `Bearer ${this.authToken}`;
|
|
7654
|
-
|
|
8165
|
+
logJwt.debug('Adding JWT token to request', {
|
|
8166
|
+
tokenPrefix: this.authToken.substring(0, 30) + '...',
|
|
8167
|
+
tokenLength: this.authToken.length,
|
|
8168
|
+
});
|
|
7655
8169
|
}
|
|
7656
8170
|
else {
|
|
7657
|
-
|
|
8171
|
+
logJwt.warn('No JWT token available for request:', { url: config.url });
|
|
7658
8172
|
}
|
|
7659
8173
|
const method = config.method?.toUpperCase() ?? 'UNKNOWN';
|
|
7660
|
-
|
|
8174
|
+
const authHeader = config.headers.Authorization;
|
|
8175
|
+
// Log full request details for debugging
|
|
8176
|
+
logJwt.info(`→ ${method} ${config.url}`, {
|
|
8177
|
+
baseURL: config.baseURL,
|
|
8178
|
+
fullURL: `${config.baseURL}${config.url}`,
|
|
8179
|
+
hasAuthHeader: !!authHeader,
|
|
8180
|
+
authHeaderValue: authHeader ? `${String(authHeader).substring(0, 50)}...` : 'MISSING',
|
|
8181
|
+
});
|
|
7661
8182
|
if (config.params && Object.keys(config.params).length > 0) {
|
|
7662
|
-
|
|
8183
|
+
logJwt.debug('Request params:', config.params);
|
|
7663
8184
|
}
|
|
7664
8185
|
if (config.data) {
|
|
7665
|
-
|
|
8186
|
+
logJwt.debug('Request body:', config.data);
|
|
7666
8187
|
}
|
|
7667
8188
|
return config;
|
|
7668
8189
|
}, (error) => {
|
|
7669
|
-
|
|
8190
|
+
logJwt.error('Request error:', error);
|
|
7670
8191
|
return Promise.reject(error);
|
|
7671
8192
|
});
|
|
7672
8193
|
this.client.interceptors.response.use((response) => {
|
|
7673
8194
|
const method = response.config.method?.toUpperCase() ?? 'UNKNOWN';
|
|
7674
|
-
|
|
8195
|
+
logJwt.debug(`← ${method} ${response.status} ${response.config.url}`);
|
|
7675
8196
|
if (response.data) {
|
|
7676
|
-
|
|
8197
|
+
logJwt.debug('Response body:', response.data);
|
|
7677
8198
|
}
|
|
7678
8199
|
return response;
|
|
7679
8200
|
}, (error) => {
|
|
7680
8201
|
const method = error.config?.method?.toUpperCase() ?? 'UNKNOWN';
|
|
7681
|
-
|
|
8202
|
+
logJwt.error(`← ${method} ${error.response?.status ?? 'ERR'} ${error.config?.url ?? 'unknown'}`);
|
|
7682
8203
|
if (error.response?.data) {
|
|
7683
|
-
|
|
8204
|
+
logJwt.error('Response body:', error.response.data);
|
|
7684
8205
|
}
|
|
7685
8206
|
return Promise.reject(error);
|
|
7686
8207
|
});
|
|
@@ -7750,7 +8271,10 @@ class AxiosHttpAdapter {
|
|
|
7750
8271
|
return this.mapResponse(response);
|
|
7751
8272
|
}
|
|
7752
8273
|
setAuthToken(token) {
|
|
7753
|
-
|
|
8274
|
+
logJwt.info('setAuthToken called:', {
|
|
8275
|
+
hasToken: !!token,
|
|
8276
|
+
tokenPrefix: token?.substring(0, 20),
|
|
8277
|
+
});
|
|
7754
8278
|
this.authToken = token;
|
|
7755
8279
|
}
|
|
7756
8280
|
getAuthToken() {
|
|
@@ -7906,7 +8430,7 @@ class SDKFactory {
|
|
|
7906
8430
|
}
|
|
7907
8431
|
}
|
|
7908
8432
|
|
|
7909
|
-
const log$
|
|
8433
|
+
const log$2 = createPrefixedLogger('SDK');
|
|
7910
8434
|
class ACubeSDK {
|
|
7911
8435
|
constructor(config, customAdapters, events = {}) {
|
|
7912
8436
|
this.events = events;
|
|
@@ -7920,22 +8444,22 @@ class ACubeSDK {
|
|
|
7920
8444
|
}
|
|
7921
8445
|
async initialize() {
|
|
7922
8446
|
if (this.isInitialized) {
|
|
7923
|
-
log$
|
|
8447
|
+
log$2.debug('SDK already initialized, skipping');
|
|
7924
8448
|
return;
|
|
7925
8449
|
}
|
|
7926
|
-
log$
|
|
8450
|
+
log$2.info('Initializing SDK', {
|
|
7927
8451
|
apiUrl: this.config.getApiUrl(),
|
|
7928
8452
|
authUrl: this.config.getAuthUrl(),
|
|
7929
8453
|
debugEnabled: this.config.isDebugEnabled(),
|
|
7930
8454
|
});
|
|
7931
8455
|
try {
|
|
7932
8456
|
if (!this.adapters) {
|
|
7933
|
-
log$
|
|
8457
|
+
log$2.debug('Loading platform adapters');
|
|
7934
8458
|
const mtlsConfig = createACubeMTLSConfig(this.config.getApiUrl(), this.config.getTimeout(), true);
|
|
7935
8459
|
this.adapters = loadPlatformAdapters({
|
|
7936
8460
|
mtlsConfig,
|
|
7937
8461
|
});
|
|
7938
|
-
log$
|
|
8462
|
+
log$2.info('Platform adapters loaded', {
|
|
7939
8463
|
hasCache: !!this.adapters.cache,
|
|
7940
8464
|
hasNetworkMonitor: !!this.adapters.networkMonitor,
|
|
7941
8465
|
hasMtls: !!this.adapters.mtls,
|
|
@@ -7948,25 +8472,30 @@ class ACubeSDK {
|
|
|
7948
8472
|
timeout: this.config.getTimeout(),
|
|
7949
8473
|
debugEnabled: this.config.isDebugEnabled(),
|
|
7950
8474
|
};
|
|
7951
|
-
log$
|
|
8475
|
+
log$2.debug('Creating DI container');
|
|
7952
8476
|
this.container = SDKFactory.createContainer(factoryConfig);
|
|
7953
|
-
log$
|
|
8477
|
+
log$2.debug('Registering auth services');
|
|
7954
8478
|
SDKFactory.registerAuthServices(this.container, this.adapters.secureStorage, factoryConfig);
|
|
7955
8479
|
if (this.adapters.cache) {
|
|
7956
|
-
log$
|
|
8480
|
+
log$2.info('Registering cache services', {
|
|
7957
8481
|
hasNetworkMonitor: !!this.adapters.networkMonitor,
|
|
7958
8482
|
});
|
|
7959
8483
|
SDKFactory.registerCacheServices(this.container, this.adapters.cache, this.adapters.networkMonitor);
|
|
7960
8484
|
}
|
|
7961
8485
|
else {
|
|
7962
|
-
log$
|
|
8486
|
+
log$2.debug('No cache adapter available, caching disabled');
|
|
7963
8487
|
}
|
|
7964
|
-
log$
|
|
8488
|
+
log$2.debug('Initializing certificate service');
|
|
7965
8489
|
this.certificateService = new CertificateService(this.adapters.secureStorage);
|
|
7966
8490
|
const tokenStorage = this.container.get(DI_TOKENS.TOKEN_STORAGE_PORT);
|
|
7967
8491
|
const httpPort = this.container.get(DI_TOKENS.HTTP_PORT);
|
|
7968
8492
|
const baseHttpPort = this.container.get(DI_TOKENS.BASE_HTTP_PORT);
|
|
7969
|
-
log$
|
|
8493
|
+
log$2.debug('HTTP ports initialized', {
|
|
8494
|
+
httpPortType: httpPort.constructor.name,
|
|
8495
|
+
baseHttpPortType: baseHttpPort.constructor.name,
|
|
8496
|
+
areSameInstance: httpPort === baseHttpPort,
|
|
8497
|
+
});
|
|
8498
|
+
log$2.debug('Initializing authentication service');
|
|
7970
8499
|
this.authService = new AuthenticationService(httpPort, tokenStorage, {
|
|
7971
8500
|
authUrl: this.config.getAuthUrl(),
|
|
7972
8501
|
timeout: this.config.getTimeout(),
|
|
@@ -7976,7 +8505,7 @@ class ACubeSDK {
|
|
|
7976
8505
|
this.events.onAuthError?.(new ACubeSDKError('AUTH_ERROR', error.message, error));
|
|
7977
8506
|
},
|
|
7978
8507
|
});
|
|
7979
|
-
log$
|
|
8508
|
+
log$2.debug('Initializing offline manager');
|
|
7980
8509
|
const queueEvents = {
|
|
7981
8510
|
onOperationAdded: (operation) => {
|
|
7982
8511
|
this.events.onOfflineOperationAdded?.(operation.id);
|
|
@@ -7998,19 +8527,29 @@ class ACubeSDK {
|
|
|
7998
8527
|
this.offlineManager.sync().catch(() => { });
|
|
7999
8528
|
}
|
|
8000
8529
|
});
|
|
8001
|
-
|
|
8530
|
+
const isAuth = await this.authService.isAuthenticated();
|
|
8531
|
+
log$2.debug('Checking authentication status during init', { isAuthenticated: isAuth });
|
|
8532
|
+
if (isAuth) {
|
|
8002
8533
|
const token = await this.authService.getAccessToken();
|
|
8534
|
+
log$2.debug('Token retrieved during init', {
|
|
8535
|
+
hasToken: !!token,
|
|
8536
|
+
tokenPrefix: token?.substring(0, 20),
|
|
8537
|
+
});
|
|
8003
8538
|
if (token) {
|
|
8004
8539
|
httpPort.setAuthToken(token);
|
|
8540
|
+
log$2.info('Auth token set on HTTP port during initialization');
|
|
8005
8541
|
}
|
|
8006
8542
|
}
|
|
8543
|
+
else {
|
|
8544
|
+
log$2.warn('User not authenticated during SDK init - token will be set after login');
|
|
8545
|
+
}
|
|
8007
8546
|
if (this.adapters?.mtls && 'setMTLSAdapter' in baseHttpPort) {
|
|
8008
|
-
log$
|
|
8547
|
+
log$2.debug('Connecting mTLS adapter to HTTP port');
|
|
8009
8548
|
const httpWithMtls = baseHttpPort;
|
|
8010
8549
|
httpWithMtls.setMTLSAdapter(this.adapters.mtls);
|
|
8011
8550
|
}
|
|
8012
8551
|
if ('setAuthStrategy' in baseHttpPort) {
|
|
8013
|
-
log$
|
|
8552
|
+
log$2.debug('Configuring auth strategy');
|
|
8014
8553
|
const jwtHandler = new JwtAuthHandler(tokenStorage);
|
|
8015
8554
|
const certificatePort = this.certificateService
|
|
8016
8555
|
? {
|
|
@@ -8059,19 +8598,19 @@ class ACubeSDK {
|
|
|
8059
8598
|
}
|
|
8060
8599
|
}
|
|
8061
8600
|
catch (certError) {
|
|
8062
|
-
log$
|
|
8601
|
+
log$2.warn('Certificate auto-configuration failed, will retry on demand', {
|
|
8063
8602
|
error: certError instanceof Error ? certError.message : certError,
|
|
8064
8603
|
});
|
|
8065
8604
|
}
|
|
8066
8605
|
}
|
|
8067
8606
|
this.isInitialized = true;
|
|
8068
|
-
log$
|
|
8607
|
+
log$2.info('SDK initialized successfully', {
|
|
8069
8608
|
hasCache: !!this.adapters.cache,
|
|
8070
8609
|
hasMtls: !!this.adapters.mtls,
|
|
8071
8610
|
});
|
|
8072
8611
|
}
|
|
8073
8612
|
catch (error) {
|
|
8074
|
-
log$
|
|
8613
|
+
log$2.error('SDK initialization failed', {
|
|
8075
8614
|
error: error instanceof Error ? error.message : error,
|
|
8076
8615
|
});
|
|
8077
8616
|
throw new ACubeSDKError('SDK_INITIALIZATION_ERROR', `Failed to initialize SDK: ${error instanceof Error ? error.message : 'Unknown error'}`, error);
|
|
@@ -8127,19 +8666,19 @@ class ACubeSDK {
|
|
|
8127
8666
|
}
|
|
8128
8667
|
async login(credentials) {
|
|
8129
8668
|
this.ensureInitialized();
|
|
8130
|
-
log$
|
|
8669
|
+
log$2.info('Login attempt', { email: credentials.email });
|
|
8131
8670
|
const user = await this.authService.login(credentials);
|
|
8132
|
-
log$
|
|
8671
|
+
log$2.info('Login successful', { roles: user.roles });
|
|
8133
8672
|
const token = await this.authService.getAccessToken();
|
|
8134
8673
|
if (token) {
|
|
8135
8674
|
this.httpPort.setAuthToken(token);
|
|
8136
|
-
log$
|
|
8675
|
+
log$2.debug('Auth token set on HTTP port');
|
|
8137
8676
|
}
|
|
8138
8677
|
return user;
|
|
8139
8678
|
}
|
|
8140
8679
|
async logout() {
|
|
8141
8680
|
this.ensureInitialized();
|
|
8142
|
-
log$
|
|
8681
|
+
log$2.info('Logout');
|
|
8143
8682
|
await this.authService.logout();
|
|
8144
8683
|
this.httpPort.setAuthToken(null);
|
|
8145
8684
|
}
|
|
@@ -8313,6 +8852,7 @@ const INITIAL_STATE = {
|
|
|
8313
8852
|
remainingMs: 0,
|
|
8314
8853
|
},
|
|
8315
8854
|
lastNotification: null,
|
|
8855
|
+
certificateMissing: false,
|
|
8316
8856
|
};
|
|
8317
8857
|
class AppStateService {
|
|
8318
8858
|
get state$() {
|
|
@@ -8327,28 +8867,33 @@ class AppStateService {
|
|
|
8327
8867
|
get warning$() {
|
|
8328
8868
|
return this.state$.pipe(map((s) => s.warning), distinctUntilChanged((a, b) => a.active === b.active && a.remainingMs === b.remainingMs));
|
|
8329
8869
|
}
|
|
8330
|
-
|
|
8870
|
+
get certificateMissing$() {
|
|
8871
|
+
return this.state$.pipe(map((s) => s.certificateMissing), distinctUntilChanged());
|
|
8872
|
+
}
|
|
8873
|
+
constructor(notifications$, networkPort, certificateMissingInput$ = of(false)) {
|
|
8331
8874
|
this.notifications$ = notifications$;
|
|
8332
8875
|
this.networkPort = networkPort;
|
|
8876
|
+
this.certificateMissingInput$ = certificateMissingInput$;
|
|
8333
8877
|
this.stateSubject = new BehaviorSubject(INITIAL_STATE);
|
|
8334
8878
|
this.destroy$ = new Subject();
|
|
8335
8879
|
this.warningTimerId = null;
|
|
8336
8880
|
this.setupSubscriptions();
|
|
8337
8881
|
}
|
|
8338
8882
|
setupSubscriptions() {
|
|
8339
|
-
combineLatest([this.notifications$, this.networkPort.online$])
|
|
8883
|
+
combineLatest([this.notifications$, this.networkPort.online$, this.certificateMissingInput$])
|
|
8340
8884
|
.pipe(takeUntil(this.destroy$))
|
|
8341
|
-
.subscribe(([notifications, isOnline]) => {
|
|
8342
|
-
this.processState(notifications, isOnline);
|
|
8885
|
+
.subscribe(([notifications, isOnline, certificateMissing]) => {
|
|
8886
|
+
this.processState(notifications, isOnline, certificateMissing);
|
|
8343
8887
|
});
|
|
8344
8888
|
}
|
|
8345
|
-
processState(notifications, isOnline) {
|
|
8889
|
+
processState(notifications, isOnline, certificateMissing) {
|
|
8346
8890
|
if (!isOnline) {
|
|
8347
8891
|
this.updateState({
|
|
8348
8892
|
mode: 'OFFLINE',
|
|
8349
8893
|
isOnline: false,
|
|
8350
8894
|
warning: { active: false, blockAt: null, remainingMs: 0 },
|
|
8351
8895
|
lastNotification: null,
|
|
8896
|
+
certificateMissing,
|
|
8352
8897
|
});
|
|
8353
8898
|
this.stopWarningTimer();
|
|
8354
8899
|
return;
|
|
@@ -8410,6 +8955,7 @@ class AppStateService {
|
|
|
8410
8955
|
isOnline: true,
|
|
8411
8956
|
warning: warningState,
|
|
8412
8957
|
lastNotification,
|
|
8958
|
+
certificateMissing,
|
|
8413
8959
|
});
|
|
8414
8960
|
}
|
|
8415
8961
|
getLatestNotificationByCode(notifications) {
|
|
@@ -8675,6 +9221,7 @@ class TelemetryService {
|
|
|
8675
9221
|
}
|
|
8676
9222
|
}
|
|
8677
9223
|
|
|
9224
|
+
const log$1 = createPrefixedLogger('SDK-MANAGER');
|
|
8678
9225
|
/**
|
|
8679
9226
|
* SDKManager - Singleton wrapper for ACubeSDK with simplified API
|
|
8680
9227
|
*
|
|
@@ -8725,6 +9272,7 @@ class SDKManager {
|
|
|
8725
9272
|
this.appStateService = null;
|
|
8726
9273
|
this.isInitialized = false;
|
|
8727
9274
|
this.isPollingActive = false;
|
|
9275
|
+
this.certificateMissingSubject = new BehaviorSubject(false);
|
|
8728
9276
|
/**
|
|
8729
9277
|
* Handle user state changes (login/logout/token expiration)
|
|
8730
9278
|
* Manages polling lifecycle based on user role
|
|
@@ -8735,9 +9283,14 @@ class SDKManager {
|
|
|
8735
9283
|
if (!this.isInitialized)
|
|
8736
9284
|
return;
|
|
8737
9285
|
if (user) {
|
|
8738
|
-
// User logged in - check role and
|
|
9286
|
+
// User logged in - check role and certificate before starting polling
|
|
8739
9287
|
const canPoll = hasAnyRole(user.roles, ['ROLE_MERCHANT', 'ROLE_CASHIER']);
|
|
8740
9288
|
if (canPoll && !this.isPollingActive) {
|
|
9289
|
+
const hasCert = await this.checkCertificate();
|
|
9290
|
+
if (!hasCert) {
|
|
9291
|
+
log$1.warn('Certificate missing — polling blocked until certificate is installed');
|
|
9292
|
+
return;
|
|
9293
|
+
}
|
|
8741
9294
|
this.notificationService?.startPolling();
|
|
8742
9295
|
await this.startTelemetryPollingAuto();
|
|
8743
9296
|
this.isPollingActive = true;
|
|
@@ -8751,6 +9304,7 @@ class SDKManager {
|
|
|
8751
9304
|
this.telemetryService?.clearTelemetry();
|
|
8752
9305
|
this.isPollingActive = false;
|
|
8753
9306
|
}
|
|
9307
|
+
this.certificateMissingSubject.next(false);
|
|
8754
9308
|
}
|
|
8755
9309
|
};
|
|
8756
9310
|
}
|
|
@@ -8817,7 +9371,7 @@ class SDKManager {
|
|
|
8817
9371
|
this.telemetryService = new TelemetryService(telemetryRepo, networkPort, {
|
|
8818
9372
|
pollIntervalMs: this.config.telemetryPollIntervalMs ?? 60000,
|
|
8819
9373
|
});
|
|
8820
|
-
this.appStateService = new AppStateService(this.notificationService.notifications$, networkPort);
|
|
9374
|
+
this.appStateService = new AppStateService(this.notificationService.notifications$, networkPort, this.certificateMissingSubject.asObservable());
|
|
8821
9375
|
if (this.events?.onAppStateChanged) {
|
|
8822
9376
|
this.appStateService.state$.subscribe(this.events.onAppStateChanged);
|
|
8823
9377
|
}
|
|
@@ -8829,9 +9383,15 @@ class SDKManager {
|
|
|
8829
9383
|
const user = await this.sdk.getCurrentUser();
|
|
8830
9384
|
const canPoll = user && hasAnyRole(user.roles, ['ROLE_MERCHANT', 'ROLE_CASHIER']);
|
|
8831
9385
|
if (canPoll) {
|
|
8832
|
-
this.
|
|
8833
|
-
|
|
8834
|
-
|
|
9386
|
+
const hasCert = await this.checkCertificate();
|
|
9387
|
+
if (hasCert) {
|
|
9388
|
+
this.notificationService.startPolling();
|
|
9389
|
+
await this.startTelemetryPollingAuto();
|
|
9390
|
+
this.isPollingActive = true;
|
|
9391
|
+
}
|
|
9392
|
+
else {
|
|
9393
|
+
log$1.warn('Certificate missing at init — polling blocked until certificate is installed');
|
|
9394
|
+
}
|
|
8835
9395
|
}
|
|
8836
9396
|
// AppStateService remains active for all users (handles OFFLINE network state)
|
|
8837
9397
|
}
|
|
@@ -8844,820 +9404,429 @@ class SDKManager {
|
|
|
8844
9404
|
return this.appStateService.state$;
|
|
8845
9405
|
}
|
|
8846
9406
|
/**
|
|
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
|
-
};
|
|
9407
|
+
* Observable stream of app mode only
|
|
9408
|
+
* Emits AppMode with distinctUntilChanged
|
|
9409
|
+
*/
|
|
9410
|
+
get mode$() {
|
|
9411
|
+
this.ensureInitialized();
|
|
9412
|
+
return this.appStateService.mode$;
|
|
8950
9413
|
}
|
|
8951
9414
|
/**
|
|
8952
|
-
*
|
|
9415
|
+
* Observable stream indicating if app is blocked
|
|
8953
9416
|
*/
|
|
8954
|
-
|
|
9417
|
+
get isBlocked$() {
|
|
8955
9418
|
this.ensureInitialized();
|
|
8956
|
-
|
|
9419
|
+
return this.appStateService.isBlocked$;
|
|
8957
9420
|
}
|
|
8958
9421
|
/**
|
|
8959
|
-
*
|
|
9422
|
+
* Observable stream of warning state with countdown
|
|
8960
9423
|
*/
|
|
8961
|
-
|
|
9424
|
+
get warning$() {
|
|
8962
9425
|
this.ensureInitialized();
|
|
8963
|
-
return this.
|
|
9426
|
+
return this.appStateService.warning$;
|
|
8964
9427
|
}
|
|
8965
9428
|
/**
|
|
8966
|
-
*
|
|
9429
|
+
* Observable stream indicating if certificate is missing
|
|
9430
|
+
* When true, polling is blocked and the user should install a certificate
|
|
8967
9431
|
*/
|
|
8968
|
-
|
|
8969
|
-
return this.
|
|
9432
|
+
get certificateMissing$() {
|
|
9433
|
+
return this.certificateMissingSubject.asObservable();
|
|
8970
9434
|
}
|
|
8971
9435
|
/**
|
|
8972
|
-
*
|
|
9436
|
+
* Observable stream of telemetry state (data, isLoading, isCached, error)
|
|
8973
9437
|
*/
|
|
8974
|
-
|
|
9438
|
+
get telemetryState$() {
|
|
8975
9439
|
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
|
-
}
|
|
9062
|
-
}
|
|
9063
|
-
generateCacheKey(url) {
|
|
9064
|
-
return url;
|
|
9440
|
+
return this.telemetryService.state$;
|
|
9065
9441
|
}
|
|
9066
|
-
|
|
9067
|
-
|
|
9068
|
-
|
|
9442
|
+
/**
|
|
9443
|
+
* Get the pemId from the installed certificate
|
|
9444
|
+
*/
|
|
9445
|
+
async getPemId() {
|
|
9446
|
+
this.ensureInitialized();
|
|
9069
9447
|
try {
|
|
9070
|
-
await this.
|
|
9448
|
+
const certInfo = await this.sdk.getCertificatesInfo();
|
|
9449
|
+
return certInfo?.pemId ?? null;
|
|
9071
9450
|
}
|
|
9072
|
-
catch
|
|
9073
|
-
|
|
9451
|
+
catch {
|
|
9452
|
+
return null;
|
|
9074
9453
|
}
|
|
9075
9454
|
}
|
|
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);
|
|
9455
|
+
/**
|
|
9456
|
+
* Start polling telemetry using the pemId from installed certificate
|
|
9457
|
+
* Returns the pemId if successful, null if no certificate is installed
|
|
9458
|
+
*/
|
|
9459
|
+
async startTelemetryPollingAuto() {
|
|
9460
|
+
this.ensureInitialized();
|
|
9461
|
+
const pemId = await this.getPemId();
|
|
9462
|
+
if (pemId) {
|
|
9463
|
+
this.telemetryService.startPolling(pemId);
|
|
9120
9464
|
}
|
|
9465
|
+
return pemId;
|
|
9121
9466
|
}
|
|
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
|
-
};
|
|
9467
|
+
/**
|
|
9468
|
+
* Start polling telemetry for a specific PEM
|
|
9469
|
+
*/
|
|
9470
|
+
startTelemetryPolling(pemId) {
|
|
9471
|
+
this.ensureInitialized();
|
|
9472
|
+
this.telemetryService.startPolling(pemId);
|
|
9176
9473
|
}
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
9181
|
-
|
|
9182
|
-
|
|
9183
|
-
userMessage: 'Certificate error: Unable to establish secure connection.',
|
|
9184
|
-
};
|
|
9474
|
+
/**
|
|
9475
|
+
* Stop telemetry polling
|
|
9476
|
+
*/
|
|
9477
|
+
stopTelemetryPolling() {
|
|
9478
|
+
this.ensureInitialized();
|
|
9479
|
+
this.telemetryService.stopPolling();
|
|
9185
9480
|
}
|
|
9186
|
-
|
|
9187
|
-
|
|
9188
|
-
|
|
9189
|
-
|
|
9481
|
+
/**
|
|
9482
|
+
* Get simplified services for product use
|
|
9483
|
+
*/
|
|
9484
|
+
getServices() {
|
|
9485
|
+
this.ensureInitialized();
|
|
9486
|
+
const sdk = this.sdk;
|
|
9487
|
+
const telemetryService = this.telemetryService;
|
|
9190
9488
|
return {
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
9194
|
-
|
|
9489
|
+
receipts: sdk.receipts,
|
|
9490
|
+
merchants: sdk.merchants,
|
|
9491
|
+
cashiers: sdk.cashiers,
|
|
9492
|
+
cashRegisters: sdk.cashRegisters,
|
|
9493
|
+
pointOfSales: sdk.pointOfSales,
|
|
9494
|
+
suppliers: sdk.suppliers,
|
|
9495
|
+
pems: sdk.pems,
|
|
9496
|
+
dailyReports: sdk.dailyReports,
|
|
9497
|
+
journals: sdk.journals,
|
|
9498
|
+
telemetry: {
|
|
9499
|
+
startPollingAuto: () => this.startTelemetryPollingAuto(),
|
|
9500
|
+
startPolling: (pemId) => telemetryService.startPolling(pemId),
|
|
9501
|
+
stopPolling: () => telemetryService.stopPolling(),
|
|
9502
|
+
getTelemetry: (pemId) => telemetryService.getTelemetry(pemId),
|
|
9503
|
+
refreshTelemetry: (pemId) => telemetryService.refreshTelemetry(pemId),
|
|
9504
|
+
triggerSync: () => telemetryService.triggerSync(),
|
|
9505
|
+
clearTelemetry: () => telemetryService.clearTelemetry(),
|
|
9506
|
+
getPemId: () => this.getPemId(),
|
|
9507
|
+
},
|
|
9508
|
+
login: (credentials) => sdk.login(credentials),
|
|
9509
|
+
logout: () => sdk.logout(),
|
|
9510
|
+
getCurrentUser: () => sdk.getCurrentUser(),
|
|
9511
|
+
isAuthenticated: () => sdk.isAuthenticated(),
|
|
9512
|
+
storeCertificate: async (certificate, privateKey, options) => {
|
|
9513
|
+
await sdk.storeCertificate(certificate, privateKey, options);
|
|
9514
|
+
this.certificateMissingSubject.next(false);
|
|
9515
|
+
// Start polling if user can poll and polling is not active
|
|
9516
|
+
if (!this.isPollingActive) {
|
|
9517
|
+
const user = await sdk.getCurrentUser();
|
|
9518
|
+
const canPoll = user && hasAnyRole(user.roles, ['ROLE_MERCHANT', 'ROLE_CASHIER']);
|
|
9519
|
+
if (canPoll) {
|
|
9520
|
+
log$1.info('Certificate installed — starting polling');
|
|
9521
|
+
this.notificationService?.startPolling();
|
|
9522
|
+
await this.startTelemetryPollingAuto();
|
|
9523
|
+
this.isPollingActive = true;
|
|
9524
|
+
}
|
|
9525
|
+
}
|
|
9526
|
+
},
|
|
9527
|
+
hasCertificate: () => sdk.hasCertificate(),
|
|
9528
|
+
clearCertificate: async () => {
|
|
9529
|
+
await sdk.clearCertificate();
|
|
9530
|
+
this.certificateMissingSubject.next(true);
|
|
9531
|
+
// Stop polling since certificate is required
|
|
9532
|
+
if (this.isPollingActive) {
|
|
9533
|
+
log$1.info('Certificate removed — stopping polling');
|
|
9534
|
+
this.notificationService?.stopPolling();
|
|
9535
|
+
this.telemetryService?.stopPolling();
|
|
9536
|
+
this.telemetryService?.clearTelemetry();
|
|
9537
|
+
this.isPollingActive = false;
|
|
9538
|
+
}
|
|
9539
|
+
},
|
|
9540
|
+
getCertificate: () => sdk.getCertificate(),
|
|
9541
|
+
getCertificatesInfo: () => sdk.getCertificatesInfo(),
|
|
9542
|
+
notifications: sdk.notifications,
|
|
9543
|
+
isOnline: () => sdk.isOnline(),
|
|
9195
9544
|
};
|
|
9196
9545
|
}
|
|
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;
|
|
9546
|
+
/**
|
|
9547
|
+
* Manually trigger a notification sync
|
|
9548
|
+
*/
|
|
9549
|
+
async syncNotifications() {
|
|
9550
|
+
this.ensureInitialized();
|
|
9551
|
+
await this.notificationService.triggerSync();
|
|
9212
9552
|
}
|
|
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
|
-
|
|
9253
|
-
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
}
|
|
9264
|
-
|
|
9265
|
-
|
|
9266
|
-
|
|
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
|
-
});
|
|
9553
|
+
/**
|
|
9554
|
+
* Manually trigger a telemetry sync
|
|
9555
|
+
*/
|
|
9556
|
+
async syncTelemetry() {
|
|
9557
|
+
this.ensureInitialized();
|
|
9558
|
+
return this.telemetryService.triggerSync();
|
|
9559
|
+
}
|
|
9560
|
+
/**
|
|
9561
|
+
* Check if the manager is initialized
|
|
9562
|
+
*/
|
|
9563
|
+
getIsInitialized() {
|
|
9564
|
+
return this.isInitialized;
|
|
9565
|
+
}
|
|
9566
|
+
/**
|
|
9567
|
+
* Get the underlying SDK instance (for advanced use cases)
|
|
9568
|
+
*/
|
|
9569
|
+
getSDK() {
|
|
9570
|
+
this.ensureInitialized();
|
|
9571
|
+
return this.sdk;
|
|
9572
|
+
}
|
|
9573
|
+
/**
|
|
9574
|
+
* Check certificate availability and update certificateMissing state.
|
|
9575
|
+
* Returns true if certificate is available, false otherwise.
|
|
9576
|
+
*/
|
|
9577
|
+
async checkCertificate() {
|
|
9578
|
+
try {
|
|
9579
|
+
const hasCert = await this.sdk.hasCertificate();
|
|
9580
|
+
this.certificateMissingSubject.next(!hasCert);
|
|
9581
|
+
return hasCert;
|
|
9582
|
+
}
|
|
9583
|
+
catch {
|
|
9584
|
+
this.certificateMissingSubject.next(true);
|
|
9585
|
+
return false;
|
|
9586
|
+
}
|
|
9587
|
+
}
|
|
9588
|
+
cleanup() {
|
|
9589
|
+
this.notificationService?.destroy();
|
|
9590
|
+
this.telemetryService?.destroy();
|
|
9591
|
+
this.appStateService?.destroy();
|
|
9592
|
+
this.sdk?.destroy();
|
|
9593
|
+
this.notificationService = null;
|
|
9594
|
+
this.telemetryService = null;
|
|
9595
|
+
this.appStateService = null;
|
|
9596
|
+
this.sdk = null;
|
|
9597
|
+
this.isInitialized = false;
|
|
9598
|
+
this.isPollingActive = false;
|
|
9599
|
+
}
|
|
9600
|
+
ensureInitialized() {
|
|
9601
|
+
if (!this.isInitialized) {
|
|
9602
|
+
throw new ACubeSDKError('SDK_NOT_INITIALIZED', 'SDKManager not initialized. Call initialize() first.');
|
|
9603
|
+
}
|
|
9604
|
+
}
|
|
9605
|
+
}
|
|
9606
|
+
SDKManager.instance = null;
|
|
9386
9607
|
|
|
9387
|
-
|
|
9388
|
-
const
|
|
9389
|
-
|
|
9390
|
-
|
|
9391
|
-
});
|
|
9608
|
+
const RECEIPT_READY = 'ready';
|
|
9609
|
+
const RECEIPT_SENT = 'sent';
|
|
9610
|
+
const RECEIPT_SORT_DESCENDING = 'descending';
|
|
9611
|
+
const RECEIPT_SORT_ASCENDING = 'ascending';
|
|
9392
9612
|
|
|
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;
|
|
9613
|
+
const log = createPrefixedLogger('CACHE-HANDLER');
|
|
9614
|
+
class CacheHandler {
|
|
9615
|
+
constructor(cache, networkMonitor) {
|
|
9616
|
+
this.cache = cache;
|
|
9617
|
+
this.networkMonitor = networkMonitor;
|
|
9618
|
+
this.currentOnlineState = true;
|
|
9619
|
+
this.setupNetworkMonitoring();
|
|
9424
9620
|
}
|
|
9425
|
-
|
|
9426
|
-
|
|
9621
|
+
setupNetworkMonitoring() {
|
|
9622
|
+
if (this.networkMonitor) {
|
|
9623
|
+
this.networkSubscription = this.networkMonitor.online$.subscribe((online) => {
|
|
9624
|
+
this.currentOnlineState = online;
|
|
9625
|
+
});
|
|
9626
|
+
}
|
|
9627
|
+
}
|
|
9628
|
+
isOnline() {
|
|
9629
|
+
if (this.networkMonitor) {
|
|
9630
|
+
return this.currentOnlineState;
|
|
9631
|
+
}
|
|
9632
|
+
if (typeof navigator !== 'undefined' && 'onLine' in navigator) {
|
|
9633
|
+
return navigator.onLine;
|
|
9634
|
+
}
|
|
9427
9635
|
return false;
|
|
9428
9636
|
}
|
|
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
|
-
});
|
|
9637
|
+
async handleCachedRequest(url, requestFn, config) {
|
|
9638
|
+
if (!this.cache || config?.useCache === false) {
|
|
9639
|
+
const response = await requestFn();
|
|
9640
|
+
return response.data;
|
|
9641
|
+
}
|
|
9642
|
+
const cacheKey = this.generateCacheKey(url);
|
|
9643
|
+
const online = this.isOnline();
|
|
9644
|
+
log.debug('Request:', { url, cacheKey, online });
|
|
9645
|
+
if (online) {
|
|
9646
|
+
try {
|
|
9647
|
+
const response = await requestFn();
|
|
9648
|
+
if (this.cache) {
|
|
9649
|
+
await this.cache.set(cacheKey, response.data).catch((error) => {
|
|
9650
|
+
log.error('Failed to cache:', error instanceof Error ? error.message : error);
|
|
9651
|
+
});
|
|
9652
|
+
}
|
|
9653
|
+
return response.data;
|
|
9654
|
+
}
|
|
9655
|
+
catch (error) {
|
|
9656
|
+
const cached = await this.cache.get(cacheKey).catch(() => null);
|
|
9657
|
+
if (cached) {
|
|
9658
|
+
log.debug('Network failed, using cache fallback');
|
|
9659
|
+
return cached.data;
|
|
9660
|
+
}
|
|
9661
|
+
throw error;
|
|
9662
|
+
}
|
|
9663
|
+
}
|
|
9664
|
+
else {
|
|
9665
|
+
const cached = await this.cache.get(cacheKey).catch(() => null);
|
|
9666
|
+
if (cached) {
|
|
9667
|
+
log.debug('Offline, returning cached data');
|
|
9668
|
+
return cached.data;
|
|
9669
|
+
}
|
|
9670
|
+
throw new Error('Offline: No cached data available');
|
|
9671
|
+
}
|
|
9672
|
+
}
|
|
9673
|
+
generateCacheKey(url) {
|
|
9674
|
+
return url;
|
|
9675
|
+
}
|
|
9676
|
+
async invalidateCache(pattern) {
|
|
9677
|
+
if (!this.cache)
|
|
9678
|
+
return;
|
|
9679
|
+
try {
|
|
9680
|
+
await this.cache.invalidate(pattern);
|
|
9681
|
+
}
|
|
9682
|
+
catch (error) {
|
|
9683
|
+
log.error('Invalidation failed:', error instanceof Error ? error.message : error);
|
|
9684
|
+
}
|
|
9685
|
+
}
|
|
9686
|
+
getCacheStatus() {
|
|
9687
|
+
return {
|
|
9688
|
+
available: !!this.cache,
|
|
9689
|
+
networkMonitorAvailable: !!this.networkMonitor,
|
|
9690
|
+
isOnline: this.isOnline(),
|
|
9691
|
+
};
|
|
9692
|
+
}
|
|
9693
|
+
destroy() {
|
|
9694
|
+
this.networkSubscription?.unsubscribe();
|
|
9695
|
+
}
|
|
9696
|
+
}
|
|
9553
9697
|
|
|
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
|
-
});
|
|
9698
|
+
function transformError(error) {
|
|
9699
|
+
if (axios.isAxiosError(error)) {
|
|
9700
|
+
const response = error.response;
|
|
9701
|
+
if (!response) {
|
|
9702
|
+
return new ACubeSDKError('NETWORK_ERROR', 'Network error occurred', error);
|
|
9703
|
+
}
|
|
9704
|
+
const status = response.status;
|
|
9705
|
+
const data = response.data;
|
|
9706
|
+
const violations = data?.violations;
|
|
9707
|
+
let message = 'Unknown error occurred';
|
|
9708
|
+
if (data?.detail) {
|
|
9709
|
+
message = data.detail;
|
|
9710
|
+
}
|
|
9711
|
+
else if (data?.title) {
|
|
9712
|
+
message = data.title;
|
|
9713
|
+
}
|
|
9714
|
+
else if (error.message) {
|
|
9715
|
+
message = error.message;
|
|
9716
|
+
}
|
|
9717
|
+
switch (status) {
|
|
9718
|
+
case 400:
|
|
9719
|
+
return new ACubeSDKError('VALIDATION_ERROR', message, error, status, violations);
|
|
9720
|
+
case 401:
|
|
9721
|
+
return new ACubeSDKError('AUTH_ERROR', message, error, status, violations);
|
|
9722
|
+
case 403:
|
|
9723
|
+
return new ACubeSDKError('FORBIDDEN_ERROR', message, error, status, violations);
|
|
9724
|
+
case 404:
|
|
9725
|
+
return new ACubeSDKError('NOT_FOUND_ERROR', message, error, status, violations);
|
|
9726
|
+
case 422:
|
|
9727
|
+
return new ACubeSDKError('VALIDATION_ERROR', message, error, status, violations);
|
|
9728
|
+
default:
|
|
9729
|
+
return new ACubeSDKError('UNKNOWN_ERROR', message, error, status, violations);
|
|
9730
|
+
}
|
|
9731
|
+
}
|
|
9732
|
+
return new ACubeSDKError('UNKNOWN_ERROR', 'Unknown error occurred', error);
|
|
9733
|
+
}
|
|
9611
9734
|
|
|
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
|
-
}));
|
|
9735
|
+
exports.ErrorCategory = void 0;
|
|
9736
|
+
(function (ErrorCategory) {
|
|
9737
|
+
ErrorCategory["SERVER_ERROR"] = "SERVER_ERROR";
|
|
9738
|
+
ErrorCategory["CLIENT_ERROR"] = "CLIENT_ERROR";
|
|
9739
|
+
ErrorCategory["AUTH_ERROR"] = "AUTH_ERROR";
|
|
9740
|
+
ErrorCategory["CERTIFICATE_ERROR"] = "CERTIFICATE_ERROR";
|
|
9741
|
+
ErrorCategory["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
9742
|
+
ErrorCategory["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
|
|
9743
|
+
})(exports.ErrorCategory || (exports.ErrorCategory = {}));
|
|
9744
|
+
function extractStatusCode(error) {
|
|
9745
|
+
if (error instanceof MTLSError && error.statusCode) {
|
|
9746
|
+
return error.statusCode;
|
|
9747
|
+
}
|
|
9748
|
+
const errorObj = error;
|
|
9749
|
+
if (errorObj?.response?.status) {
|
|
9750
|
+
return errorObj.response.status;
|
|
9751
|
+
}
|
|
9752
|
+
if (typeof errorObj?.statusCode === 'number') {
|
|
9753
|
+
return errorObj.statusCode;
|
|
9754
|
+
}
|
|
9755
|
+
return undefined;
|
|
9756
|
+
}
|
|
9757
|
+
function classifyError(error) {
|
|
9758
|
+
const statusCode = extractStatusCode(error);
|
|
9759
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
9760
|
+
if (statusCode && statusCode >= 500 && statusCode < 600) {
|
|
9649
9761
|
return {
|
|
9650
|
-
|
|
9651
|
-
|
|
9652
|
-
|
|
9762
|
+
category: exports.ErrorCategory.SERVER_ERROR,
|
|
9763
|
+
statusCode,
|
|
9764
|
+
message: errorMessage,
|
|
9765
|
+
shouldRetry: false,
|
|
9766
|
+
userMessage: `Server error (${statusCode}): The server encountered an error.`,
|
|
9767
|
+
};
|
|
9768
|
+
}
|
|
9769
|
+
if (statusCode === 401 || statusCode === 403) {
|
|
9770
|
+
return {
|
|
9771
|
+
category: exports.ErrorCategory.AUTH_ERROR,
|
|
9772
|
+
statusCode,
|
|
9773
|
+
message: errorMessage,
|
|
9774
|
+
shouldRetry: false,
|
|
9775
|
+
userMessage: `Authentication error (${statusCode}): Invalid credentials or insufficient permissions.`,
|
|
9776
|
+
};
|
|
9777
|
+
}
|
|
9778
|
+
if (statusCode && statusCode >= 400 && statusCode < 500) {
|
|
9779
|
+
return {
|
|
9780
|
+
category: exports.ErrorCategory.CLIENT_ERROR,
|
|
9781
|
+
statusCode,
|
|
9782
|
+
message: errorMessage,
|
|
9783
|
+
shouldRetry: false,
|
|
9784
|
+
userMessage: `Request error (${statusCode}): ${errorMessage}`,
|
|
9785
|
+
};
|
|
9786
|
+
}
|
|
9787
|
+
if (error instanceof MTLSError) {
|
|
9788
|
+
return {
|
|
9789
|
+
category: exports.ErrorCategory.CERTIFICATE_ERROR,
|
|
9790
|
+
statusCode,
|
|
9791
|
+
message: errorMessage,
|
|
9792
|
+
shouldRetry: true,
|
|
9793
|
+
userMessage: 'Certificate error: Unable to establish secure connection.',
|
|
9794
|
+
};
|
|
9795
|
+
}
|
|
9796
|
+
if (!statusCode &&
|
|
9797
|
+
(errorMessage.toLowerCase().includes('network') ||
|
|
9798
|
+
errorMessage.toLowerCase().includes('timeout') ||
|
|
9799
|
+
errorMessage.toLowerCase().includes('connection'))) {
|
|
9800
|
+
return {
|
|
9801
|
+
category: exports.ErrorCategory.NETWORK_ERROR,
|
|
9802
|
+
message: errorMessage,
|
|
9803
|
+
shouldRetry: true,
|
|
9804
|
+
userMessage: 'Network error: Unable to connect to server.',
|
|
9653
9805
|
};
|
|
9654
9806
|
}
|
|
9655
9807
|
return {
|
|
9656
|
-
|
|
9657
|
-
|
|
9658
|
-
|
|
9808
|
+
category: exports.ErrorCategory.UNKNOWN_ERROR,
|
|
9809
|
+
statusCode,
|
|
9810
|
+
message: errorMessage,
|
|
9811
|
+
shouldRetry: false,
|
|
9812
|
+
userMessage: `Unexpected error: ${errorMessage}`,
|
|
9659
9813
|
};
|
|
9660
|
-
}
|
|
9814
|
+
}
|
|
9815
|
+
function shouldReconfigureCertificate(error) {
|
|
9816
|
+
const classification = classifyError(error);
|
|
9817
|
+
return classification.category === exports.ErrorCategory.CERTIFICATE_ERROR;
|
|
9818
|
+
}
|
|
9819
|
+
function shouldRetryRequest(error, isRetryAttempt) {
|
|
9820
|
+
if (isRetryAttempt) {
|
|
9821
|
+
return false;
|
|
9822
|
+
}
|
|
9823
|
+
const classification = classifyError(error);
|
|
9824
|
+
return classification.shouldRetry;
|
|
9825
|
+
}
|
|
9826
|
+
function getUserFriendlyMessage(error) {
|
|
9827
|
+
const classification = classifyError(error);
|
|
9828
|
+
return classification.userMessage;
|
|
9829
|
+
}
|
|
9661
9830
|
|
|
9662
9831
|
var MTLSErrorType;
|
|
9663
9832
|
(function (MTLSErrorType) {
|