@arsedizioni/ars-utils 20.4.5 → 20.4.7

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.
@@ -1070,6 +1070,138 @@ class SystemUtils {
1070
1070
  const luminance = 0.299 * r + 0.587 * g + 0.114 * b;
1071
1071
  return luminance > minimumLuminance;
1072
1072
  }
1073
+ /**
1074
+ * Get a unique machine id
1075
+ * @returns : the machine id
1076
+ */
1077
+ static getMachineId() {
1078
+ const fingerprint = this.collectFingerprint();
1079
+ const hash = this.generateHash(JSON.stringify(fingerprint));
1080
+ return this.formatAsGuid(hash);
1081
+ }
1082
+ /**
1083
+ * Format as guid
1084
+ */
1085
+ static formatAsGuid(hash) {
1086
+ const paddedHash = hash.padEnd(32, '0');
1087
+ return `${paddedHash.substring(0, 8)}-${paddedHash.substring(8, 12)}-${paddedHash.substring(12, 16)}-${paddedHash.substring(16, 20)}-${paddedHash.substring(20, 32)}`;
1088
+ }
1089
+ /**
1090
+ * Browser fingerprint
1091
+ */
1092
+ static collectFingerprint() {
1093
+ const nav = navigator;
1094
+ return {
1095
+ // Only hardware specs
1096
+ hardwareConcurrency: nav.hardwareConcurrency || 0,
1097
+ deviceMemory: nav.deviceMemory || 0,
1098
+ maxTouchPoints: nav.maxTouchPoints || 0,
1099
+ screenResolution: `${screen.width}x${screen.height}`,
1100
+ colorDepth: screen.colorDepth,
1101
+ pixelDepth: screen.pixelDepth,
1102
+ availScreen: `${screen.availWidth}x${screen.availHeight}`,
1103
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
1104
+ timezoneOffset: new Date().getTimezoneOffset(),
1105
+ webgl: this.getWebGLFingerprint(),
1106
+ platform: this.normalizePlatform(nav.platform || ''),
1107
+ fonts: this.detectFonts()
1108
+ };
1109
+ }
1110
+ /**
1111
+ * Platform
1112
+ */
1113
+ static normalizePlatform(platform) {
1114
+ platform = platform.toLowerCase();
1115
+ if (platform.includes('win'))
1116
+ return 'windows';
1117
+ if (platform.includes('mac'))
1118
+ return 'macos';
1119
+ if (platform.includes('linux'))
1120
+ return 'linux';
1121
+ return platform;
1122
+ }
1123
+ /**
1124
+ * WebGL fingerprinting
1125
+ */
1126
+ static getWebGLFingerprint() {
1127
+ try {
1128
+ const canvas = document.createElement('canvas');
1129
+ const gl = canvas.getContext('webgl') ||
1130
+ canvas.getContext('experimental-webgl');
1131
+ if (!gl)
1132
+ return '';
1133
+ const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
1134
+ if (!debugInfo)
1135
+ return '';
1136
+ const vendor = gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL);
1137
+ const renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
1138
+ return `${vendor}~${renderer}`;
1139
+ }
1140
+ catch (e) {
1141
+ return '';
1142
+ }
1143
+ }
1144
+ /**
1145
+ * Get all fonts
1146
+ */
1147
+ static detectFonts() {
1148
+ const baseFonts = ['monospace', 'sans-serif', 'serif'];
1149
+ const testFonts = [
1150
+ 'Arial', 'Verdana', 'Times New Roman', 'Courier New',
1151
+ 'Georgia', 'Palatino', 'Comic Sans MS', 'Impact'
1152
+ ];
1153
+ const canvas = document.createElement('canvas');
1154
+ const ctx = canvas.getContext('2d');
1155
+ if (!ctx)
1156
+ return '';
1157
+ const testString = 'mmmmmmmmmmlli';
1158
+ const testSize = '72px';
1159
+ const baseMeasurements = {};
1160
+ baseFonts.forEach(font => {
1161
+ ctx.font = `${testSize} ${font}`;
1162
+ baseMeasurements[font] = ctx.measureText(testString).width;
1163
+ });
1164
+ const detectedFonts = [];
1165
+ testFonts.forEach(font => {
1166
+ let detected = false;
1167
+ for (const baseFont of baseFonts) {
1168
+ ctx.font = `${testSize} ${font}, ${baseFont}`;
1169
+ const width = ctx.measureText(testString).width;
1170
+ if (width !== baseMeasurements[baseFont]) {
1171
+ detected = true;
1172
+ break;
1173
+ }
1174
+ }
1175
+ if (detected) {
1176
+ detectedFonts.push(font);
1177
+ }
1178
+ });
1179
+ return detectedFonts.join(',');
1180
+ }
1181
+ /**
1182
+ * Generate hash SHA-256-like
1183
+ */
1184
+ static generateHash(str) {
1185
+ let h1 = 0xdeadbeef;
1186
+ let h2 = 0x41c6ce57;
1187
+ let h3 = 0x12345678;
1188
+ let h4 = 0x9abcdef0;
1189
+ for (let i = 0; i < str.length; i++) {
1190
+ const ch = str.charCodeAt(i);
1191
+ h1 = Math.imul(h1 ^ ch, 2654435761);
1192
+ h2 = Math.imul(h2 ^ ch, 1597334677);
1193
+ h3 = Math.imul(h3 ^ ch, 2246822507);
1194
+ h4 = Math.imul(h4 ^ ch, 3266489909);
1195
+ }
1196
+ h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
1197
+ h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
1198
+ h3 = Math.imul(h3 ^ (h3 >>> 16), 2246822507) ^ Math.imul(h4 ^ (h4 >>> 13), 3266489909);
1199
+ h4 = Math.imul(h4 ^ (h4 >>> 16), 2246822507) ^ Math.imul(h3 ^ (h3 >>> 13), 3266489909);
1200
+ return (h1 >>> 0).toString(16).padStart(8, '0') +
1201
+ (h2 >>> 0).toString(16).padStart(8, '0') +
1202
+ (h3 >>> 0).toString(16).padStart(8, '0') +
1203
+ (h4 >>> 0).toString(16).padStart(8, '0');
1204
+ }
1073
1205
  }
1074
1206
 
1075
1207
  class DateIntervalChangeDirective {