elysia 2.0.0-beta.3 → 2.0.0-beta.5
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/adapter/bun/index.d.ts +1 -1
- package/dist/adapter/bun/index.js +347 -90
- package/dist/adapter/bun/index.mjs +349 -92
- package/dist/adapter/utils.js +48 -33
- package/dist/adapter/utils.mjs +48 -33
- package/dist/base.d.ts +16 -5
- package/dist/base.js +173 -74
- package/dist/base.mjs +174 -75
- package/dist/compile/aot.d.ts +1 -0
- package/dist/compile/aot.js +3 -0
- package/dist/compile/aot.mjs +3 -0
- package/dist/compile/handler/frozen-validator.d.ts +4 -1
- package/dist/compile/handler/frozen-validator.js +8 -3
- package/dist/compile/handler/frozen-validator.mjs +5 -3
- package/dist/compile/handler/index.d.ts +1 -1
- package/dist/compile/handler/index.js +3 -3
- package/dist/compile/handler/index.mjs +3 -3
- package/dist/compile/handler/reconstruct.d.ts +1 -1
- package/dist/compile/handler/reconstruct.js +4 -3
- package/dist/compile/handler/reconstruct.mjs +4 -3
- package/dist/compile/jit-probe.d.ts +2 -2
- package/dist/context.d.ts +1 -1
- package/dist/context.js +5 -1
- package/dist/context.mjs +5 -1
- package/dist/index.d.ts +2 -2
- package/dist/package.js +1 -1
- package/dist/package.mjs +1 -1
- package/dist/plugin/aot/core.d.ts +11 -5
- package/dist/plugin/aot/core.js +86 -38
- package/dist/plugin/aot/core.mjs +87 -41
- package/dist/plugin/aot/esbuild.js +8 -2
- package/dist/plugin/aot/esbuild.mjs +9 -3
- package/dist/plugin/aot/hooks.d.ts +2 -1
- package/dist/plugin/aot/hooks.js +3 -3
- package/dist/plugin/aot/hooks.mjs +4 -4
- package/dist/plugin/aot/source.d.ts +4 -1
- package/dist/plugin/aot/source.js +21 -14
- package/dist/plugin/aot/source.mjs +21 -14
- package/dist/plugin/aot/worker.js +2 -2
- package/dist/plugin/aot/worker.mjs +2 -2
- package/dist/route-table.d.ts +3 -0
- package/dist/route-table.js +4 -1
- package/dist/route-table.mjs +4 -1
- package/dist/schema-snapshot.js +17 -22
- package/dist/schema-snapshot.mjs +17 -22
- package/dist/sucrose.js +6 -0
- package/dist/sucrose.mjs +6 -0
- package/dist/type/bridge-live.d.ts +1 -1
- package/dist/type/bridge.d.ts +1 -1
- package/dist/type/coerce.d.ts +1 -1
- package/dist/type/constants.d.ts +1 -1
- package/dist/type/validator/index.js +10 -7
- package/dist/type/validator/index.mjs +10 -7
- package/dist/type/validator/validator-cache.js +17 -9
- package/dist/type/validator/validator-cache.mjs +17 -9
- package/dist/types.d.ts +39 -5
- package/dist/utils.d.ts +10 -1
- package/dist/utils.js +8 -1
- package/dist/utils.mjs +6 -1
- package/dist/ws/context.d.ts +4 -1
- package/dist/ws/context.js +21 -3
- package/dist/ws/context.mjs +20 -3
- package/dist/ws/route.js +93 -21
- package/dist/ws/route.mjs +96 -24
- package/package.json +2 -2
|
@@ -1,13 +1,83 @@
|
|
|
1
1
|
import { createAdapter } from "../index.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { flattenChain, getLoosePath, nullObject } from "../../utils.mjs";
|
|
2
|
+
import { flattenChain, getLoosePath, isSocketQuiet, nullObject, throwLifecycleErrors } from "../../utils.mjs";
|
|
4
3
|
import { WebStandardAdapter } from "../web-standard/index.mjs";
|
|
5
4
|
import { frozenRootOf, resolvedWsOf } from "../../generation.mjs";
|
|
6
5
|
import { origin } from "../origin.mjs";
|
|
7
6
|
import { buildNativeStaticResponse } from "../../compile/handler/index.mjs";
|
|
8
|
-
import { routeRow } from "../../route-table.mjs";
|
|
7
|
+
import { RouteFlag, routeRow } from "../../route-table.mjs";
|
|
9
8
|
|
|
10
9
|
//#region src/adapter/bun/index.ts
|
|
10
|
+
function runLifecycle(epoch, callback, ...args) {
|
|
11
|
+
const invoking = epoch.invoking;
|
|
12
|
+
epoch.invoking = true;
|
|
13
|
+
try {
|
|
14
|
+
return callback(...args);
|
|
15
|
+
} finally {
|
|
16
|
+
epoch.invoking = invoking;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const unavailableFetch = () => new Response(null, {
|
|
20
|
+
headers: { connection: "close" },
|
|
21
|
+
status: 503
|
|
22
|
+
});
|
|
23
|
+
function reloadServer(server, serve) {
|
|
24
|
+
try {
|
|
25
|
+
server.reload(serve);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
if (!serve.routes) throw error;
|
|
28
|
+
delete serve.routes;
|
|
29
|
+
console.warn("[Elysia] Native static promotion was skipped:", error);
|
|
30
|
+
try {
|
|
31
|
+
server.reload(serve);
|
|
32
|
+
} catch (fallbackError) {
|
|
33
|
+
console.error("[Elysia] Failed to reload Bun server:", fallbackError);
|
|
34
|
+
throw fallbackError;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function releaseLifecycle(app, ext, createdExt, stop, registerCleanup) {
|
|
39
|
+
if (ext.stop === stop) delete ext.stop;
|
|
40
|
+
if (ext.cleanupEpoch === registerCleanup) delete ext.cleanupEpoch;
|
|
41
|
+
if (createdExt && app["~ext"] === ext && !Object.keys(ext).length) app["~ext"] = void 0;
|
|
42
|
+
}
|
|
43
|
+
function clearAppServer(app, server) {
|
|
44
|
+
if (app.server === server) app.server = void 0;
|
|
45
|
+
}
|
|
46
|
+
async function waitForServerRequests(server, abandoned) {
|
|
47
|
+
while (server.pendingRequests && !abandoned()) await Bun.sleep(1);
|
|
48
|
+
}
|
|
49
|
+
async function settleServerWebSockets(lifecycle, errors) {
|
|
50
|
+
if (!lifecycle) return true;
|
|
51
|
+
lifecycle.closing = true;
|
|
52
|
+
let failed = false;
|
|
53
|
+
do {
|
|
54
|
+
for (const socket of lifecycle.sockets) if (socket.readyState < 3) try {
|
|
55
|
+
socket.terminate();
|
|
56
|
+
} catch (error) {
|
|
57
|
+
errors.push(error);
|
|
58
|
+
failed = true;
|
|
59
|
+
}
|
|
60
|
+
if (failed) return false;
|
|
61
|
+
await Bun.sleep(1);
|
|
62
|
+
const abandon = lifecycle.abandon === true;
|
|
63
|
+
for (const socket of lifecycle.sockets) if (abandon || isSocketQuiet(socket)) {
|
|
64
|
+
delete socket.data["~lifecycleRun"];
|
|
65
|
+
lifecycle.sockets.delete(socket);
|
|
66
|
+
}
|
|
67
|
+
} while (lifecycle.sockets.size);
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
const closeServerIdle = (server, errors) => {
|
|
71
|
+
const close = server.closeIdleConnections;
|
|
72
|
+
if (!close) return true;
|
|
73
|
+
try {
|
|
74
|
+
close.call(server);
|
|
75
|
+
return true;
|
|
76
|
+
} catch (error) {
|
|
77
|
+
errors.push(error);
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
11
81
|
/**
|
|
12
82
|
* ! This may looks like it would cause race condition, but it is not
|
|
13
83
|
* Bun is single-threaded and synchronous, so the `finally` block will
|
|
@@ -25,15 +95,7 @@ const withOrigin = (fetch) => (request, server) => {
|
|
|
25
95
|
origin.request = void 0;
|
|
26
96
|
}
|
|
27
97
|
};
|
|
28
|
-
const
|
|
29
|
-
"GET",
|
|
30
|
-
"POST",
|
|
31
|
-
"PUT",
|
|
32
|
-
"DELETE",
|
|
33
|
-
"PATCH",
|
|
34
|
-
"HEAD",
|
|
35
|
-
"OPTIONS"
|
|
36
|
-
]);
|
|
98
|
+
const isNativeStaticMethod = (method) => method === "GET" || method === "POST" || method === "PUT" || method === "DELETE" || method === "PATCH" || method === "HEAD" || method === "OPTIONS";
|
|
37
99
|
function collectStaticRoutes(app) {
|
|
38
100
|
if (app["~config"]?.nativeStaticResponse === false) return;
|
|
39
101
|
app.fetch;
|
|
@@ -43,59 +105,58 @@ function collectStaticRoutes(app) {
|
|
|
43
105
|
const table = app["~generation"]?.routeTable ?? app["~routeTable"];
|
|
44
106
|
const length = table?.length ?? 0;
|
|
45
107
|
if (!table || !length) return;
|
|
46
|
-
const methods = table
|
|
47
|
-
const paths = table.path;
|
|
48
|
-
const handlers = table.handler;
|
|
108
|
+
const { method: methods, path: paths, handler: handlers, flags } = table;
|
|
49
109
|
let hasCandidate = false;
|
|
50
110
|
for (let i = 0; i < length; i++) {
|
|
111
|
+
if (!isNativeStaticMethod(methods[i]) || (flags[i] & RouteFlag.Dynamic) !== 0) continue;
|
|
51
112
|
const h = handlers[i];
|
|
52
|
-
if (typeof h
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
113
|
+
if (typeof h !== "function" && !(h instanceof Error) && !(h instanceof Promise)) {
|
|
114
|
+
hasCandidate = true;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
56
117
|
}
|
|
57
118
|
if (!hasCandidate) return;
|
|
58
|
-
const ready = nullObject();
|
|
59
119
|
const strictPath = frozenRoot["~config"]?.strictPath === true;
|
|
60
|
-
const
|
|
61
|
-
for (let i = 0; i < length; i++)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (needEncodeRegex.test(path)) {
|
|
72
|
-
const encoded = encodeURI(path);
|
|
73
|
-
if (encoded !== path) set.add(encoded);
|
|
74
|
-
}
|
|
120
|
+
const routeIndex = /* @__PURE__ */ new Map();
|
|
121
|
+
for (let i = 0; i < length; i++) {
|
|
122
|
+
const method = methods[i];
|
|
123
|
+
if (!isNativeStaticMethod(method) || (flags[i] & RouteFlag.Dynamic) !== 0) continue;
|
|
124
|
+
const path = paths[i];
|
|
125
|
+
let pathsByMethod = routeIndex.get(method);
|
|
126
|
+
if (!pathsByMethod) routeIndex.set(method, pathsByMethod = /* @__PURE__ */ new Map());
|
|
127
|
+
pathsByMethod.set(path, i);
|
|
128
|
+
if (!strictPath && (flags[i] & RouteFlag.Encode) !== 0) {
|
|
129
|
+
const encoded = encodeURI(path);
|
|
130
|
+
if (encoded !== path && !pathsByMethod.has(encoded)) pathsByMethod.set(encoded, -1);
|
|
75
131
|
}
|
|
76
132
|
}
|
|
77
|
-
const
|
|
78
|
-
|
|
133
|
+
const ready = nullObject();
|
|
134
|
+
let hasReady = false;
|
|
135
|
+
const add = (method, path, value, needsEncode) => {
|
|
136
|
+
if (needsEncode) path = encodeURI(path);
|
|
79
137
|
(ready[path] ??= nullObject())[method] = value;
|
|
138
|
+
hasReady = true;
|
|
80
139
|
};
|
|
81
140
|
for (let i = 0; i < length; i++) {
|
|
82
141
|
const method = methods[i];
|
|
83
|
-
|
|
142
|
+
const routeFlags = flags[i];
|
|
143
|
+
if (!isNativeStaticMethod(method) || (routeFlags & RouteFlag.Dynamic) !== 0) continue;
|
|
144
|
+
const pathsByMethod = routeIndex.get(method);
|
|
84
145
|
const path = paths[i];
|
|
85
|
-
if (
|
|
86
|
-
if (!nativeStaticMethods.has(method)) continue;
|
|
146
|
+
if (pathsByMethod.get(path) !== i) continue;
|
|
87
147
|
const h = handlers[i];
|
|
88
148
|
if (typeof h === "function" || h instanceof Error || h instanceof Promise) continue;
|
|
89
149
|
const value = buildNativeStaticResponse(routeRow(table, i), app);
|
|
90
150
|
if (!value) continue;
|
|
91
|
-
|
|
92
|
-
|
|
151
|
+
const needsEncode = (routeFlags & RouteFlag.Encode) !== 0;
|
|
152
|
+
add(method, path, value, needsEncode);
|
|
153
|
+
if (!strictPath) {
|
|
93
154
|
const loose = getLoosePath(path);
|
|
94
|
-
if (loose !== path && !
|
|
155
|
+
if (loose !== path && !pathsByMethod.has(loose)) add(method, loose, value, needsEncode);
|
|
95
156
|
}
|
|
96
157
|
}
|
|
97
|
-
if (!
|
|
98
|
-
return
|
|
158
|
+
if (!hasReady) return;
|
|
159
|
+
return ready;
|
|
99
160
|
}
|
|
100
161
|
const BunAdapter = createAdapter({
|
|
101
162
|
name: "bun",
|
|
@@ -104,23 +165,41 @@ const BunAdapter = createAdapter({
|
|
|
104
165
|
parse: WebStandardAdapter.parse,
|
|
105
166
|
response: WebStandardAdapter.response,
|
|
106
167
|
listen(app, options, callback) {
|
|
107
|
-
|
|
168
|
+
if (app.server || app["~ext"]?.stop) throw new Error("[Elysia] Cannot call listen() while a server or teardown is active");
|
|
169
|
+
function gatedFetch(request, server) {
|
|
170
|
+
return live ? live(request, server) : Promise.resolve(ready).then(async () => {
|
|
171
|
+
if (!live) {
|
|
172
|
+
await startupShutdown;
|
|
173
|
+
throw new Error("[Elysia] Server was stopped before it was ready");
|
|
174
|
+
}
|
|
175
|
+
return live(request, server);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
108
178
|
const optionsIsObject = typeof options === "object";
|
|
109
|
-
let live;
|
|
110
|
-
const gatedFetch = (request, server) => live ? live(request, server) : Promise.resolve(ready).then(() => {
|
|
111
|
-
if (!live) throw new Error("[Elysia] Server was stopped before it was ready");
|
|
112
|
-
return live(request, server);
|
|
113
|
-
});
|
|
114
179
|
const _options = optionsIsObject ? { ...options } : {
|
|
115
180
|
port: +options,
|
|
116
181
|
fetch: gatedFetch
|
|
117
182
|
};
|
|
118
183
|
if (optionsIsObject) _options.fetch = gatedFetch;
|
|
184
|
+
const _config = app["~config"]?.serve;
|
|
119
185
|
const serve = _config ? {
|
|
120
186
|
..._config,
|
|
121
187
|
..._options
|
|
122
188
|
} : _options;
|
|
189
|
+
const server = app.server = Bun.serve(serve.routes || serve.error ? {
|
|
190
|
+
...serve,
|
|
191
|
+
error: unavailableFetch,
|
|
192
|
+
routes: {}
|
|
193
|
+
} : serve);
|
|
194
|
+
let live;
|
|
195
|
+
let cancelled = false;
|
|
196
|
+
let startupShutdown;
|
|
197
|
+
let shutdownAttempt;
|
|
123
198
|
let ready;
|
|
199
|
+
let wsLifecycle;
|
|
200
|
+
let modulesReady;
|
|
201
|
+
let built;
|
|
202
|
+
let pendingSetups;
|
|
124
203
|
const build = () => {
|
|
125
204
|
const fetch = app.fetch;
|
|
126
205
|
let routes;
|
|
@@ -133,7 +212,17 @@ const BunAdapter = createAdapter({
|
|
|
133
212
|
if (app["~hasWS"]) {
|
|
134
213
|
const resolved = resolvedWsOf(app);
|
|
135
214
|
if (!resolved) throw new Error("[Elysia] internal: WebSocket routes are present but no capability provider was resolved");
|
|
136
|
-
|
|
215
|
+
const handler = resolved.provider.buildGlobalWSHandler();
|
|
216
|
+
websocket = resolved.config ? Object.assign(handler, resolved.config) : handler;
|
|
217
|
+
const setter = Object.getOwnPropertyDescriptor(handler, "~lifecycle")?.set;
|
|
218
|
+
if (setter) {
|
|
219
|
+
wsLifecycle = {
|
|
220
|
+
closing: false,
|
|
221
|
+
sockets: /* @__PURE__ */ new Set(),
|
|
222
|
+
run: runLifecycle.bind(null, epoch)
|
|
223
|
+
};
|
|
224
|
+
setter.call(handler, wsLifecycle);
|
|
225
|
+
}
|
|
137
226
|
}
|
|
138
227
|
return {
|
|
139
228
|
fetch,
|
|
@@ -141,75 +230,243 @@ const BunAdapter = createAdapter({
|
|
|
141
230
|
websocket
|
|
142
231
|
};
|
|
143
232
|
};
|
|
144
|
-
let
|
|
145
|
-
|
|
146
|
-
|
|
233
|
+
let startupFailure;
|
|
234
|
+
let forceRequested = false;
|
|
235
|
+
let forceDone = false;
|
|
236
|
+
let nativeStopped = false;
|
|
237
|
+
let abandoned = false;
|
|
238
|
+
let published = false;
|
|
239
|
+
let cleanupCompleted = false;
|
|
240
|
+
let idleRequired = false;
|
|
241
|
+
let cleanupFailures;
|
|
242
|
+
let observedOutcome;
|
|
243
|
+
let persistentCleanup;
|
|
244
|
+
let persistentCleanupLength = -1;
|
|
245
|
+
const epochCleanup = [];
|
|
246
|
+
const createdExt = app["~ext"] === void 0;
|
|
247
|
+
const ext = app["~ext"] ??= nullObject();
|
|
248
|
+
const epoch = {
|
|
249
|
+
setup: false,
|
|
250
|
+
invoking: false
|
|
251
|
+
};
|
|
252
|
+
const snapshotCleanup = () => {
|
|
253
|
+
if (persistentCleanupLength !== -1) return;
|
|
254
|
+
persistentCleanup = ext.cleanup;
|
|
255
|
+
persistentCleanupLength = persistentCleanup?.length ?? 0;
|
|
256
|
+
};
|
|
257
|
+
const registerCleanup = (handler) => {
|
|
258
|
+
if (ext.cleanupEpoch !== registerCleanup) return false;
|
|
259
|
+
if (!epoch.setup) throw new Error("[Elysia] .cleanup() called after its setup epoch settled");
|
|
260
|
+
if (Array.isArray(handler)) epochCleanup.push(...handler);
|
|
261
|
+
else epochCleanup.push(handler);
|
|
262
|
+
return true;
|
|
263
|
+
};
|
|
264
|
+
const gate = () => {
|
|
265
|
+
if (!published) return;
|
|
266
|
+
live = void 0;
|
|
267
|
+
if (wsLifecycle) wsLifecycle.closing = true;
|
|
147
268
|
try {
|
|
148
|
-
server.reload(
|
|
269
|
+
server.reload({
|
|
270
|
+
...serve,
|
|
271
|
+
fetch: unavailableFetch,
|
|
272
|
+
error: unavailableFetch,
|
|
273
|
+
routes: {}
|
|
274
|
+
});
|
|
149
275
|
} catch (error) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
276
|
+
return { error };
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
const nativeStop = async (closeActiveConnections, errors) => {
|
|
280
|
+
try {
|
|
281
|
+
await server.stop(closeActiveConnections);
|
|
282
|
+
nativeStopped = true;
|
|
283
|
+
if (closeActiveConnections) {
|
|
284
|
+
forceDone = true;
|
|
285
|
+
forceRequested = false;
|
|
286
|
+
}
|
|
287
|
+
return true;
|
|
288
|
+
} catch (error) {
|
|
289
|
+
if (closeActiveConnections) forceRequested = true;
|
|
290
|
+
errors.push(error);
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
const isAbandoned = () => abandoned;
|
|
295
|
+
const quiesce = async (force) => {
|
|
296
|
+
const errors = [];
|
|
297
|
+
if (!published) return {
|
|
298
|
+
errors,
|
|
299
|
+
releasable: await nativeStop(true, errors),
|
|
300
|
+
safe: true
|
|
301
|
+
};
|
|
302
|
+
if (force && !idleRequired && !wsLifecycle?.closing) {
|
|
303
|
+
const stopped = await nativeStop(true, errors);
|
|
304
|
+
return {
|
|
305
|
+
errors,
|
|
306
|
+
releasable: stopped,
|
|
307
|
+
safe: stopped
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
const gateFailure = gate();
|
|
311
|
+
if (gateFailure) errors.push(gateFailure.error);
|
|
312
|
+
let socketsSettled = await settleServerWebSockets(wsLifecycle, errors);
|
|
313
|
+
await Promise.resolve();
|
|
314
|
+
let close = forceRequested || gateFailure !== void 0 || !socketsSettled;
|
|
315
|
+
if (!close && nativeStopped) await waitForServerRequests(server, isAbandoned);
|
|
316
|
+
idleRequired = true;
|
|
317
|
+
let stopped = true;
|
|
318
|
+
while (!nativeStopped || forceRequested && !forceDone) {
|
|
319
|
+
if (nativeStopped) close = true;
|
|
320
|
+
stopped = await nativeStop(close, errors);
|
|
321
|
+
if (stopped) {
|
|
322
|
+
if (socketsSettled) socketsSettled = await settleServerWebSockets(wsLifecycle, errors);
|
|
323
|
+
continue;
|
|
158
324
|
}
|
|
325
|
+
if (close) break;
|
|
326
|
+
await waitForServerRequests(server, isAbandoned);
|
|
327
|
+
close = true;
|
|
159
328
|
}
|
|
329
|
+
let idleClosed = true;
|
|
330
|
+
if (idleRequired) {
|
|
331
|
+
idleClosed = closeServerIdle(server, errors);
|
|
332
|
+
if (idleClosed && !gateFailure) idleRequired = false;
|
|
333
|
+
}
|
|
334
|
+
const safe = socketsSettled && stopped && idleClosed && (!gateFailure || forceDone);
|
|
335
|
+
return {
|
|
336
|
+
errors,
|
|
337
|
+
releasable: safe,
|
|
338
|
+
safe
|
|
339
|
+
};
|
|
160
340
|
};
|
|
161
341
|
const setup = () => {
|
|
162
|
-
|
|
342
|
+
snapshotCleanup();
|
|
343
|
+
const onSetup = ext.setup;
|
|
163
344
|
if (!onSetup) return;
|
|
164
|
-
|
|
345
|
+
epoch.setup = true;
|
|
346
|
+
ext.cleanupEpoch = registerCleanup;
|
|
165
347
|
for (let i = 0; i < onSetup.length; i++) {
|
|
166
|
-
|
|
167
|
-
|
|
348
|
+
if (cancelled) break;
|
|
349
|
+
try {
|
|
350
|
+
const result = runLifecycle(epoch, onSetup[i], app);
|
|
351
|
+
if (result && typeof result.then === "function") (pendingSetups ??= []).push(Promise.resolve(result));
|
|
352
|
+
} catch (error) {
|
|
353
|
+
if (!pendingSetups) epoch.setup = false;
|
|
354
|
+
stop(true, { error }).catch(() => {});
|
|
355
|
+
throw error;
|
|
356
|
+
}
|
|
168
357
|
}
|
|
169
|
-
if (pendingSetups) return Promise.all(pendingSetups);
|
|
358
|
+
if (pendingSetups) return Promise.all(pendingSetups).then(() => epoch.setup = false);
|
|
359
|
+
epoch.setup = false;
|
|
170
360
|
};
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
361
|
+
const stop = (closeActiveConnections, failure) => {
|
|
362
|
+
const reentrant = epoch.invoking;
|
|
363
|
+
if (failure) startupFailure ??= failure;
|
|
364
|
+
if (closeActiveConnections === true && !forceDone) forceRequested = true;
|
|
365
|
+
cancelled = true;
|
|
366
|
+
clearAppServer(app, server);
|
|
367
|
+
let outcome;
|
|
368
|
+
if (shutdownAttempt) {
|
|
369
|
+
if (closeActiveConnections === true && !forceDone) {
|
|
370
|
+
abandoned = true;
|
|
371
|
+
if (wsLifecycle) wsLifecycle.abandon = true;
|
|
372
|
+
}
|
|
373
|
+
outcome = shutdownAttempt;
|
|
374
|
+
} else {
|
|
375
|
+
const { promise, resolve, reject } = Promise.withResolvers();
|
|
376
|
+
outcome = shutdownAttempt = promise;
|
|
377
|
+
const quiescence = quiesce(forceRequested || !published);
|
|
378
|
+
let releasable = false;
|
|
379
|
+
(async () => {
|
|
380
|
+
try {
|
|
381
|
+
await Promise.resolve();
|
|
382
|
+
if (modulesReady) try {
|
|
383
|
+
await modulesReady;
|
|
384
|
+
} catch (error) {
|
|
385
|
+
startupFailure ??= { error };
|
|
386
|
+
}
|
|
387
|
+
if (pendingSetups) {
|
|
388
|
+
const setupResults = await Promise.allSettled(pendingSetups);
|
|
389
|
+
if (!startupFailure) for (let i = 0; i < setupResults.length; i++) {
|
|
390
|
+
const result = setupResults[i];
|
|
391
|
+
if (result.status === "rejected") {
|
|
392
|
+
startupFailure = { error: result.reason };
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
epoch.setup = false;
|
|
398
|
+
const errors = [];
|
|
399
|
+
const quiesced = await quiescence;
|
|
400
|
+
if (startupFailure) errors.push(startupFailure.error);
|
|
401
|
+
errors.push(...quiesced.errors);
|
|
402
|
+
releasable = quiesced.releasable;
|
|
403
|
+
if (!quiesced.safe) {
|
|
404
|
+
if (cleanupFailures) errors.push(...cleanupFailures);
|
|
405
|
+
throwLifecycleErrors(errors);
|
|
406
|
+
}
|
|
407
|
+
if (!cleanupCompleted) {
|
|
408
|
+
snapshotCleanup();
|
|
409
|
+
if (ext.stop === stop) {
|
|
410
|
+
const length = persistentCleanupLength + epochCleanup.length;
|
|
411
|
+
for (let n = 0; n < length; n++) {
|
|
412
|
+
if (ext.stop !== stop) break;
|
|
413
|
+
const i = startupFailure ? length - 1 - n : n;
|
|
414
|
+
const handler = i < persistentCleanupLength ? persistentCleanup[i] : epochCleanup[i - persistentCleanupLength];
|
|
415
|
+
try {
|
|
416
|
+
await runLifecycle(epoch, handler, app);
|
|
417
|
+
} catch (error) {
|
|
418
|
+
(cleanupFailures ??= []).push(error);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
cleanupCompleted = true;
|
|
423
|
+
epochCleanup.length = 0;
|
|
424
|
+
}
|
|
425
|
+
if (cleanupFailures) errors.push(...cleanupFailures);
|
|
426
|
+
if (releasable) releaseLifecycle(app, ext, createdExt, stop, registerCleanup);
|
|
427
|
+
throwLifecycleErrors(errors);
|
|
428
|
+
} finally {
|
|
429
|
+
epoch.setup = false;
|
|
430
|
+
if (!releasable) shutdownAttempt = void 0;
|
|
431
|
+
}
|
|
432
|
+
})().then(resolve, reject);
|
|
179
433
|
}
|
|
180
|
-
|
|
181
|
-
if (
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
434
|
+
if (!published) startupShutdown ??= outcome;
|
|
435
|
+
if (!reentrant) return outcome;
|
|
436
|
+
if (observedOutcome !== outcome) {
|
|
437
|
+
observedOutcome = outcome;
|
|
438
|
+
outcome.catch((error) => {
|
|
439
|
+
console.error("[Elysia] stop() failed:", error);
|
|
440
|
+
});
|
|
186
441
|
}
|
|
187
|
-
return
|
|
442
|
+
return Promise.resolve();
|
|
188
443
|
};
|
|
444
|
+
ext.stop = stop;
|
|
189
445
|
const publish = () => {
|
|
190
|
-
if (app.server !== server) return;
|
|
446
|
+
if (cancelled || app.server !== server) return;
|
|
191
447
|
built ??= build();
|
|
192
448
|
live = serve.fetch = withOrigin(built.fetch);
|
|
449
|
+
published = true;
|
|
193
450
|
if (built.websocket) serve.websocket = built.websocket;
|
|
194
|
-
if (built.routes) serve.routes = built.routes
|
|
195
|
-
|
|
451
|
+
if (built.routes) serve.routes = built.routes;
|
|
452
|
+
reloadServer(server, serve);
|
|
196
453
|
if (callback) callback(server);
|
|
197
454
|
};
|
|
198
455
|
const start = () => {
|
|
199
|
-
|
|
456
|
+
modulesReady = void 0;
|
|
457
|
+
if (cancelled || app.server !== server) return;
|
|
200
458
|
const setupReady = setup();
|
|
201
459
|
if (setupReady && typeof setupReady.then === "function") return Promise.resolve(setupReady).then(publish);
|
|
202
460
|
publish();
|
|
203
461
|
};
|
|
204
462
|
try {
|
|
205
|
-
|
|
206
|
-
|
|
463
|
+
ready = (modulesReady = app.modules).then(start);
|
|
464
|
+
ready = ready.catch((error) => stop(true, { error }));
|
|
207
465
|
ready.catch((error) => {
|
|
208
466
|
console.error("[Elysia] listen() failed:", error);
|
|
209
|
-
return rollback(error);
|
|
210
467
|
});
|
|
211
468
|
} catch (error) {
|
|
212
|
-
|
|
469
|
+
stop(true, { error }).catch(console.error);
|
|
213
470
|
throw error;
|
|
214
471
|
}
|
|
215
472
|
}
|