@jsenv/core 36.3.0 → 37.0.0
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/js/autoreload.js +6 -5
- package/dist/js/import_meta_hot.js +4 -4
- package/dist/js/server_events_client.js +422 -304
- package/dist/jsenv_core.js +3819 -3256
- package/package.json +18 -17
- package/src/build/build.js +342 -658
- package/src/build/build_urls_generator.js +8 -8
- package/src/build/build_versions_manager.js +495 -0
- package/src/build/version_mappings_injection.js +27 -16
- package/src/dev/file_service.js +80 -91
- package/src/dev/start_dev_server.js +5 -3
- package/src/kitchen/errors.js +16 -16
- package/src/kitchen/fetched_content_compliance.js +4 -8
- package/src/kitchen/kitchen.js +367 -939
- package/src/kitchen/prepend_content.js +13 -35
- package/src/kitchen/url_graph/references.js +713 -0
- package/src/kitchen/url_graph/sort_by_dependencies.js +2 -2
- package/src/kitchen/url_graph/url_content.js +96 -0
- package/src/kitchen/url_graph/url_graph.js +439 -0
- package/src/kitchen/url_graph/url_graph_report.js +6 -4
- package/src/kitchen/url_graph/url_graph_visitor.js +14 -12
- package/src/kitchen/url_graph/url_info_transformations.js +180 -184
- package/src/plugins/autoreload/client/autoreload.js +1 -0
- package/src/plugins/autoreload/client/reload.js +6 -6
- package/src/plugins/autoreload/jsenv_plugin_autoreload.js +2 -2
- package/src/plugins/autoreload/jsenv_plugin_autoreload_client.js +2 -2
- package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +84 -78
- package/src/plugins/autoreload/jsenv_plugin_hot_search_param.js +52 -0
- package/src/plugins/cache_control/jsenv_plugin_cache_control.js +1 -1
- package/src/plugins/commonjs_globals/jsenv_plugin_commonjs_globals.js +2 -2
- package/src/plugins/global_scenarios/jsenv_plugin_global_scenarios.js +3 -3
- package/src/plugins/import_meta_hot/client/import_meta_hot.js +4 -4
- package/src/plugins/import_meta_hot/jsenv_plugin_import_meta_hot.js +18 -20
- package/src/plugins/import_meta_scenarios/jsenv_plugin_import_meta_scenarios.js +2 -2
- package/src/plugins/importmap/jsenv_plugin_importmap.js +35 -37
- package/src/plugins/inlining/jsenv_plugin_inlining.js +1 -17
- package/src/plugins/inlining/jsenv_plugin_inlining_as_data_url.js +70 -50
- package/src/plugins/inlining/jsenv_plugin_inlining_into_html.js +72 -54
- package/src/plugins/plugin_controller.js +92 -27
- package/src/plugins/protocol_file/jsenv_plugin_protocol_file.js +18 -20
- package/src/plugins/reference_analysis/css/jsenv_plugin_css_reference_analysis.js +4 -5
- package/src/plugins/reference_analysis/data_urls/jsenv_plugin_data_urls_analysis.js +18 -16
- package/src/plugins/reference_analysis/directory/jsenv_plugin_directory_reference_analysis.js +13 -20
- package/src/plugins/reference_analysis/html/jsenv_plugin_html_reference_analysis.js +55 -72
- package/src/plugins/reference_analysis/js/jsenv_plugin_js_reference_analysis.js +33 -42
- package/src/plugins/reference_analysis/jsenv_plugin_reference_analysis.js +16 -7
- package/src/plugins/reference_analysis/webmanifest/jsenv_plugin_webmanifest_reference_analysis.js +4 -3
- package/src/plugins/resolution_node_esm/jsenv_plugin_node_esm_resolution.js +16 -6
- package/src/plugins/resolution_node_esm/node_esm_resolver.js +30 -24
- package/src/plugins/resolution_web/jsenv_plugin_web_resolution.js +8 -5
- package/src/plugins/ribbon/jsenv_plugin_ribbon.js +3 -3
- package/src/plugins/server_events/client/server_events_client.js +460 -15
- package/src/plugins/server_events/jsenv_plugin_server_events_client_injection.js +13 -29
- package/src/plugins/version_search_param/jsenv_plugin_version_search_param.js +1 -1
- package/src/build/version_generator.js +0 -19
- package/src/kitchen/url_graph/url_graph_loader.js +0 -77
- package/src/kitchen/url_graph.js +0 -322
- package/src/plugins/autoreload/jsenv_plugin_hmr.js +0 -42
- package/src/plugins/resolution_node_esm/url_type_from_reference.js +0 -13
- package/src/plugins/server_events/client/connection_manager.js +0 -170
- package/src/plugins/server_events/client/event_source_connection.js +0 -83
- package/src/plugins/server_events/client/events_manager.js +0 -75
- package/src/plugins/server_events/client/web_socket_connection.js +0 -81
- /package/src/kitchen/{url_specifier_encoding.js → url_graph/url_specifier_encoding.js} +0 -0
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { readFileSync } from "node:fs";
|
|
11
|
-
import { bufferToEtag } from "@jsenv/filesystem";
|
|
12
11
|
import {
|
|
13
12
|
applyNodeEsmResolution,
|
|
14
13
|
readCustomConditionsFromProcessArgs,
|
|
@@ -29,19 +28,22 @@ export const createNodeEsmResolver = ({
|
|
|
29
28
|
"import",
|
|
30
29
|
];
|
|
31
30
|
|
|
32
|
-
return (reference
|
|
31
|
+
return (reference) => {
|
|
33
32
|
if (reference.type === "package_json") {
|
|
34
33
|
return reference.specifier;
|
|
35
34
|
}
|
|
35
|
+
const { ownerUrlInfo } = reference;
|
|
36
36
|
if (reference.specifier === "/") {
|
|
37
|
-
const { mainFilePath, rootDirectoryUrl } = context;
|
|
37
|
+
const { mainFilePath, rootDirectoryUrl } = ownerUrlInfo.context;
|
|
38
38
|
return String(new URL(mainFilePath, rootDirectoryUrl));
|
|
39
39
|
}
|
|
40
40
|
if (reference.specifier[0] === "/") {
|
|
41
|
-
return new URL(
|
|
42
|
-
.
|
|
41
|
+
return new URL(
|
|
42
|
+
reference.specifier.slice(1),
|
|
43
|
+
ownerUrlInfo.context.rootDirectoryUrl,
|
|
44
|
+
).href;
|
|
43
45
|
}
|
|
44
|
-
const parentUrl = reference.baseUrl ||
|
|
46
|
+
const parentUrl = reference.baseUrl || ownerUrlInfo.url;
|
|
45
47
|
if (!parentUrl.startsWith("file:")) {
|
|
46
48
|
return new URL(reference.specifier, parentUrl).href;
|
|
47
49
|
}
|
|
@@ -51,7 +53,7 @@ export const createNodeEsmResolver = ({
|
|
|
51
53
|
specifier: reference.specifier,
|
|
52
54
|
preservesSymlink,
|
|
53
55
|
});
|
|
54
|
-
if (context.dev) {
|
|
56
|
+
if (ownerUrlInfo.context.dev) {
|
|
55
57
|
const dependsOnPackageJson =
|
|
56
58
|
type !== "relative_specifier" &&
|
|
57
59
|
type !== "absolute_specifier" &&
|
|
@@ -62,7 +64,6 @@ export const createNodeEsmResolver = ({
|
|
|
62
64
|
// must be invalidated when corresponding package.json changes
|
|
63
65
|
addRelationshipWithPackageJson({
|
|
64
66
|
reference,
|
|
65
|
-
context,
|
|
66
67
|
packageJsonUrl: `${packageDirectoryUrl}package.json`,
|
|
67
68
|
field: type.startsWith("field:")
|
|
68
69
|
? `#${type.slice("field:".length)}`
|
|
@@ -70,7 +71,7 @@ export const createNodeEsmResolver = ({
|
|
|
70
71
|
});
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
|
-
if (context.dev) {
|
|
74
|
+
if (ownerUrlInfo.context.dev) {
|
|
74
75
|
// without this check a file inside a project without package.json
|
|
75
76
|
// could be considered as a node module if there is a ancestor package.json
|
|
76
77
|
// but we want to version only node modules
|
|
@@ -78,7 +79,7 @@ export const createNodeEsmResolver = ({
|
|
|
78
79
|
const packageDirectoryUrl = defaultLookupPackageScope(url);
|
|
79
80
|
if (
|
|
80
81
|
packageDirectoryUrl &&
|
|
81
|
-
packageDirectoryUrl !== context.rootDirectoryUrl
|
|
82
|
+
packageDirectoryUrl !== ownerUrlInfo.context.rootDirectoryUrl
|
|
82
83
|
) {
|
|
83
84
|
const packageVersion =
|
|
84
85
|
defaultReadPackageJson(packageDirectoryUrl).version;
|
|
@@ -86,7 +87,6 @@ export const createNodeEsmResolver = ({
|
|
|
86
87
|
if (packageVersion) {
|
|
87
88
|
addRelationshipWithPackageJson({
|
|
88
89
|
reference,
|
|
89
|
-
context,
|
|
90
90
|
packageJsonUrl: `${packageDirectoryUrl}package.json`,
|
|
91
91
|
field: "version",
|
|
92
92
|
hasVersioningEffect: true,
|
|
@@ -101,29 +101,35 @@ export const createNodeEsmResolver = ({
|
|
|
101
101
|
};
|
|
102
102
|
|
|
103
103
|
const addRelationshipWithPackageJson = ({
|
|
104
|
-
|
|
104
|
+
reference,
|
|
105
105
|
packageJsonUrl,
|
|
106
106
|
field,
|
|
107
107
|
hasVersioningEffect = false,
|
|
108
108
|
}) => {
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
109
|
+
const { ownerUrlInfo } = reference;
|
|
110
|
+
for (const referenceToOther of ownerUrlInfo.referenceToOthersSet) {
|
|
111
|
+
if (
|
|
112
|
+
referenceToOther.type === "package_json" &&
|
|
113
|
+
referenceToOther.subtype === field
|
|
114
|
+
) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
114
117
|
}
|
|
115
|
-
const
|
|
118
|
+
const packageJsonReference = reference.addImplicit({
|
|
116
119
|
type: "package_json",
|
|
117
120
|
subtype: field,
|
|
118
121
|
specifier: packageJsonUrl,
|
|
119
|
-
isImplicit: true,
|
|
120
122
|
hasVersioningEffect,
|
|
123
|
+
isWeak: true,
|
|
121
124
|
});
|
|
122
|
-
|
|
125
|
+
// we don't cook package.json files, we just maintain their content
|
|
126
|
+
// to be able to check if it has changed later on
|
|
127
|
+
if (packageJsonReference.urlInfo.content === undefined) {
|
|
123
128
|
const packageJsonContentAsBuffer = readFileSync(new URL(packageJsonUrl));
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
129
|
+
packageJsonReference.urlInfo.type = "json";
|
|
130
|
+
packageJsonReference.urlInfo.kitchen.urlInfoTransformer.setContent(
|
|
131
|
+
packageJsonReference.urlInfo,
|
|
132
|
+
String(packageJsonContentAsBuffer),
|
|
133
|
+
);
|
|
128
134
|
}
|
|
129
135
|
};
|
|
@@ -2,20 +2,23 @@ export const jsenvPluginWebResolution = () => {
|
|
|
2
2
|
return {
|
|
3
3
|
name: "jsenv:web_resolution",
|
|
4
4
|
appliesDuring: "*",
|
|
5
|
-
resolveReference: (reference
|
|
5
|
+
resolveReference: (reference) => {
|
|
6
|
+
const { ownerUrlInfo } = reference;
|
|
6
7
|
if (reference.specifier === "/") {
|
|
7
|
-
const { mainFilePath, rootDirectoryUrl } = context;
|
|
8
|
+
const { mainFilePath, rootDirectoryUrl } = ownerUrlInfo.context;
|
|
8
9
|
return String(new URL(mainFilePath, rootDirectoryUrl));
|
|
9
10
|
}
|
|
10
11
|
if (reference.specifier[0] === "/") {
|
|
11
|
-
return new URL(
|
|
12
|
-
.
|
|
12
|
+
return new URL(
|
|
13
|
+
reference.specifier.slice(1),
|
|
14
|
+
ownerUrlInfo.context.rootDirectoryUrl,
|
|
15
|
+
).href;
|
|
13
16
|
}
|
|
14
17
|
return new URL(
|
|
15
18
|
reference.specifier,
|
|
16
19
|
// baseUrl happens second argument to new URL() is different from
|
|
17
20
|
// import.meta.url or document.currentScript.src
|
|
18
|
-
reference.baseUrl ||
|
|
21
|
+
reference.baseUrl || ownerUrlInfo.url,
|
|
19
22
|
).href;
|
|
20
23
|
},
|
|
21
24
|
};
|
|
@@ -24,7 +24,7 @@ export const jsenvPluginRibbon = ({
|
|
|
24
24
|
name: "jsenv:ribbon",
|
|
25
25
|
appliesDuring: "dev",
|
|
26
26
|
transformUrlContent: {
|
|
27
|
-
html: (urlInfo
|
|
27
|
+
html: (urlInfo) => {
|
|
28
28
|
if (urlInfo.data.isJsenvToolbar || urlInfo.data.noribbon) {
|
|
29
29
|
return null;
|
|
30
30
|
}
|
|
@@ -36,14 +36,14 @@ export const jsenvPluginRibbon = ({
|
|
|
36
36
|
return null;
|
|
37
37
|
}
|
|
38
38
|
const htmlAst = parseHtmlString(urlInfo.content);
|
|
39
|
-
const
|
|
39
|
+
const ribbonClientFileReference = urlInfo.dependencies.inject({
|
|
40
40
|
type: "script",
|
|
41
41
|
subtype: "js_module",
|
|
42
42
|
expectedType: "js_module",
|
|
43
43
|
specifier: ribbonClientFileUrl.href,
|
|
44
44
|
});
|
|
45
45
|
const paramsJson = JSON.stringify(
|
|
46
|
-
{ text: context.dev ? "DEV" : "BUILD" },
|
|
46
|
+
{ text: urlInfo.context.dev ? "DEV" : "BUILD" },
|
|
47
47
|
null,
|
|
48
48
|
" ",
|
|
49
49
|
);
|
|
@@ -1,16 +1,461 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
let createEventsManager;
|
|
2
|
+
events_manager: {
|
|
3
|
+
createEventsManager = ({ effect = () => {} } = {}) => {
|
|
4
|
+
const callbacksMap = new Map();
|
|
5
|
+
let cleanup;
|
|
6
|
+
const addCallbacks = (namedCallbacks) => {
|
|
7
|
+
let callbacksMapSize = callbacksMap.size;
|
|
8
|
+
Object.keys(namedCallbacks).forEach((eventName) => {
|
|
9
|
+
const callback = namedCallbacks[eventName];
|
|
10
|
+
const existingCallbacks = callbacksMap.get(eventName);
|
|
11
|
+
let callbacks;
|
|
12
|
+
if (existingCallbacks) {
|
|
13
|
+
callbacks = existingCallbacks;
|
|
14
|
+
} else {
|
|
15
|
+
callbacks = [];
|
|
16
|
+
callbacksMap.set(eventName, callbacks);
|
|
17
|
+
}
|
|
18
|
+
callbacks.push(callback);
|
|
19
|
+
});
|
|
20
|
+
if (effect && callbacksMapSize === 0 && callbacksMapSize.size > 0) {
|
|
21
|
+
cleanup = effect();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let removed = false;
|
|
25
|
+
return () => {
|
|
26
|
+
if (removed) return;
|
|
27
|
+
removed = true;
|
|
28
|
+
callbacksMapSize = callbacksMap.size;
|
|
29
|
+
Object.keys(namedCallbacks).forEach((eventName) => {
|
|
30
|
+
const callback = namedCallbacks[eventName];
|
|
31
|
+
const callbacks = callbacksMap.get(eventName);
|
|
32
|
+
if (callbacks) {
|
|
33
|
+
const index = callbacks.indexOf(callback);
|
|
34
|
+
if (index > -1) {
|
|
35
|
+
callbacks.splice(index, 1);
|
|
36
|
+
if (callbacks.length === 0) {
|
|
37
|
+
callbacksMap.delete(eventName);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
namedCallbacks = null; // allow garbage collect
|
|
43
|
+
if (
|
|
44
|
+
cleanup &&
|
|
45
|
+
typeof cleanup === "function" &&
|
|
46
|
+
callbacksMapSize > 0 &&
|
|
47
|
+
callbacksMapSize.size === 0
|
|
48
|
+
) {
|
|
49
|
+
cleanup();
|
|
50
|
+
cleanup = null;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const triggerCallbacks = (event) => {
|
|
56
|
+
const callbacks = callbacksMap.get(event.type);
|
|
57
|
+
if (callbacks) {
|
|
58
|
+
callbacks.forEach((callback) => {
|
|
59
|
+
callback(event);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const destroy = () => {
|
|
65
|
+
callbacksMap.clear();
|
|
66
|
+
if (cleanup) {
|
|
67
|
+
cleanup();
|
|
68
|
+
cleanup = null;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
addCallbacks,
|
|
74
|
+
triggerCallbacks,
|
|
75
|
+
destroy,
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let createConnectionManager;
|
|
81
|
+
connection_manager: {
|
|
82
|
+
const READY_STATES = {
|
|
83
|
+
CONNECTING: "connecting",
|
|
84
|
+
OPEN: "open",
|
|
85
|
+
CLOSING: "closing",
|
|
86
|
+
CLOSED: "closed",
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
createConnectionManager = (
|
|
90
|
+
attemptConnection,
|
|
91
|
+
{ logs, retry, retryAfter, retryMaxAttempt, retryAllocatedMs },
|
|
92
|
+
) => {
|
|
93
|
+
const readyState = {
|
|
94
|
+
value: READY_STATES.CLOSED,
|
|
95
|
+
goTo: (value) => {
|
|
96
|
+
if (value === readyState.value) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
readyState.value = value;
|
|
100
|
+
readyState.onchange();
|
|
101
|
+
},
|
|
102
|
+
onchange: () => {},
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
let _disconnect = () => {};
|
|
106
|
+
const connect = () => {
|
|
107
|
+
if (
|
|
108
|
+
readyState.value === READY_STATES.CONNECTING ||
|
|
109
|
+
readyState.value === READY_STATES.OPEN
|
|
110
|
+
) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
let retryCount = 0;
|
|
115
|
+
let msSpent = 0;
|
|
116
|
+
const attempt = () => {
|
|
117
|
+
readyState.goTo(READY_STATES.CONNECTING);
|
|
118
|
+
let timeout;
|
|
119
|
+
const cancelAttempt = attemptConnection({
|
|
120
|
+
onClosed: () => {
|
|
121
|
+
if (!retry) {
|
|
122
|
+
readyState.goTo(READY_STATES.CLOSED);
|
|
123
|
+
if (logs) {
|
|
124
|
+
console.info(`[jsenv] failed to connect to server`);
|
|
125
|
+
}
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (retryCount > retryMaxAttempt) {
|
|
129
|
+
readyState.goTo(READY_STATES.CLOSED);
|
|
130
|
+
if (logs) {
|
|
131
|
+
console.info(
|
|
132
|
+
`[jsenv] could not connect to server after ${retryMaxAttempt} attempt`,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (retryAllocatedMs && msSpent > retryAllocatedMs) {
|
|
138
|
+
readyState.goTo(READY_STATES.CLOSED);
|
|
139
|
+
if (logs) {
|
|
140
|
+
console.info(
|
|
141
|
+
`[jsenv] could not connect to server in less than ${retryAllocatedMs}ms`,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
// if closed while open -> connection lost
|
|
147
|
+
// otherwise it's the attempt to connect for the first time
|
|
148
|
+
// or to reconnect
|
|
149
|
+
if (readyState.value === READY_STATES.OPEN) {
|
|
150
|
+
if (logs) {
|
|
151
|
+
console.info(
|
|
152
|
+
`[jsenv] server connection lost; retrying to connect`,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
retryCount++;
|
|
157
|
+
timeout = setTimeout(() => {
|
|
158
|
+
msSpent += retryAfter;
|
|
159
|
+
attempt();
|
|
160
|
+
}, retryAfter);
|
|
161
|
+
},
|
|
162
|
+
onOpen: () => {
|
|
163
|
+
readyState.goTo(READY_STATES.OPEN);
|
|
164
|
+
if (logs) {
|
|
165
|
+
// console.info(`[jsenv] connected to server`);
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
_disconnect = () => {
|
|
170
|
+
cancelAttempt();
|
|
171
|
+
clearTimeout(timeout);
|
|
172
|
+
readyState.goTo(READY_STATES.CLOSED);
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
attempt();
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const disconnect = () => {
|
|
179
|
+
if (
|
|
180
|
+
readyState.value !== READY_STATES.CONNECTING &&
|
|
181
|
+
readyState.value !== READY_STATES.OPEN
|
|
182
|
+
) {
|
|
183
|
+
if (logs) {
|
|
184
|
+
console.warn(
|
|
185
|
+
`disconnect() ignored because connection is ${readyState.value}`,
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
return _disconnect();
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const removePageUnloadListener = listenPageUnload(() => {
|
|
194
|
+
if (
|
|
195
|
+
readyState.value === READY_STATES.CONNECTING ||
|
|
196
|
+
readyState.value === READY_STATES.OPEN
|
|
197
|
+
) {
|
|
198
|
+
_disconnect();
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
readyState,
|
|
204
|
+
connect,
|
|
205
|
+
disconnect,
|
|
206
|
+
destroy: () => {
|
|
207
|
+
removePageUnloadListener();
|
|
208
|
+
disconnect();
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// const listenPageMightFreeze = (callback) => {
|
|
214
|
+
// const removePageHideListener = listenEvent(window, "pagehide", (pageHideEvent) => {
|
|
215
|
+
// if (pageHideEvent.persisted === true) {
|
|
216
|
+
// callback(pageHideEvent)
|
|
217
|
+
// }
|
|
218
|
+
// })
|
|
219
|
+
// return removePageHideListener
|
|
220
|
+
// }
|
|
221
|
+
|
|
222
|
+
// const listenPageFreeze = (callback) => {
|
|
223
|
+
// const removeFreezeListener = listenEvent(document, "freeze", (freezeEvent) => {
|
|
224
|
+
// callback(freezeEvent)
|
|
225
|
+
// })
|
|
226
|
+
// return removeFreezeListener
|
|
227
|
+
// }
|
|
228
|
+
|
|
229
|
+
// const listenPageIsRestored = (callback) => {
|
|
230
|
+
// const removeResumeListener = listenEvent(document, "resume", (resumeEvent) => {
|
|
231
|
+
// removePageshowListener()
|
|
232
|
+
// callback(resumeEvent)
|
|
233
|
+
// })
|
|
234
|
+
// const removePageshowListener = listenEvent(window, "pageshow", (pageshowEvent) => {
|
|
235
|
+
// if (pageshowEvent.persisted === true) {
|
|
236
|
+
// removePageshowListener()
|
|
237
|
+
// removeResumeListener()
|
|
238
|
+
// callback(pageshowEvent)
|
|
239
|
+
// }
|
|
240
|
+
// })
|
|
241
|
+
// return () => {
|
|
242
|
+
// removeResumeListener()
|
|
243
|
+
// removePageshowListener()
|
|
244
|
+
// }
|
|
245
|
+
// }
|
|
246
|
+
|
|
247
|
+
const listenPageUnload = (callback) => {
|
|
248
|
+
const removePageHideListener = listenEvent(
|
|
249
|
+
window,
|
|
250
|
+
"pagehide",
|
|
251
|
+
(pageHideEvent) => {
|
|
252
|
+
if (pageHideEvent.persisted !== true) {
|
|
253
|
+
callback(pageHideEvent);
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
);
|
|
257
|
+
return removePageHideListener;
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const listenEvent = (emitter, event, callback) => {
|
|
261
|
+
emitter.addEventListener(event, callback);
|
|
262
|
+
return () => {
|
|
263
|
+
emitter.removeEventListener(event, callback);
|
|
264
|
+
};
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
let createWebSocketConnection;
|
|
269
|
+
connection_using_websocket: {
|
|
270
|
+
createWebSocketConnection = (
|
|
271
|
+
websocketUrl,
|
|
272
|
+
{
|
|
273
|
+
logs,
|
|
274
|
+
protocols = ["jsenv"],
|
|
275
|
+
useEventsToManageConnection = true,
|
|
276
|
+
retry = false,
|
|
277
|
+
retryAfter = 1000,
|
|
278
|
+
retryMaxAttempt = Infinity,
|
|
279
|
+
retryAllocatedMs = Infinity,
|
|
280
|
+
} = {},
|
|
281
|
+
) => {
|
|
282
|
+
const connectionManager = createConnectionManager(
|
|
283
|
+
({ onClosed, onOpen }) => {
|
|
284
|
+
let socket = new WebSocket(websocketUrl, protocols);
|
|
285
|
+
let interval;
|
|
286
|
+
const cleanup = () => {
|
|
287
|
+
if (socket) {
|
|
288
|
+
socket.onerror = null;
|
|
289
|
+
socket.onopen = null;
|
|
290
|
+
socket.onclose = null;
|
|
291
|
+
socket.onmessage = null;
|
|
292
|
+
socket = null;
|
|
293
|
+
clearInterval(interval);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
socket.onerror = () => {
|
|
297
|
+
cleanup();
|
|
298
|
+
onClosed();
|
|
299
|
+
};
|
|
300
|
+
socket.onopen = () => {
|
|
301
|
+
socket.onopen = null;
|
|
302
|
+
onOpen();
|
|
303
|
+
interval = setInterval(() => {
|
|
304
|
+
socket.send('{"type":"ping"}');
|
|
305
|
+
}, 30_000);
|
|
306
|
+
};
|
|
307
|
+
socket.onclose = () => {
|
|
308
|
+
cleanup();
|
|
309
|
+
onClosed();
|
|
310
|
+
};
|
|
311
|
+
socket.onmessage = (messageEvent) => {
|
|
312
|
+
const event = JSON.parse(messageEvent.data);
|
|
313
|
+
eventsManager.triggerCallbacks(event);
|
|
314
|
+
};
|
|
315
|
+
return () => {
|
|
316
|
+
if (socket) {
|
|
317
|
+
socket.close();
|
|
318
|
+
cleanup();
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
},
|
|
322
|
+
{ logs, retry, retryAfter, retryMaxAttempt, retryAllocatedMs },
|
|
323
|
+
);
|
|
324
|
+
const eventsManager = createEventsManager({
|
|
325
|
+
effect: () => {
|
|
326
|
+
if (useEventsToManageConnection) {
|
|
327
|
+
connectionManager.connect();
|
|
328
|
+
return () => {
|
|
329
|
+
connectionManager.disconnect();
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
return null;
|
|
333
|
+
},
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
return {
|
|
337
|
+
readyState: connectionManager.readyState,
|
|
338
|
+
connect: connectionManager.connect,
|
|
339
|
+
disconnect: connectionManager.disconnect,
|
|
340
|
+
listenEvents: (namedCallbacks) => {
|
|
341
|
+
return eventsManager.addCallbacks(namedCallbacks);
|
|
342
|
+
},
|
|
343
|
+
destroy: () => {
|
|
344
|
+
connectionManager.destroy();
|
|
345
|
+
eventsManager.destroy();
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// let createEventSourceConnection;
|
|
352
|
+
// connection_using_event_source: {
|
|
353
|
+
// createEventSourceConnection = (
|
|
354
|
+
// eventSourceUrl,
|
|
355
|
+
// {
|
|
356
|
+
// withCredentials = true,
|
|
357
|
+
// lastEventId,
|
|
358
|
+
// useEventsToManageConnection = true,
|
|
359
|
+
// retry = false,
|
|
360
|
+
// retryMaxAttempt = Infinity,
|
|
361
|
+
// retryAllocatedMs = Infinity,
|
|
362
|
+
// } = {},
|
|
363
|
+
// ) => {
|
|
364
|
+
// const eventSourceOrigin = new URL(eventSourceUrl).origin;
|
|
365
|
+
// const attemptConnection = ({ onOpen, onClosed }) => {
|
|
366
|
+
// const url = lastEventId
|
|
367
|
+
// ? addLastEventIdIntoUrlSearchParams(eventSourceUrl, lastEventId)
|
|
368
|
+
// : eventSourceUrl;
|
|
369
|
+
// let eventSource = new EventSource(url, { withCredentials });
|
|
370
|
+
// eventSource.onerror = () => {
|
|
371
|
+
// eventSource.onerror = null;
|
|
372
|
+
// eventSource.onopen = null;
|
|
373
|
+
// eventSource.onmessage = null;
|
|
374
|
+
// eventSource = null;
|
|
375
|
+
// onClosed();
|
|
376
|
+
// };
|
|
377
|
+
// eventSource.onopen = () => {
|
|
378
|
+
// eventSource.onopen = null;
|
|
379
|
+
// onOpen();
|
|
380
|
+
// };
|
|
381
|
+
// eventSource.onmessage = (messageEvent) => {
|
|
382
|
+
// if (messageEvent.origin === eventSourceOrigin) {
|
|
383
|
+
// if (messageEvent.lastEventId) {
|
|
384
|
+
// lastEventId = messageEvent.lastEventId;
|
|
385
|
+
// }
|
|
386
|
+
// const event = JSON.parse(messageEvent.data);
|
|
387
|
+
// eventsManager.triggerCallbacks(event);
|
|
388
|
+
// }
|
|
389
|
+
// };
|
|
390
|
+
// return () => {
|
|
391
|
+
// if (eventSource) {
|
|
392
|
+
// eventSource.close();
|
|
393
|
+
// }
|
|
394
|
+
// };
|
|
395
|
+
// };
|
|
396
|
+
// const connectionManager = createConnectionManager(attemptConnection, {
|
|
397
|
+
// retry,
|
|
398
|
+
// retryMaxAttempt,
|
|
399
|
+
// retryAllocatedMs,
|
|
400
|
+
// });
|
|
401
|
+
// const eventsManager = createEventsManager({
|
|
402
|
+
// effect: () => {
|
|
403
|
+
// if (useEventsToManageConnection) {
|
|
404
|
+
// connectionManager.connect();
|
|
405
|
+
// return () => {
|
|
406
|
+
// connectionManager.disconnect();
|
|
407
|
+
// };
|
|
408
|
+
// }
|
|
409
|
+
// return null;
|
|
410
|
+
// },
|
|
411
|
+
// });
|
|
412
|
+
|
|
413
|
+
// return {
|
|
414
|
+
// readyState: connectionManager.readyState,
|
|
415
|
+
// listenEvents: (namedCallbacks) => {
|
|
416
|
+
// return eventsManager.addCallbacks(namedCallbacks);
|
|
417
|
+
// },
|
|
418
|
+
// destroy: () => {
|
|
419
|
+
// connectionManager.destroy();
|
|
420
|
+
// eventsManager.destroy();
|
|
421
|
+
// },
|
|
422
|
+
// };
|
|
423
|
+
// };
|
|
424
|
+
|
|
425
|
+
// const addLastEventIdIntoUrlSearchParams = (url, lastEventId) => {
|
|
426
|
+
// if (url.indexOf("?") === -1) {
|
|
427
|
+
// url += "?";
|
|
428
|
+
// } else {
|
|
429
|
+
// url += "&";
|
|
430
|
+
// }
|
|
431
|
+
// return `${url}last-event-id=${encodeURIComponent(lastEventId)}`;
|
|
432
|
+
// };
|
|
433
|
+
// }
|
|
434
|
+
|
|
435
|
+
const serverEventsInterface = {
|
|
436
|
+
readyState: {},
|
|
437
|
+
connect: () => {},
|
|
438
|
+
disconnect: () => {},
|
|
439
|
+
listenEvents: () => {},
|
|
440
|
+
setup: ({ logs }) => {
|
|
441
|
+
const websocketScheme = self.location.protocol === "https:" ? "wss" : "ws";
|
|
442
|
+
const websocketUrl = `${websocketScheme}://${self.location.host}${self.location.pathname}${self.location.search}`;
|
|
443
|
+
const websocketConnection = createWebSocketConnection(websocketUrl, {
|
|
444
|
+
logs,
|
|
445
|
+
retry: true,
|
|
446
|
+
retryAllocatedMs: 10_000,
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
const { readyState, connect, disconnect, listenEvents } =
|
|
450
|
+
websocketConnection;
|
|
451
|
+
|
|
452
|
+
serverEventsInterface.readyState = readyState;
|
|
453
|
+
serverEventsInterface.connect = connect;
|
|
454
|
+
serverEventsInterface.disconnect = disconnect;
|
|
455
|
+
serverEventsInterface.listenEvents = listenEvents;
|
|
456
|
+
|
|
457
|
+
connect();
|
|
458
|
+
},
|
|
15
459
|
};
|
|
16
|
-
|
|
460
|
+
|
|
461
|
+
window.__server_events__ = serverEventsInterface;
|