@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.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import axios from 'axios';
|
|
2
1
|
import * as z from 'zod';
|
|
2
|
+
import axios from 'axios';
|
|
3
3
|
|
|
4
4
|
/******************************************************************************
|
|
5
5
|
Copyright (c) Microsoft Corporation.
|
|
@@ -1392,6 +1392,15 @@ function from(input, scheduler) {
|
|
|
1392
1392
|
return scheduler ? scheduled(input, scheduler) : innerFrom(input);
|
|
1393
1393
|
}
|
|
1394
1394
|
|
|
1395
|
+
function of() {
|
|
1396
|
+
var args = [];
|
|
1397
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
1398
|
+
args[_i] = arguments[_i];
|
|
1399
|
+
}
|
|
1400
|
+
var scheduler = popScheduler(args);
|
|
1401
|
+
return from(args, scheduler);
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1395
1404
|
function isValidDate(value) {
|
|
1396
1405
|
return value instanceof Date && !isNaN(value);
|
|
1397
1406
|
}
|
|
@@ -1838,131 +1847,436 @@ function extractRoles(jwtRoles) {
|
|
|
1838
1847
|
return allRoles;
|
|
1839
1848
|
}
|
|
1840
1849
|
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
return
|
|
1844
|
-
}
|
|
1845
|
-
get isAuthenticated$() {
|
|
1846
|
-
return this.userSubject.pipe(map((user) => user !== null), distinctUntilChanged(), takeUntil(this.destroy$));
|
|
1847
|
-
}
|
|
1848
|
-
get authState$() {
|
|
1849
|
-
return this.authStateSubject.asObservable();
|
|
1850
|
+
function clearObject(input) {
|
|
1851
|
+
if (input === null || input === undefined || input === '') {
|
|
1852
|
+
return undefined;
|
|
1850
1853
|
}
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
this.userSubject = new BehaviorSubject(null);
|
|
1857
|
-
this.authStateSubject = new BehaviorSubject('idle');
|
|
1858
|
-
this.destroy$ = new Subject();
|
|
1854
|
+
if (Array.isArray(input)) {
|
|
1855
|
+
const cleanedArray = input
|
|
1856
|
+
.map((item) => clearObject(item))
|
|
1857
|
+
.filter((item) => item !== undefined);
|
|
1858
|
+
return cleanedArray;
|
|
1859
1859
|
}
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
const
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
}
|
|
1867
|
-
const jwtPayload = parseJwt(response.data.token);
|
|
1868
|
-
const expiresAt = jwtPayload.exp * 1000;
|
|
1869
|
-
await this.tokenStorage.saveAccessToken(response.data.token, expiresAt);
|
|
1870
|
-
const user = this.createUserFromPayload(jwtPayload);
|
|
1871
|
-
await this.tokenStorage.saveUser(user);
|
|
1872
|
-
this.userSubject.next(user);
|
|
1873
|
-
this.authStateSubject.next('authenticated');
|
|
1874
|
-
this.events.onUserChanged?.(user);
|
|
1875
|
-
return user;
|
|
1876
|
-
}
|
|
1877
|
-
catch (error) {
|
|
1878
|
-
this.authStateSubject.next('error');
|
|
1879
|
-
throw error;
|
|
1860
|
+
if (typeof input === 'object' && input.constructor === Object) {
|
|
1861
|
+
const cleaned = {};
|
|
1862
|
+
for (const [key, value] of Object.entries(input)) {
|
|
1863
|
+
const cleanedValue = clearObject(value);
|
|
1864
|
+
if (cleanedValue !== undefined) {
|
|
1865
|
+
cleaned[key] = cleanedValue;
|
|
1866
|
+
}
|
|
1880
1867
|
}
|
|
1868
|
+
return cleaned;
|
|
1881
1869
|
}
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1870
|
+
return input;
|
|
1871
|
+
}
|
|
1872
|
+
function clearObjectShallow(obj) {
|
|
1873
|
+
if (!obj || typeof obj !== 'object') {
|
|
1874
|
+
return {};
|
|
1887
1875
|
}
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
if (
|
|
1891
|
-
|
|
1892
|
-
}
|
|
1893
|
-
const storedUser = await this.tokenStorage.getUser();
|
|
1894
|
-
if (storedUser) {
|
|
1895
|
-
this.userSubject.next(storedUser);
|
|
1896
|
-
this.authStateSubject.next('authenticated');
|
|
1897
|
-
return storedUser;
|
|
1898
|
-
}
|
|
1899
|
-
const token = await this.tokenStorage.getAccessToken();
|
|
1900
|
-
if (!token) {
|
|
1901
|
-
return null;
|
|
1902
|
-
}
|
|
1903
|
-
const jwtPayload = parseJwt(token);
|
|
1904
|
-
if (isTokenExpired(jwtPayload)) {
|
|
1905
|
-
await this.tokenStorage.clearTokens();
|
|
1906
|
-
return null;
|
|
1876
|
+
const cleaned = {};
|
|
1877
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
1878
|
+
if (value !== null && value !== undefined && value !== '') {
|
|
1879
|
+
cleaned[key] = value;
|
|
1907
1880
|
}
|
|
1908
|
-
const user = this.createUserFromPayload(jwtPayload);
|
|
1909
|
-
await this.tokenStorage.saveUser(user);
|
|
1910
|
-
this.userSubject.next(user);
|
|
1911
|
-
this.authStateSubject.next('authenticated');
|
|
1912
|
-
return user;
|
|
1913
1881
|
}
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1882
|
+
return cleaned;
|
|
1883
|
+
}
|
|
1884
|
+
function isEmpty(value) {
|
|
1885
|
+
return value === null || value === undefined || value === '';
|
|
1886
|
+
}
|
|
1887
|
+
function hasNonEmptyValues(obj) {
|
|
1888
|
+
if (!obj || typeof obj !== 'object') {
|
|
1889
|
+
return false;
|
|
1921
1890
|
}
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1891
|
+
return Object.values(obj).some((value) => !isEmpty(value));
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
/**
|
|
1895
|
+
* Platform detection utilities
|
|
1896
|
+
*/
|
|
1897
|
+
/**
|
|
1898
|
+
* Detect the current platform
|
|
1899
|
+
*/
|
|
1900
|
+
function detectPlatform() {
|
|
1901
|
+
// Check for React Native
|
|
1902
|
+
if (typeof global !== 'undefined' &&
|
|
1903
|
+
global.__DEV__ !== undefined &&
|
|
1904
|
+
typeof global.navigator !== 'undefined' &&
|
|
1905
|
+
global.navigator.product === 'ReactNative') {
|
|
1906
|
+
return {
|
|
1907
|
+
platform: 'react-native',
|
|
1908
|
+
isReactNative: true,
|
|
1909
|
+
isWeb: false,
|
|
1910
|
+
isNode: false,
|
|
1911
|
+
isExpo: checkExpo(),
|
|
1912
|
+
};
|
|
1936
1913
|
}
|
|
1937
|
-
|
|
1914
|
+
// Check for Web/Browser
|
|
1915
|
+
if (typeof window !== 'undefined' &&
|
|
1916
|
+
typeof window.document !== 'undefined' &&
|
|
1917
|
+
typeof window.navigator !== 'undefined') {
|
|
1938
1918
|
return {
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
pid: jwtPayload.pid,
|
|
1945
|
-
expiresAt: jwtPayload.exp * 1000,
|
|
1919
|
+
platform: 'web',
|
|
1920
|
+
isReactNative: false,
|
|
1921
|
+
isWeb: true,
|
|
1922
|
+
isNode: false,
|
|
1923
|
+
isExpo: false,
|
|
1946
1924
|
};
|
|
1947
1925
|
}
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1926
|
+
// Check for Node.js
|
|
1927
|
+
if (typeof process !== 'undefined' && process.versions && process.versions.node) {
|
|
1928
|
+
return {
|
|
1929
|
+
platform: 'node',
|
|
1930
|
+
isReactNative: false,
|
|
1931
|
+
isWeb: false,
|
|
1932
|
+
isNode: true,
|
|
1933
|
+
isExpo: false,
|
|
1934
|
+
};
|
|
1951
1935
|
}
|
|
1936
|
+
// Unknown platform
|
|
1937
|
+
return {
|
|
1938
|
+
platform: 'unknown',
|
|
1939
|
+
isReactNative: false,
|
|
1940
|
+
isWeb: false,
|
|
1941
|
+
isNode: false,
|
|
1942
|
+
isExpo: false,
|
|
1943
|
+
};
|
|
1952
1944
|
}
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1945
|
+
/**
|
|
1946
|
+
* Check if running in Expo
|
|
1947
|
+
*/
|
|
1948
|
+
function checkExpo() {
|
|
1949
|
+
try {
|
|
1950
|
+
return (typeof global !== 'undefined' &&
|
|
1951
|
+
(typeof global.Expo !== 'undefined' || typeof global.expo !== 'undefined'));
|
|
1958
1952
|
}
|
|
1959
|
-
|
|
1960
|
-
return
|
|
1953
|
+
catch {
|
|
1954
|
+
return false;
|
|
1961
1955
|
}
|
|
1962
|
-
|
|
1963
|
-
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
function getDefaultExportFromCjs (x) {
|
|
1959
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
var picocolors_browser = {exports: {}};
|
|
1963
|
+
|
|
1964
|
+
var x=String;
|
|
1965
|
+
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}};
|
|
1966
|
+
picocolors_browser.exports=create();
|
|
1967
|
+
picocolors_browser.exports.createColors = create;
|
|
1968
|
+
|
|
1969
|
+
var picocolors_browserExports = picocolors_browser.exports;
|
|
1970
|
+
var pc = /*@__PURE__*/getDefaultExportFromCjs(picocolors_browserExports);
|
|
1971
|
+
|
|
1972
|
+
const levelColors = {
|
|
1973
|
+
debug: {
|
|
1974
|
+
prefix: pc.gray,
|
|
1975
|
+
message: pc.gray,
|
|
1976
|
+
data: pc.dim,
|
|
1977
|
+
},
|
|
1978
|
+
info: {
|
|
1979
|
+
prefix: pc.cyan,
|
|
1980
|
+
message: pc.white,
|
|
1981
|
+
data: pc.dim,
|
|
1982
|
+
},
|
|
1983
|
+
warn: {
|
|
1984
|
+
prefix: pc.yellow,
|
|
1985
|
+
message: pc.yellow,
|
|
1986
|
+
data: pc.dim,
|
|
1987
|
+
},
|
|
1988
|
+
error: {
|
|
1989
|
+
prefix: pc.red,
|
|
1990
|
+
message: pc.red,
|
|
1991
|
+
data: pc.dim,
|
|
1992
|
+
},
|
|
1993
|
+
};
|
|
1994
|
+
const levelIcons = {
|
|
1995
|
+
debug: '🔍',
|
|
1996
|
+
info: 'ℹ️ ',
|
|
1997
|
+
warn: '⚠️ ',
|
|
1998
|
+
error: '❌',
|
|
1999
|
+
};
|
|
2000
|
+
/**
|
|
2001
|
+
* Format data for pretty display in logs
|
|
2002
|
+
*/
|
|
2003
|
+
function formatData(data, level) {
|
|
2004
|
+
if (data === undefined || data === null) {
|
|
2005
|
+
return String(data);
|
|
1964
2006
|
}
|
|
1965
|
-
|
|
2007
|
+
// For primitive types, just return as string
|
|
2008
|
+
if (typeof data !== 'object') {
|
|
2009
|
+
return String(data);
|
|
2010
|
+
}
|
|
2011
|
+
// For Error objects, format nicely
|
|
2012
|
+
if (data instanceof Error) {
|
|
2013
|
+
return `${data.name}: ${data.message}${data.stack ? `\n${pc.dim(data.stack)}` : ''}`;
|
|
2014
|
+
}
|
|
2015
|
+
try {
|
|
2016
|
+
// Apply level-specific coloring
|
|
2017
|
+
const colors = levelColors[level];
|
|
2018
|
+
return colors.data(JSON.stringify(data, null, 2));
|
|
2019
|
+
}
|
|
2020
|
+
catch {
|
|
2021
|
+
// Fallback to JSON.stringify if pretty-format fails
|
|
2022
|
+
try {
|
|
2023
|
+
return JSON.stringify(data, null, 2);
|
|
2024
|
+
}
|
|
2025
|
+
catch {
|
|
2026
|
+
return String(data);
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
class Logger {
|
|
2031
|
+
constructor() {
|
|
2032
|
+
this.enabled = false;
|
|
2033
|
+
}
|
|
2034
|
+
setEnabled(enabled) {
|
|
2035
|
+
this.enabled = enabled;
|
|
2036
|
+
}
|
|
2037
|
+
isEnabled() {
|
|
2038
|
+
return this.enabled;
|
|
2039
|
+
}
|
|
2040
|
+
debug(prefix, message, data) {
|
|
2041
|
+
if (!this.enabled)
|
|
2042
|
+
return;
|
|
2043
|
+
this.log('debug', prefix, message, data);
|
|
2044
|
+
}
|
|
2045
|
+
info(prefix, message, data) {
|
|
2046
|
+
if (!this.enabled)
|
|
2047
|
+
return;
|
|
2048
|
+
this.log('info', prefix, message, data);
|
|
2049
|
+
}
|
|
2050
|
+
warn(prefix, message, data) {
|
|
2051
|
+
this.log('warn', prefix, message, data);
|
|
2052
|
+
}
|
|
2053
|
+
error(prefix, message, data) {
|
|
2054
|
+
this.log('error', prefix, message, data);
|
|
2055
|
+
}
|
|
2056
|
+
log(level, prefix, message, data) {
|
|
2057
|
+
const colors = levelColors[level];
|
|
2058
|
+
const icon = levelIcons[level];
|
|
2059
|
+
const isoTime = new Date().toISOString().split('T')[1] ?? '';
|
|
2060
|
+
const timestamp = pc.dim(isoTime.slice(0, 12));
|
|
2061
|
+
const formattedPrefix = colors.prefix(`[${prefix}]`);
|
|
2062
|
+
const formattedMessage = colors.message(message);
|
|
2063
|
+
const consoleMethod = level === 'debug'
|
|
2064
|
+
? console.debug
|
|
2065
|
+
: level === 'info'
|
|
2066
|
+
? console.info
|
|
2067
|
+
: level === 'warn'
|
|
2068
|
+
? console.warn
|
|
2069
|
+
: console.error;
|
|
2070
|
+
const header = `${timestamp} ${icon} ${formattedPrefix} ${formattedMessage}`;
|
|
2071
|
+
if (data !== undefined) {
|
|
2072
|
+
const formattedData = formatData(data, level);
|
|
2073
|
+
// Check if data is an object (multi-line) or primitive (single-line)
|
|
2074
|
+
const isMultiLine = typeof data === 'object' && data !== null;
|
|
2075
|
+
if (isMultiLine) {
|
|
2076
|
+
consoleMethod(`${header}\n${formattedData}`);
|
|
2077
|
+
}
|
|
2078
|
+
else {
|
|
2079
|
+
consoleMethod(`${header}`, formattedData);
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
else {
|
|
2083
|
+
consoleMethod(header);
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
const logger = new Logger();
|
|
2088
|
+
function createPrefixedLogger(prefix) {
|
|
2089
|
+
return {
|
|
2090
|
+
debug: (message, data) => logger.debug(prefix, message, data),
|
|
2091
|
+
info: (message, data) => logger.info(prefix, message, data),
|
|
2092
|
+
warn: (message, data) => logger.warn(prefix, message, data),
|
|
2093
|
+
error: (message, data) => logger.error(prefix, message, data),
|
|
2094
|
+
};
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
/**
|
|
2098
|
+
* Formats a numeric string value to have exactly the specified number of decimal places.
|
|
2099
|
+
* E.g., "1" → "1.00", "10" → "10.00", "1.5" → "1.50"
|
|
2100
|
+
* Returns undefined for undefined input (preserves optional fields).
|
|
2101
|
+
*
|
|
2102
|
+
* @param value - The string value to format
|
|
2103
|
+
* @param decimals - Number of decimal places (default: 2)
|
|
2104
|
+
* @returns Formatted string or undefined if input is undefined
|
|
2105
|
+
*/
|
|
2106
|
+
function formatDecimal(value, decimals = 2) {
|
|
2107
|
+
if (value === undefined)
|
|
2108
|
+
return undefined;
|
|
2109
|
+
const num = parseFloat(value);
|
|
2110
|
+
if (isNaN(num))
|
|
2111
|
+
return value;
|
|
2112
|
+
return num.toFixed(decimals);
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
const log$h = createPrefixedLogger('AUTH-SERVICE');
|
|
2116
|
+
class AuthenticationService {
|
|
2117
|
+
get user$() {
|
|
2118
|
+
return this.userSubject.asObservable();
|
|
2119
|
+
}
|
|
2120
|
+
get isAuthenticated$() {
|
|
2121
|
+
return this.userSubject.pipe(map((user) => user !== null), distinctUntilChanged(), takeUntil(this.destroy$));
|
|
2122
|
+
}
|
|
2123
|
+
get authState$() {
|
|
2124
|
+
return this.authStateSubject.asObservable();
|
|
2125
|
+
}
|
|
2126
|
+
constructor(httpPort, tokenStorage, config, events = {}) {
|
|
2127
|
+
this.httpPort = httpPort;
|
|
2128
|
+
this.tokenStorage = tokenStorage;
|
|
2129
|
+
this.config = config;
|
|
2130
|
+
this.events = events;
|
|
2131
|
+
this.userSubject = new BehaviorSubject(null);
|
|
2132
|
+
this.authStateSubject = new BehaviorSubject('idle');
|
|
2133
|
+
this.destroy$ = new Subject();
|
|
2134
|
+
}
|
|
2135
|
+
async login(credentials) {
|
|
2136
|
+
this.authStateSubject.next('authenticating');
|
|
2137
|
+
log$h.info('Login attempt', {
|
|
2138
|
+
authUrl: this.config.authUrl,
|
|
2139
|
+
email: credentials.email,
|
|
2140
|
+
});
|
|
2141
|
+
try {
|
|
2142
|
+
const response = await this.httpPort.post(`${this.config.authUrl}/login`, {
|
|
2143
|
+
email: credentials.email,
|
|
2144
|
+
password: credentials.password,
|
|
2145
|
+
});
|
|
2146
|
+
const jwtPayload = parseJwt(response.data.token);
|
|
2147
|
+
const expiresAt = jwtPayload.exp * 1000;
|
|
2148
|
+
log$h.info('Login successful', {
|
|
2149
|
+
authUrl: this.config.authUrl,
|
|
2150
|
+
tokenPrefix: response.data.token.substring(0, 30) + '...',
|
|
2151
|
+
expiresAt: new Date(expiresAt).toISOString(),
|
|
2152
|
+
});
|
|
2153
|
+
await this.tokenStorage.saveAccessToken(response.data.token, expiresAt);
|
|
2154
|
+
const user = this.createUserFromPayload(jwtPayload);
|
|
2155
|
+
await this.tokenStorage.saveUser(user);
|
|
2156
|
+
this.userSubject.next(user);
|
|
2157
|
+
this.authStateSubject.next('authenticated');
|
|
2158
|
+
this.events.onUserChanged?.(user);
|
|
2159
|
+
return user;
|
|
2160
|
+
}
|
|
2161
|
+
catch (error) {
|
|
2162
|
+
this.authStateSubject.next('error');
|
|
2163
|
+
throw error;
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
async logout() {
|
|
2167
|
+
await this.tokenStorage.clearTokens();
|
|
2168
|
+
this.userSubject.next(null);
|
|
2169
|
+
this.authStateSubject.next('idle');
|
|
2170
|
+
this.events.onUserChanged?.(null);
|
|
2171
|
+
}
|
|
2172
|
+
async getCurrentUser() {
|
|
2173
|
+
// Always verify token is valid before returning user
|
|
2174
|
+
const token = await this.tokenStorage.getAccessToken();
|
|
2175
|
+
if (!token) {
|
|
2176
|
+
// No token - clear any stale user state
|
|
2177
|
+
log$h.debug('getCurrentUser: No token in storage');
|
|
2178
|
+
if (this.userSubject.value) {
|
|
2179
|
+
this.userSubject.next(null);
|
|
2180
|
+
this.authStateSubject.next('idle');
|
|
2181
|
+
}
|
|
2182
|
+
return null;
|
|
2183
|
+
}
|
|
2184
|
+
log$h.debug('getCurrentUser: Token found', {
|
|
2185
|
+
tokenPrefix: token.substring(0, 30) + '...',
|
|
2186
|
+
tokenLength: token.length,
|
|
2187
|
+
});
|
|
2188
|
+
const jwtPayload = parseJwt(token);
|
|
2189
|
+
if (isTokenExpired(jwtPayload)) {
|
|
2190
|
+
// Token expired - clear everything
|
|
2191
|
+
log$h.warn('getCurrentUser: Token expired');
|
|
2192
|
+
await this.tokenStorage.clearTokens();
|
|
2193
|
+
this.userSubject.next(null);
|
|
2194
|
+
this.authStateSubject.next('idle');
|
|
2195
|
+
this.events.onUserChanged?.(null);
|
|
2196
|
+
return null;
|
|
2197
|
+
}
|
|
2198
|
+
// Token is valid - return cached user if available
|
|
2199
|
+
const currentUser = this.userSubject.value;
|
|
2200
|
+
if (currentUser) {
|
|
2201
|
+
log$h.debug('getCurrentUser: Returning cached user', {
|
|
2202
|
+
email: currentUser.email,
|
|
2203
|
+
roles: currentUser.roles,
|
|
2204
|
+
});
|
|
2205
|
+
return currentUser;
|
|
2206
|
+
}
|
|
2207
|
+
// Check stored user
|
|
2208
|
+
const storedUser = await this.tokenStorage.getUser();
|
|
2209
|
+
if (storedUser) {
|
|
2210
|
+
this.userSubject.next(storedUser);
|
|
2211
|
+
this.authStateSubject.next('authenticated');
|
|
2212
|
+
return storedUser;
|
|
2213
|
+
}
|
|
2214
|
+
// Create user from token
|
|
2215
|
+
const user = this.createUserFromPayload(jwtPayload);
|
|
2216
|
+
await this.tokenStorage.saveUser(user);
|
|
2217
|
+
this.userSubject.next(user);
|
|
2218
|
+
this.authStateSubject.next('authenticated');
|
|
2219
|
+
return user;
|
|
2220
|
+
}
|
|
2221
|
+
async isAuthenticated() {
|
|
2222
|
+
const token = await this.tokenStorage.getAccessToken();
|
|
2223
|
+
if (!token) {
|
|
2224
|
+
log$h.debug('isAuthenticated: No token in storage');
|
|
2225
|
+
return false;
|
|
2226
|
+
}
|
|
2227
|
+
const jwtPayload = parseJwt(token);
|
|
2228
|
+
const expired = isTokenExpired(jwtPayload);
|
|
2229
|
+
log$h.debug('isAuthenticated: Token check', {
|
|
2230
|
+
hasToken: true,
|
|
2231
|
+
expired,
|
|
2232
|
+
expiresAt: new Date(jwtPayload.exp * 1000).toISOString(),
|
|
2233
|
+
});
|
|
2234
|
+
return !expired;
|
|
2235
|
+
}
|
|
2236
|
+
async getAccessToken() {
|
|
2237
|
+
const token = await this.tokenStorage.getAccessToken();
|
|
2238
|
+
if (!token) {
|
|
2239
|
+
return null;
|
|
2240
|
+
}
|
|
2241
|
+
const jwtPayload = parseJwt(token);
|
|
2242
|
+
if (isTokenExpired(jwtPayload)) {
|
|
2243
|
+
await this.tokenStorage.clearTokens();
|
|
2244
|
+
this.userSubject.next(null);
|
|
2245
|
+
this.authStateSubject.next('idle');
|
|
2246
|
+
this.events.onUserChanged?.(null);
|
|
2247
|
+
return null;
|
|
2248
|
+
}
|
|
2249
|
+
return token;
|
|
2250
|
+
}
|
|
2251
|
+
createUserFromPayload(jwtPayload) {
|
|
2252
|
+
return {
|
|
2253
|
+
id: jwtPayload.uid.toString(),
|
|
2254
|
+
email: jwtPayload.username,
|
|
2255
|
+
username: jwtPayload.username,
|
|
2256
|
+
roles: jwtPayload.roles,
|
|
2257
|
+
fid: jwtPayload.fid,
|
|
2258
|
+
pid: jwtPayload.pid,
|
|
2259
|
+
expiresAt: jwtPayload.exp * 1000,
|
|
2260
|
+
};
|
|
2261
|
+
}
|
|
2262
|
+
destroy() {
|
|
2263
|
+
this.destroy$.next();
|
|
2264
|
+
this.destroy$.complete();
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
const CERTIFICATE_KEY = 'acube_certificate';
|
|
2269
|
+
class CertificateService {
|
|
2270
|
+
get certificate$() {
|
|
2271
|
+
return this.certificateSubject.asObservable();
|
|
2272
|
+
}
|
|
2273
|
+
get hasCertificate$() {
|
|
2274
|
+
return this.certificateSubject.pipe(map((cert) => cert !== null), distinctUntilChanged());
|
|
2275
|
+
}
|
|
2276
|
+
get state$() {
|
|
2277
|
+
return this.stateSubject.asObservable();
|
|
2278
|
+
}
|
|
2279
|
+
constructor(secureStorage) {
|
|
1966
2280
|
this.secureStorage = secureStorage;
|
|
1967
2281
|
this.certificateSubject = new BehaviorSubject(null);
|
|
1968
2282
|
this.stateSubject = new BehaviorSubject('idle');
|
|
@@ -2057,406 +2371,677 @@ function hasAnyRole(userRoles, required) {
|
|
|
2057
2371
|
return Object.values(userRoles).some((roles) => required.some((role) => roles.includes(role)));
|
|
2058
2372
|
}
|
|
2059
2373
|
|
|
2060
|
-
class
|
|
2061
|
-
constructor(
|
|
2062
|
-
this.
|
|
2063
|
-
this.mtlsHandler = mtlsHandler;
|
|
2064
|
-
this.userProvider = userProvider;
|
|
2065
|
-
this.mtlsAdapter = mtlsAdapter;
|
|
2374
|
+
class ConfigManager {
|
|
2375
|
+
constructor(userConfig) {
|
|
2376
|
+
this.config = this.buildConfig(userConfig);
|
|
2066
2377
|
}
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
}
|
|
2077
|
-
if (userRole === 'CASHIER') {
|
|
2078
|
-
if (!isReceiptEndpoint) {
|
|
2079
|
-
return { mode: 'jwt', usePort444: false };
|
|
2080
|
-
}
|
|
2081
|
-
if (platform === 'mobile') {
|
|
2082
|
-
return { mode: 'mtls', usePort444: true };
|
|
2083
|
-
}
|
|
2084
|
-
return { mode: 'jwt', usePort444: true };
|
|
2085
|
-
}
|
|
2086
|
-
if (userRole === 'MERCHANT') {
|
|
2087
|
-
if (!isReceiptEndpoint) {
|
|
2088
|
-
return { mode: 'jwt', usePort444: false };
|
|
2089
|
-
}
|
|
2090
|
-
if (this.isReturnableItemsEndpoint(url)) {
|
|
2091
|
-
return { mode: 'mtls', usePort444: true };
|
|
2092
|
-
}
|
|
2093
|
-
if (method === 'GET') {
|
|
2094
|
-
if (this.isDetailedReceiptEndpoint(url)) {
|
|
2095
|
-
if (platform === 'mobile') {
|
|
2096
|
-
return { mode: 'mtls', usePort444: true };
|
|
2097
|
-
}
|
|
2098
|
-
return { mode: 'jwt', usePort444: true };
|
|
2099
|
-
}
|
|
2100
|
-
return { mode: 'jwt', usePort444: false };
|
|
2101
|
-
}
|
|
2102
|
-
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {
|
|
2103
|
-
if (platform === 'mobile') {
|
|
2104
|
-
return { mode: 'mtls', usePort444: true };
|
|
2105
|
-
}
|
|
2106
|
-
return { mode: 'jwt', usePort444: true };
|
|
2107
|
-
}
|
|
2108
|
-
return { mode: 'jwt', usePort444: false };
|
|
2109
|
-
}
|
|
2110
|
-
if (explicitMode) {
|
|
2111
|
-
if (userRole === 'SUPPLIER' && explicitMode === 'mtls') {
|
|
2112
|
-
return { mode: 'jwt', usePort444: false };
|
|
2113
|
-
}
|
|
2114
|
-
return {
|
|
2115
|
-
mode: explicitMode,
|
|
2116
|
-
usePort444: explicitMode === 'mtls' || (platform === 'web' && isReceiptEndpoint),
|
|
2117
|
-
};
|
|
2118
|
-
}
|
|
2119
|
-
if (platform === 'web') {
|
|
2120
|
-
return { mode: 'jwt', usePort444: isReceiptEndpoint };
|
|
2121
|
-
}
|
|
2122
|
-
if (isReceiptEndpoint && platform === 'mobile') {
|
|
2123
|
-
return { mode: 'mtls', usePort444: true };
|
|
2124
|
-
}
|
|
2125
|
-
return { mode: 'jwt', usePort444: false };
|
|
2126
|
-
}
|
|
2127
|
-
async getAuthHeaders() {
|
|
2128
|
-
return this.jwtHandler.getAuthHeaders();
|
|
2129
|
-
}
|
|
2130
|
-
getMtlsHandler() {
|
|
2131
|
-
return this.mtlsHandler;
|
|
2132
|
-
}
|
|
2133
|
-
getJwtHandler() {
|
|
2134
|
-
return this.jwtHandler;
|
|
2378
|
+
buildConfig(userConfig) {
|
|
2379
|
+
return {
|
|
2380
|
+
environment: userConfig.environment,
|
|
2381
|
+
apiUrl: this.getDefaultApiUrl(userConfig.environment),
|
|
2382
|
+
authUrl: this.getDefaultAuthUrl(userConfig.environment),
|
|
2383
|
+
timeout: 30000,
|
|
2384
|
+
retryAttempts: 3,
|
|
2385
|
+
debug: userConfig.debug ?? false,
|
|
2386
|
+
customHeaders: {},
|
|
2387
|
+
};
|
|
2135
2388
|
}
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2389
|
+
getDefaultApiUrl(environment) {
|
|
2390
|
+
switch (environment) {
|
|
2391
|
+
case 'production':
|
|
2392
|
+
return 'https://ereceipts-it.acubeapi.com';
|
|
2393
|
+
case 'development':
|
|
2394
|
+
return 'https://ereceipts-it.dev.acubeapi.com';
|
|
2395
|
+
case 'sandbox':
|
|
2396
|
+
default:
|
|
2397
|
+
return 'https://ereceipts-it-sandbox.acubeapi.com';
|
|
2139
2398
|
}
|
|
2140
|
-
const platformInfo = this.mtlsAdapter.getPlatformInfo();
|
|
2141
|
-
return platformInfo.platform === 'web' ? 'web' : 'mobile';
|
|
2142
2399
|
}
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
if (hasRole(user.roles, 'ROLE_SUPPLIER')) {
|
|
2152
|
-
return 'SUPPLIER';
|
|
2153
|
-
}
|
|
2154
|
-
if (hasRole(user.roles, 'ROLE_MERCHANT')) {
|
|
2155
|
-
return 'MERCHANT';
|
|
2156
|
-
}
|
|
2157
|
-
if (hasRole(user.roles, 'ROLE_CASHIER')) {
|
|
2158
|
-
return 'CASHIER';
|
|
2400
|
+
getDefaultAuthUrl(environment) {
|
|
2401
|
+
switch (environment) {
|
|
2402
|
+
case 'production':
|
|
2403
|
+
return 'https://common.api.acubeapi.com';
|
|
2404
|
+
case 'development':
|
|
2405
|
+
case 'sandbox':
|
|
2406
|
+
default:
|
|
2407
|
+
return 'https://common-sandbox.api.acubeapi.com';
|
|
2159
2408
|
}
|
|
2160
|
-
return null;
|
|
2161
|
-
}
|
|
2162
|
-
isReceiptEndpoint(url) {
|
|
2163
|
-
return url.includes('/receipts') || url.includes('/mf1/receipts');
|
|
2164
|
-
}
|
|
2165
|
-
isReturnableItemsEndpoint(url) {
|
|
2166
|
-
return !!(url.match(/\/receipts\/[a-f0-9-]+\/returnable-items$/) ||
|
|
2167
|
-
url.match(/\/mf1\/receipts\/[a-f0-9-]+\/returnable-items$/));
|
|
2168
2409
|
}
|
|
2169
|
-
|
|
2170
|
-
return
|
|
2171
|
-
|
|
2410
|
+
getConfig() {
|
|
2411
|
+
return {
|
|
2412
|
+
environment: this.config.environment,
|
|
2413
|
+
debug: this.config.debug,
|
|
2414
|
+
};
|
|
2172
2415
|
}
|
|
2173
|
-
|
|
2174
|
-
return
|
|
2416
|
+
getApiUrl() {
|
|
2417
|
+
return this.config.apiUrl;
|
|
2175
2418
|
}
|
|
2176
|
-
|
|
2177
|
-
return
|
|
2419
|
+
getAuthUrl() {
|
|
2420
|
+
return this.config.authUrl;
|
|
2178
2421
|
}
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
class JwtAuthHandler {
|
|
2182
|
-
constructor(tokenStorage) {
|
|
2183
|
-
this.tokenStorage = tokenStorage;
|
|
2422
|
+
getEnvironment() {
|
|
2423
|
+
return this.config.environment;
|
|
2184
2424
|
}
|
|
2185
|
-
|
|
2186
|
-
return
|
|
2425
|
+
isDebugEnabled() {
|
|
2426
|
+
return this.config.debug;
|
|
2187
2427
|
}
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
if (!token) {
|
|
2191
|
-
return {};
|
|
2192
|
-
}
|
|
2193
|
-
return { Authorization: `Bearer ${token}` };
|
|
2428
|
+
getTimeout() {
|
|
2429
|
+
return this.config.timeout;
|
|
2194
2430
|
}
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
function clearObject(input) {
|
|
2198
|
-
if (input === null || input === undefined || input === '') {
|
|
2199
|
-
return undefined;
|
|
2431
|
+
getRetryAttempts() {
|
|
2432
|
+
return this.config.retryAttempts;
|
|
2200
2433
|
}
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
.map((item) => clearObject(item))
|
|
2204
|
-
.filter((item) => item !== undefined);
|
|
2205
|
-
return cleanedArray;
|
|
2434
|
+
getCustomHeaders() {
|
|
2435
|
+
return { ...this.config.customHeaders };
|
|
2206
2436
|
}
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
cleaned[key] = cleanedValue;
|
|
2213
|
-
}
|
|
2437
|
+
updateConfig(updates) {
|
|
2438
|
+
if (updates.environment) {
|
|
2439
|
+
this.config.environment = updates.environment;
|
|
2440
|
+
this.config.apiUrl = this.getDefaultApiUrl(updates.environment);
|
|
2441
|
+
this.config.authUrl = this.getDefaultAuthUrl(updates.environment);
|
|
2214
2442
|
}
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
return input;
|
|
2218
|
-
}
|
|
2219
|
-
function clearObjectShallow(obj) {
|
|
2220
|
-
if (!obj || typeof obj !== 'object') {
|
|
2221
|
-
return {};
|
|
2222
|
-
}
|
|
2223
|
-
const cleaned = {};
|
|
2224
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
2225
|
-
if (value !== null && value !== undefined && value !== '') {
|
|
2226
|
-
cleaned[key] = value;
|
|
2443
|
+
if (updates.debug !== undefined) {
|
|
2444
|
+
this.config.debug = updates.debug;
|
|
2227
2445
|
}
|
|
2228
2446
|
}
|
|
2229
|
-
return cleaned;
|
|
2230
|
-
}
|
|
2231
|
-
function isEmpty(value) {
|
|
2232
|
-
return value === null || value === undefined || value === '';
|
|
2233
|
-
}
|
|
2234
|
-
function hasNonEmptyValues(obj) {
|
|
2235
|
-
if (!obj || typeof obj !== 'object') {
|
|
2236
|
-
return false;
|
|
2237
|
-
}
|
|
2238
|
-
return Object.values(obj).some((value) => !isEmpty(value));
|
|
2239
2447
|
}
|
|
2240
2448
|
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2449
|
+
// Enum options arrays
|
|
2450
|
+
const VAT_RATE_CODE_OPTIONS = [
|
|
2451
|
+
'4.00',
|
|
2452
|
+
'5.00',
|
|
2453
|
+
'10.00',
|
|
2454
|
+
'22.00',
|
|
2455
|
+
'2.00',
|
|
2456
|
+
'6.40',
|
|
2457
|
+
'7.00',
|
|
2458
|
+
'7.30',
|
|
2459
|
+
'7.50',
|
|
2460
|
+
'7.65',
|
|
2461
|
+
'7.95',
|
|
2462
|
+
'8.30',
|
|
2463
|
+
'8.50',
|
|
2464
|
+
'8.80',
|
|
2465
|
+
'9.50',
|
|
2466
|
+
'12.30',
|
|
2467
|
+
'N1',
|
|
2468
|
+
'N2',
|
|
2469
|
+
'N3',
|
|
2470
|
+
'N4',
|
|
2471
|
+
'N5',
|
|
2472
|
+
'N6',
|
|
2473
|
+
];
|
|
2474
|
+
const GOOD_OR_SERVICE_OPTIONS = ['goods', 'service'];
|
|
2475
|
+
const RECEIPT_PROOF_TYPE_OPTIONS = ['POS', 'VR', 'ND'];
|
|
2476
|
+
// Enum types for receipt validation
|
|
2477
|
+
const VatRateCodeSchema = z.enum(VAT_RATE_CODE_OPTIONS);
|
|
2478
|
+
const GoodOrServiceSchema = z.enum(GOOD_OR_SERVICE_OPTIONS);
|
|
2479
|
+
const ReceiptProofTypeSchema = z.enum(RECEIPT_PROOF_TYPE_OPTIONS);
|
|
2480
|
+
// Receipt Item Schema
|
|
2481
|
+
const ReceiptItemSchema = z.object({
|
|
2482
|
+
type: GoodOrServiceSchema.optional(),
|
|
2483
|
+
quantity: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2484
|
+
description: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2485
|
+
unit_price: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2486
|
+
vat_rate_code: VatRateCodeSchema.optional(),
|
|
2487
|
+
simplified_vat_allocation: z.boolean().optional(),
|
|
2488
|
+
discount: z.string().nullable().optional(),
|
|
2489
|
+
is_down_payment_or_voucher_redemption: z.boolean().optional(),
|
|
2490
|
+
complimentary: z.boolean().optional(),
|
|
2491
|
+
});
|
|
2492
|
+
// Main Receipt Input Schema
|
|
2493
|
+
const ReceiptInputSchema = z
|
|
2494
|
+
.object({
|
|
2495
|
+
items: z.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
|
|
2496
|
+
customer_tax_code: z.string().optional(),
|
|
2497
|
+
customer_lottery_code: z.string().optional(),
|
|
2498
|
+
discount: z.string().nullable().optional(),
|
|
2499
|
+
invoice_issuing: z.boolean().optional(),
|
|
2500
|
+
uncollected_dcr_to_ssn: z.boolean().optional(),
|
|
2501
|
+
services_uncollected_amount: z.string().nullable().optional(),
|
|
2502
|
+
goods_uncollected_amount: z.string().nullable().optional(),
|
|
2503
|
+
cash_payment_amount: z.string().nullable().optional(),
|
|
2504
|
+
electronic_payment_amount: z.string().nullable().optional(),
|
|
2505
|
+
ticket_restaurant_payment_amount: z.string().nullable().optional(),
|
|
2506
|
+
ticket_restaurant_quantity: z.number().optional(),
|
|
2507
|
+
})
|
|
2508
|
+
.refine((data) => {
|
|
2509
|
+
// At least one payment method should be provided
|
|
2510
|
+
const hasCashPayment = data.cash_payment_amount && parseFloat(data.cash_payment_amount) > 0;
|
|
2511
|
+
const hasElectronicPayment = data.electronic_payment_amount && parseFloat(data.electronic_payment_amount) > 0;
|
|
2512
|
+
const hasTicketPayment = data.ticket_restaurant_payment_amount &&
|
|
2513
|
+
parseFloat(data.ticket_restaurant_payment_amount) > 0;
|
|
2514
|
+
return hasCashPayment || hasElectronicPayment || hasTicketPayment;
|
|
2515
|
+
}, {
|
|
2516
|
+
error: 'At least one payment method is required',
|
|
2517
|
+
path: ['payment_methods'],
|
|
2518
|
+
})
|
|
2519
|
+
.refine((data) => {
|
|
2520
|
+
// only one between customer_tax_code and customer_lottery_code can be provided
|
|
2521
|
+
return !data.customer_tax_code || !data.customer_lottery_code;
|
|
2522
|
+
}, {
|
|
2523
|
+
error: 'Only one between customer_tax_code and customer_lottery_code can be provided',
|
|
2524
|
+
path: ['customer_tax_code', 'customer_lottery_code'],
|
|
2525
|
+
});
|
|
2526
|
+
// Receipt Return or Void via PEM Schema
|
|
2527
|
+
const ReceiptReturnOrVoidViaPEMInputSchema = z.object({
|
|
2528
|
+
device_id: z.string().optional(),
|
|
2529
|
+
items: z.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
|
|
2530
|
+
document_number: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2531
|
+
document_datetime: z.string().optional(),
|
|
2532
|
+
lottery_code: z.string().optional(),
|
|
2533
|
+
});
|
|
2534
|
+
// Receipt Return or Void with Proof Schema
|
|
2535
|
+
const ReceiptReturnOrVoidWithProofInputSchema = z.object({
|
|
2536
|
+
items: z.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
|
|
2537
|
+
proof: ReceiptProofTypeSchema,
|
|
2538
|
+
document_datetime: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2539
|
+
});
|
|
2540
|
+
// Void Receipt Schema
|
|
2541
|
+
const VoidReceiptInputSchema = z.object({
|
|
2542
|
+
document_number: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2543
|
+
});
|
|
2544
|
+
const ReceiptReturnItemSchema = z
|
|
2545
|
+
.array(z.object({
|
|
2546
|
+
id: z.number(),
|
|
2547
|
+
quantity: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2548
|
+
}))
|
|
2549
|
+
.min(1, { error: 'arrayMin1' });
|
|
2550
|
+
// Receipt Return Schema
|
|
2551
|
+
const ReceiptReturnInputSchema = z.object({
|
|
2552
|
+
items: z.array(ReceiptReturnItemSchema).min(1, { error: 'arrayMin1' }),
|
|
2553
|
+
document_number: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2554
|
+
});
|
|
2555
|
+
|
|
2556
|
+
// Cashier Create Input Schema (MF1)
|
|
2557
|
+
const CashierCreateInputSchema = z.object({
|
|
2558
|
+
email: z
|
|
2559
|
+
.string()
|
|
2560
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2561
|
+
.max(255, { error: 'emailMaxLength' })
|
|
2562
|
+
.email({ error: 'invalidEmail' }),
|
|
2563
|
+
password: z
|
|
2564
|
+
.string()
|
|
2565
|
+
.min(8, { error: 'passwordMinLength' })
|
|
2566
|
+
.max(40, { error: 'passwordMaxLength' }),
|
|
2567
|
+
name: z.string().min(1, { error: 'fieldIsRequired' }).max(255, { error: 'nameMaxLength' }),
|
|
2568
|
+
display_name: z
|
|
2569
|
+
.string()
|
|
2570
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2571
|
+
.max(255, { error: 'displayNameMaxLength' }),
|
|
2572
|
+
});
|
|
2573
|
+
|
|
2574
|
+
// Enum options arrays
|
|
2575
|
+
const PEM_STATUS_OPTIONS = [
|
|
2576
|
+
'NEW',
|
|
2577
|
+
'REGISTERED',
|
|
2578
|
+
'ACTIVATED',
|
|
2579
|
+
'ONLINE',
|
|
2580
|
+
'OFFLINE',
|
|
2581
|
+
'DISCARDED',
|
|
2582
|
+
];
|
|
2583
|
+
// Address Schema (reusable)
|
|
2584
|
+
const AddressSchema = z.object({
|
|
2585
|
+
street_address: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2586
|
+
street_number: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2587
|
+
zip_code: z
|
|
2588
|
+
.string()
|
|
2589
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2590
|
+
.regex(/^\d{5}$/, { error: 'invalidZipCode' }),
|
|
2591
|
+
city: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2592
|
+
province: z
|
|
2593
|
+
.string()
|
|
2594
|
+
.min(2, { error: 'provinceMinLength' })
|
|
2595
|
+
.max(2, { error: 'provinceMaxLength' })
|
|
2596
|
+
.toUpperCase(),
|
|
2597
|
+
});
|
|
2598
|
+
// PEM Status Schema
|
|
2599
|
+
const PEMStatusSchema = z.enum(PEM_STATUS_OPTIONS);
|
|
2600
|
+
// Activation Request Schema
|
|
2601
|
+
const ActivationRequestSchema = z.object({
|
|
2602
|
+
registration_key: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2603
|
+
});
|
|
2604
|
+
// PEM Status Offline Request Schema
|
|
2605
|
+
const PEMStatusOfflineRequestSchema = z.object({
|
|
2606
|
+
timestamp: z
|
|
2607
|
+
.string()
|
|
2608
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2609
|
+
.refine((val) => !isNaN(Date.parse(val)), {
|
|
2610
|
+
error: 'invalidDateFormat',
|
|
2611
|
+
}),
|
|
2612
|
+
reason: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2613
|
+
});
|
|
2614
|
+
|
|
2615
|
+
// Cash Register Create Schema
|
|
2616
|
+
const CashRegisterCreateSchema = z.object({
|
|
2617
|
+
pem_serial_number: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2618
|
+
name: z.string().min(1, { error: 'fieldIsRequired' }).max(100, { error: 'nameMaxLength' }),
|
|
2619
|
+
});
|
|
2620
|
+
|
|
2621
|
+
// VAT number validation regex (Partita IVA - 11 digits)
|
|
2622
|
+
const VAT_NUMBER_REGEX = /^\d{11}$/;
|
|
2623
|
+
// Fiscal code validation regex (Codice Fiscale - 11 digits only for merchants)
|
|
2624
|
+
const FISCAL_CODE_REGEX = /^\d{11}$/;
|
|
2625
|
+
// Password validation regex (from OpenAPI spec)
|
|
2626
|
+
const PASSWORD_REGEX = /^((?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{10,}).*)$/;
|
|
2627
|
+
// Merchant Create Input Schema
|
|
2628
|
+
const MerchantCreateInputSchema = z
|
|
2629
|
+
.object({
|
|
2630
|
+
vat_number: z
|
|
2631
|
+
.string()
|
|
2632
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2633
|
+
.regex(VAT_NUMBER_REGEX, { error: 'invalidVatNumber' }),
|
|
2634
|
+
fiscal_code: z.string().regex(FISCAL_CODE_REGEX, { error: 'invalidFiscalCode' }).optional(),
|
|
2635
|
+
business_name: z.string().max(200, { error: 'businessNameMaxLength' }).optional().nullable(),
|
|
2636
|
+
first_name: z.string().max(100, { error: 'firstNameMaxLength' }).optional().nullable(),
|
|
2637
|
+
last_name: z.string().max(100, { error: 'lastNameMaxLength' }).optional().nullable(),
|
|
2638
|
+
email: z.string().min(1, { error: 'fieldIsRequired' }).email({ error: 'invalidEmail' }),
|
|
2639
|
+
password: z
|
|
2640
|
+
.string()
|
|
2641
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2642
|
+
.regex(PASSWORD_REGEX, { error: 'passwordComplexity' }),
|
|
2643
|
+
address: AddressSchema.optional(),
|
|
2644
|
+
})
|
|
2645
|
+
.refine((data) => {
|
|
2646
|
+
const hasBusinessName = data.business_name && data.business_name.trim() !== '';
|
|
2647
|
+
const hasPersonalNames = (data.first_name && data.first_name.trim() !== '') ||
|
|
2648
|
+
(data.last_name && data.last_name.trim() !== '');
|
|
2649
|
+
// If business name is set, first/last name must not be provided
|
|
2650
|
+
if (hasBusinessName && hasPersonalNames) {
|
|
2651
|
+
return false;
|
|
2299
2652
|
}
|
|
2300
|
-
|
|
2653
|
+
// At least one naming method must be provided
|
|
2654
|
+
if (!hasBusinessName && !hasPersonalNames) {
|
|
2301
2655
|
return false;
|
|
2302
2656
|
}
|
|
2303
|
-
|
|
2657
|
+
return true;
|
|
2658
|
+
}, {
|
|
2659
|
+
error: 'businessNameOrPersonalNamesRequired',
|
|
2660
|
+
path: ['business_name'],
|
|
2661
|
+
});
|
|
2662
|
+
// Merchant Update Input Schema
|
|
2663
|
+
const MerchantUpdateInputSchema = z.object({
|
|
2664
|
+
business_name: z.string().max(200, { error: 'businessNameMaxLength' }).optional().nullable(),
|
|
2665
|
+
first_name: z.string().max(100, { error: 'firstNameMaxLength' }).optional().nullable(),
|
|
2666
|
+
last_name: z.string().max(100, { error: 'lastNameMaxLength' }).optional().nullable(),
|
|
2667
|
+
address: AddressSchema.optional().nullable(),
|
|
2668
|
+
});
|
|
2304
2669
|
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2670
|
+
// Enum options arrays
|
|
2671
|
+
const PEM_TYPE_OPTIONS = ['AP', 'SP', 'TM', 'PV'];
|
|
2672
|
+
// PEM Data Schema
|
|
2673
|
+
const PemDataSchema = z.object({
|
|
2674
|
+
version: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
2675
|
+
type: z.enum(PEM_TYPE_OPTIONS, {
|
|
2676
|
+
error: 'invalidPemType',
|
|
2677
|
+
}),
|
|
2678
|
+
});
|
|
2679
|
+
// PEM Create Input Schema
|
|
2680
|
+
const PemCreateInputSchema = z.object({
|
|
2681
|
+
merchant_uuid: z.string().min(1, { error: 'fieldIsRequired' }).uuid({ error: 'invalidUuid' }),
|
|
2682
|
+
address: AddressSchema.optional(),
|
|
2683
|
+
/* external_pem_data: PemDataSchema.optional(), */
|
|
2684
|
+
});
|
|
2308
2685
|
|
|
2309
|
-
|
|
2686
|
+
// Italian Fiscal ID validation regex (Codice Fiscale for individuals or Partita IVA for companies)
|
|
2687
|
+
const FISCAL_ID_REGEX = /^([A-Z]{6}[0-9LMNPQRSTUV]{2}[ABCDEHLMPRST][0-9LMNPQRSTUV]{2}[A-Z][0-9LMNPQRSTUV]{3}[A-Z]|[0-9]{11})$/;
|
|
2688
|
+
// Supplier Create Input Schema
|
|
2689
|
+
const SupplierCreateInputSchema = z.object({
|
|
2690
|
+
fiscal_id: z
|
|
2691
|
+
.string()
|
|
2692
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2693
|
+
.regex(FISCAL_ID_REGEX, { error: 'invalidFiscalId' })
|
|
2694
|
+
.toUpperCase(),
|
|
2695
|
+
name: z.string().min(1, { error: 'fieldIsRequired' }).max(200, { error: 'nameMaxLength' }),
|
|
2696
|
+
address: AddressSchema.optional(),
|
|
2697
|
+
});
|
|
2698
|
+
// Supplier Update Input Schema
|
|
2699
|
+
const SupplierUpdateInputSchema = z.object({
|
|
2700
|
+
name: z.string().min(1, { error: 'fieldIsRequired' }).max(200, { error: 'nameMaxLength' }),
|
|
2701
|
+
address: AddressSchema.optional(),
|
|
2702
|
+
});
|
|
2310
2703
|
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2704
|
+
// Journal Close Input Schema
|
|
2705
|
+
const JournalCloseInputSchema = z.object({
|
|
2706
|
+
closing_timestamp: z
|
|
2707
|
+
.string()
|
|
2708
|
+
.min(1, { error: 'fieldIsRequired' })
|
|
2709
|
+
.refine((val) => !isNaN(Date.parse(val)), {
|
|
2710
|
+
error: 'invalidDateFormat',
|
|
2711
|
+
}),
|
|
2712
|
+
reason: z.string().max(255, { error: 'reasonMaxLength' }).optional(),
|
|
2713
|
+
});
|
|
2315
2714
|
|
|
2316
|
-
|
|
2317
|
-
|
|
2715
|
+
// Daily Report Status Options
|
|
2716
|
+
const DAILY_REPORT_STATUS_OPTIONS = ['pending', 'sent', 'error'];
|
|
2717
|
+
// Daily Report Status Schema
|
|
2718
|
+
const DailyReportStatusSchema = z.enum(DAILY_REPORT_STATUS_OPTIONS, {
|
|
2719
|
+
error: 'invalidDailyReportStatus',
|
|
2720
|
+
});
|
|
2721
|
+
// Daily Reports List Parameters Schema
|
|
2722
|
+
const DailyReportsParamsSchema = z.object({
|
|
2723
|
+
pem_serial_number: z.string().min(1, { error: 'fieldIsRequired' }).optional(),
|
|
2724
|
+
date_from: z
|
|
2725
|
+
.string()
|
|
2726
|
+
.refine((val) => !isNaN(Date.parse(val)), {
|
|
2727
|
+
error: 'invalidDateFormat',
|
|
2728
|
+
})
|
|
2729
|
+
.optional(),
|
|
2730
|
+
date_to: z
|
|
2731
|
+
.string()
|
|
2732
|
+
.refine((val) => !isNaN(Date.parse(val)), {
|
|
2733
|
+
error: 'invalidDateFormat',
|
|
2734
|
+
})
|
|
2735
|
+
.optional(),
|
|
2736
|
+
status: DailyReportStatusSchema.optional(),
|
|
2737
|
+
page: z.number().min(1, { error: 'pageMinValue' }).optional(),
|
|
2738
|
+
});
|
|
2739
|
+
|
|
2740
|
+
const NotificationScopeSchema = z.object({
|
|
2741
|
+
type: z.literal('global'),
|
|
2742
|
+
});
|
|
2743
|
+
const PemStatusSchema = z.enum(['ONLINE', 'OFFLINE']);
|
|
2744
|
+
const NotificationDataBlockAtSchema = z.object({
|
|
2745
|
+
block_at: z.string(),
|
|
2746
|
+
});
|
|
2747
|
+
const NotificationDataPemStatusSchema = z.object({
|
|
2748
|
+
from: PemStatusSchema,
|
|
2749
|
+
to: PemStatusSchema,
|
|
2750
|
+
});
|
|
2751
|
+
const NotificationBaseSchema = z.object({
|
|
2752
|
+
uuid: z.string().uuid({ error: 'invalidUuid' }),
|
|
2753
|
+
scope: NotificationScopeSchema,
|
|
2754
|
+
source: z.enum(['system', 'Italian Tax Authority']),
|
|
2755
|
+
level: z.enum(['info', 'warning', 'error', 'critical']),
|
|
2756
|
+
created_at: z.string(),
|
|
2757
|
+
});
|
|
2758
|
+
const NotificationMf2UnreachableSchema = NotificationBaseSchema.extend({
|
|
2759
|
+
type: z.literal('INTERNAL_COMMUNICATION_FAILURE'),
|
|
2760
|
+
code: z.literal('SYS-W-01'),
|
|
2761
|
+
data: NotificationDataBlockAtSchema,
|
|
2762
|
+
});
|
|
2763
|
+
const NotificationPemsBlockedSchema = NotificationBaseSchema.extend({
|
|
2764
|
+
type: z.literal('PEM_STATUS_CHANGED'),
|
|
2765
|
+
code: z.literal('SYS-C-01'),
|
|
2766
|
+
data: NotificationDataPemStatusSchema,
|
|
2767
|
+
});
|
|
2768
|
+
const NotificationPemBackOnlineSchema = NotificationBaseSchema.extend({
|
|
2769
|
+
type: z.literal('PEM_STATUS_CHANGED'),
|
|
2770
|
+
code: z.literal('SYS-I-01'),
|
|
2771
|
+
data: NotificationDataPemStatusSchema,
|
|
2772
|
+
});
|
|
2773
|
+
const NotificationSchema = z.discriminatedUnion('code', [
|
|
2774
|
+
NotificationMf2UnreachableSchema,
|
|
2775
|
+
NotificationPemsBlockedSchema,
|
|
2776
|
+
NotificationPemBackOnlineSchema,
|
|
2777
|
+
]);
|
|
2778
|
+
const NotificationListResponseSchema = z.object({
|
|
2779
|
+
members: z.array(NotificationSchema),
|
|
2780
|
+
});
|
|
2781
|
+
|
|
2782
|
+
const TelemetryMerchantSchema = z.object({
|
|
2783
|
+
vat_number: z.string(),
|
|
2784
|
+
fiscal_code: z.string().nullable(),
|
|
2785
|
+
business_name: z.string(),
|
|
2786
|
+
});
|
|
2787
|
+
const TelemetrySupplierSchema = z.object({
|
|
2788
|
+
vat_number: z.string(),
|
|
2789
|
+
fiscal_code: z.string().nullable(),
|
|
2790
|
+
business_name: z.string(),
|
|
2791
|
+
});
|
|
2792
|
+
const TelemetrySoftwareVersionSchema = z.object({
|
|
2793
|
+
version: z.string(),
|
|
2794
|
+
swid: z.string(),
|
|
2795
|
+
installed_at: z.string(),
|
|
2796
|
+
status: z.enum(['active', 'inactive', 'archived']),
|
|
2797
|
+
});
|
|
2798
|
+
const TelemetrySoftwareSchema = z.object({
|
|
2799
|
+
code: z.string(),
|
|
2800
|
+
name: z.string(),
|
|
2801
|
+
approval_reference: z.string(),
|
|
2802
|
+
version_info: TelemetrySoftwareVersionSchema,
|
|
2803
|
+
});
|
|
2804
|
+
const PendingReceiptsSchema = z.object({
|
|
2805
|
+
count: z.number().int().nonnegative(),
|
|
2806
|
+
total_amount: z.string(),
|
|
2807
|
+
});
|
|
2808
|
+
const TransmissionSchema = z.object({
|
|
2809
|
+
attempted_at: z.string(),
|
|
2810
|
+
outcome: z.enum(['success', 'failed', 'pending']),
|
|
2811
|
+
});
|
|
2812
|
+
const MessageSchema = z.object({
|
|
2813
|
+
received_at: z.string(),
|
|
2814
|
+
content: z.string(),
|
|
2815
|
+
});
|
|
2816
|
+
const LotterySecretRequestSchema = z.object({
|
|
2817
|
+
requested_at: z.string(),
|
|
2818
|
+
outcome: z.enum(['success', 'failed', 'pending']),
|
|
2819
|
+
});
|
|
2820
|
+
const LotterySchema = z.object({
|
|
2821
|
+
last_transmission: TransmissionSchema,
|
|
2822
|
+
secret_request: LotterySecretRequestSchema,
|
|
2823
|
+
});
|
|
2824
|
+
const TelemetrySchema = z.object({
|
|
2825
|
+
pem_id: z.string(),
|
|
2826
|
+
pem_status: z.enum(['ONLINE', 'OFFLINE', 'ERROR']),
|
|
2827
|
+
pem_status_changed_at: z.string(),
|
|
2828
|
+
merchant: TelemetryMerchantSchema,
|
|
2829
|
+
supplier: TelemetrySupplierSchema,
|
|
2830
|
+
software: TelemetrySoftwareSchema,
|
|
2831
|
+
last_communication_at: z.string(),
|
|
2832
|
+
pending_receipts: PendingReceiptsSchema,
|
|
2833
|
+
last_receipt_transmission: TransmissionSchema,
|
|
2834
|
+
last_message_from_mf2: MessageSchema,
|
|
2835
|
+
ade_corrispettivi_transmission: TransmissionSchema,
|
|
2836
|
+
last_message_from_ade: MessageSchema,
|
|
2837
|
+
lottery: LotterySchema,
|
|
2838
|
+
});
|
|
2318
2839
|
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2840
|
+
// Receipt schemas and types
|
|
2841
|
+
// Common validation utilities
|
|
2842
|
+
const ValidationMessages = {
|
|
2843
|
+
fieldIsRequired: 'This field is required',
|
|
2844
|
+
arrayMin1: 'At least one item is required',
|
|
2845
|
+
paymentMethodRequired: 'At least one payment method is required',
|
|
2846
|
+
invalidEmail: 'Please enter a valid email address',
|
|
2847
|
+
passwordMinLength: 'Password must be at least 8 characters long',
|
|
2848
|
+
passwordComplexity: 'Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character',
|
|
2849
|
+
invalidZipCode: 'Please enter a valid 5-digit zip code',
|
|
2850
|
+
provinceMinLength: 'Province code must be 2 characters',
|
|
2851
|
+
provinceMaxLength: 'Province code must be 2 characters',
|
|
2852
|
+
invalidDateFormat: 'Please enter a valid date',
|
|
2853
|
+
nameMaxLength: 'Name is too long',
|
|
2854
|
+
invalidFiscalId: 'Please enter a valid Italian fiscal ID (Codice Fiscale or Partita IVA)',
|
|
2855
|
+
invalidVatNumber: 'Please enter a valid VAT number (11 digits)',
|
|
2856
|
+
invalidFiscalCode: 'Please enter a valid fiscal code (11 digits)',
|
|
2857
|
+
businessNameMaxLength: 'Business name is too long (max 200 characters)',
|
|
2858
|
+
businessNameOrPersonalNamesRequired: 'Please provide either a business name or first/last name, but not both',
|
|
2859
|
+
firstNameMaxLength: 'First name is too long (max 100 characters)',
|
|
2860
|
+
lastNameMaxLength: 'Last name is too long (max 100 characters)',
|
|
2861
|
+
invalidUuid: 'Please enter a valid UUID',
|
|
2862
|
+
invalidPemType: 'PEM type must be one of: AP, SP, TM, PV',
|
|
2863
|
+
reasonMaxLength: 'Reason is too long (max 255 characters)',
|
|
2864
|
+
pageMinValue: 'Page number must be at least 1',
|
|
2865
|
+
invalidDailyReportStatus: 'Daily report status must be one of: pending, sent, error',
|
|
2866
|
+
displayNameMaxLength: 'Display name is too long (max 255 characters)',
|
|
2346
2867
|
};
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
return
|
|
2868
|
+
// Validation helper functions
|
|
2869
|
+
const validateInput = (schema, data) => {
|
|
2870
|
+
const result = schema.safeParse(data);
|
|
2871
|
+
if (!result.success) {
|
|
2872
|
+
const errors = result.error.issues.map((error) => ({
|
|
2873
|
+
field: error.path.join('.'),
|
|
2874
|
+
message: error.message,
|
|
2875
|
+
code: error.code,
|
|
2876
|
+
}));
|
|
2877
|
+
return {
|
|
2878
|
+
success: false,
|
|
2879
|
+
errors,
|
|
2880
|
+
data: null,
|
|
2881
|
+
};
|
|
2357
2882
|
}
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2883
|
+
return {
|
|
2884
|
+
success: true,
|
|
2885
|
+
errors: [],
|
|
2886
|
+
data: result.data,
|
|
2887
|
+
};
|
|
2888
|
+
};
|
|
2889
|
+
|
|
2890
|
+
class ACubeSDKError extends Error {
|
|
2891
|
+
constructor(type, message, originalError, statusCode, violations) {
|
|
2892
|
+
super(message);
|
|
2893
|
+
this.type = type;
|
|
2894
|
+
this.originalError = originalError;
|
|
2895
|
+
this.statusCode = statusCode;
|
|
2896
|
+
this.name = 'ACubeSDKError';
|
|
2897
|
+
this.violations = violations;
|
|
2361
2898
|
}
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
const log$g = createPrefixedLogger('AUTH-STRATEGY');
|
|
2902
|
+
class AuthStrategy {
|
|
2903
|
+
constructor(jwtHandler, mtlsHandler, userProvider, mtlsAdapter) {
|
|
2904
|
+
this.jwtHandler = jwtHandler;
|
|
2905
|
+
this.mtlsHandler = mtlsHandler;
|
|
2906
|
+
this.userProvider = userProvider;
|
|
2907
|
+
this.mtlsAdapter = mtlsAdapter;
|
|
2366
2908
|
}
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
return JSON.stringify(data, null, 2);
|
|
2909
|
+
async determineAuthConfig(url, method, explicitMode) {
|
|
2910
|
+
if (this.isNotificationEndpoint(url) || this.isTelemetryEndpoint(url)) {
|
|
2911
|
+
return { mode: 'mtls', usePort444: true };
|
|
2371
2912
|
}
|
|
2372
|
-
|
|
2373
|
-
|
|
2913
|
+
const platform = this.detectPlatform();
|
|
2914
|
+
const userRole = await this.getUserRole();
|
|
2915
|
+
const isReceiptEndpoint = this.isReceiptEndpoint(url);
|
|
2916
|
+
log$g.debug('Determining auth config', {
|
|
2917
|
+
url,
|
|
2918
|
+
method,
|
|
2919
|
+
platform,
|
|
2920
|
+
userRole,
|
|
2921
|
+
isReceiptEndpoint,
|
|
2922
|
+
explicitMode,
|
|
2923
|
+
});
|
|
2924
|
+
if (userRole === 'SUPPLIER') {
|
|
2925
|
+
return { mode: 'jwt', usePort444: false };
|
|
2374
2926
|
}
|
|
2927
|
+
if (userRole === 'CASHIER') {
|
|
2928
|
+
if (!isReceiptEndpoint) {
|
|
2929
|
+
return { mode: 'jwt', usePort444: false };
|
|
2930
|
+
}
|
|
2931
|
+
if (platform === 'mobile') {
|
|
2932
|
+
return { mode: 'mtls', usePort444: true };
|
|
2933
|
+
}
|
|
2934
|
+
return { mode: 'jwt', usePort444: true };
|
|
2935
|
+
}
|
|
2936
|
+
if (userRole === 'MERCHANT') {
|
|
2937
|
+
if (!isReceiptEndpoint) {
|
|
2938
|
+
return { mode: 'jwt', usePort444: false };
|
|
2939
|
+
}
|
|
2940
|
+
if (this.isReturnableItemsEndpoint(url)) {
|
|
2941
|
+
return { mode: 'mtls', usePort444: true };
|
|
2942
|
+
}
|
|
2943
|
+
if (method === 'GET') {
|
|
2944
|
+
if (this.isDetailedReceiptEndpoint(url)) {
|
|
2945
|
+
if (platform === 'mobile') {
|
|
2946
|
+
return { mode: 'mtls', usePort444: true };
|
|
2947
|
+
}
|
|
2948
|
+
return { mode: 'jwt', usePort444: true };
|
|
2949
|
+
}
|
|
2950
|
+
return { mode: 'jwt', usePort444: false };
|
|
2951
|
+
}
|
|
2952
|
+
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {
|
|
2953
|
+
if (platform === 'mobile') {
|
|
2954
|
+
return { mode: 'mtls', usePort444: true };
|
|
2955
|
+
}
|
|
2956
|
+
return { mode: 'jwt', usePort444: true };
|
|
2957
|
+
}
|
|
2958
|
+
return { mode: 'jwt', usePort444: false };
|
|
2959
|
+
}
|
|
2960
|
+
if (explicitMode) {
|
|
2961
|
+
if (userRole === 'SUPPLIER' && explicitMode === 'mtls') {
|
|
2962
|
+
return { mode: 'jwt', usePort444: false };
|
|
2963
|
+
}
|
|
2964
|
+
return {
|
|
2965
|
+
mode: explicitMode,
|
|
2966
|
+
usePort444: explicitMode === 'mtls' || (platform === 'web' && isReceiptEndpoint),
|
|
2967
|
+
};
|
|
2968
|
+
}
|
|
2969
|
+
if (platform === 'web') {
|
|
2970
|
+
return { mode: 'jwt', usePort444: isReceiptEndpoint };
|
|
2971
|
+
}
|
|
2972
|
+
if (isReceiptEndpoint && platform === 'mobile') {
|
|
2973
|
+
return { mode: 'mtls', usePort444: true };
|
|
2974
|
+
}
|
|
2975
|
+
return { mode: 'jwt', usePort444: false };
|
|
2375
2976
|
}
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
constructor() {
|
|
2379
|
-
this.enabled = false;
|
|
2380
|
-
}
|
|
2381
|
-
setEnabled(enabled) {
|
|
2382
|
-
this.enabled = enabled;
|
|
2383
|
-
}
|
|
2384
|
-
isEnabled() {
|
|
2385
|
-
return this.enabled;
|
|
2386
|
-
}
|
|
2387
|
-
debug(prefix, message, data) {
|
|
2388
|
-
if (!this.enabled)
|
|
2389
|
-
return;
|
|
2390
|
-
this.log('debug', prefix, message, data);
|
|
2977
|
+
async getAuthHeaders() {
|
|
2978
|
+
return this.jwtHandler.getAuthHeaders();
|
|
2391
2979
|
}
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
return;
|
|
2395
|
-
this.log('info', prefix, message, data);
|
|
2980
|
+
getMtlsHandler() {
|
|
2981
|
+
return this.mtlsHandler;
|
|
2396
2982
|
}
|
|
2397
|
-
|
|
2398
|
-
this.
|
|
2983
|
+
getJwtHandler() {
|
|
2984
|
+
return this.jwtHandler;
|
|
2399
2985
|
}
|
|
2400
|
-
|
|
2401
|
-
this.
|
|
2986
|
+
detectPlatform() {
|
|
2987
|
+
if (!this.mtlsAdapter) {
|
|
2988
|
+
return 'web';
|
|
2989
|
+
}
|
|
2990
|
+
const platformInfo = this.mtlsAdapter.getPlatformInfo();
|
|
2991
|
+
return platformInfo.platform === 'web' ? 'web' : 'mobile';
|
|
2402
2992
|
}
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
const isoTime = new Date().toISOString().split('T')[1] ?? '';
|
|
2407
|
-
const timestamp = pc.dim(isoTime.slice(0, 12));
|
|
2408
|
-
const formattedPrefix = colors.prefix(`[${prefix}]`);
|
|
2409
|
-
const formattedMessage = colors.message(message);
|
|
2410
|
-
const consoleMethod = level === 'debug'
|
|
2411
|
-
? console.debug
|
|
2412
|
-
: level === 'info'
|
|
2413
|
-
? console.info
|
|
2414
|
-
: level === 'warn'
|
|
2415
|
-
? console.warn
|
|
2416
|
-
: console.error;
|
|
2417
|
-
const header = `${timestamp} ${icon} ${formattedPrefix} ${formattedMessage}`;
|
|
2418
|
-
if (data !== undefined) {
|
|
2419
|
-
const formattedData = formatData(data, level);
|
|
2420
|
-
// Check if data is an object (multi-line) or primitive (single-line)
|
|
2421
|
-
const isMultiLine = typeof data === 'object' && data !== null;
|
|
2422
|
-
if (isMultiLine) {
|
|
2423
|
-
consoleMethod(`${header}\n${formattedData}`);
|
|
2424
|
-
}
|
|
2425
|
-
else {
|
|
2426
|
-
consoleMethod(`${header}`, formattedData);
|
|
2427
|
-
}
|
|
2993
|
+
async getUserRole() {
|
|
2994
|
+
if (!this.userProvider) {
|
|
2995
|
+
return null;
|
|
2428
2996
|
}
|
|
2429
|
-
|
|
2430
|
-
|
|
2997
|
+
const user = await this.userProvider.getCurrentUser();
|
|
2998
|
+
if (!user || !user.roles) {
|
|
2999
|
+
return null;
|
|
3000
|
+
}
|
|
3001
|
+
if (hasRole(user.roles, 'ROLE_SUPPLIER')) {
|
|
3002
|
+
return 'SUPPLIER';
|
|
3003
|
+
}
|
|
3004
|
+
if (hasRole(user.roles, 'ROLE_MERCHANT')) {
|
|
3005
|
+
return 'MERCHANT';
|
|
3006
|
+
}
|
|
3007
|
+
if (hasRole(user.roles, 'ROLE_CASHIER')) {
|
|
3008
|
+
return 'CASHIER';
|
|
2431
3009
|
}
|
|
3010
|
+
return null;
|
|
3011
|
+
}
|
|
3012
|
+
isReceiptEndpoint(url) {
|
|
3013
|
+
return url.includes('/receipts') || url.includes('/mf1/receipts');
|
|
3014
|
+
}
|
|
3015
|
+
isReturnableItemsEndpoint(url) {
|
|
3016
|
+
return !!(url.match(/\/receipts\/[a-f0-9-]+\/returnable-items$/) ||
|
|
3017
|
+
url.match(/\/mf1\/receipts\/[a-f0-9-]+\/returnable-items$/));
|
|
3018
|
+
}
|
|
3019
|
+
isDetailedReceiptEndpoint(url) {
|
|
3020
|
+
return !!(url.match(/\/receipts\/[a-f0-9-]+\/details$/) ||
|
|
3021
|
+
url.match(/\/mf1\/receipts\/[a-f0-9-]+\/details$/));
|
|
3022
|
+
}
|
|
3023
|
+
isNotificationEndpoint(url) {
|
|
3024
|
+
return url.includes('/mf1/notifications');
|
|
3025
|
+
}
|
|
3026
|
+
isTelemetryEndpoint(url) {
|
|
3027
|
+
return !!url.match(/\/mf1\/pems\/[^/]+\/telemetry/);
|
|
2432
3028
|
}
|
|
2433
|
-
}
|
|
2434
|
-
const logger = new Logger();
|
|
2435
|
-
function createPrefixedLogger(prefix) {
|
|
2436
|
-
return {
|
|
2437
|
-
debug: (message, data) => logger.debug(prefix, message, data),
|
|
2438
|
-
info: (message, data) => logger.info(prefix, message, data),
|
|
2439
|
-
warn: (message, data) => logger.warn(prefix, message, data),
|
|
2440
|
-
error: (message, data) => logger.error(prefix, message, data),
|
|
2441
|
-
};
|
|
2442
3029
|
}
|
|
2443
3030
|
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
return value;
|
|
2459
|
-
return num.toFixed(decimals);
|
|
3031
|
+
class JwtAuthHandler {
|
|
3032
|
+
constructor(tokenStorage) {
|
|
3033
|
+
this.tokenStorage = tokenStorage;
|
|
3034
|
+
}
|
|
3035
|
+
async getAuthConfig(_url, _method) {
|
|
3036
|
+
return { mode: 'jwt', usePort444: false };
|
|
3037
|
+
}
|
|
3038
|
+
async getAuthHeaders() {
|
|
3039
|
+
const token = await this.tokenStorage.getAccessToken();
|
|
3040
|
+
if (!token) {
|
|
3041
|
+
return {};
|
|
3042
|
+
}
|
|
3043
|
+
return { Authorization: `Bearer ${token}` };
|
|
3044
|
+
}
|
|
2460
3045
|
}
|
|
2461
3046
|
|
|
2462
3047
|
const log$f = createPrefixedLogger('MTLS-HANDLER');
|
|
@@ -5989,92 +6574,6 @@ function createACubeMTLSConfig(baseUrl, timeout, autoInitialize = true, forcePor
|
|
|
5989
6574
|
};
|
|
5990
6575
|
}
|
|
5991
6576
|
|
|
5992
|
-
class ConfigManager {
|
|
5993
|
-
constructor(userConfig) {
|
|
5994
|
-
this.config = this.buildConfig(userConfig);
|
|
5995
|
-
}
|
|
5996
|
-
buildConfig(userConfig) {
|
|
5997
|
-
return {
|
|
5998
|
-
environment: userConfig.environment,
|
|
5999
|
-
apiUrl: this.getDefaultApiUrl(userConfig.environment),
|
|
6000
|
-
authUrl: this.getDefaultAuthUrl(userConfig.environment),
|
|
6001
|
-
timeout: 30000,
|
|
6002
|
-
retryAttempts: 3,
|
|
6003
|
-
debug: userConfig.debug ?? false,
|
|
6004
|
-
customHeaders: {},
|
|
6005
|
-
};
|
|
6006
|
-
}
|
|
6007
|
-
getDefaultApiUrl(environment) {
|
|
6008
|
-
switch (environment) {
|
|
6009
|
-
case 'production':
|
|
6010
|
-
return 'https://ereceipts-it.acubeapi.com';
|
|
6011
|
-
case 'development':
|
|
6012
|
-
return 'https://ereceipts-it.dev.acubeapi.com';
|
|
6013
|
-
case 'sandbox':
|
|
6014
|
-
default:
|
|
6015
|
-
return 'https://ereceipts-it-sandbox.acubeapi.com';
|
|
6016
|
-
}
|
|
6017
|
-
}
|
|
6018
|
-
getDefaultAuthUrl(environment) {
|
|
6019
|
-
switch (environment) {
|
|
6020
|
-
case 'production':
|
|
6021
|
-
return 'https://common.api.acubeapi.com';
|
|
6022
|
-
case 'development':
|
|
6023
|
-
case 'sandbox':
|
|
6024
|
-
default:
|
|
6025
|
-
return 'https://common-sandbox.api.acubeapi.com';
|
|
6026
|
-
}
|
|
6027
|
-
}
|
|
6028
|
-
getConfig() {
|
|
6029
|
-
return {
|
|
6030
|
-
environment: this.config.environment,
|
|
6031
|
-
debug: this.config.debug,
|
|
6032
|
-
};
|
|
6033
|
-
}
|
|
6034
|
-
getApiUrl() {
|
|
6035
|
-
return this.config.apiUrl;
|
|
6036
|
-
}
|
|
6037
|
-
getAuthUrl() {
|
|
6038
|
-
return this.config.authUrl;
|
|
6039
|
-
}
|
|
6040
|
-
getEnvironment() {
|
|
6041
|
-
return this.config.environment;
|
|
6042
|
-
}
|
|
6043
|
-
isDebugEnabled() {
|
|
6044
|
-
return this.config.debug;
|
|
6045
|
-
}
|
|
6046
|
-
getTimeout() {
|
|
6047
|
-
return this.config.timeout;
|
|
6048
|
-
}
|
|
6049
|
-
getRetryAttempts() {
|
|
6050
|
-
return this.config.retryAttempts;
|
|
6051
|
-
}
|
|
6052
|
-
getCustomHeaders() {
|
|
6053
|
-
return { ...this.config.customHeaders };
|
|
6054
|
-
}
|
|
6055
|
-
updateConfig(updates) {
|
|
6056
|
-
if (updates.environment) {
|
|
6057
|
-
this.config.environment = updates.environment;
|
|
6058
|
-
this.config.apiUrl = this.getDefaultApiUrl(updates.environment);
|
|
6059
|
-
this.config.authUrl = this.getDefaultAuthUrl(updates.environment);
|
|
6060
|
-
}
|
|
6061
|
-
if (updates.debug !== undefined) {
|
|
6062
|
-
this.config.debug = updates.debug;
|
|
6063
|
-
}
|
|
6064
|
-
}
|
|
6065
|
-
}
|
|
6066
|
-
|
|
6067
|
-
class ACubeSDKError extends Error {
|
|
6068
|
-
constructor(type, message, originalError, statusCode, violations) {
|
|
6069
|
-
super(message);
|
|
6070
|
-
this.type = type;
|
|
6071
|
-
this.originalError = originalError;
|
|
6072
|
-
this.statusCode = statusCode;
|
|
6073
|
-
this.name = 'ACubeSDKError';
|
|
6074
|
-
this.violations = violations;
|
|
6075
|
-
}
|
|
6076
|
-
}
|
|
6077
|
-
|
|
6078
6577
|
const DI_TOKENS = {
|
|
6079
6578
|
HTTP_PORT: Symbol('HTTP_PORT'),
|
|
6080
6579
|
BASE_HTTP_PORT: Symbol('BASE_HTTP_PORT'),
|
|
@@ -7500,7 +7999,11 @@ class CachingHttpDecorator {
|
|
|
7500
7999
|
return response;
|
|
7501
8000
|
}
|
|
7502
8001
|
setAuthToken(token) {
|
|
7503
|
-
log$3.debug('
|
|
8002
|
+
log$3.debug('CachingHttpDecorator.setAuthToken called:', {
|
|
8003
|
+
hasToken: !!token,
|
|
8004
|
+
tokenPrefix: token?.substring(0, 20),
|
|
8005
|
+
underlyingHttpType: this.http.constructor.name,
|
|
8006
|
+
});
|
|
7504
8007
|
this.authToken = token;
|
|
7505
8008
|
this.http.setAuthToken(token);
|
|
7506
8009
|
}
|
|
@@ -7533,7 +8036,8 @@ class CachingHttpDecorator {
|
|
|
7533
8036
|
}
|
|
7534
8037
|
}
|
|
7535
8038
|
|
|
7536
|
-
const
|
|
8039
|
+
const logJwt = createPrefixedLogger('HTTP-JWT');
|
|
8040
|
+
const logMtls = createPrefixedLogger('HTTP-MTLS');
|
|
7537
8041
|
class AxiosHttpAdapter {
|
|
7538
8042
|
constructor(config) {
|
|
7539
8043
|
this.authToken = null;
|
|
@@ -7551,23 +8055,30 @@ class AxiosHttpAdapter {
|
|
|
7551
8055
|
}
|
|
7552
8056
|
setMTLSAdapter(adapter) {
|
|
7553
8057
|
this.mtlsAdapter = adapter;
|
|
7554
|
-
|
|
8058
|
+
logMtls.debug('mTLS adapter configured:', !!adapter);
|
|
7555
8059
|
}
|
|
7556
8060
|
setAuthStrategy(strategy) {
|
|
7557
8061
|
this.authStrategy = strategy;
|
|
7558
|
-
|
|
8062
|
+
logJwt.debug('Auth strategy configured:', !!strategy);
|
|
7559
8063
|
}
|
|
7560
8064
|
async shouldUseMTLS(url, method) {
|
|
7561
8065
|
if (!this.mtlsAdapter) {
|
|
8066
|
+
logJwt.debug(`No mTLS adapter, using JWT for ${method} ${url}`);
|
|
7562
8067
|
return false;
|
|
7563
8068
|
}
|
|
7564
8069
|
if (this.authStrategy) {
|
|
7565
8070
|
const config = await this.authStrategy.determineAuthConfig(url, method);
|
|
7566
|
-
|
|
8071
|
+
const logger = config.mode === 'mtls' ? logMtls : logJwt;
|
|
8072
|
+
logger.debug(`Auth config for ${method} ${url}:`, config);
|
|
7567
8073
|
return config.mode === 'mtls';
|
|
7568
8074
|
}
|
|
7569
8075
|
// Fallback: use mTLS for mf1/mf2 endpoints if no strategy
|
|
7570
|
-
|
|
8076
|
+
// This should rarely happen - only before SDK is fully initialized
|
|
8077
|
+
const useMtls = url.startsWith('/mf1') || url.startsWith('/mf2');
|
|
8078
|
+
if (useMtls) {
|
|
8079
|
+
logMtls.warn(`No auth strategy set, falling back to mTLS for ${method} ${url}`);
|
|
8080
|
+
}
|
|
8081
|
+
return useMtls;
|
|
7571
8082
|
}
|
|
7572
8083
|
async makeMTLSRequest(url, method, data, config) {
|
|
7573
8084
|
if (!this.mtlsAdapter) {
|
|
@@ -7580,10 +8091,10 @@ class AxiosHttpAdapter {
|
|
|
7580
8091
|
};
|
|
7581
8092
|
if (this.authToken) {
|
|
7582
8093
|
headers['Authorization'] = `Bearer ${this.authToken}`;
|
|
7583
|
-
|
|
8094
|
+
logMtls.debug('JWT token present for mTLS request');
|
|
7584
8095
|
}
|
|
7585
8096
|
else {
|
|
7586
|
-
|
|
8097
|
+
logMtls.warn('No JWT token for mTLS request');
|
|
7587
8098
|
}
|
|
7588
8099
|
const mtlsConfig = {
|
|
7589
8100
|
url: fullUrl,
|
|
@@ -7592,15 +8103,15 @@ class AxiosHttpAdapter {
|
|
|
7592
8103
|
data,
|
|
7593
8104
|
timeout: config?.timeout,
|
|
7594
8105
|
};
|
|
7595
|
-
|
|
8106
|
+
logMtls.debug(`mTLS ${method} ${fullUrl}`);
|
|
7596
8107
|
if (data) {
|
|
7597
|
-
|
|
8108
|
+
logMtls.debug('Request body:', data);
|
|
7598
8109
|
}
|
|
7599
8110
|
try {
|
|
7600
8111
|
const response = await this.mtlsAdapter.request(mtlsConfig);
|
|
7601
|
-
|
|
8112
|
+
logMtls.debug(`mTLS Response ${response.status} from ${fullUrl}`);
|
|
7602
8113
|
if (response.data) {
|
|
7603
|
-
|
|
8114
|
+
logMtls.debug('Response body:', response.data);
|
|
7604
8115
|
}
|
|
7605
8116
|
return {
|
|
7606
8117
|
data: response.data,
|
|
@@ -7609,11 +8120,11 @@ class AxiosHttpAdapter {
|
|
|
7609
8120
|
};
|
|
7610
8121
|
}
|
|
7611
8122
|
catch (error) {
|
|
7612
|
-
|
|
8123
|
+
logMtls.error(`mTLS Response error from ${fullUrl}:`, error);
|
|
7613
8124
|
if (error && typeof error === 'object' && 'response' in error) {
|
|
7614
8125
|
const axiosError = error;
|
|
7615
8126
|
if (axiosError.response?.data) {
|
|
7616
|
-
|
|
8127
|
+
logMtls.error('Response body:', axiosError.response.data);
|
|
7617
8128
|
}
|
|
7618
8129
|
}
|
|
7619
8130
|
throw error;
|
|
@@ -7628,36 +8139,46 @@ class AxiosHttpAdapter {
|
|
|
7628
8139
|
this.client.interceptors.request.use((config) => {
|
|
7629
8140
|
if (this.authToken) {
|
|
7630
8141
|
config.headers.Authorization = `Bearer ${this.authToken}`;
|
|
7631
|
-
|
|
8142
|
+
logJwt.debug('Adding JWT token to request', {
|
|
8143
|
+
tokenPrefix: this.authToken.substring(0, 30) + '...',
|
|
8144
|
+
tokenLength: this.authToken.length,
|
|
8145
|
+
});
|
|
7632
8146
|
}
|
|
7633
8147
|
else {
|
|
7634
|
-
|
|
8148
|
+
logJwt.warn('No JWT token available for request:', { url: config.url });
|
|
7635
8149
|
}
|
|
7636
8150
|
const method = config.method?.toUpperCase() ?? 'UNKNOWN';
|
|
7637
|
-
|
|
8151
|
+
const authHeader = config.headers.Authorization;
|
|
8152
|
+
// Log full request details for debugging
|
|
8153
|
+
logJwt.info(`→ ${method} ${config.url}`, {
|
|
8154
|
+
baseURL: config.baseURL,
|
|
8155
|
+
fullURL: `${config.baseURL}${config.url}`,
|
|
8156
|
+
hasAuthHeader: !!authHeader,
|
|
8157
|
+
authHeaderValue: authHeader ? `${String(authHeader).substring(0, 50)}...` : 'MISSING',
|
|
8158
|
+
});
|
|
7638
8159
|
if (config.params && Object.keys(config.params).length > 0) {
|
|
7639
|
-
|
|
8160
|
+
logJwt.debug('Request params:', config.params);
|
|
7640
8161
|
}
|
|
7641
8162
|
if (config.data) {
|
|
7642
|
-
|
|
8163
|
+
logJwt.debug('Request body:', config.data);
|
|
7643
8164
|
}
|
|
7644
8165
|
return config;
|
|
7645
8166
|
}, (error) => {
|
|
7646
|
-
|
|
8167
|
+
logJwt.error('Request error:', error);
|
|
7647
8168
|
return Promise.reject(error);
|
|
7648
8169
|
});
|
|
7649
8170
|
this.client.interceptors.response.use((response) => {
|
|
7650
8171
|
const method = response.config.method?.toUpperCase() ?? 'UNKNOWN';
|
|
7651
|
-
|
|
8172
|
+
logJwt.debug(`← ${method} ${response.status} ${response.config.url}`);
|
|
7652
8173
|
if (response.data) {
|
|
7653
|
-
|
|
8174
|
+
logJwt.debug('Response body:', response.data);
|
|
7654
8175
|
}
|
|
7655
8176
|
return response;
|
|
7656
8177
|
}, (error) => {
|
|
7657
8178
|
const method = error.config?.method?.toUpperCase() ?? 'UNKNOWN';
|
|
7658
|
-
|
|
8179
|
+
logJwt.error(`← ${method} ${error.response?.status ?? 'ERR'} ${error.config?.url ?? 'unknown'}`);
|
|
7659
8180
|
if (error.response?.data) {
|
|
7660
|
-
|
|
8181
|
+
logJwt.error('Response body:', error.response.data);
|
|
7661
8182
|
}
|
|
7662
8183
|
return Promise.reject(error);
|
|
7663
8184
|
});
|
|
@@ -7727,7 +8248,10 @@ class AxiosHttpAdapter {
|
|
|
7727
8248
|
return this.mapResponse(response);
|
|
7728
8249
|
}
|
|
7729
8250
|
setAuthToken(token) {
|
|
7730
|
-
|
|
8251
|
+
logJwt.info('setAuthToken called:', {
|
|
8252
|
+
hasToken: !!token,
|
|
8253
|
+
tokenPrefix: token?.substring(0, 20),
|
|
8254
|
+
});
|
|
7731
8255
|
this.authToken = token;
|
|
7732
8256
|
}
|
|
7733
8257
|
getAuthToken() {
|
|
@@ -7883,7 +8407,7 @@ class SDKFactory {
|
|
|
7883
8407
|
}
|
|
7884
8408
|
}
|
|
7885
8409
|
|
|
7886
|
-
const log$
|
|
8410
|
+
const log$2 = createPrefixedLogger('SDK');
|
|
7887
8411
|
class ACubeSDK {
|
|
7888
8412
|
constructor(config, customAdapters, events = {}) {
|
|
7889
8413
|
this.events = events;
|
|
@@ -7897,22 +8421,22 @@ class ACubeSDK {
|
|
|
7897
8421
|
}
|
|
7898
8422
|
async initialize() {
|
|
7899
8423
|
if (this.isInitialized) {
|
|
7900
|
-
log$
|
|
8424
|
+
log$2.debug('SDK already initialized, skipping');
|
|
7901
8425
|
return;
|
|
7902
8426
|
}
|
|
7903
|
-
log$
|
|
8427
|
+
log$2.info('Initializing SDK', {
|
|
7904
8428
|
apiUrl: this.config.getApiUrl(),
|
|
7905
8429
|
authUrl: this.config.getAuthUrl(),
|
|
7906
8430
|
debugEnabled: this.config.isDebugEnabled(),
|
|
7907
8431
|
});
|
|
7908
8432
|
try {
|
|
7909
8433
|
if (!this.adapters) {
|
|
7910
|
-
log$
|
|
8434
|
+
log$2.debug('Loading platform adapters');
|
|
7911
8435
|
const mtlsConfig = createACubeMTLSConfig(this.config.getApiUrl(), this.config.getTimeout(), true);
|
|
7912
8436
|
this.adapters = loadPlatformAdapters({
|
|
7913
8437
|
mtlsConfig,
|
|
7914
8438
|
});
|
|
7915
|
-
log$
|
|
8439
|
+
log$2.info('Platform adapters loaded', {
|
|
7916
8440
|
hasCache: !!this.adapters.cache,
|
|
7917
8441
|
hasNetworkMonitor: !!this.adapters.networkMonitor,
|
|
7918
8442
|
hasMtls: !!this.adapters.mtls,
|
|
@@ -7925,25 +8449,30 @@ class ACubeSDK {
|
|
|
7925
8449
|
timeout: this.config.getTimeout(),
|
|
7926
8450
|
debugEnabled: this.config.isDebugEnabled(),
|
|
7927
8451
|
};
|
|
7928
|
-
log$
|
|
8452
|
+
log$2.debug('Creating DI container');
|
|
7929
8453
|
this.container = SDKFactory.createContainer(factoryConfig);
|
|
7930
|
-
log$
|
|
8454
|
+
log$2.debug('Registering auth services');
|
|
7931
8455
|
SDKFactory.registerAuthServices(this.container, this.adapters.secureStorage, factoryConfig);
|
|
7932
8456
|
if (this.adapters.cache) {
|
|
7933
|
-
log$
|
|
8457
|
+
log$2.info('Registering cache services', {
|
|
7934
8458
|
hasNetworkMonitor: !!this.adapters.networkMonitor,
|
|
7935
8459
|
});
|
|
7936
8460
|
SDKFactory.registerCacheServices(this.container, this.adapters.cache, this.adapters.networkMonitor);
|
|
7937
8461
|
}
|
|
7938
8462
|
else {
|
|
7939
|
-
log$
|
|
8463
|
+
log$2.debug('No cache adapter available, caching disabled');
|
|
7940
8464
|
}
|
|
7941
|
-
log$
|
|
8465
|
+
log$2.debug('Initializing certificate service');
|
|
7942
8466
|
this.certificateService = new CertificateService(this.adapters.secureStorage);
|
|
7943
8467
|
const tokenStorage = this.container.get(DI_TOKENS.TOKEN_STORAGE_PORT);
|
|
7944
8468
|
const httpPort = this.container.get(DI_TOKENS.HTTP_PORT);
|
|
7945
8469
|
const baseHttpPort = this.container.get(DI_TOKENS.BASE_HTTP_PORT);
|
|
7946
|
-
log$
|
|
8470
|
+
log$2.debug('HTTP ports initialized', {
|
|
8471
|
+
httpPortType: httpPort.constructor.name,
|
|
8472
|
+
baseHttpPortType: baseHttpPort.constructor.name,
|
|
8473
|
+
areSameInstance: httpPort === baseHttpPort,
|
|
8474
|
+
});
|
|
8475
|
+
log$2.debug('Initializing authentication service');
|
|
7947
8476
|
this.authService = new AuthenticationService(httpPort, tokenStorage, {
|
|
7948
8477
|
authUrl: this.config.getAuthUrl(),
|
|
7949
8478
|
timeout: this.config.getTimeout(),
|
|
@@ -7953,7 +8482,7 @@ class ACubeSDK {
|
|
|
7953
8482
|
this.events.onAuthError?.(new ACubeSDKError('AUTH_ERROR', error.message, error));
|
|
7954
8483
|
},
|
|
7955
8484
|
});
|
|
7956
|
-
log$
|
|
8485
|
+
log$2.debug('Initializing offline manager');
|
|
7957
8486
|
const queueEvents = {
|
|
7958
8487
|
onOperationAdded: (operation) => {
|
|
7959
8488
|
this.events.onOfflineOperationAdded?.(operation.id);
|
|
@@ -7975,19 +8504,29 @@ class ACubeSDK {
|
|
|
7975
8504
|
this.offlineManager.sync().catch(() => { });
|
|
7976
8505
|
}
|
|
7977
8506
|
});
|
|
7978
|
-
|
|
8507
|
+
const isAuth = await this.authService.isAuthenticated();
|
|
8508
|
+
log$2.debug('Checking authentication status during init', { isAuthenticated: isAuth });
|
|
8509
|
+
if (isAuth) {
|
|
7979
8510
|
const token = await this.authService.getAccessToken();
|
|
8511
|
+
log$2.debug('Token retrieved during init', {
|
|
8512
|
+
hasToken: !!token,
|
|
8513
|
+
tokenPrefix: token?.substring(0, 20),
|
|
8514
|
+
});
|
|
7980
8515
|
if (token) {
|
|
7981
8516
|
httpPort.setAuthToken(token);
|
|
8517
|
+
log$2.info('Auth token set on HTTP port during initialization');
|
|
7982
8518
|
}
|
|
7983
8519
|
}
|
|
8520
|
+
else {
|
|
8521
|
+
log$2.warn('User not authenticated during SDK init - token will be set after login');
|
|
8522
|
+
}
|
|
7984
8523
|
if (this.adapters?.mtls && 'setMTLSAdapter' in baseHttpPort) {
|
|
7985
|
-
log$
|
|
8524
|
+
log$2.debug('Connecting mTLS adapter to HTTP port');
|
|
7986
8525
|
const httpWithMtls = baseHttpPort;
|
|
7987
8526
|
httpWithMtls.setMTLSAdapter(this.adapters.mtls);
|
|
7988
8527
|
}
|
|
7989
8528
|
if ('setAuthStrategy' in baseHttpPort) {
|
|
7990
|
-
log$
|
|
8529
|
+
log$2.debug('Configuring auth strategy');
|
|
7991
8530
|
const jwtHandler = new JwtAuthHandler(tokenStorage);
|
|
7992
8531
|
const certificatePort = this.certificateService
|
|
7993
8532
|
? {
|
|
@@ -8036,19 +8575,19 @@ class ACubeSDK {
|
|
|
8036
8575
|
}
|
|
8037
8576
|
}
|
|
8038
8577
|
catch (certError) {
|
|
8039
|
-
log$
|
|
8578
|
+
log$2.warn('Certificate auto-configuration failed, will retry on demand', {
|
|
8040
8579
|
error: certError instanceof Error ? certError.message : certError,
|
|
8041
8580
|
});
|
|
8042
8581
|
}
|
|
8043
8582
|
}
|
|
8044
8583
|
this.isInitialized = true;
|
|
8045
|
-
log$
|
|
8584
|
+
log$2.info('SDK initialized successfully', {
|
|
8046
8585
|
hasCache: !!this.adapters.cache,
|
|
8047
8586
|
hasMtls: !!this.adapters.mtls,
|
|
8048
8587
|
});
|
|
8049
8588
|
}
|
|
8050
8589
|
catch (error) {
|
|
8051
|
-
log$
|
|
8590
|
+
log$2.error('SDK initialization failed', {
|
|
8052
8591
|
error: error instanceof Error ? error.message : error,
|
|
8053
8592
|
});
|
|
8054
8593
|
throw new ACubeSDKError('SDK_INITIALIZATION_ERROR', `Failed to initialize SDK: ${error instanceof Error ? error.message : 'Unknown error'}`, error);
|
|
@@ -8104,19 +8643,19 @@ class ACubeSDK {
|
|
|
8104
8643
|
}
|
|
8105
8644
|
async login(credentials) {
|
|
8106
8645
|
this.ensureInitialized();
|
|
8107
|
-
log$
|
|
8646
|
+
log$2.info('Login attempt', { email: credentials.email });
|
|
8108
8647
|
const user = await this.authService.login(credentials);
|
|
8109
|
-
log$
|
|
8648
|
+
log$2.info('Login successful', { roles: user.roles });
|
|
8110
8649
|
const token = await this.authService.getAccessToken();
|
|
8111
8650
|
if (token) {
|
|
8112
8651
|
this.httpPort.setAuthToken(token);
|
|
8113
|
-
log$
|
|
8652
|
+
log$2.debug('Auth token set on HTTP port');
|
|
8114
8653
|
}
|
|
8115
8654
|
return user;
|
|
8116
8655
|
}
|
|
8117
8656
|
async logout() {
|
|
8118
8657
|
this.ensureInitialized();
|
|
8119
|
-
log$
|
|
8658
|
+
log$2.info('Logout');
|
|
8120
8659
|
await this.authService.logout();
|
|
8121
8660
|
this.httpPort.setAuthToken(null);
|
|
8122
8661
|
}
|
|
@@ -8290,6 +8829,7 @@ const INITIAL_STATE = {
|
|
|
8290
8829
|
remainingMs: 0,
|
|
8291
8830
|
},
|
|
8292
8831
|
lastNotification: null,
|
|
8832
|
+
certificateMissing: false,
|
|
8293
8833
|
};
|
|
8294
8834
|
class AppStateService {
|
|
8295
8835
|
get state$() {
|
|
@@ -8304,28 +8844,33 @@ class AppStateService {
|
|
|
8304
8844
|
get warning$() {
|
|
8305
8845
|
return this.state$.pipe(map((s) => s.warning), distinctUntilChanged((a, b) => a.active === b.active && a.remainingMs === b.remainingMs));
|
|
8306
8846
|
}
|
|
8307
|
-
|
|
8847
|
+
get certificateMissing$() {
|
|
8848
|
+
return this.state$.pipe(map((s) => s.certificateMissing), distinctUntilChanged());
|
|
8849
|
+
}
|
|
8850
|
+
constructor(notifications$, networkPort, certificateMissingInput$ = of(false)) {
|
|
8308
8851
|
this.notifications$ = notifications$;
|
|
8309
8852
|
this.networkPort = networkPort;
|
|
8853
|
+
this.certificateMissingInput$ = certificateMissingInput$;
|
|
8310
8854
|
this.stateSubject = new BehaviorSubject(INITIAL_STATE);
|
|
8311
8855
|
this.destroy$ = new Subject();
|
|
8312
8856
|
this.warningTimerId = null;
|
|
8313
8857
|
this.setupSubscriptions();
|
|
8314
8858
|
}
|
|
8315
8859
|
setupSubscriptions() {
|
|
8316
|
-
combineLatest([this.notifications$, this.networkPort.online$])
|
|
8860
|
+
combineLatest([this.notifications$, this.networkPort.online$, this.certificateMissingInput$])
|
|
8317
8861
|
.pipe(takeUntil(this.destroy$))
|
|
8318
|
-
.subscribe(([notifications, isOnline]) => {
|
|
8319
|
-
this.processState(notifications, isOnline);
|
|
8862
|
+
.subscribe(([notifications, isOnline, certificateMissing]) => {
|
|
8863
|
+
this.processState(notifications, isOnline, certificateMissing);
|
|
8320
8864
|
});
|
|
8321
8865
|
}
|
|
8322
|
-
processState(notifications, isOnline) {
|
|
8866
|
+
processState(notifications, isOnline, certificateMissing) {
|
|
8323
8867
|
if (!isOnline) {
|
|
8324
8868
|
this.updateState({
|
|
8325
8869
|
mode: 'OFFLINE',
|
|
8326
8870
|
isOnline: false,
|
|
8327
8871
|
warning: { active: false, blockAt: null, remainingMs: 0 },
|
|
8328
8872
|
lastNotification: null,
|
|
8873
|
+
certificateMissing,
|
|
8329
8874
|
});
|
|
8330
8875
|
this.stopWarningTimer();
|
|
8331
8876
|
return;
|
|
@@ -8387,6 +8932,7 @@ class AppStateService {
|
|
|
8387
8932
|
isOnline: true,
|
|
8388
8933
|
warning: warningState,
|
|
8389
8934
|
lastNotification,
|
|
8935
|
+
certificateMissing,
|
|
8390
8936
|
});
|
|
8391
8937
|
}
|
|
8392
8938
|
getLatestNotificationByCode(notifications) {
|
|
@@ -8652,6 +9198,7 @@ class TelemetryService {
|
|
|
8652
9198
|
}
|
|
8653
9199
|
}
|
|
8654
9200
|
|
|
9201
|
+
const log$1 = createPrefixedLogger('SDK-MANAGER');
|
|
8655
9202
|
/**
|
|
8656
9203
|
* SDKManager - Singleton wrapper for ACubeSDK with simplified API
|
|
8657
9204
|
*
|
|
@@ -8702,6 +9249,7 @@ class SDKManager {
|
|
|
8702
9249
|
this.appStateService = null;
|
|
8703
9250
|
this.isInitialized = false;
|
|
8704
9251
|
this.isPollingActive = false;
|
|
9252
|
+
this.certificateMissingSubject = new BehaviorSubject(false);
|
|
8705
9253
|
/**
|
|
8706
9254
|
* Handle user state changes (login/logout/token expiration)
|
|
8707
9255
|
* Manages polling lifecycle based on user role
|
|
@@ -8712,9 +9260,14 @@ class SDKManager {
|
|
|
8712
9260
|
if (!this.isInitialized)
|
|
8713
9261
|
return;
|
|
8714
9262
|
if (user) {
|
|
8715
|
-
// User logged in - check role and
|
|
9263
|
+
// User logged in - check role and certificate before starting polling
|
|
8716
9264
|
const canPoll = hasAnyRole(user.roles, ['ROLE_MERCHANT', 'ROLE_CASHIER']);
|
|
8717
9265
|
if (canPoll && !this.isPollingActive) {
|
|
9266
|
+
const hasCert = await this.checkCertificate();
|
|
9267
|
+
if (!hasCert) {
|
|
9268
|
+
log$1.warn('Certificate missing — polling blocked until certificate is installed');
|
|
9269
|
+
return;
|
|
9270
|
+
}
|
|
8718
9271
|
this.notificationService?.startPolling();
|
|
8719
9272
|
await this.startTelemetryPollingAuto();
|
|
8720
9273
|
this.isPollingActive = true;
|
|
@@ -8728,6 +9281,7 @@ class SDKManager {
|
|
|
8728
9281
|
this.telemetryService?.clearTelemetry();
|
|
8729
9282
|
this.isPollingActive = false;
|
|
8730
9283
|
}
|
|
9284
|
+
this.certificateMissingSubject.next(false);
|
|
8731
9285
|
}
|
|
8732
9286
|
};
|
|
8733
9287
|
}
|
|
@@ -8794,7 +9348,7 @@ class SDKManager {
|
|
|
8794
9348
|
this.telemetryService = new TelemetryService(telemetryRepo, networkPort, {
|
|
8795
9349
|
pollIntervalMs: this.config.telemetryPollIntervalMs ?? 60000,
|
|
8796
9350
|
});
|
|
8797
|
-
this.appStateService = new AppStateService(this.notificationService.notifications$, networkPort);
|
|
9351
|
+
this.appStateService = new AppStateService(this.notificationService.notifications$, networkPort, this.certificateMissingSubject.asObservable());
|
|
8798
9352
|
if (this.events?.onAppStateChanged) {
|
|
8799
9353
|
this.appStateService.state$.subscribe(this.events.onAppStateChanged);
|
|
8800
9354
|
}
|
|
@@ -8806,9 +9360,15 @@ class SDKManager {
|
|
|
8806
9360
|
const user = await this.sdk.getCurrentUser();
|
|
8807
9361
|
const canPoll = user && hasAnyRole(user.roles, ['ROLE_MERCHANT', 'ROLE_CASHIER']);
|
|
8808
9362
|
if (canPoll) {
|
|
8809
|
-
this.
|
|
8810
|
-
|
|
8811
|
-
|
|
9363
|
+
const hasCert = await this.checkCertificate();
|
|
9364
|
+
if (hasCert) {
|
|
9365
|
+
this.notificationService.startPolling();
|
|
9366
|
+
await this.startTelemetryPollingAuto();
|
|
9367
|
+
this.isPollingActive = true;
|
|
9368
|
+
}
|
|
9369
|
+
else {
|
|
9370
|
+
log$1.warn('Certificate missing at init — polling blocked until certificate is installed');
|
|
9371
|
+
}
|
|
8812
9372
|
}
|
|
8813
9373
|
// AppStateService remains active for all users (handles OFFLINE network state)
|
|
8814
9374
|
}
|
|
@@ -8821,820 +9381,429 @@ class SDKManager {
|
|
|
8821
9381
|
return this.appStateService.state$;
|
|
8822
9382
|
}
|
|
8823
9383
|
/**
|
|
8824
|
-
* Observable stream of app mode only
|
|
8825
|
-
* Emits AppMode with distinctUntilChanged
|
|
8826
|
-
*/
|
|
8827
|
-
get mode$() {
|
|
8828
|
-
this.ensureInitialized();
|
|
8829
|
-
return this.appStateService.mode$;
|
|
8830
|
-
}
|
|
8831
|
-
/**
|
|
8832
|
-
* Observable stream indicating if app is blocked
|
|
8833
|
-
*/
|
|
8834
|
-
get isBlocked$() {
|
|
8835
|
-
this.ensureInitialized();
|
|
8836
|
-
return this.appStateService.isBlocked$;
|
|
8837
|
-
}
|
|
8838
|
-
/**
|
|
8839
|
-
* Observable stream of warning state with countdown
|
|
8840
|
-
*/
|
|
8841
|
-
get warning$() {
|
|
8842
|
-
this.ensureInitialized();
|
|
8843
|
-
return this.appStateService.warning$;
|
|
8844
|
-
}
|
|
8845
|
-
/**
|
|
8846
|
-
* Observable stream of telemetry state (data, isLoading, isCached, error)
|
|
8847
|
-
*/
|
|
8848
|
-
get telemetryState$() {
|
|
8849
|
-
this.ensureInitialized();
|
|
8850
|
-
return this.telemetryService.state$;
|
|
8851
|
-
}
|
|
8852
|
-
/**
|
|
8853
|
-
* Get the pemId from the installed certificate
|
|
8854
|
-
*/
|
|
8855
|
-
async getPemId() {
|
|
8856
|
-
this.ensureInitialized();
|
|
8857
|
-
try {
|
|
8858
|
-
const certInfo = await this.sdk.getCertificatesInfo();
|
|
8859
|
-
return certInfo?.pemId ?? null;
|
|
8860
|
-
}
|
|
8861
|
-
catch {
|
|
8862
|
-
return null;
|
|
8863
|
-
}
|
|
8864
|
-
}
|
|
8865
|
-
/**
|
|
8866
|
-
* Start polling telemetry using the pemId from installed certificate
|
|
8867
|
-
* Returns the pemId if successful, null if no certificate is installed
|
|
8868
|
-
*/
|
|
8869
|
-
async startTelemetryPollingAuto() {
|
|
8870
|
-
this.ensureInitialized();
|
|
8871
|
-
const pemId = await this.getPemId();
|
|
8872
|
-
if (pemId) {
|
|
8873
|
-
this.telemetryService.startPolling(pemId);
|
|
8874
|
-
}
|
|
8875
|
-
return pemId;
|
|
8876
|
-
}
|
|
8877
|
-
/**
|
|
8878
|
-
* Start polling telemetry for a specific PEM
|
|
8879
|
-
*/
|
|
8880
|
-
startTelemetryPolling(pemId) {
|
|
8881
|
-
this.ensureInitialized();
|
|
8882
|
-
this.telemetryService.startPolling(pemId);
|
|
8883
|
-
}
|
|
8884
|
-
/**
|
|
8885
|
-
* Stop telemetry polling
|
|
8886
|
-
*/
|
|
8887
|
-
stopTelemetryPolling() {
|
|
8888
|
-
this.ensureInitialized();
|
|
8889
|
-
this.telemetryService.stopPolling();
|
|
8890
|
-
}
|
|
8891
|
-
/**
|
|
8892
|
-
* Get simplified services for product use
|
|
8893
|
-
*/
|
|
8894
|
-
getServices() {
|
|
8895
|
-
this.ensureInitialized();
|
|
8896
|
-
const sdk = this.sdk;
|
|
8897
|
-
const telemetryService = this.telemetryService;
|
|
8898
|
-
return {
|
|
8899
|
-
receipts: sdk.receipts,
|
|
8900
|
-
merchants: sdk.merchants,
|
|
8901
|
-
cashiers: sdk.cashiers,
|
|
8902
|
-
cashRegisters: sdk.cashRegisters,
|
|
8903
|
-
pointOfSales: sdk.pointOfSales,
|
|
8904
|
-
suppliers: sdk.suppliers,
|
|
8905
|
-
pems: sdk.pems,
|
|
8906
|
-
dailyReports: sdk.dailyReports,
|
|
8907
|
-
journals: sdk.journals,
|
|
8908
|
-
telemetry: {
|
|
8909
|
-
startPollingAuto: () => this.startTelemetryPollingAuto(),
|
|
8910
|
-
startPolling: (pemId) => telemetryService.startPolling(pemId),
|
|
8911
|
-
stopPolling: () => telemetryService.stopPolling(),
|
|
8912
|
-
getTelemetry: (pemId) => telemetryService.getTelemetry(pemId),
|
|
8913
|
-
refreshTelemetry: (pemId) => telemetryService.refreshTelemetry(pemId),
|
|
8914
|
-
triggerSync: () => telemetryService.triggerSync(),
|
|
8915
|
-
clearTelemetry: () => telemetryService.clearTelemetry(),
|
|
8916
|
-
getPemId: () => this.getPemId(),
|
|
8917
|
-
},
|
|
8918
|
-
login: (credentials) => sdk.login(credentials),
|
|
8919
|
-
logout: () => sdk.logout(),
|
|
8920
|
-
getCurrentUser: () => sdk.getCurrentUser(),
|
|
8921
|
-
isAuthenticated: () => sdk.isAuthenticated(),
|
|
8922
|
-
storeCertificate: (certificate, privateKey, options) => sdk.storeCertificate(certificate, privateKey, options),
|
|
8923
|
-
hasCertificate: () => sdk.hasCertificate(),
|
|
8924
|
-
clearCertificate: () => sdk.clearCertificate(),
|
|
8925
|
-
isOnline: () => sdk.isOnline(),
|
|
8926
|
-
};
|
|
9384
|
+
* Observable stream of app mode only
|
|
9385
|
+
* Emits AppMode with distinctUntilChanged
|
|
9386
|
+
*/
|
|
9387
|
+
get mode$() {
|
|
9388
|
+
this.ensureInitialized();
|
|
9389
|
+
return this.appStateService.mode$;
|
|
8927
9390
|
}
|
|
8928
9391
|
/**
|
|
8929
|
-
*
|
|
9392
|
+
* Observable stream indicating if app is blocked
|
|
8930
9393
|
*/
|
|
8931
|
-
|
|
9394
|
+
get isBlocked$() {
|
|
8932
9395
|
this.ensureInitialized();
|
|
8933
|
-
|
|
9396
|
+
return this.appStateService.isBlocked$;
|
|
8934
9397
|
}
|
|
8935
9398
|
/**
|
|
8936
|
-
*
|
|
9399
|
+
* Observable stream of warning state with countdown
|
|
8937
9400
|
*/
|
|
8938
|
-
|
|
9401
|
+
get warning$() {
|
|
8939
9402
|
this.ensureInitialized();
|
|
8940
|
-
return this.
|
|
9403
|
+
return this.appStateService.warning$;
|
|
8941
9404
|
}
|
|
8942
9405
|
/**
|
|
8943
|
-
*
|
|
9406
|
+
* Observable stream indicating if certificate is missing
|
|
9407
|
+
* When true, polling is blocked and the user should install a certificate
|
|
8944
9408
|
*/
|
|
8945
|
-
|
|
8946
|
-
return this.
|
|
9409
|
+
get certificateMissing$() {
|
|
9410
|
+
return this.certificateMissingSubject.asObservable();
|
|
8947
9411
|
}
|
|
8948
9412
|
/**
|
|
8949
|
-
*
|
|
9413
|
+
* Observable stream of telemetry state (data, isLoading, isCached, error)
|
|
8950
9414
|
*/
|
|
8951
|
-
|
|
9415
|
+
get telemetryState$() {
|
|
8952
9416
|
this.ensureInitialized();
|
|
8953
|
-
return this.
|
|
8954
|
-
}
|
|
8955
|
-
cleanup() {
|
|
8956
|
-
this.notificationService?.destroy();
|
|
8957
|
-
this.telemetryService?.destroy();
|
|
8958
|
-
this.appStateService?.destroy();
|
|
8959
|
-
this.sdk?.destroy();
|
|
8960
|
-
this.notificationService = null;
|
|
8961
|
-
this.telemetryService = null;
|
|
8962
|
-
this.appStateService = null;
|
|
8963
|
-
this.sdk = null;
|
|
8964
|
-
this.isInitialized = false;
|
|
8965
|
-
this.isPollingActive = false;
|
|
8966
|
-
}
|
|
8967
|
-
ensureInitialized() {
|
|
8968
|
-
if (!this.isInitialized) {
|
|
8969
|
-
throw new ACubeSDKError('SDK_NOT_INITIALIZED', 'SDKManager not initialized. Call initialize() first.');
|
|
8970
|
-
}
|
|
8971
|
-
}
|
|
8972
|
-
}
|
|
8973
|
-
SDKManager.instance = null;
|
|
8974
|
-
|
|
8975
|
-
const RECEIPT_READY = 'ready';
|
|
8976
|
-
const RECEIPT_SENT = 'sent';
|
|
8977
|
-
const RECEIPT_SORT_DESCENDING = 'descending';
|
|
8978
|
-
const RECEIPT_SORT_ASCENDING = 'ascending';
|
|
8979
|
-
|
|
8980
|
-
const log = createPrefixedLogger('CACHE-HANDLER');
|
|
8981
|
-
class CacheHandler {
|
|
8982
|
-
constructor(cache, networkMonitor) {
|
|
8983
|
-
this.cache = cache;
|
|
8984
|
-
this.networkMonitor = networkMonitor;
|
|
8985
|
-
this.currentOnlineState = true;
|
|
8986
|
-
this.setupNetworkMonitoring();
|
|
8987
|
-
}
|
|
8988
|
-
setupNetworkMonitoring() {
|
|
8989
|
-
if (this.networkMonitor) {
|
|
8990
|
-
this.networkSubscription = this.networkMonitor.online$.subscribe((online) => {
|
|
8991
|
-
this.currentOnlineState = online;
|
|
8992
|
-
});
|
|
8993
|
-
}
|
|
8994
|
-
}
|
|
8995
|
-
isOnline() {
|
|
8996
|
-
if (this.networkMonitor) {
|
|
8997
|
-
return this.currentOnlineState;
|
|
8998
|
-
}
|
|
8999
|
-
if (typeof navigator !== 'undefined' && 'onLine' in navigator) {
|
|
9000
|
-
return navigator.onLine;
|
|
9001
|
-
}
|
|
9002
|
-
return false;
|
|
9003
|
-
}
|
|
9004
|
-
async handleCachedRequest(url, requestFn, config) {
|
|
9005
|
-
if (!this.cache || config?.useCache === false) {
|
|
9006
|
-
const response = await requestFn();
|
|
9007
|
-
return response.data;
|
|
9008
|
-
}
|
|
9009
|
-
const cacheKey = this.generateCacheKey(url);
|
|
9010
|
-
const online = this.isOnline();
|
|
9011
|
-
log.debug('Request:', { url, cacheKey, online });
|
|
9012
|
-
if (online) {
|
|
9013
|
-
try {
|
|
9014
|
-
const response = await requestFn();
|
|
9015
|
-
if (this.cache) {
|
|
9016
|
-
await this.cache.set(cacheKey, response.data).catch((error) => {
|
|
9017
|
-
log.error('Failed to cache:', error instanceof Error ? error.message : error);
|
|
9018
|
-
});
|
|
9019
|
-
}
|
|
9020
|
-
return response.data;
|
|
9021
|
-
}
|
|
9022
|
-
catch (error) {
|
|
9023
|
-
const cached = await this.cache.get(cacheKey).catch(() => null);
|
|
9024
|
-
if (cached) {
|
|
9025
|
-
log.debug('Network failed, using cache fallback');
|
|
9026
|
-
return cached.data;
|
|
9027
|
-
}
|
|
9028
|
-
throw error;
|
|
9029
|
-
}
|
|
9030
|
-
}
|
|
9031
|
-
else {
|
|
9032
|
-
const cached = await this.cache.get(cacheKey).catch(() => null);
|
|
9033
|
-
if (cached) {
|
|
9034
|
-
log.debug('Offline, returning cached data');
|
|
9035
|
-
return cached.data;
|
|
9036
|
-
}
|
|
9037
|
-
throw new Error('Offline: No cached data available');
|
|
9038
|
-
}
|
|
9039
|
-
}
|
|
9040
|
-
generateCacheKey(url) {
|
|
9041
|
-
return url;
|
|
9417
|
+
return this.telemetryService.state$;
|
|
9042
9418
|
}
|
|
9043
|
-
|
|
9044
|
-
|
|
9045
|
-
|
|
9419
|
+
/**
|
|
9420
|
+
* Get the pemId from the installed certificate
|
|
9421
|
+
*/
|
|
9422
|
+
async getPemId() {
|
|
9423
|
+
this.ensureInitialized();
|
|
9046
9424
|
try {
|
|
9047
|
-
await this.
|
|
9425
|
+
const certInfo = await this.sdk.getCertificatesInfo();
|
|
9426
|
+
return certInfo?.pemId ?? null;
|
|
9048
9427
|
}
|
|
9049
|
-
catch
|
|
9050
|
-
|
|
9428
|
+
catch {
|
|
9429
|
+
return null;
|
|
9051
9430
|
}
|
|
9052
9431
|
}
|
|
9053
|
-
|
|
9054
|
-
|
|
9055
|
-
|
|
9056
|
-
|
|
9057
|
-
|
|
9058
|
-
|
|
9059
|
-
|
|
9060
|
-
|
|
9061
|
-
|
|
9062
|
-
}
|
|
9063
|
-
}
|
|
9064
|
-
|
|
9065
|
-
function transformError(error) {
|
|
9066
|
-
if (axios.isAxiosError(error)) {
|
|
9067
|
-
const response = error.response;
|
|
9068
|
-
if (!response) {
|
|
9069
|
-
return new ACubeSDKError('NETWORK_ERROR', 'Network error occurred', error);
|
|
9070
|
-
}
|
|
9071
|
-
const status = response.status;
|
|
9072
|
-
const data = response.data;
|
|
9073
|
-
const violations = data?.violations;
|
|
9074
|
-
let message = 'Unknown error occurred';
|
|
9075
|
-
if (data?.detail) {
|
|
9076
|
-
message = data.detail;
|
|
9077
|
-
}
|
|
9078
|
-
else if (data?.title) {
|
|
9079
|
-
message = data.title;
|
|
9080
|
-
}
|
|
9081
|
-
else if (error.message) {
|
|
9082
|
-
message = error.message;
|
|
9083
|
-
}
|
|
9084
|
-
switch (status) {
|
|
9085
|
-
case 400:
|
|
9086
|
-
return new ACubeSDKError('VALIDATION_ERROR', message, error, status, violations);
|
|
9087
|
-
case 401:
|
|
9088
|
-
return new ACubeSDKError('AUTH_ERROR', message, error, status, violations);
|
|
9089
|
-
case 403:
|
|
9090
|
-
return new ACubeSDKError('FORBIDDEN_ERROR', message, error, status, violations);
|
|
9091
|
-
case 404:
|
|
9092
|
-
return new ACubeSDKError('NOT_FOUND_ERROR', message, error, status, violations);
|
|
9093
|
-
case 422:
|
|
9094
|
-
return new ACubeSDKError('VALIDATION_ERROR', message, error, status, violations);
|
|
9095
|
-
default:
|
|
9096
|
-
return new ACubeSDKError('UNKNOWN_ERROR', message, error, status, violations);
|
|
9432
|
+
/**
|
|
9433
|
+
* Start polling telemetry using the pemId from installed certificate
|
|
9434
|
+
* Returns the pemId if successful, null if no certificate is installed
|
|
9435
|
+
*/
|
|
9436
|
+
async startTelemetryPollingAuto() {
|
|
9437
|
+
this.ensureInitialized();
|
|
9438
|
+
const pemId = await this.getPemId();
|
|
9439
|
+
if (pemId) {
|
|
9440
|
+
this.telemetryService.startPolling(pemId);
|
|
9097
9441
|
}
|
|
9442
|
+
return pemId;
|
|
9098
9443
|
}
|
|
9099
|
-
|
|
9100
|
-
|
|
9101
|
-
|
|
9102
|
-
|
|
9103
|
-
(
|
|
9104
|
-
|
|
9105
|
-
ErrorCategory["CLIENT_ERROR"] = "CLIENT_ERROR";
|
|
9106
|
-
ErrorCategory["AUTH_ERROR"] = "AUTH_ERROR";
|
|
9107
|
-
ErrorCategory["CERTIFICATE_ERROR"] = "CERTIFICATE_ERROR";
|
|
9108
|
-
ErrorCategory["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
9109
|
-
ErrorCategory["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
|
|
9110
|
-
})(ErrorCategory || (ErrorCategory = {}));
|
|
9111
|
-
function extractStatusCode(error) {
|
|
9112
|
-
if (error instanceof MTLSError && error.statusCode) {
|
|
9113
|
-
return error.statusCode;
|
|
9114
|
-
}
|
|
9115
|
-
const errorObj = error;
|
|
9116
|
-
if (errorObj?.response?.status) {
|
|
9117
|
-
return errorObj.response.status;
|
|
9118
|
-
}
|
|
9119
|
-
if (typeof errorObj?.statusCode === 'number') {
|
|
9120
|
-
return errorObj.statusCode;
|
|
9121
|
-
}
|
|
9122
|
-
return undefined;
|
|
9123
|
-
}
|
|
9124
|
-
function classifyError(error) {
|
|
9125
|
-
const statusCode = extractStatusCode(error);
|
|
9126
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
9127
|
-
if (statusCode && statusCode >= 500 && statusCode < 600) {
|
|
9128
|
-
return {
|
|
9129
|
-
category: ErrorCategory.SERVER_ERROR,
|
|
9130
|
-
statusCode,
|
|
9131
|
-
message: errorMessage,
|
|
9132
|
-
shouldRetry: false,
|
|
9133
|
-
userMessage: `Server error (${statusCode}): The server encountered an error.`,
|
|
9134
|
-
};
|
|
9135
|
-
}
|
|
9136
|
-
if (statusCode === 401 || statusCode === 403) {
|
|
9137
|
-
return {
|
|
9138
|
-
category: ErrorCategory.AUTH_ERROR,
|
|
9139
|
-
statusCode,
|
|
9140
|
-
message: errorMessage,
|
|
9141
|
-
shouldRetry: false,
|
|
9142
|
-
userMessage: `Authentication error (${statusCode}): Invalid credentials or insufficient permissions.`,
|
|
9143
|
-
};
|
|
9144
|
-
}
|
|
9145
|
-
if (statusCode && statusCode >= 400 && statusCode < 500) {
|
|
9146
|
-
return {
|
|
9147
|
-
category: ErrorCategory.CLIENT_ERROR,
|
|
9148
|
-
statusCode,
|
|
9149
|
-
message: errorMessage,
|
|
9150
|
-
shouldRetry: false,
|
|
9151
|
-
userMessage: `Request error (${statusCode}): ${errorMessage}`,
|
|
9152
|
-
};
|
|
9444
|
+
/**
|
|
9445
|
+
* Start polling telemetry for a specific PEM
|
|
9446
|
+
*/
|
|
9447
|
+
startTelemetryPolling(pemId) {
|
|
9448
|
+
this.ensureInitialized();
|
|
9449
|
+
this.telemetryService.startPolling(pemId);
|
|
9153
9450
|
}
|
|
9154
|
-
|
|
9155
|
-
|
|
9156
|
-
|
|
9157
|
-
|
|
9158
|
-
|
|
9159
|
-
|
|
9160
|
-
userMessage: 'Certificate error: Unable to establish secure connection.',
|
|
9161
|
-
};
|
|
9451
|
+
/**
|
|
9452
|
+
* Stop telemetry polling
|
|
9453
|
+
*/
|
|
9454
|
+
stopTelemetryPolling() {
|
|
9455
|
+
this.ensureInitialized();
|
|
9456
|
+
this.telemetryService.stopPolling();
|
|
9162
9457
|
}
|
|
9163
|
-
|
|
9164
|
-
|
|
9165
|
-
|
|
9166
|
-
|
|
9458
|
+
/**
|
|
9459
|
+
* Get simplified services for product use
|
|
9460
|
+
*/
|
|
9461
|
+
getServices() {
|
|
9462
|
+
this.ensureInitialized();
|
|
9463
|
+
const sdk = this.sdk;
|
|
9464
|
+
const telemetryService = this.telemetryService;
|
|
9167
9465
|
return {
|
|
9168
|
-
|
|
9169
|
-
|
|
9170
|
-
|
|
9171
|
-
|
|
9466
|
+
receipts: sdk.receipts,
|
|
9467
|
+
merchants: sdk.merchants,
|
|
9468
|
+
cashiers: sdk.cashiers,
|
|
9469
|
+
cashRegisters: sdk.cashRegisters,
|
|
9470
|
+
pointOfSales: sdk.pointOfSales,
|
|
9471
|
+
suppliers: sdk.suppliers,
|
|
9472
|
+
pems: sdk.pems,
|
|
9473
|
+
dailyReports: sdk.dailyReports,
|
|
9474
|
+
journals: sdk.journals,
|
|
9475
|
+
telemetry: {
|
|
9476
|
+
startPollingAuto: () => this.startTelemetryPollingAuto(),
|
|
9477
|
+
startPolling: (pemId) => telemetryService.startPolling(pemId),
|
|
9478
|
+
stopPolling: () => telemetryService.stopPolling(),
|
|
9479
|
+
getTelemetry: (pemId) => telemetryService.getTelemetry(pemId),
|
|
9480
|
+
refreshTelemetry: (pemId) => telemetryService.refreshTelemetry(pemId),
|
|
9481
|
+
triggerSync: () => telemetryService.triggerSync(),
|
|
9482
|
+
clearTelemetry: () => telemetryService.clearTelemetry(),
|
|
9483
|
+
getPemId: () => this.getPemId(),
|
|
9484
|
+
},
|
|
9485
|
+
login: (credentials) => sdk.login(credentials),
|
|
9486
|
+
logout: () => sdk.logout(),
|
|
9487
|
+
getCurrentUser: () => sdk.getCurrentUser(),
|
|
9488
|
+
isAuthenticated: () => sdk.isAuthenticated(),
|
|
9489
|
+
storeCertificate: async (certificate, privateKey, options) => {
|
|
9490
|
+
await sdk.storeCertificate(certificate, privateKey, options);
|
|
9491
|
+
this.certificateMissingSubject.next(false);
|
|
9492
|
+
// Start polling if user can poll and polling is not active
|
|
9493
|
+
if (!this.isPollingActive) {
|
|
9494
|
+
const user = await sdk.getCurrentUser();
|
|
9495
|
+
const canPoll = user && hasAnyRole(user.roles, ['ROLE_MERCHANT', 'ROLE_CASHIER']);
|
|
9496
|
+
if (canPoll) {
|
|
9497
|
+
log$1.info('Certificate installed — starting polling');
|
|
9498
|
+
this.notificationService?.startPolling();
|
|
9499
|
+
await this.startTelemetryPollingAuto();
|
|
9500
|
+
this.isPollingActive = true;
|
|
9501
|
+
}
|
|
9502
|
+
}
|
|
9503
|
+
},
|
|
9504
|
+
hasCertificate: () => sdk.hasCertificate(),
|
|
9505
|
+
clearCertificate: async () => {
|
|
9506
|
+
await sdk.clearCertificate();
|
|
9507
|
+
this.certificateMissingSubject.next(true);
|
|
9508
|
+
// Stop polling since certificate is required
|
|
9509
|
+
if (this.isPollingActive) {
|
|
9510
|
+
log$1.info('Certificate removed — stopping polling');
|
|
9511
|
+
this.notificationService?.stopPolling();
|
|
9512
|
+
this.telemetryService?.stopPolling();
|
|
9513
|
+
this.telemetryService?.clearTelemetry();
|
|
9514
|
+
this.isPollingActive = false;
|
|
9515
|
+
}
|
|
9516
|
+
},
|
|
9517
|
+
getCertificate: () => sdk.getCertificate(),
|
|
9518
|
+
getCertificatesInfo: () => sdk.getCertificatesInfo(),
|
|
9519
|
+
notifications: sdk.notifications,
|
|
9520
|
+
isOnline: () => sdk.isOnline(),
|
|
9172
9521
|
};
|
|
9173
9522
|
}
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
};
|
|
9181
|
-
}
|
|
9182
|
-
function shouldReconfigureCertificate(error) {
|
|
9183
|
-
const classification = classifyError(error);
|
|
9184
|
-
return classification.category === ErrorCategory.CERTIFICATE_ERROR;
|
|
9185
|
-
}
|
|
9186
|
-
function shouldRetryRequest(error, isRetryAttempt) {
|
|
9187
|
-
if (isRetryAttempt) {
|
|
9188
|
-
return false;
|
|
9523
|
+
/**
|
|
9524
|
+
* Manually trigger a notification sync
|
|
9525
|
+
*/
|
|
9526
|
+
async syncNotifications() {
|
|
9527
|
+
this.ensureInitialized();
|
|
9528
|
+
await this.notificationService.triggerSync();
|
|
9189
9529
|
}
|
|
9190
|
-
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
}
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
|
|
9204
|
-
|
|
9205
|
-
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
|
|
9209
|
-
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
|
|
9213
|
-
|
|
9214
|
-
|
|
9215
|
-
|
|
9216
|
-
|
|
9217
|
-
|
|
9218
|
-
|
|
9219
|
-
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
|
|
9227
|
-
|
|
9228
|
-
|
|
9229
|
-
|
|
9230
|
-
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9234
|
-
|
|
9235
|
-
|
|
9236
|
-
|
|
9237
|
-
|
|
9238
|
-
|
|
9239
|
-
|
|
9240
|
-
}
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
items: z.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
|
|
9245
|
-
customer_tax_code: z.string().optional(),
|
|
9246
|
-
customer_lottery_code: z.string().optional(),
|
|
9247
|
-
discount: z.string().nullable().optional(),
|
|
9248
|
-
invoice_issuing: z.boolean().optional(),
|
|
9249
|
-
uncollected_dcr_to_ssn: z.boolean().optional(),
|
|
9250
|
-
services_uncollected_amount: z.string().nullable().optional(),
|
|
9251
|
-
goods_uncollected_amount: z.string().nullable().optional(),
|
|
9252
|
-
cash_payment_amount: z.string().nullable().optional(),
|
|
9253
|
-
electronic_payment_amount: z.string().nullable().optional(),
|
|
9254
|
-
ticket_restaurant_payment_amount: z.string().nullable().optional(),
|
|
9255
|
-
ticket_restaurant_quantity: z.number().optional(),
|
|
9256
|
-
})
|
|
9257
|
-
.refine((data) => {
|
|
9258
|
-
// At least one payment method should be provided
|
|
9259
|
-
const hasCashPayment = data.cash_payment_amount && parseFloat(data.cash_payment_amount) > 0;
|
|
9260
|
-
const hasElectronicPayment = data.electronic_payment_amount && parseFloat(data.electronic_payment_amount) > 0;
|
|
9261
|
-
const hasTicketPayment = data.ticket_restaurant_payment_amount &&
|
|
9262
|
-
parseFloat(data.ticket_restaurant_payment_amount) > 0;
|
|
9263
|
-
return hasCashPayment || hasElectronicPayment || hasTicketPayment;
|
|
9264
|
-
}, {
|
|
9265
|
-
error: 'At least one payment method is required',
|
|
9266
|
-
path: ['payment_methods'],
|
|
9267
|
-
})
|
|
9268
|
-
.refine((data) => {
|
|
9269
|
-
// only one between customer_tax_code and customer_lottery_code can be provided
|
|
9270
|
-
return !data.customer_tax_code || !data.customer_lottery_code;
|
|
9271
|
-
}, {
|
|
9272
|
-
error: 'Only one between customer_tax_code and customer_lottery_code can be provided',
|
|
9273
|
-
path: ['customer_tax_code', 'customer_lottery_code'],
|
|
9274
|
-
});
|
|
9275
|
-
// Receipt Return or Void via PEM Schema
|
|
9276
|
-
const ReceiptReturnOrVoidViaPEMInputSchema = z.object({
|
|
9277
|
-
device_id: z.string().optional(),
|
|
9278
|
-
items: z.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
|
|
9279
|
-
document_number: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9280
|
-
document_datetime: z.string().optional(),
|
|
9281
|
-
lottery_code: z.string().optional(),
|
|
9282
|
-
});
|
|
9283
|
-
// Receipt Return or Void with Proof Schema
|
|
9284
|
-
const ReceiptReturnOrVoidWithProofInputSchema = z.object({
|
|
9285
|
-
items: z.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
|
|
9286
|
-
proof: ReceiptProofTypeSchema,
|
|
9287
|
-
document_datetime: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9288
|
-
});
|
|
9289
|
-
// Void Receipt Schema
|
|
9290
|
-
const VoidReceiptInputSchema = z.object({
|
|
9291
|
-
document_number: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9292
|
-
});
|
|
9293
|
-
const ReceiptReturnItemSchema = z
|
|
9294
|
-
.array(z.object({
|
|
9295
|
-
id: z.number(),
|
|
9296
|
-
quantity: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9297
|
-
}))
|
|
9298
|
-
.min(1, { error: 'arrayMin1' });
|
|
9299
|
-
// Receipt Return Schema
|
|
9300
|
-
const ReceiptReturnInputSchema = z.object({
|
|
9301
|
-
items: z.array(ReceiptReturnItemSchema).min(1, { error: 'arrayMin1' }),
|
|
9302
|
-
document_number: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9303
|
-
});
|
|
9304
|
-
|
|
9305
|
-
// Cashier Create Input Schema (MF1)
|
|
9306
|
-
const CashierCreateInputSchema = z.object({
|
|
9307
|
-
email: z
|
|
9308
|
-
.string()
|
|
9309
|
-
.min(1, { error: 'fieldIsRequired' })
|
|
9310
|
-
.max(255, { error: 'emailMaxLength' })
|
|
9311
|
-
.email({ error: 'invalidEmail' }),
|
|
9312
|
-
password: z
|
|
9313
|
-
.string()
|
|
9314
|
-
.min(8, { error: 'passwordMinLength' })
|
|
9315
|
-
.max(40, { error: 'passwordMaxLength' }),
|
|
9316
|
-
name: z.string().min(1, { error: 'fieldIsRequired' }).max(255, { error: 'nameMaxLength' }),
|
|
9317
|
-
display_name: z
|
|
9318
|
-
.string()
|
|
9319
|
-
.min(1, { error: 'fieldIsRequired' })
|
|
9320
|
-
.max(255, { error: 'displayNameMaxLength' }),
|
|
9321
|
-
});
|
|
9322
|
-
|
|
9323
|
-
// Enum options arrays
|
|
9324
|
-
const PEM_STATUS_OPTIONS = [
|
|
9325
|
-
'NEW',
|
|
9326
|
-
'REGISTERED',
|
|
9327
|
-
'ACTIVATED',
|
|
9328
|
-
'ONLINE',
|
|
9329
|
-
'OFFLINE',
|
|
9330
|
-
'DISCARDED',
|
|
9331
|
-
];
|
|
9332
|
-
// Address Schema (reusable)
|
|
9333
|
-
const AddressSchema = z.object({
|
|
9334
|
-
street_address: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9335
|
-
street_number: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9336
|
-
zip_code: z
|
|
9337
|
-
.string()
|
|
9338
|
-
.min(1, { error: 'fieldIsRequired' })
|
|
9339
|
-
.regex(/^\d{5}$/, { error: 'invalidZipCode' }),
|
|
9340
|
-
city: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9341
|
-
province: z
|
|
9342
|
-
.string()
|
|
9343
|
-
.min(2, { error: 'provinceMinLength' })
|
|
9344
|
-
.max(2, { error: 'provinceMaxLength' })
|
|
9345
|
-
.toUpperCase(),
|
|
9346
|
-
});
|
|
9347
|
-
// PEM Status Schema
|
|
9348
|
-
const PEMStatusSchema = z.enum(PEM_STATUS_OPTIONS);
|
|
9349
|
-
// Activation Request Schema
|
|
9350
|
-
const ActivationRequestSchema = z.object({
|
|
9351
|
-
registration_key: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9352
|
-
});
|
|
9353
|
-
// PEM Status Offline Request Schema
|
|
9354
|
-
const PEMStatusOfflineRequestSchema = z.object({
|
|
9355
|
-
timestamp: z
|
|
9356
|
-
.string()
|
|
9357
|
-
.min(1, { error: 'fieldIsRequired' })
|
|
9358
|
-
.refine((val) => !isNaN(Date.parse(val)), {
|
|
9359
|
-
error: 'invalidDateFormat',
|
|
9360
|
-
}),
|
|
9361
|
-
reason: z.string().min(1, { error: 'fieldIsRequired' }),
|
|
9362
|
-
});
|
|
9530
|
+
/**
|
|
9531
|
+
* Manually trigger a telemetry sync
|
|
9532
|
+
*/
|
|
9533
|
+
async syncTelemetry() {
|
|
9534
|
+
this.ensureInitialized();
|
|
9535
|
+
return this.telemetryService.triggerSync();
|
|
9536
|
+
}
|
|
9537
|
+
/**
|
|
9538
|
+
* Check if the manager is initialized
|
|
9539
|
+
*/
|
|
9540
|
+
getIsInitialized() {
|
|
9541
|
+
return this.isInitialized;
|
|
9542
|
+
}
|
|
9543
|
+
/**
|
|
9544
|
+
* Get the underlying SDK instance (for advanced use cases)
|
|
9545
|
+
*/
|
|
9546
|
+
getSDK() {
|
|
9547
|
+
this.ensureInitialized();
|
|
9548
|
+
return this.sdk;
|
|
9549
|
+
}
|
|
9550
|
+
/**
|
|
9551
|
+
* Check certificate availability and update certificateMissing state.
|
|
9552
|
+
* Returns true if certificate is available, false otherwise.
|
|
9553
|
+
*/
|
|
9554
|
+
async checkCertificate() {
|
|
9555
|
+
try {
|
|
9556
|
+
const hasCert = await this.sdk.hasCertificate();
|
|
9557
|
+
this.certificateMissingSubject.next(!hasCert);
|
|
9558
|
+
return hasCert;
|
|
9559
|
+
}
|
|
9560
|
+
catch {
|
|
9561
|
+
this.certificateMissingSubject.next(true);
|
|
9562
|
+
return false;
|
|
9563
|
+
}
|
|
9564
|
+
}
|
|
9565
|
+
cleanup() {
|
|
9566
|
+
this.notificationService?.destroy();
|
|
9567
|
+
this.telemetryService?.destroy();
|
|
9568
|
+
this.appStateService?.destroy();
|
|
9569
|
+
this.sdk?.destroy();
|
|
9570
|
+
this.notificationService = null;
|
|
9571
|
+
this.telemetryService = null;
|
|
9572
|
+
this.appStateService = null;
|
|
9573
|
+
this.sdk = null;
|
|
9574
|
+
this.isInitialized = false;
|
|
9575
|
+
this.isPollingActive = false;
|
|
9576
|
+
}
|
|
9577
|
+
ensureInitialized() {
|
|
9578
|
+
if (!this.isInitialized) {
|
|
9579
|
+
throw new ACubeSDKError('SDK_NOT_INITIALIZED', 'SDKManager not initialized. Call initialize() first.');
|
|
9580
|
+
}
|
|
9581
|
+
}
|
|
9582
|
+
}
|
|
9583
|
+
SDKManager.instance = null;
|
|
9363
9584
|
|
|
9364
|
-
|
|
9365
|
-
const
|
|
9366
|
-
|
|
9367
|
-
|
|
9368
|
-
});
|
|
9585
|
+
const RECEIPT_READY = 'ready';
|
|
9586
|
+
const RECEIPT_SENT = 'sent';
|
|
9587
|
+
const RECEIPT_SORT_DESCENDING = 'descending';
|
|
9588
|
+
const RECEIPT_SORT_ASCENDING = 'ascending';
|
|
9369
9589
|
|
|
9370
|
-
|
|
9371
|
-
|
|
9372
|
-
|
|
9373
|
-
|
|
9374
|
-
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
const MerchantCreateInputSchema = z
|
|
9378
|
-
.object({
|
|
9379
|
-
vat_number: z
|
|
9380
|
-
.string()
|
|
9381
|
-
.min(1, { error: 'fieldIsRequired' })
|
|
9382
|
-
.regex(VAT_NUMBER_REGEX, { error: 'invalidVatNumber' }),
|
|
9383
|
-
fiscal_code: z.string().regex(FISCAL_CODE_REGEX, { error: 'invalidFiscalCode' }).optional(),
|
|
9384
|
-
business_name: z.string().max(200, { error: 'businessNameMaxLength' }).optional().nullable(),
|
|
9385
|
-
first_name: z.string().max(100, { error: 'firstNameMaxLength' }).optional().nullable(),
|
|
9386
|
-
last_name: z.string().max(100, { error: 'lastNameMaxLength' }).optional().nullable(),
|
|
9387
|
-
email: z.string().min(1, { error: 'fieldIsRequired' }).email({ error: 'invalidEmail' }),
|
|
9388
|
-
password: z
|
|
9389
|
-
.string()
|
|
9390
|
-
.min(1, { error: 'fieldIsRequired' })
|
|
9391
|
-
.regex(PASSWORD_REGEX, { error: 'passwordComplexity' }),
|
|
9392
|
-
address: AddressSchema.optional(),
|
|
9393
|
-
})
|
|
9394
|
-
.refine((data) => {
|
|
9395
|
-
const hasBusinessName = data.business_name && data.business_name.trim() !== '';
|
|
9396
|
-
const hasPersonalNames = (data.first_name && data.first_name.trim() !== '') ||
|
|
9397
|
-
(data.last_name && data.last_name.trim() !== '');
|
|
9398
|
-
// If business name is set, first/last name must not be provided
|
|
9399
|
-
if (hasBusinessName && hasPersonalNames) {
|
|
9400
|
-
return false;
|
|
9590
|
+
const log = createPrefixedLogger('CACHE-HANDLER');
|
|
9591
|
+
class CacheHandler {
|
|
9592
|
+
constructor(cache, networkMonitor) {
|
|
9593
|
+
this.cache = cache;
|
|
9594
|
+
this.networkMonitor = networkMonitor;
|
|
9595
|
+
this.currentOnlineState = true;
|
|
9596
|
+
this.setupNetworkMonitoring();
|
|
9401
9597
|
}
|
|
9402
|
-
|
|
9403
|
-
|
|
9598
|
+
setupNetworkMonitoring() {
|
|
9599
|
+
if (this.networkMonitor) {
|
|
9600
|
+
this.networkSubscription = this.networkMonitor.online$.subscribe((online) => {
|
|
9601
|
+
this.currentOnlineState = online;
|
|
9602
|
+
});
|
|
9603
|
+
}
|
|
9604
|
+
}
|
|
9605
|
+
isOnline() {
|
|
9606
|
+
if (this.networkMonitor) {
|
|
9607
|
+
return this.currentOnlineState;
|
|
9608
|
+
}
|
|
9609
|
+
if (typeof navigator !== 'undefined' && 'onLine' in navigator) {
|
|
9610
|
+
return navigator.onLine;
|
|
9611
|
+
}
|
|
9404
9612
|
return false;
|
|
9405
9613
|
}
|
|
9406
|
-
|
|
9407
|
-
|
|
9408
|
-
|
|
9409
|
-
|
|
9410
|
-
}
|
|
9411
|
-
|
|
9412
|
-
const
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
|
|
9420
|
-
|
|
9421
|
-
|
|
9422
|
-
|
|
9423
|
-
|
|
9424
|
-
|
|
9425
|
-
|
|
9426
|
-
|
|
9427
|
-
|
|
9428
|
-
|
|
9429
|
-
|
|
9430
|
-
|
|
9431
|
-
|
|
9432
|
-
|
|
9433
|
-
|
|
9434
|
-
|
|
9435
|
-
|
|
9436
|
-
|
|
9437
|
-
|
|
9438
|
-
|
|
9439
|
-
|
|
9440
|
-
|
|
9441
|
-
|
|
9442
|
-
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
9453
|
-
|
|
9454
|
-
|
|
9455
|
-
|
|
9456
|
-
|
|
9457
|
-
|
|
9458
|
-
|
|
9459
|
-
|
|
9460
|
-
|
|
9461
|
-
|
|
9462
|
-
|
|
9463
|
-
|
|
9464
|
-
|
|
9465
|
-
|
|
9466
|
-
// Daily Report Status Schema
|
|
9467
|
-
const DailyReportStatusSchema = z.enum(DAILY_REPORT_STATUS_OPTIONS, {
|
|
9468
|
-
error: 'invalidDailyReportStatus',
|
|
9469
|
-
});
|
|
9470
|
-
// Daily Reports List Parameters Schema
|
|
9471
|
-
const DailyReportsParamsSchema = z.object({
|
|
9472
|
-
pem_serial_number: z.string().min(1, { error: 'fieldIsRequired' }).optional(),
|
|
9473
|
-
date_from: z
|
|
9474
|
-
.string()
|
|
9475
|
-
.refine((val) => !isNaN(Date.parse(val)), {
|
|
9476
|
-
error: 'invalidDateFormat',
|
|
9477
|
-
})
|
|
9478
|
-
.optional(),
|
|
9479
|
-
date_to: z
|
|
9480
|
-
.string()
|
|
9481
|
-
.refine((val) => !isNaN(Date.parse(val)), {
|
|
9482
|
-
error: 'invalidDateFormat',
|
|
9483
|
-
})
|
|
9484
|
-
.optional(),
|
|
9485
|
-
status: DailyReportStatusSchema.optional(),
|
|
9486
|
-
page: z.number().min(1, { error: 'pageMinValue' }).optional(),
|
|
9487
|
-
});
|
|
9488
|
-
|
|
9489
|
-
const NotificationScopeSchema = z.object({
|
|
9490
|
-
type: z.literal('global'),
|
|
9491
|
-
});
|
|
9492
|
-
const PemStatusSchema = z.enum(['ONLINE', 'OFFLINE']);
|
|
9493
|
-
const NotificationDataBlockAtSchema = z.object({
|
|
9494
|
-
block_at: z.string(),
|
|
9495
|
-
});
|
|
9496
|
-
const NotificationDataPemStatusSchema = z.object({
|
|
9497
|
-
from: PemStatusSchema,
|
|
9498
|
-
to: PemStatusSchema,
|
|
9499
|
-
});
|
|
9500
|
-
const NotificationBaseSchema = z.object({
|
|
9501
|
-
uuid: z.string().uuid({ error: 'invalidUuid' }),
|
|
9502
|
-
scope: NotificationScopeSchema,
|
|
9503
|
-
source: z.enum(['system', 'Italian Tax Authority']),
|
|
9504
|
-
level: z.enum(['info', 'warning', 'error', 'critical']),
|
|
9505
|
-
created_at: z.string(),
|
|
9506
|
-
});
|
|
9507
|
-
const NotificationMf2UnreachableSchema = NotificationBaseSchema.extend({
|
|
9508
|
-
type: z.literal('INTERNAL_COMMUNICATION_FAILURE'),
|
|
9509
|
-
code: z.literal('SYS-W-01'),
|
|
9510
|
-
data: NotificationDataBlockAtSchema,
|
|
9511
|
-
});
|
|
9512
|
-
const NotificationPemsBlockedSchema = NotificationBaseSchema.extend({
|
|
9513
|
-
type: z.literal('PEM_STATUS_CHANGED'),
|
|
9514
|
-
code: z.literal('SYS-C-01'),
|
|
9515
|
-
data: NotificationDataPemStatusSchema,
|
|
9516
|
-
});
|
|
9517
|
-
const NotificationPemBackOnlineSchema = NotificationBaseSchema.extend({
|
|
9518
|
-
type: z.literal('PEM_STATUS_CHANGED'),
|
|
9519
|
-
code: z.literal('SYS-I-01'),
|
|
9520
|
-
data: NotificationDataPemStatusSchema,
|
|
9521
|
-
});
|
|
9522
|
-
const NotificationSchema = z.discriminatedUnion('code', [
|
|
9523
|
-
NotificationMf2UnreachableSchema,
|
|
9524
|
-
NotificationPemsBlockedSchema,
|
|
9525
|
-
NotificationPemBackOnlineSchema,
|
|
9526
|
-
]);
|
|
9527
|
-
const NotificationListResponseSchema = z.object({
|
|
9528
|
-
members: z.array(NotificationSchema),
|
|
9529
|
-
});
|
|
9614
|
+
async handleCachedRequest(url, requestFn, config) {
|
|
9615
|
+
if (!this.cache || config?.useCache === false) {
|
|
9616
|
+
const response = await requestFn();
|
|
9617
|
+
return response.data;
|
|
9618
|
+
}
|
|
9619
|
+
const cacheKey = this.generateCacheKey(url);
|
|
9620
|
+
const online = this.isOnline();
|
|
9621
|
+
log.debug('Request:', { url, cacheKey, online });
|
|
9622
|
+
if (online) {
|
|
9623
|
+
try {
|
|
9624
|
+
const response = await requestFn();
|
|
9625
|
+
if (this.cache) {
|
|
9626
|
+
await this.cache.set(cacheKey, response.data).catch((error) => {
|
|
9627
|
+
log.error('Failed to cache:', error instanceof Error ? error.message : error);
|
|
9628
|
+
});
|
|
9629
|
+
}
|
|
9630
|
+
return response.data;
|
|
9631
|
+
}
|
|
9632
|
+
catch (error) {
|
|
9633
|
+
const cached = await this.cache.get(cacheKey).catch(() => null);
|
|
9634
|
+
if (cached) {
|
|
9635
|
+
log.debug('Network failed, using cache fallback');
|
|
9636
|
+
return cached.data;
|
|
9637
|
+
}
|
|
9638
|
+
throw error;
|
|
9639
|
+
}
|
|
9640
|
+
}
|
|
9641
|
+
else {
|
|
9642
|
+
const cached = await this.cache.get(cacheKey).catch(() => null);
|
|
9643
|
+
if (cached) {
|
|
9644
|
+
log.debug('Offline, returning cached data');
|
|
9645
|
+
return cached.data;
|
|
9646
|
+
}
|
|
9647
|
+
throw new Error('Offline: No cached data available');
|
|
9648
|
+
}
|
|
9649
|
+
}
|
|
9650
|
+
generateCacheKey(url) {
|
|
9651
|
+
return url;
|
|
9652
|
+
}
|
|
9653
|
+
async invalidateCache(pattern) {
|
|
9654
|
+
if (!this.cache)
|
|
9655
|
+
return;
|
|
9656
|
+
try {
|
|
9657
|
+
await this.cache.invalidate(pattern);
|
|
9658
|
+
}
|
|
9659
|
+
catch (error) {
|
|
9660
|
+
log.error('Invalidation failed:', error instanceof Error ? error.message : error);
|
|
9661
|
+
}
|
|
9662
|
+
}
|
|
9663
|
+
getCacheStatus() {
|
|
9664
|
+
return {
|
|
9665
|
+
available: !!this.cache,
|
|
9666
|
+
networkMonitorAvailable: !!this.networkMonitor,
|
|
9667
|
+
isOnline: this.isOnline(),
|
|
9668
|
+
};
|
|
9669
|
+
}
|
|
9670
|
+
destroy() {
|
|
9671
|
+
this.networkSubscription?.unsubscribe();
|
|
9672
|
+
}
|
|
9673
|
+
}
|
|
9530
9674
|
|
|
9531
|
-
|
|
9532
|
-
|
|
9533
|
-
|
|
9534
|
-
|
|
9535
|
-
|
|
9536
|
-
|
|
9537
|
-
|
|
9538
|
-
|
|
9539
|
-
|
|
9540
|
-
|
|
9541
|
-
|
|
9542
|
-
|
|
9543
|
-
|
|
9544
|
-
|
|
9545
|
-
|
|
9546
|
-
}
|
|
9547
|
-
|
|
9548
|
-
|
|
9549
|
-
|
|
9550
|
-
|
|
9551
|
-
|
|
9552
|
-
|
|
9553
|
-
|
|
9554
|
-
|
|
9555
|
-
|
|
9556
|
-
|
|
9557
|
-
|
|
9558
|
-
|
|
9559
|
-
|
|
9560
|
-
|
|
9561
|
-
|
|
9562
|
-
|
|
9563
|
-
|
|
9564
|
-
}
|
|
9565
|
-
|
|
9566
|
-
|
|
9567
|
-
outcome: z.enum(['success', 'failed', 'pending']),
|
|
9568
|
-
});
|
|
9569
|
-
const LotterySchema = z.object({
|
|
9570
|
-
last_transmission: TransmissionSchema,
|
|
9571
|
-
secret_request: LotterySecretRequestSchema,
|
|
9572
|
-
});
|
|
9573
|
-
const TelemetrySchema = z.object({
|
|
9574
|
-
pem_id: z.string(),
|
|
9575
|
-
pem_status: z.enum(['ONLINE', 'OFFLINE', 'ERROR']),
|
|
9576
|
-
pem_status_changed_at: z.string(),
|
|
9577
|
-
merchant: TelemetryMerchantSchema,
|
|
9578
|
-
supplier: TelemetrySupplierSchema,
|
|
9579
|
-
software: TelemetrySoftwareSchema,
|
|
9580
|
-
last_communication_at: z.string(),
|
|
9581
|
-
pending_receipts: PendingReceiptsSchema,
|
|
9582
|
-
last_receipt_transmission: TransmissionSchema,
|
|
9583
|
-
last_message_from_mf2: MessageSchema,
|
|
9584
|
-
ade_corrispettivi_transmission: TransmissionSchema,
|
|
9585
|
-
last_message_from_ade: MessageSchema,
|
|
9586
|
-
lottery: LotterySchema,
|
|
9587
|
-
});
|
|
9675
|
+
function transformError(error) {
|
|
9676
|
+
if (axios.isAxiosError(error)) {
|
|
9677
|
+
const response = error.response;
|
|
9678
|
+
if (!response) {
|
|
9679
|
+
return new ACubeSDKError('NETWORK_ERROR', 'Network error occurred', error);
|
|
9680
|
+
}
|
|
9681
|
+
const status = response.status;
|
|
9682
|
+
const data = response.data;
|
|
9683
|
+
const violations = data?.violations;
|
|
9684
|
+
let message = 'Unknown error occurred';
|
|
9685
|
+
if (data?.detail) {
|
|
9686
|
+
message = data.detail;
|
|
9687
|
+
}
|
|
9688
|
+
else if (data?.title) {
|
|
9689
|
+
message = data.title;
|
|
9690
|
+
}
|
|
9691
|
+
else if (error.message) {
|
|
9692
|
+
message = error.message;
|
|
9693
|
+
}
|
|
9694
|
+
switch (status) {
|
|
9695
|
+
case 400:
|
|
9696
|
+
return new ACubeSDKError('VALIDATION_ERROR', message, error, status, violations);
|
|
9697
|
+
case 401:
|
|
9698
|
+
return new ACubeSDKError('AUTH_ERROR', message, error, status, violations);
|
|
9699
|
+
case 403:
|
|
9700
|
+
return new ACubeSDKError('FORBIDDEN_ERROR', message, error, status, violations);
|
|
9701
|
+
case 404:
|
|
9702
|
+
return new ACubeSDKError('NOT_FOUND_ERROR', message, error, status, violations);
|
|
9703
|
+
case 422:
|
|
9704
|
+
return new ACubeSDKError('VALIDATION_ERROR', message, error, status, violations);
|
|
9705
|
+
default:
|
|
9706
|
+
return new ACubeSDKError('UNKNOWN_ERROR', message, error, status, violations);
|
|
9707
|
+
}
|
|
9708
|
+
}
|
|
9709
|
+
return new ACubeSDKError('UNKNOWN_ERROR', 'Unknown error occurred', error);
|
|
9710
|
+
}
|
|
9588
9711
|
|
|
9589
|
-
|
|
9590
|
-
|
|
9591
|
-
|
|
9592
|
-
|
|
9593
|
-
|
|
9594
|
-
|
|
9595
|
-
|
|
9596
|
-
|
|
9597
|
-
|
|
9598
|
-
|
|
9599
|
-
|
|
9600
|
-
|
|
9601
|
-
|
|
9602
|
-
|
|
9603
|
-
|
|
9604
|
-
|
|
9605
|
-
|
|
9606
|
-
|
|
9607
|
-
|
|
9608
|
-
|
|
9609
|
-
|
|
9610
|
-
|
|
9611
|
-
|
|
9612
|
-
|
|
9613
|
-
|
|
9614
|
-
|
|
9615
|
-
displayNameMaxLength: 'Display name is too long (max 255 characters)',
|
|
9616
|
-
};
|
|
9617
|
-
// Validation helper functions
|
|
9618
|
-
const validateInput = (schema, data) => {
|
|
9619
|
-
const result = schema.safeParse(data);
|
|
9620
|
-
if (!result.success) {
|
|
9621
|
-
const errors = result.error.issues.map((error) => ({
|
|
9622
|
-
field: error.path.join('.'),
|
|
9623
|
-
message: error.message,
|
|
9624
|
-
code: error.code,
|
|
9625
|
-
}));
|
|
9712
|
+
var ErrorCategory;
|
|
9713
|
+
(function (ErrorCategory) {
|
|
9714
|
+
ErrorCategory["SERVER_ERROR"] = "SERVER_ERROR";
|
|
9715
|
+
ErrorCategory["CLIENT_ERROR"] = "CLIENT_ERROR";
|
|
9716
|
+
ErrorCategory["AUTH_ERROR"] = "AUTH_ERROR";
|
|
9717
|
+
ErrorCategory["CERTIFICATE_ERROR"] = "CERTIFICATE_ERROR";
|
|
9718
|
+
ErrorCategory["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
9719
|
+
ErrorCategory["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
|
|
9720
|
+
})(ErrorCategory || (ErrorCategory = {}));
|
|
9721
|
+
function extractStatusCode(error) {
|
|
9722
|
+
if (error instanceof MTLSError && error.statusCode) {
|
|
9723
|
+
return error.statusCode;
|
|
9724
|
+
}
|
|
9725
|
+
const errorObj = error;
|
|
9726
|
+
if (errorObj?.response?.status) {
|
|
9727
|
+
return errorObj.response.status;
|
|
9728
|
+
}
|
|
9729
|
+
if (typeof errorObj?.statusCode === 'number') {
|
|
9730
|
+
return errorObj.statusCode;
|
|
9731
|
+
}
|
|
9732
|
+
return undefined;
|
|
9733
|
+
}
|
|
9734
|
+
function classifyError(error) {
|
|
9735
|
+
const statusCode = extractStatusCode(error);
|
|
9736
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
9737
|
+
if (statusCode && statusCode >= 500 && statusCode < 600) {
|
|
9626
9738
|
return {
|
|
9627
|
-
|
|
9628
|
-
|
|
9629
|
-
|
|
9739
|
+
category: ErrorCategory.SERVER_ERROR,
|
|
9740
|
+
statusCode,
|
|
9741
|
+
message: errorMessage,
|
|
9742
|
+
shouldRetry: false,
|
|
9743
|
+
userMessage: `Server error (${statusCode}): The server encountered an error.`,
|
|
9744
|
+
};
|
|
9745
|
+
}
|
|
9746
|
+
if (statusCode === 401 || statusCode === 403) {
|
|
9747
|
+
return {
|
|
9748
|
+
category: ErrorCategory.AUTH_ERROR,
|
|
9749
|
+
statusCode,
|
|
9750
|
+
message: errorMessage,
|
|
9751
|
+
shouldRetry: false,
|
|
9752
|
+
userMessage: `Authentication error (${statusCode}): Invalid credentials or insufficient permissions.`,
|
|
9753
|
+
};
|
|
9754
|
+
}
|
|
9755
|
+
if (statusCode && statusCode >= 400 && statusCode < 500) {
|
|
9756
|
+
return {
|
|
9757
|
+
category: ErrorCategory.CLIENT_ERROR,
|
|
9758
|
+
statusCode,
|
|
9759
|
+
message: errorMessage,
|
|
9760
|
+
shouldRetry: false,
|
|
9761
|
+
userMessage: `Request error (${statusCode}): ${errorMessage}`,
|
|
9762
|
+
};
|
|
9763
|
+
}
|
|
9764
|
+
if (error instanceof MTLSError) {
|
|
9765
|
+
return {
|
|
9766
|
+
category: ErrorCategory.CERTIFICATE_ERROR,
|
|
9767
|
+
statusCode,
|
|
9768
|
+
message: errorMessage,
|
|
9769
|
+
shouldRetry: true,
|
|
9770
|
+
userMessage: 'Certificate error: Unable to establish secure connection.',
|
|
9771
|
+
};
|
|
9772
|
+
}
|
|
9773
|
+
if (!statusCode &&
|
|
9774
|
+
(errorMessage.toLowerCase().includes('network') ||
|
|
9775
|
+
errorMessage.toLowerCase().includes('timeout') ||
|
|
9776
|
+
errorMessage.toLowerCase().includes('connection'))) {
|
|
9777
|
+
return {
|
|
9778
|
+
category: ErrorCategory.NETWORK_ERROR,
|
|
9779
|
+
message: errorMessage,
|
|
9780
|
+
shouldRetry: true,
|
|
9781
|
+
userMessage: 'Network error: Unable to connect to server.',
|
|
9630
9782
|
};
|
|
9631
9783
|
}
|
|
9632
9784
|
return {
|
|
9633
|
-
|
|
9634
|
-
|
|
9635
|
-
|
|
9785
|
+
category: ErrorCategory.UNKNOWN_ERROR,
|
|
9786
|
+
statusCode,
|
|
9787
|
+
message: errorMessage,
|
|
9788
|
+
shouldRetry: false,
|
|
9789
|
+
userMessage: `Unexpected error: ${errorMessage}`,
|
|
9636
9790
|
};
|
|
9637
|
-
}
|
|
9791
|
+
}
|
|
9792
|
+
function shouldReconfigureCertificate(error) {
|
|
9793
|
+
const classification = classifyError(error);
|
|
9794
|
+
return classification.category === ErrorCategory.CERTIFICATE_ERROR;
|
|
9795
|
+
}
|
|
9796
|
+
function shouldRetryRequest(error, isRetryAttempt) {
|
|
9797
|
+
if (isRetryAttempt) {
|
|
9798
|
+
return false;
|
|
9799
|
+
}
|
|
9800
|
+
const classification = classifyError(error);
|
|
9801
|
+
return classification.shouldRetry;
|
|
9802
|
+
}
|
|
9803
|
+
function getUserFriendlyMessage(error) {
|
|
9804
|
+
const classification = classifyError(error);
|
|
9805
|
+
return classification.userMessage;
|
|
9806
|
+
}
|
|
9638
9807
|
|
|
9639
9808
|
var MTLSErrorType;
|
|
9640
9809
|
(function (MTLSErrorType) {
|