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