@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.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
- class AuthenticationService {
1842
- get user$() {
1843
- return this.userSubject.asObservable();
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
- constructor(httpPort, tokenStorage, config, events = {}) {
1852
- this.httpPort = httpPort;
1853
- this.tokenStorage = tokenStorage;
1854
- this.config = config;
1855
- this.events = events;
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
- async login(credentials) {
1861
- this.authStateSubject.next('authenticating');
1862
- try {
1863
- const response = await this.httpPort.post(`${this.config.authUrl}/login`, {
1864
- email: credentials.email,
1865
- password: credentials.password,
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
- async logout() {
1883
- await this.tokenStorage.clearTokens();
1884
- this.userSubject.next(null);
1885
- this.authStateSubject.next('idle');
1886
- this.events.onUserChanged?.(null);
1870
+ return input;
1871
+ }
1872
+ function clearObjectShallow(obj) {
1873
+ if (!obj || typeof obj !== 'object') {
1874
+ return {};
1887
1875
  }
1888
- async getCurrentUser() {
1889
- const currentUser = this.userSubject.value;
1890
- if (currentUser) {
1891
- return currentUser;
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
- async isAuthenticated() {
1915
- const token = await this.tokenStorage.getAccessToken();
1916
- if (!token) {
1917
- return false;
1918
- }
1919
- const jwtPayload = parseJwt(token);
1920
- return !isTokenExpired(jwtPayload);
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
- async getAccessToken() {
1923
- const token = await this.tokenStorage.getAccessToken();
1924
- if (!token) {
1925
- return null;
1926
- }
1927
- const jwtPayload = parseJwt(token);
1928
- if (isTokenExpired(jwtPayload)) {
1929
- await this.tokenStorage.clearTokens();
1930
- this.userSubject.next(null);
1931
- this.authStateSubject.next('idle');
1932
- this.events.onUserChanged?.(null);
1933
- return null;
1934
- }
1935
- return token;
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
- createUserFromPayload(jwtPayload) {
1914
+ // Check for Web/Browser
1915
+ if (typeof window !== 'undefined' &&
1916
+ typeof window.document !== 'undefined' &&
1917
+ typeof window.navigator !== 'undefined') {
1938
1918
  return {
1939
- id: jwtPayload.uid.toString(),
1940
- email: jwtPayload.username,
1941
- username: jwtPayload.username,
1942
- roles: jwtPayload.roles,
1943
- fid: jwtPayload.fid,
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
- destroy() {
1949
- this.destroy$.next();
1950
- this.destroy$.complete();
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
- const CERTIFICATE_KEY = 'acube_certificate';
1955
- class CertificateService {
1956
- get certificate$() {
1957
- return this.certificateSubject.asObservable();
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
- get hasCertificate$() {
1960
- return this.certificateSubject.pipe(map((cert) => cert !== null), distinctUntilChanged());
1953
+ catch {
1954
+ return false;
1961
1955
  }
1962
- get state$() {
1963
- return this.stateSubject.asObservable();
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
- constructor(secureStorage) {
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 AuthStrategy {
2061
- constructor(jwtHandler, mtlsHandler, userProvider, mtlsAdapter) {
2062
- this.jwtHandler = jwtHandler;
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
- async determineAuthConfig(url, method, explicitMode) {
2068
- if (this.isNotificationEndpoint(url) || this.isTelemetryEndpoint(url)) {
2069
- return { mode: 'mtls', usePort444: true };
2070
- }
2071
- const platform = this.detectPlatform();
2072
- const userRole = await this.getUserRole();
2073
- const isReceiptEndpoint = this.isReceiptEndpoint(url);
2074
- if (userRole === 'SUPPLIER') {
2075
- return { mode: 'jwt', usePort444: false };
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
- detectPlatform() {
2137
- if (!this.mtlsAdapter) {
2138
- return 'web';
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
- async getUserRole() {
2144
- if (!this.userProvider) {
2145
- return null;
2146
- }
2147
- const user = await this.userProvider.getCurrentUser();
2148
- if (!user || !user.roles) {
2149
- return null;
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
- isDetailedReceiptEndpoint(url) {
2170
- return !!(url.match(/\/receipts\/[a-f0-9-]+\/details$/) ||
2171
- url.match(/\/mf1\/receipts\/[a-f0-9-]+\/details$/));
2410
+ getConfig() {
2411
+ return {
2412
+ environment: this.config.environment,
2413
+ debug: this.config.debug,
2414
+ };
2172
2415
  }
2173
- isNotificationEndpoint(url) {
2174
- return url.includes('/mf1/notifications');
2416
+ getApiUrl() {
2417
+ return this.config.apiUrl;
2175
2418
  }
2176
- isTelemetryEndpoint(url) {
2177
- return !!url.match(/\/mf1\/pems\/[^/]+\/telemetry/);
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
- async getAuthConfig(_url, _method) {
2186
- return { mode: 'jwt', usePort444: false };
2425
+ isDebugEnabled() {
2426
+ return this.config.debug;
2187
2427
  }
2188
- async getAuthHeaders() {
2189
- const token = await this.tokenStorage.getAccessToken();
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
- if (Array.isArray(input)) {
2202
- const cleanedArray = input
2203
- .map((item) => clearObject(item))
2204
- .filter((item) => item !== undefined);
2205
- return cleanedArray;
2434
+ getCustomHeaders() {
2435
+ return { ...this.config.customHeaders };
2206
2436
  }
2207
- if (typeof input === 'object' && input.constructor === Object) {
2208
- const cleaned = {};
2209
- for (const [key, value] of Object.entries(input)) {
2210
- const cleanedValue = clearObject(value);
2211
- if (cleanedValue !== undefined) {
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
- return cleaned;
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
- * Platform detection utilities
2243
- */
2244
- /**
2245
- * Detect the current platform
2246
- */
2247
- function detectPlatform() {
2248
- // Check for React Native
2249
- if (typeof global !== 'undefined' &&
2250
- global.__DEV__ !== undefined &&
2251
- typeof global.navigator !== 'undefined' &&
2252
- global.navigator.product === 'ReactNative') {
2253
- return {
2254
- platform: 'react-native',
2255
- isReactNative: true,
2256
- isWeb: false,
2257
- isNode: false,
2258
- isExpo: checkExpo(),
2259
- };
2260
- }
2261
- // Check for Web/Browser
2262
- if (typeof window !== 'undefined' &&
2263
- typeof window.document !== 'undefined' &&
2264
- typeof window.navigator !== 'undefined') {
2265
- return {
2266
- platform: 'web',
2267
- isReactNative: false,
2268
- isWeb: true,
2269
- isNode: false,
2270
- isExpo: false,
2271
- };
2272
- }
2273
- // Check for Node.js
2274
- if (typeof process !== 'undefined' && process.versions && process.versions.node) {
2275
- return {
2276
- platform: 'node',
2277
- isReactNative: false,
2278
- isWeb: false,
2279
- isNode: true,
2280
- isExpo: false,
2281
- };
2282
- }
2283
- // Unknown platform
2284
- return {
2285
- platform: 'unknown',
2286
- isReactNative: false,
2287
- isWeb: false,
2288
- isNode: false,
2289
- isExpo: false,
2290
- };
2291
- }
2292
- /**
2293
- * Check if running in Expo
2294
- */
2295
- function checkExpo() {
2296
- try {
2297
- return (typeof global !== 'undefined' &&
2298
- (typeof global.Expo !== 'undefined' || typeof global.expo !== 'undefined'));
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
- catch {
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
- function getDefaultExportFromCjs (x) {
2306
- return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
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
- var picocolors_browser = {exports: {}};
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
- var x=String;
2312
- 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}};
2313
- picocolors_browser.exports=create();
2314
- picocolors_browser.exports.createColors = create;
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
- var picocolors_browserExports = picocolors_browser.exports;
2317
- var pc = /*@__PURE__*/getDefaultExportFromCjs(picocolors_browserExports);
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
- const levelColors = {
2320
- debug: {
2321
- prefix: pc.gray,
2322
- message: pc.gray,
2323
- data: pc.dim,
2324
- },
2325
- info: {
2326
- prefix: pc.cyan,
2327
- message: pc.white,
2328
- data: pc.dim,
2329
- },
2330
- warn: {
2331
- prefix: pc.yellow,
2332
- message: pc.yellow,
2333
- data: pc.dim,
2334
- },
2335
- error: {
2336
- prefix: pc.red,
2337
- message: pc.red,
2338
- data: pc.dim,
2339
- },
2340
- };
2341
- const levelIcons = {
2342
- debug: '🔍',
2343
- info: 'ℹ️ ',
2344
- warn: '⚠️ ',
2345
- error: '',
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
- * Format data for pretty display in logs
2349
- */
2350
- function formatData(data, level) {
2351
- if (data === undefined || data === null) {
2352
- return String(data);
2353
- }
2354
- // For primitive types, just return as string
2355
- if (typeof data !== 'object') {
2356
- return String(data);
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
- // For Error objects, format nicely
2359
- if (data instanceof Error) {
2360
- return `${data.name}: ${data.message}${data.stack ? `\n${pc.dim(data.stack)}` : ''}`;
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
- try {
2363
- // Apply level-specific coloring
2364
- const colors = levelColors[level];
2365
- return colors.data(JSON.stringify(data, null, 2));
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
- catch {
2368
- // Fallback to JSON.stringify if pretty-format fails
2369
- try {
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
- catch {
2373
- return String(data);
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
- class Logger {
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
- info(prefix, message, data) {
2393
- if (!this.enabled)
2394
- return;
2395
- this.log('info', prefix, message, data);
2980
+ getMtlsHandler() {
2981
+ return this.mtlsHandler;
2396
2982
  }
2397
- warn(prefix, message, data) {
2398
- this.log('warn', prefix, message, data);
2983
+ getJwtHandler() {
2984
+ return this.jwtHandler;
2399
2985
  }
2400
- error(prefix, message, data) {
2401
- this.log('error', prefix, message, data);
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
- log(level, prefix, message, data) {
2404
- const colors = levelColors[level];
2405
- const icon = levelIcons[level];
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
- else {
2430
- consoleMethod(header);
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
- * Formats a numeric string value to have exactly the specified number of decimal places.
2446
- * E.g., "1" → "1.00", "10" → "10.00", "1.5" → "1.50"
2447
- * Returns undefined for undefined input (preserves optional fields).
2448
- *
2449
- * @param value - The string value to format
2450
- * @param decimals - Number of decimal places (default: 2)
2451
- * @returns Formatted string or undefined if input is undefined
2452
- */
2453
- function formatDecimal(value, decimals = 2) {
2454
- if (value === undefined)
2455
- return undefined;
2456
- const num = parseFloat(value);
2457
- if (isNaN(num))
2458
- return value;
2459
- return num.toFixed(decimals);
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('Auth token updated:', { hasToken: !!token });
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 log$2 = createPrefixedLogger('HTTP-MTLS');
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
- log$2.debug('mTLS adapter configured:', !!adapter);
8058
+ logMtls.debug('mTLS adapter configured:', !!adapter);
7555
8059
  }
7556
8060
  setAuthStrategy(strategy) {
7557
8061
  this.authStrategy = strategy;
7558
- log$2.debug('Auth strategy configured:', !!strategy);
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
- log$2.debug(`Auth config for ${method} ${url}:`, config);
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
- return url.startsWith('/mf1') || url.startsWith('/mf2');
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
- log$2.debug('JWT token present for mTLS request');
8094
+ logMtls.debug('JWT token present for mTLS request');
7584
8095
  }
7585
8096
  else {
7586
- log$2.warn('No JWT token for mTLS request');
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
- log$2.debug(`mTLS ${method} ${fullUrl}`);
8106
+ logMtls.debug(`mTLS ${method} ${fullUrl}`);
7596
8107
  if (data) {
7597
- log$2.debug('Request body:', data);
8108
+ logMtls.debug('Request body:', data);
7598
8109
  }
7599
8110
  try {
7600
8111
  const response = await this.mtlsAdapter.request(mtlsConfig);
7601
- log$2.debug(`mTLS Response ${response.status} from ${fullUrl}`);
8112
+ logMtls.debug(`mTLS Response ${response.status} from ${fullUrl}`);
7602
8113
  if (response.data) {
7603
- log$2.debug('Response body:', response.data);
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
- log$2.error(`mTLS Response error from ${fullUrl}:`, error);
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
- log$2.error('Response body:', axiosError.response.data);
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
- log$2.debug('Adding JWT token to request');
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
- log$2.warn('No JWT token available for request:', { url: config.url });
8148
+ logJwt.warn('No JWT token available for request:', { url: config.url });
7635
8149
  }
7636
8150
  const method = config.method?.toUpperCase() ?? 'UNKNOWN';
7637
- log$2.debug(`→ ${method} ${config.url}`);
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
- log$2.debug('Request params:', config.params);
8160
+ logJwt.debug('Request params:', config.params);
7640
8161
  }
7641
8162
  if (config.data) {
7642
- log$2.debug('Request body:', config.data);
8163
+ logJwt.debug('Request body:', config.data);
7643
8164
  }
7644
8165
  return config;
7645
8166
  }, (error) => {
7646
- log$2.error('Request error:', error);
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
- log$2.debug(`← ${method} ${response.status} ${response.config.url}`);
8172
+ logJwt.debug(`← ${method} ${response.status} ${response.config.url}`);
7652
8173
  if (response.data) {
7653
- log$2.debug('Response body:', response.data);
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
- log$2.error(`← ${method} ${error.response?.status ?? 'ERR'} ${error.config?.url ?? 'unknown'}`);
8179
+ logJwt.error(`← ${method} ${error.response?.status ?? 'ERR'} ${error.config?.url ?? 'unknown'}`);
7659
8180
  if (error.response?.data) {
7660
- log$2.error('Response body:', error.response.data);
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
- log$2.info('setAuthToken called:', { hasToken: !!token, tokenPrefix: token?.substring(0, 20) });
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$1 = createPrefixedLogger('SDK');
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$1.debug('SDK already initialized, skipping');
8424
+ log$2.debug('SDK already initialized, skipping');
7901
8425
  return;
7902
8426
  }
7903
- log$1.info('Initializing SDK', {
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$1.debug('Loading platform adapters');
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$1.info('Platform adapters loaded', {
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$1.debug('Creating DI container');
8452
+ log$2.debug('Creating DI container');
7929
8453
  this.container = SDKFactory.createContainer(factoryConfig);
7930
- log$1.debug('Registering auth services');
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$1.info('Registering cache services', {
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$1.debug('No cache adapter available, caching disabled');
8463
+ log$2.debug('No cache adapter available, caching disabled');
7940
8464
  }
7941
- log$1.debug('Initializing certificate service');
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$1.debug('Initializing authentication service');
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$1.debug('Initializing offline manager');
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
- if (await this.authService.isAuthenticated()) {
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$1.debug('Connecting mTLS adapter to HTTP port');
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$1.debug('Configuring auth strategy');
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$1.warn('Certificate auto-configuration failed, will retry on demand', {
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$1.info('SDK initialized successfully', {
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$1.error('SDK initialization failed', {
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$1.info('Login attempt', { email: credentials.email });
8646
+ log$2.info('Login attempt', { email: credentials.email });
8108
8647
  const user = await this.authService.login(credentials);
8109
- log$1.info('Login successful', { roles: user.roles });
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$1.debug('Auth token set on HTTP port');
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$1.info('Logout');
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
- constructor(notifications$, networkPort) {
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 start polling if allowed
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.notificationService.startPolling();
8810
- await this.startTelemetryPollingAuto();
8811
- this.isPollingActive = true;
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
- * Manually trigger a notification sync
9392
+ * Observable stream indicating if app is blocked
8930
9393
  */
8931
- async syncNotifications() {
9394
+ get isBlocked$() {
8932
9395
  this.ensureInitialized();
8933
- await this.notificationService.triggerSync();
9396
+ return this.appStateService.isBlocked$;
8934
9397
  }
8935
9398
  /**
8936
- * Manually trigger a telemetry sync
9399
+ * Observable stream of warning state with countdown
8937
9400
  */
8938
- async syncTelemetry() {
9401
+ get warning$() {
8939
9402
  this.ensureInitialized();
8940
- return this.telemetryService.triggerSync();
9403
+ return this.appStateService.warning$;
8941
9404
  }
8942
9405
  /**
8943
- * Check if the manager is initialized
9406
+ * Observable stream indicating if certificate is missing
9407
+ * When true, polling is blocked and the user should install a certificate
8944
9408
  */
8945
- getIsInitialized() {
8946
- return this.isInitialized;
9409
+ get certificateMissing$() {
9410
+ return this.certificateMissingSubject.asObservable();
8947
9411
  }
8948
9412
  /**
8949
- * Get the underlying SDK instance (for advanced use cases)
9413
+ * Observable stream of telemetry state (data, isLoading, isCached, error)
8950
9414
  */
8951
- getSDK() {
9415
+ get telemetryState$() {
8952
9416
  this.ensureInitialized();
8953
- return this.sdk;
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
- async invalidateCache(pattern) {
9044
- if (!this.cache)
9045
- return;
9419
+ /**
9420
+ * Get the pemId from the installed certificate
9421
+ */
9422
+ async getPemId() {
9423
+ this.ensureInitialized();
9046
9424
  try {
9047
- await this.cache.invalidate(pattern);
9425
+ const certInfo = await this.sdk.getCertificatesInfo();
9426
+ return certInfo?.pemId ?? null;
9048
9427
  }
9049
- catch (error) {
9050
- log.error('Invalidation failed:', error instanceof Error ? error.message : error);
9428
+ catch {
9429
+ return null;
9051
9430
  }
9052
9431
  }
9053
- getCacheStatus() {
9054
- return {
9055
- available: !!this.cache,
9056
- networkMonitorAvailable: !!this.networkMonitor,
9057
- isOnline: this.isOnline(),
9058
- };
9059
- }
9060
- destroy() {
9061
- this.networkSubscription?.unsubscribe();
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
- return new ACubeSDKError('UNKNOWN_ERROR', 'Unknown error occurred', error);
9100
- }
9101
-
9102
- var ErrorCategory;
9103
- (function (ErrorCategory) {
9104
- ErrorCategory["SERVER_ERROR"] = "SERVER_ERROR";
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
- if (error instanceof MTLSError) {
9155
- return {
9156
- category: ErrorCategory.CERTIFICATE_ERROR,
9157
- statusCode,
9158
- message: errorMessage,
9159
- shouldRetry: true,
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
- if (!statusCode &&
9164
- (errorMessage.toLowerCase().includes('network') ||
9165
- errorMessage.toLowerCase().includes('timeout') ||
9166
- errorMessage.toLowerCase().includes('connection'))) {
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
- category: ErrorCategory.NETWORK_ERROR,
9169
- message: errorMessage,
9170
- shouldRetry: true,
9171
- userMessage: 'Network error: Unable to connect to server.',
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
- return {
9175
- category: ErrorCategory.UNKNOWN_ERROR,
9176
- statusCode,
9177
- message: errorMessage,
9178
- shouldRetry: false,
9179
- userMessage: `Unexpected error: ${errorMessage}`,
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
- const classification = classifyError(error);
9191
- return classification.shouldRetry;
9192
- }
9193
- function getUserFriendlyMessage(error) {
9194
- const classification = classifyError(error);
9195
- return classification.userMessage;
9196
- }
9197
-
9198
- // Enum options arrays
9199
- const VAT_RATE_CODE_OPTIONS = [
9200
- '4.00',
9201
- '5.00',
9202
- '10.00',
9203
- '22.00',
9204
- '2.00',
9205
- '6.40',
9206
- '7.00',
9207
- '7.30',
9208
- '7.50',
9209
- '7.65',
9210
- '7.95',
9211
- '8.30',
9212
- '8.50',
9213
- '8.80',
9214
- '9.50',
9215
- '12.30',
9216
- 'N1',
9217
- 'N2',
9218
- 'N3',
9219
- 'N4',
9220
- 'N5',
9221
- 'N6',
9222
- ];
9223
- const GOOD_OR_SERVICE_OPTIONS = ['goods', 'service'];
9224
- const RECEIPT_PROOF_TYPE_OPTIONS = ['POS', 'VR', 'ND'];
9225
- // Enum types for receipt validation
9226
- const VatRateCodeSchema = z.enum(VAT_RATE_CODE_OPTIONS);
9227
- const GoodOrServiceSchema = z.enum(GOOD_OR_SERVICE_OPTIONS);
9228
- const ReceiptProofTypeSchema = z.enum(RECEIPT_PROOF_TYPE_OPTIONS);
9229
- // Receipt Item Schema
9230
- const ReceiptItemSchema = z.object({
9231
- type: GoodOrServiceSchema.optional(),
9232
- quantity: z.string().min(1, { error: 'fieldIsRequired' }),
9233
- description: z.string().min(1, { error: 'fieldIsRequired' }),
9234
- unit_price: z.string().min(1, { error: 'fieldIsRequired' }),
9235
- vat_rate_code: VatRateCodeSchema.optional(),
9236
- simplified_vat_allocation: z.boolean().optional(),
9237
- discount: z.string().nullable().optional(),
9238
- is_down_payment_or_voucher_redemption: z.boolean().optional(),
9239
- complimentary: z.boolean().optional(),
9240
- });
9241
- // Main Receipt Input Schema
9242
- const ReceiptInputSchema = z
9243
- .object({
9244
- items: z.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
9245
- customer_tax_code: z.string().optional(),
9246
- customer_lottery_code: z.string().optional(),
9247
- discount: z.string().nullable().optional(),
9248
- invoice_issuing: z.boolean().optional(),
9249
- uncollected_dcr_to_ssn: z.boolean().optional(),
9250
- services_uncollected_amount: z.string().nullable().optional(),
9251
- goods_uncollected_amount: z.string().nullable().optional(),
9252
- cash_payment_amount: z.string().nullable().optional(),
9253
- electronic_payment_amount: z.string().nullable().optional(),
9254
- ticket_restaurant_payment_amount: z.string().nullable().optional(),
9255
- ticket_restaurant_quantity: z.number().optional(),
9256
- })
9257
- .refine((data) => {
9258
- // At least one payment method should be provided
9259
- const hasCashPayment = data.cash_payment_amount && parseFloat(data.cash_payment_amount) > 0;
9260
- const hasElectronicPayment = data.electronic_payment_amount && parseFloat(data.electronic_payment_amount) > 0;
9261
- const hasTicketPayment = data.ticket_restaurant_payment_amount &&
9262
- parseFloat(data.ticket_restaurant_payment_amount) > 0;
9263
- return hasCashPayment || hasElectronicPayment || hasTicketPayment;
9264
- }, {
9265
- error: 'At least one payment method is required',
9266
- path: ['payment_methods'],
9267
- })
9268
- .refine((data) => {
9269
- // only one between customer_tax_code and customer_lottery_code can be provided
9270
- return !data.customer_tax_code || !data.customer_lottery_code;
9271
- }, {
9272
- error: 'Only one between customer_tax_code and customer_lottery_code can be provided',
9273
- path: ['customer_tax_code', 'customer_lottery_code'],
9274
- });
9275
- // Receipt Return or Void via PEM Schema
9276
- const ReceiptReturnOrVoidViaPEMInputSchema = z.object({
9277
- device_id: z.string().optional(),
9278
- items: z.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
9279
- document_number: z.string().min(1, { error: 'fieldIsRequired' }),
9280
- document_datetime: z.string().optional(),
9281
- lottery_code: z.string().optional(),
9282
- });
9283
- // Receipt Return or Void with Proof Schema
9284
- const ReceiptReturnOrVoidWithProofInputSchema = z.object({
9285
- items: z.array(ReceiptItemSchema).min(1, { error: 'arrayMin1' }),
9286
- proof: ReceiptProofTypeSchema,
9287
- document_datetime: z.string().min(1, { error: 'fieldIsRequired' }),
9288
- });
9289
- // Void Receipt Schema
9290
- const VoidReceiptInputSchema = z.object({
9291
- document_number: z.string().min(1, { error: 'fieldIsRequired' }),
9292
- });
9293
- const ReceiptReturnItemSchema = z
9294
- .array(z.object({
9295
- id: z.number(),
9296
- quantity: z.string().min(1, { error: 'fieldIsRequired' }),
9297
- }))
9298
- .min(1, { error: 'arrayMin1' });
9299
- // Receipt Return Schema
9300
- const ReceiptReturnInputSchema = z.object({
9301
- items: z.array(ReceiptReturnItemSchema).min(1, { error: 'arrayMin1' }),
9302
- document_number: z.string().min(1, { error: 'fieldIsRequired' }),
9303
- });
9304
-
9305
- // Cashier Create Input Schema (MF1)
9306
- const CashierCreateInputSchema = z.object({
9307
- email: z
9308
- .string()
9309
- .min(1, { error: 'fieldIsRequired' })
9310
- .max(255, { error: 'emailMaxLength' })
9311
- .email({ error: 'invalidEmail' }),
9312
- password: z
9313
- .string()
9314
- .min(8, { error: 'passwordMinLength' })
9315
- .max(40, { error: 'passwordMaxLength' }),
9316
- name: z.string().min(1, { error: 'fieldIsRequired' }).max(255, { error: 'nameMaxLength' }),
9317
- display_name: z
9318
- .string()
9319
- .min(1, { error: 'fieldIsRequired' })
9320
- .max(255, { error: 'displayNameMaxLength' }),
9321
- });
9322
-
9323
- // Enum options arrays
9324
- const PEM_STATUS_OPTIONS = [
9325
- 'NEW',
9326
- 'REGISTERED',
9327
- 'ACTIVATED',
9328
- 'ONLINE',
9329
- 'OFFLINE',
9330
- 'DISCARDED',
9331
- ];
9332
- // Address Schema (reusable)
9333
- const AddressSchema = z.object({
9334
- street_address: z.string().min(1, { error: 'fieldIsRequired' }),
9335
- street_number: z.string().min(1, { error: 'fieldIsRequired' }),
9336
- zip_code: z
9337
- .string()
9338
- .min(1, { error: 'fieldIsRequired' })
9339
- .regex(/^\d{5}$/, { error: 'invalidZipCode' }),
9340
- city: z.string().min(1, { error: 'fieldIsRequired' }),
9341
- province: z
9342
- .string()
9343
- .min(2, { error: 'provinceMinLength' })
9344
- .max(2, { error: 'provinceMaxLength' })
9345
- .toUpperCase(),
9346
- });
9347
- // PEM Status Schema
9348
- const PEMStatusSchema = z.enum(PEM_STATUS_OPTIONS);
9349
- // Activation Request Schema
9350
- const ActivationRequestSchema = z.object({
9351
- registration_key: z.string().min(1, { error: 'fieldIsRequired' }),
9352
- });
9353
- // PEM Status Offline Request Schema
9354
- const PEMStatusOfflineRequestSchema = z.object({
9355
- timestamp: z
9356
- .string()
9357
- .min(1, { error: 'fieldIsRequired' })
9358
- .refine((val) => !isNaN(Date.parse(val)), {
9359
- error: 'invalidDateFormat',
9360
- }),
9361
- reason: z.string().min(1, { error: 'fieldIsRequired' }),
9362
- });
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
- // Cash Register Create Schema
9365
- const CashRegisterCreateSchema = z.object({
9366
- pem_serial_number: z.string().min(1, { error: 'fieldIsRequired' }),
9367
- name: z.string().min(1, { error: 'fieldIsRequired' }).max(100, { error: 'nameMaxLength' }),
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
- // VAT number validation regex (Partita IVA - 11 digits)
9371
- const VAT_NUMBER_REGEX = /^\d{11}$/;
9372
- // Fiscal code validation regex (Codice Fiscale - 11 digits only for merchants)
9373
- const FISCAL_CODE_REGEX = /^\d{11}$/;
9374
- // Password validation regex (from OpenAPI spec)
9375
- const PASSWORD_REGEX = /^((?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{10,}).*)$/;
9376
- // Merchant Create Input Schema
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
- // At least one naming method must be provided
9403
- if (!hasBusinessName && !hasPersonalNames) {
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
- return true;
9407
- }, {
9408
- error: 'businessNameOrPersonalNamesRequired',
9409
- path: ['business_name'],
9410
- });
9411
- // Merchant Update Input Schema
9412
- const MerchantUpdateInputSchema = z.object({
9413
- business_name: z.string().max(200, { error: 'businessNameMaxLength' }).optional().nullable(),
9414
- first_name: z.string().max(100, { error: 'firstNameMaxLength' }).optional().nullable(),
9415
- last_name: z.string().max(100, { error: 'lastNameMaxLength' }).optional().nullable(),
9416
- address: AddressSchema.optional().nullable(),
9417
- });
9418
-
9419
- // Enum options arrays
9420
- const PEM_TYPE_OPTIONS = ['AP', 'SP', 'TM', 'PV'];
9421
- // PEM Data Schema
9422
- const PemDataSchema = z.object({
9423
- version: z.string().min(1, { error: 'fieldIsRequired' }),
9424
- type: z.enum(PEM_TYPE_OPTIONS, {
9425
- error: 'invalidPemType',
9426
- }),
9427
- });
9428
- // PEM Create Input Schema
9429
- const PemCreateInputSchema = z.object({
9430
- merchant_uuid: z.string().min(1, { error: 'fieldIsRequired' }).uuid({ error: 'invalidUuid' }),
9431
- address: AddressSchema.optional(),
9432
- /* external_pem_data: PemDataSchema.optional(), */
9433
- });
9434
-
9435
- // Italian Fiscal ID validation regex (Codice Fiscale for individuals or Partita IVA for companies)
9436
- const FISCAL_ID_REGEX = /^([A-Z]{6}[0-9LMNPQRSTUV]{2}[ABCDEHLMPRST][0-9LMNPQRSTUV]{2}[A-Z][0-9LMNPQRSTUV]{3}[A-Z]|[0-9]{11})$/;
9437
- // Supplier Create Input Schema
9438
- const SupplierCreateInputSchema = z.object({
9439
- fiscal_id: z
9440
- .string()
9441
- .min(1, { error: 'fieldIsRequired' })
9442
- .regex(FISCAL_ID_REGEX, { error: 'invalidFiscalId' })
9443
- .toUpperCase(),
9444
- name: z.string().min(1, { error: 'fieldIsRequired' }).max(200, { error: 'nameMaxLength' }),
9445
- address: AddressSchema.optional(),
9446
- });
9447
- // Supplier Update Input Schema
9448
- const SupplierUpdateInputSchema = z.object({
9449
- name: z.string().min(1, { error: 'fieldIsRequired' }).max(200, { error: 'nameMaxLength' }),
9450
- address: AddressSchema.optional(),
9451
- });
9452
-
9453
- // Journal Close Input Schema
9454
- const JournalCloseInputSchema = z.object({
9455
- closing_timestamp: z
9456
- .string()
9457
- .min(1, { error: 'fieldIsRequired' })
9458
- .refine((val) => !isNaN(Date.parse(val)), {
9459
- error: 'invalidDateFormat',
9460
- }),
9461
- reason: z.string().max(255, { error: 'reasonMaxLength' }).optional(),
9462
- });
9463
-
9464
- // Daily Report Status Options
9465
- const DAILY_REPORT_STATUS_OPTIONS = ['pending', 'sent', 'error'];
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
- const TelemetryMerchantSchema = z.object({
9532
- vat_number: z.string(),
9533
- fiscal_code: z.string().nullable(),
9534
- business_name: z.string(),
9535
- });
9536
- const TelemetrySupplierSchema = z.object({
9537
- vat_number: z.string(),
9538
- fiscal_code: z.string().nullable(),
9539
- business_name: z.string(),
9540
- });
9541
- const TelemetrySoftwareVersionSchema = z.object({
9542
- version: z.string(),
9543
- swid: z.string(),
9544
- installed_at: z.string(),
9545
- status: z.enum(['active', 'inactive', 'archived']),
9546
- });
9547
- const TelemetrySoftwareSchema = z.object({
9548
- code: z.string(),
9549
- name: z.string(),
9550
- approval_reference: z.string(),
9551
- version_info: TelemetrySoftwareVersionSchema,
9552
- });
9553
- const PendingReceiptsSchema = z.object({
9554
- count: z.number().int().nonnegative(),
9555
- total_amount: z.string(),
9556
- });
9557
- const TransmissionSchema = z.object({
9558
- attempted_at: z.string(),
9559
- outcome: z.enum(['success', 'failed', 'pending']),
9560
- });
9561
- const MessageSchema = z.object({
9562
- received_at: z.string(),
9563
- content: z.string(),
9564
- });
9565
- const LotterySecretRequestSchema = z.object({
9566
- requested_at: z.string(),
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
- // Receipt schemas and types
9590
- // Common validation utilities
9591
- const ValidationMessages = {
9592
- fieldIsRequired: 'This field is required',
9593
- arrayMin1: 'At least one item is required',
9594
- paymentMethodRequired: 'At least one payment method is required',
9595
- invalidEmail: 'Please enter a valid email address',
9596
- passwordMinLength: 'Password must be at least 8 characters long',
9597
- passwordComplexity: 'Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character',
9598
- invalidZipCode: 'Please enter a valid 5-digit zip code',
9599
- provinceMinLength: 'Province code must be 2 characters',
9600
- provinceMaxLength: 'Province code must be 2 characters',
9601
- invalidDateFormat: 'Please enter a valid date',
9602
- nameMaxLength: 'Name is too long',
9603
- invalidFiscalId: 'Please enter a valid Italian fiscal ID (Codice Fiscale or Partita IVA)',
9604
- invalidVatNumber: 'Please enter a valid VAT number (11 digits)',
9605
- invalidFiscalCode: 'Please enter a valid fiscal code (11 digits)',
9606
- businessNameMaxLength: 'Business name is too long (max 200 characters)',
9607
- businessNameOrPersonalNamesRequired: 'Please provide either a business name or first/last name, but not both',
9608
- firstNameMaxLength: 'First name is too long (max 100 characters)',
9609
- lastNameMaxLength: 'Last name is too long (max 100 characters)',
9610
- invalidUuid: 'Please enter a valid UUID',
9611
- invalidPemType: 'PEM type must be one of: AP, SP, TM, PV',
9612
- reasonMaxLength: 'Reason is too long (max 255 characters)',
9613
- pageMinValue: 'Page number must be at least 1',
9614
- invalidDailyReportStatus: 'Daily report status must be one of: pending, sent, error',
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
- success: false,
9628
- errors,
9629
- data: null,
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
- success: true,
9634
- errors: [],
9635
- data: result.data,
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) {