@jsenv/core 40.0.1 → 40.0.2

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.
@@ -225,7 +225,7 @@ var runtime = (function (exports) {
225
225
  // AsyncIterator objects; they just return a Promise for the value of
226
226
  // the final result produced by the iterator.
227
227
  exports.async = function(innerFn, outerFn, self, tryLocsList, PromiseImpl) {
228
- if (PromiseImpl === undefined) PromiseImpl = Promise;
228
+ if (PromiseImpl === void 0) PromiseImpl = Promise;
229
229
 
230
230
  var iter = new AsyncIterator(
231
231
  wrap(innerFn, outerFn, self, tryLocsList),
@@ -82,7 +82,6 @@ let createConnectionManager;
82
82
  const READY_STATES = {
83
83
  CONNECTING: "connecting",
84
84
  OPEN: "open",
85
- CLOSING: "closing",
86
85
  CLOSED: "closed",
87
86
  };
88
87
 
@@ -0,0 +1,206 @@
1
+ import { assertAndNormalizeDirectoryUrl, createLogger, Abort, raceProcessTeardownEvents, createTaskLog } from "./process_teardown_events.js";
2
+ import { startServer, jsenvServiceCORS, jsenvServiceErrorHandler, jsenvAccessControlAllowedHeaders, createFileSystemFetch } from "@jsenv/server";
3
+ import { existsSync } from "node:fs";
4
+ import { urlToExtension, urlToPathname } from "./main.js";
5
+ import "node:process";
6
+ import "node:os";
7
+ import "node:tty";
8
+ import "string-width";
9
+ import "node:url";
10
+ import "@jsenv/sourcemap";
11
+
12
+ /*
13
+ * startBuildServer is mean to interact with the build files;
14
+ * files that will be deployed to production server(s).
15
+ * We want to be as close as possible from the production in order to:
16
+ * - run lighthouse
17
+ * - run an automated test tool such as cypress, playwright
18
+ * - see exactly how build file behaves (debug, measure perf, etc)
19
+ * For these reasons "startBuildServer" must be as close as possible from a static file server.
20
+ * It is not meant to provide a nice developper experience: this is the role "startDevServer".
21
+ *
22
+ * Conclusion:
23
+ * "startBuildServer" must be as close as possible from a static file server because
24
+ * we want to be in the user shoes and we should not alter build files.
25
+ */
26
+
27
+
28
+ /**
29
+ * Start a server for build files.
30
+ * @param {Object} buildServerParameters
31
+ * @param {string|url} buildServerParameters.buildDirectoryUrl Directory where build files are written
32
+ * @return {Object} A build server object
33
+ */
34
+ const startBuildServer = async ({
35
+ buildDirectoryUrl,
36
+ buildMainFilePath = "index.html",
37
+ port = 9779,
38
+ routes,
39
+ services = [],
40
+ acceptAnyIp,
41
+ hostname,
42
+ https,
43
+ http2,
44
+ logLevel,
45
+ serverLogLevel = "warn",
46
+
47
+ signal = new AbortController().signal,
48
+ handleSIGINT = true,
49
+ keepProcessAlive = true,
50
+
51
+ ...rest
52
+ }) => {
53
+ // params validation
54
+ {
55
+ const unexpectedParamNames = Object.keys(rest);
56
+ if (unexpectedParamNames.length > 0) {
57
+ throw new TypeError(
58
+ `${unexpectedParamNames.join(",")}: there is no such param`,
59
+ );
60
+ }
61
+ buildDirectoryUrl = assertAndNormalizeDirectoryUrl(
62
+ buildDirectoryUrl,
63
+ "buildDirectoryUrl",
64
+ );
65
+
66
+ if (buildMainFilePath) {
67
+ if (typeof buildMainFilePath !== "string") {
68
+ throw new TypeError(
69
+ `buildMainFilePath must be a string, got ${buildMainFilePath}`,
70
+ );
71
+ }
72
+ if (buildMainFilePath[0] === "/") {
73
+ buildMainFilePath = buildMainFilePath.slice(1);
74
+ } else {
75
+ const buildMainFileUrl = new URL(buildMainFilePath, buildDirectoryUrl)
76
+ .href;
77
+ if (!buildMainFileUrl.startsWith(buildDirectoryUrl)) {
78
+ throw new Error(
79
+ `buildMainFilePath must be relative, got ${buildMainFilePath}`,
80
+ );
81
+ }
82
+ buildMainFilePath = buildMainFileUrl.slice(buildDirectoryUrl.length);
83
+ }
84
+ if (!existsSync(new URL(buildMainFilePath, buildDirectoryUrl))) {
85
+ buildMainFilePath = null;
86
+ }
87
+ }
88
+ }
89
+
90
+ const logger = createLogger({ logLevel });
91
+ const operation = Abort.startOperation();
92
+ operation.addAbortSignal(signal);
93
+ if (handleSIGINT) {
94
+ operation.addAbortSource((abort) => {
95
+ return raceProcessTeardownEvents(
96
+ {
97
+ SIGINT: true,
98
+ },
99
+ abort,
100
+ );
101
+ });
102
+ }
103
+
104
+ const startBuildServerTask = createTaskLog("start build server", {
105
+ disabled: !logger.levels.info,
106
+ });
107
+ const server = await startServer({
108
+ signal,
109
+ stopOnExit: false,
110
+ stopOnSIGINT: false,
111
+ stopOnInternalError: false,
112
+ keepProcessAlive,
113
+ logLevel: serverLogLevel,
114
+ startLog: false,
115
+
116
+ https,
117
+ http2,
118
+ acceptAnyIp,
119
+ hostname,
120
+ port,
121
+ serverTiming: true,
122
+ requestWaitingMs: 60_000,
123
+ routes,
124
+ services: [
125
+ jsenvServiceCORS({
126
+ accessControlAllowRequestOrigin: true,
127
+ accessControlAllowRequestMethod: true,
128
+ accessControlAllowRequestHeaders: true,
129
+ accessControlAllowedRequestHeaders: jsenvAccessControlAllowedHeaders,
130
+ accessControlAllowCredentials: true,
131
+ timingAllowOrigin: true,
132
+ }),
133
+ ...services,
134
+ jsenvBuildFileService({
135
+ buildDirectoryUrl,
136
+ buildMainFilePath,
137
+ }),
138
+ jsenvServiceErrorHandler({
139
+ sendErrorDetails: true,
140
+ }),
141
+ ],
142
+ });
143
+ startBuildServerTask.done();
144
+ if (hostname) {
145
+ delete server.origins.localip;
146
+ delete server.origins.externalip;
147
+ }
148
+ logger.info(``);
149
+ Object.keys(server.origins).forEach((key) => {
150
+ logger.info(`- ${server.origins[key]}`);
151
+ });
152
+ logger.info(``);
153
+ return {
154
+ origin: server.origin,
155
+ stop: () => {
156
+ server.stop();
157
+ },
158
+ };
159
+ };
160
+
161
+ const jsenvBuildFileService = ({ buildDirectoryUrl, buildMainFilePath }) => {
162
+ return {
163
+ name: "jsenv:build_files",
164
+ routes: [
165
+ {
166
+ endpoint: "GET *",
167
+ description: "Serve static files.",
168
+ fetch: (request, helpers) => {
169
+ const urlIsVersioned = new URL(request.url).searchParams.has("v");
170
+ if (buildMainFilePath && request.resource === "/") {
171
+ request = {
172
+ ...request,
173
+ resource: `/${buildMainFilePath}`,
174
+ };
175
+ }
176
+ const urlObject = new URL(
177
+ request.resource.slice(1),
178
+ buildDirectoryUrl,
179
+ );
180
+ return createFileSystemFetch(buildDirectoryUrl, {
181
+ cacheControl: urlIsVersioned
182
+ ? `private,max-age=${SECONDS_IN_30_DAYS},immutable`
183
+ : "private,max-age=0,must-revalidate",
184
+ etagEnabled: true,
185
+ compressionEnabled: true,
186
+ rootDirectoryUrl: buildDirectoryUrl,
187
+ canReadDirectory: true,
188
+ ENOENTFallback: () => {
189
+ if (
190
+ !urlToExtension(urlObject) &&
191
+ !urlToPathname(urlObject).endsWith("/")
192
+ ) {
193
+ return new URL(buildMainFilePath, buildDirectoryUrl);
194
+ }
195
+ return null;
196
+ },
197
+ })(request, helpers);
198
+ },
199
+ },
200
+ ],
201
+ };
202
+ };
203
+
204
+ const SECONDS_IN_30_DAYS = 60 * 60 * 24 * 30;
205
+
206
+ export { startBuildServer };