@geogirafe/lib-geoportal 0.6.0-1428599141 → 0.6.0-1428917543
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/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"name": "GeoGirafe PSC",
|
|
6
6
|
"url": "https://doc.geomapfish.dev"
|
|
7
7
|
},
|
|
8
|
-
"version": "0.6.0-
|
|
8
|
+
"version": "0.6.0-1428917543",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"engines": {
|
|
11
11
|
"node": ">=20.0.0"
|
|
@@ -35,10 +35,12 @@
|
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"buffer": "6.0.3",
|
|
37
37
|
"d3": "7.9.0",
|
|
38
|
+
"error-stack-parser": "2.1.4",
|
|
38
39
|
"file-saver": "2.0.5",
|
|
39
40
|
"gridjs": "6.2.0",
|
|
40
41
|
"lz-string": "1.5.0",
|
|
41
42
|
"on-change": "5.0.1",
|
|
43
|
+
"source-map-js": "1.2.0",
|
|
42
44
|
"tippy.js": "6.3.7",
|
|
43
45
|
"uhtml": "4.5.9",
|
|
44
46
|
"uuid": "10.0.0",
|
|
@@ -58,8 +60,8 @@
|
|
|
58
60
|
"@geoblocks/print": "0.7.8",
|
|
59
61
|
"@types/babel__generator": "7.6.8",
|
|
60
62
|
"@types/d3": "7.4.3",
|
|
61
|
-
"@types/geojson": "7946.0.14",
|
|
62
63
|
"@types/file-saver": "2.0.7",
|
|
64
|
+
"@types/geojson": "7946.0.14",
|
|
63
65
|
"@types/proj4": "2.5.5",
|
|
64
66
|
"@types/uuid": "10.0.0",
|
|
65
67
|
"@typescript-eslint/eslint-plugin": "7.15.0",
|
package/public/about.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"0.6.0-
|
|
1
|
+
{"version":"0.6.0-1428917543", "build":"1428917543", "date":"27/08/2024"}
|
|
@@ -4,8 +4,16 @@ import StateManager from '../state/statemanager';
|
|
|
4
4
|
declare class ErrorManager extends GirafeSingleton {
|
|
5
5
|
configManager: ConfigManager;
|
|
6
6
|
stateManager: StateManager;
|
|
7
|
+
private sourceMaps;
|
|
7
8
|
constructor(type: string);
|
|
8
9
|
private getErrorMessage;
|
|
9
10
|
private listenToAllErrors;
|
|
11
|
+
private getStackTrace;
|
|
12
|
+
private pushMessage;
|
|
13
|
+
private getContextUrl;
|
|
14
|
+
private getOriginalStackTrace;
|
|
15
|
+
private loadAllSourceMaps;
|
|
16
|
+
private loadSourceMap;
|
|
17
|
+
private getNormalizedPath;
|
|
10
18
|
}
|
|
11
19
|
export default ErrorManager;
|
|
@@ -2,6 +2,9 @@ import { v4 as uuidv4 } from 'uuid';
|
|
|
2
2
|
import GirafeSingleton from '../../base/GirafeSingleton';
|
|
3
3
|
import ConfigManager from '../configuration/configmanager';
|
|
4
4
|
import StateManager from '../state/statemanager';
|
|
5
|
+
import ErrorStackParser from 'error-stack-parser';
|
|
6
|
+
import { SourceMapConsumer } from 'source-map-js';
|
|
7
|
+
import ShareManager from '../share/sharemanager';
|
|
5
8
|
class ErrorManager extends GirafeSingleton {
|
|
6
9
|
constructor(type) {
|
|
7
10
|
super(type);
|
|
@@ -17,13 +20,25 @@ class ErrorManager extends GirafeSingleton {
|
|
|
17
20
|
writable: true,
|
|
18
21
|
value: void 0
|
|
19
22
|
});
|
|
23
|
+
Object.defineProperty(this, "sourceMaps", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
writable: true,
|
|
27
|
+
value: {}
|
|
28
|
+
});
|
|
20
29
|
this.configManager = ConfigManager.getInstance();
|
|
21
30
|
this.stateManager = StateManager.getInstance();
|
|
22
31
|
this.listenToAllErrors();
|
|
23
32
|
}
|
|
24
|
-
getErrorMessage(title, stack) {
|
|
33
|
+
getErrorMessage(title, stack, contextUrl) {
|
|
25
34
|
const description = `
|
|
26
|
-
##
|
|
35
|
+
## Debug Infos
|
|
36
|
+
|
|
37
|
+
### Context
|
|
38
|
+
|
|
39
|
+
${contextUrl}
|
|
40
|
+
|
|
41
|
+
### Callstack
|
|
27
42
|
|
|
28
43
|
\`\`\`
|
|
29
44
|
${stack}
|
|
@@ -36,33 +51,127 @@ ${stack}
|
|
|
36
51
|
}
|
|
37
52
|
listenToAllErrors() {
|
|
38
53
|
// Listen to all uncatched errors
|
|
39
|
-
window.onerror = (_message, file, line, col, error) => {
|
|
54
|
+
window.onerror = async (_message, file, line, col, error) => {
|
|
40
55
|
const title = error?.message ?? `Unknown error in ${file} at line ${line} and column ${col}.`;
|
|
41
|
-
const stack =
|
|
42
|
-
this.
|
|
43
|
-
id: uuidv4(),
|
|
44
|
-
text: this.getErrorMessage(title, stack),
|
|
45
|
-
type: 'error'
|
|
46
|
-
});
|
|
56
|
+
const stack = await this.getStackTrace(error);
|
|
57
|
+
this.pushMessage(title, stack);
|
|
47
58
|
return false;
|
|
48
59
|
};
|
|
49
60
|
// Listen to all unhandled HTTPRequest rejections
|
|
50
|
-
window.addEventListener('unhandledrejection', (
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
window.addEventListener('unhandledrejection', async (event) => {
|
|
62
|
+
const error = event.reason ?? new Error('Unknown reason');
|
|
63
|
+
const title = `Unhandled rejection: ${error.message}`;
|
|
64
|
+
const stack = await this.getStackTrace(error);
|
|
65
|
+
this.pushMessage(title, stack);
|
|
66
|
+
return false;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
async getStackTrace(error) {
|
|
70
|
+
let stack = 'Unknown stack';
|
|
71
|
+
if (error?.stack) {
|
|
72
|
+
try {
|
|
73
|
+
// NOTE REG: Specific to Vite: When running in debug mode, we do not need to calculate the original stacktrace
|
|
74
|
+
stack = import.meta.env.DEV ? error.stack : await this.getOriginalStackTrace(error);
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
// If an error accurs during the calculation of the stacktrace, we just ignore it
|
|
78
|
+
// and return the original stacktrace. Otherwise it could lead to an infinite exception loop.
|
|
79
|
+
console.warn(e);
|
|
80
|
+
stack = error.stack;
|
|
57
81
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
82
|
+
}
|
|
83
|
+
return stack;
|
|
84
|
+
}
|
|
85
|
+
pushMessage(title, stack) {
|
|
86
|
+
// Add new errormessage only if not already present
|
|
87
|
+
const pendingMessages = this.stateManager.state.infobox.elements.map((ele) => ele.text);
|
|
88
|
+
const contextUrl = this.getContextUrl();
|
|
89
|
+
const errorMessage = this.getErrorMessage(title, stack, contextUrl);
|
|
90
|
+
if (!pendingMessages.includes(errorMessage)) {
|
|
91
|
+
this.stateManager.state.infobox.elements.push({
|
|
92
|
+
id: uuidv4(),
|
|
93
|
+
text: errorMessage,
|
|
94
|
+
type: 'error'
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
getContextUrl() {
|
|
99
|
+
const base = window.location.href.split('#')[0];
|
|
100
|
+
const hash = ShareManager.getInstance().getStateToShare();
|
|
101
|
+
return `${base}#${hash}`;
|
|
102
|
+
}
|
|
103
|
+
async getOriginalStackTrace(error) {
|
|
104
|
+
if (Object.keys(this.sourceMaps).length === 0) {
|
|
105
|
+
await this.loadAllSourceMaps();
|
|
106
|
+
}
|
|
107
|
+
// Map the minified stacktrace with the original one
|
|
108
|
+
const stackFrames = ErrorStackParser.parse(error);
|
|
109
|
+
const originalStackFrames = stackFrames.map((frame) => {
|
|
110
|
+
const sourceMap = this.sourceMaps[this.getNormalizedPath(frame.fileName)];
|
|
111
|
+
if (sourceMap) {
|
|
112
|
+
const originalPosition = sourceMap.originalPositionFor({
|
|
113
|
+
line: frame.lineNumber,
|
|
114
|
+
column: frame.columnNumber
|
|
63
115
|
});
|
|
64
|
-
|
|
116
|
+
if (originalPosition.source) {
|
|
117
|
+
return {
|
|
118
|
+
fileName: originalPosition.source,
|
|
119
|
+
lineNumber: originalPosition.line,
|
|
120
|
+
columnNumber: originalPosition.column,
|
|
121
|
+
functionName: frame.functionName
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Default : return the original frame
|
|
126
|
+
return frame;
|
|
65
127
|
});
|
|
128
|
+
return originalStackFrames
|
|
129
|
+
.map((frame) => {
|
|
130
|
+
return `${frame.functionName || 'anonymous'} (${frame.fileName}:${frame.lineNumber}:${frame.columnNumber})`;
|
|
131
|
+
})
|
|
132
|
+
.join('\n');
|
|
133
|
+
}
|
|
134
|
+
async loadAllSourceMaps() {
|
|
135
|
+
const scripts = document.getElementsByTagName('script');
|
|
136
|
+
for (const script of scripts) {
|
|
137
|
+
await this.loadSourceMap(this.getNormalizedPath(script.src));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async loadSourceMap(jsFilename) {
|
|
141
|
+
const response = await fetch(jsFilename);
|
|
142
|
+
const content = await response.text();
|
|
143
|
+
// Load sourcemap of this specific file
|
|
144
|
+
let regex = /\/\/# sourceMappingURL=([^\s]+)/;
|
|
145
|
+
let match = RegExp(regex).exec(content);
|
|
146
|
+
if (match) {
|
|
147
|
+
const sourceMapUrl = this.getNormalizedPath(jsFilename, match[1]);
|
|
148
|
+
if (!(jsFilename in this.sourceMaps)) {
|
|
149
|
+
const sourceMapResponse = await fetch(sourceMapUrl);
|
|
150
|
+
const sourceMapContent = await sourceMapResponse.json();
|
|
151
|
+
const sourceMap = new SourceMapConsumer(sourceMapContent);
|
|
152
|
+
this.sourceMaps[jsFilename] = sourceMap;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
console.info('No Sourcemap found');
|
|
157
|
+
}
|
|
158
|
+
// Load sourcemaps of all other dependent bundles
|
|
159
|
+
regex = /from *"([^\s]+)"/g;
|
|
160
|
+
const matches = content.matchAll(regex);
|
|
161
|
+
for (match of matches) {
|
|
162
|
+
for (let i = 1; i < match.length; ++i) {
|
|
163
|
+
const sourceMapUrl = this.getNormalizedPath(jsFilename, match[1]);
|
|
164
|
+
await this.loadSourceMap(sourceMapUrl);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
getNormalizedPath(initialUrl, newFilename) {
|
|
169
|
+
if (!initialUrl) {
|
|
170
|
+
throw new Error('No path to normalize');
|
|
171
|
+
}
|
|
172
|
+
const path = newFilename ? initialUrl.substring(0, initialUrl.lastIndexOf('/') + 1) + newFilename : initialUrl;
|
|
173
|
+
const url = new URL(path);
|
|
174
|
+
return url.origin + url.pathname;
|
|
66
175
|
}
|
|
67
176
|
}
|
|
68
177
|
export default ErrorManager;
|
|
@@ -14,11 +14,11 @@ describe('ErrorManager', () => {
|
|
|
14
14
|
afterAll(() => {
|
|
15
15
|
MockHelper.stopMocking();
|
|
16
16
|
});
|
|
17
|
-
it('should listen to all uncaught errors and add error message to infobox', () => {
|
|
17
|
+
it('should listen to all uncaught errors and add error message to infobox', async () => {
|
|
18
18
|
const error = new Error('Test error');
|
|
19
19
|
const stack = 'Error stack trace';
|
|
20
20
|
error.stack = stack;
|
|
21
|
-
window.onerror(error.message, 'testfile.js', 10, 20, error);
|
|
21
|
+
await window.onerror(error.message, 'testfile.js', 10, 20, error);
|
|
22
22
|
expect(StateManager.getInstance().state.infobox.elements.length).toEqual(1);
|
|
23
23
|
const element = StateManager.getInstance().state.infobox.elements[0];
|
|
24
24
|
expect(element.id).toEqual('mock-uuid');
|
|
@@ -26,13 +26,13 @@ describe('ErrorManager', () => {
|
|
|
26
26
|
expect(element.text).toContain(encodeURIComponent('Error stack trace'));
|
|
27
27
|
expect(element.text).toContain(encodeURIComponent('Test error'));
|
|
28
28
|
});
|
|
29
|
-
it('should listen to all unhandledrejection and add error message to infobox', () => {
|
|
29
|
+
it('should listen to all unhandledrejection and add error message to infobox', async () => {
|
|
30
30
|
const reason = new Error('Unhandled rejection');
|
|
31
31
|
const stack = 'Rejection stack trace';
|
|
32
32
|
reason.stack = stack;
|
|
33
33
|
const event = new CustomEvent('unhandledrejection', { detail: { reason } });
|
|
34
34
|
Object.assign(event, { reason });
|
|
35
|
-
window.dispatchEvent(event);
|
|
35
|
+
await window.dispatchEvent(event);
|
|
36
36
|
expect(StateManager.getInstance().state.infobox.elements.length).toEqual(1);
|
|
37
37
|
const element = StateManager.getInstance().state.infobox.elements[0];
|
|
38
38
|
expect(element.id).toEqual('mock-uuid');
|
|
@@ -40,24 +40,32 @@ describe('ErrorManager', () => {
|
|
|
40
40
|
expect(element.text).toContain(encodeURIComponent('Rejection stack trace'));
|
|
41
41
|
expect(element.text).toContain(encodeURIComponent('Unhandled rejection'));
|
|
42
42
|
});
|
|
43
|
-
it('should handle unknown rejection without reason and stack', () => {
|
|
43
|
+
it('should handle unknown rejection without reason and stack', async () => {
|
|
44
44
|
const event = new CustomEvent('unhandledrejection', { detail: {} });
|
|
45
45
|
Object.assign(event, {});
|
|
46
|
-
window.dispatchEvent(event);
|
|
46
|
+
await window.dispatchEvent(event);
|
|
47
47
|
expect(StateManager.getInstance().state.infobox.elements.length).toEqual(1);
|
|
48
48
|
const element = StateManager.getInstance().state.infobox.elements[0];
|
|
49
49
|
expect(element.id).toEqual('mock-uuid');
|
|
50
50
|
expect(element.type).toEqual('error');
|
|
51
51
|
});
|
|
52
|
-
it('should add all errors to infobox', () => {
|
|
52
|
+
it('should add all errors to infobox', async () => {
|
|
53
53
|
const error = new Error('Test error');
|
|
54
|
-
window.onerror(error.message, 'testfile.js', 10, 20, error);
|
|
54
|
+
await window.onerror(error.message, 'testfile.js', 10, 20, error);
|
|
55
55
|
expect(StateManager.getInstance().state.infobox.elements.length).toEqual(1);
|
|
56
|
-
|
|
56
|
+
const error2 = new Error('Test error 2');
|
|
57
|
+
await window.onerror(error.message, 'testfile2.js', 10, 20, error2);
|
|
57
58
|
expect(StateManager.getInstance().state.infobox.elements.length).toEqual(2);
|
|
58
59
|
const event = new CustomEvent('unhandledrejection', { detail: {} });
|
|
59
|
-
Object.assign(event, {});
|
|
60
|
-
window.dispatchEvent(event);
|
|
60
|
+
Object.assign(event, { error });
|
|
61
|
+
await window.dispatchEvent(event);
|
|
61
62
|
expect(StateManager.getInstance().state.infobox.elements.length).toEqual(3);
|
|
62
63
|
});
|
|
64
|
+
it('should not add dupplicated errors to infobox', async () => {
|
|
65
|
+
const error = new Error('Test error');
|
|
66
|
+
await window.onerror(error.message, 'testfile.js', 10, 20, error);
|
|
67
|
+
expect(StateManager.getInstance().state.infobox.elements.length).toEqual(1);
|
|
68
|
+
await window.onerror(error.message, 'testfile.js', 10, 20, error);
|
|
69
|
+
expect(StateManager.getInstance().state.infobox.elements.length).toEqual(1);
|
|
70
|
+
});
|
|
63
71
|
});
|