elysia 2.0.0-beta.26 → 2.0.0-beta.27
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.js +0 -19
- package/dist/adapter/bun/index.mjs +0 -19
- package/dist/adapter/origin.js +0 -57
- package/dist/adapter/origin.mjs +0 -57
- package/dist/adapter/utils.js +1 -25
- package/dist/adapter/utils.mjs +1 -25
- package/dist/adapter/web-standard/handler.js +3 -1
- package/dist/adapter/web-standard/handler.mjs +3 -1
- package/dist/base.js +21 -136
- package/dist/base.mjs +22 -137
- package/dist/compile/aot-capture.js +0 -2
- package/dist/compile/aot-capture.mjs +0 -2
- package/dist/compile/aot-emit.js +0 -13
- package/dist/compile/aot-emit.mjs +0 -13
- package/dist/compile/aot-reconstruct.js +0 -26
- package/dist/compile/aot-reconstruct.mjs +0 -26
- package/dist/compile/aot.js +0 -7
- package/dist/compile/aot.mjs +0 -7
- package/dist/compile/handler/frozen-validator.js +0 -11
- package/dist/compile/handler/frozen-validator.mjs +0 -11
- package/dist/compile/handler/index.js +0 -9
- package/dist/compile/handler/index.mjs +0 -9
- package/dist/compile/handler/jit.d.ts +5 -1
- package/dist/compile/handler/jit.js +2 -15
- package/dist/compile/handler/jit.mjs +3 -16
- package/dist/compile/handler/params.js +0 -3
- package/dist/compile/handler/params.mjs +0 -3
- package/dist/compile/handler/utils.js +3 -14
- package/dist/compile/handler/utils.mjs +3 -14
- package/dist/compiled.js +0 -9
- package/dist/compiled.mjs +0 -9
- package/dist/constants.js +18 -2
- package/dist/constants.mjs +18 -2
- package/dist/cookie/crypto.d.ts +1 -1
- package/dist/cookie/crypto.js +8 -7
- package/dist/cookie/crypto.mjs +8 -7
- package/dist/cookie/utils.js +0 -13
- package/dist/cookie/utils.mjs +0 -13
- package/dist/error.js +0 -73
- package/dist/error.mjs +0 -73
- package/dist/handler/error.js +0 -26
- package/dist/handler/error.mjs +0 -26
- package/dist/handler/fetch.js +12 -23
- package/dist/handler/fetch.mjs +12 -23
- package/dist/handler/utils.js +5 -5
- package/dist/handler/utils.mjs +5 -5
- package/dist/package.js +1 -1
- package/dist/package.mjs +1 -1
- package/dist/parse-query.js +0 -4
- package/dist/parse-query.mjs +0 -4
- package/dist/plugin/aot/bun.js +0 -26
- package/dist/plugin/aot/bun.mjs +0 -26
- package/dist/plugin/aot/core.d.ts +1 -1
- package/dist/plugin/aot/core.js +0 -32
- package/dist/plugin/aot/core.mjs +0 -32
- package/dist/plugin/aot/esbuild.js +0 -19
- package/dist/plugin/aot/esbuild.mjs +0 -19
- package/dist/plugin/aot/rspack-loader.js +0 -16
- package/dist/plugin/aot/rspack-loader.mjs +0 -16
- package/dist/plugin/aot/rspack.js +0 -14
- package/dist/plugin/aot/rspack.mjs +0 -14
- package/dist/plugin/aot/unplugin.js +0 -19
- package/dist/plugin/aot/unplugin.mjs +0 -19
- package/dist/plugin/aot/vite.js +0 -14
- package/dist/plugin/aot/vite.mjs +0 -14
- package/dist/plugin/trace.js +0 -16
- package/dist/plugin/trace.mjs +0 -16
- package/dist/plugin/websocket.js +0 -21
- package/dist/plugin/websocket.mjs +0 -21
- package/dist/schema-snapshot.js +0 -4
- package/dist/schema-snapshot.mjs +0 -4
- package/dist/sucrose.js +0 -4
- package/dist/sucrose.mjs +0 -4
- package/dist/trace.js +0 -1
- package/dist/trace.mjs +0 -1
- package/dist/type/bridge.js +22 -20
- package/dist/type/bridge.mjs +22 -20
- package/dist/type/coerce-plan.js +0 -1
- package/dist/type/coerce-plan.mjs +0 -1
- package/dist/type/coerce.js +0 -4
- package/dist/type/coerce.mjs +0 -4
- package/dist/type/constants.d.ts +1 -1
- package/dist/type/elysia/numeric-enum.js +0 -4
- package/dist/type/elysia/numeric-enum.mjs +0 -4
- package/dist/type/elysia/problem.js +0 -6
- package/dist/type/elysia/problem.mjs +0 -6
- package/dist/type/index.js +0 -11
- package/dist/type/index.mjs +0 -11
- package/dist/type/shared.js +0 -6
- package/dist/type/shared.mjs +0 -6
- package/dist/type/sync-require.js +0 -7
- package/dist/type/sync-require.mjs +0 -7
- package/dist/type/typebox-type.js +0 -6
- package/dist/type/typebox-type.mjs +0 -6
- package/dist/type/typebox-value.js +0 -3
- package/dist/type/typebox-value.mjs +0 -3
- package/dist/type/validator/default-precompute.js +0 -7
- package/dist/type/validator/default-precompute.mjs +0 -7
- package/dist/type/validator/exact-mirror-live.js +0 -7
- package/dist/type/validator/exact-mirror-live.mjs +1 -7
- package/dist/type/validator/exact-mirror.js +15 -7
- package/dist/type/validator/exact-mirror.mjs +15 -7
- package/dist/type/validator/index.js +1 -4
- package/dist/type/validator/index.mjs +1 -4
- package/dist/universal/file.js +0 -7
- package/dist/universal/file.mjs +0 -7
- package/dist/utils.js +10 -90
- package/dist/utils.mjs +10 -90
- package/dist/ws/context.js +14 -15
- package/dist/ws/context.mjs +14 -15
- package/dist/ws/parser.js +2 -2
- package/dist/ws/parser.mjs +2 -2
- package/dist/ws/route.js +112 -52
- package/dist/ws/route.mjs +113 -53
- package/package.json +1 -1
|
@@ -52,15 +52,6 @@ async function settleServerWebSockets(lifecycle, errors) {
|
|
|
52
52
|
} while (lifecycle.sockets.size);
|
|
53
53
|
return true;
|
|
54
54
|
}
|
|
55
|
-
/**
|
|
56
|
-
* ! This may looks like it would cause race condition, but it is not
|
|
57
|
-
* Bun is single-threaded and synchronous, so the `finally` block will
|
|
58
|
-
* always run before the next request comes in
|
|
59
|
-
* @see ../origin.ts
|
|
60
|
-
*
|
|
61
|
-
* `finally` runs on the synchronous return of `fetch` (the returned promise is
|
|
62
|
-
* not awaited), so the slot is live only for the handler's synchronous prologue
|
|
63
|
-
*/
|
|
64
55
|
const withOrigin = (fetch) => (request, server) => {
|
|
65
56
|
require_adapter_origin.origin.request = request;
|
|
66
57
|
try {
|
|
@@ -92,16 +83,6 @@ function isNativePath(path) {
|
|
|
92
83
|
}
|
|
93
84
|
return true;
|
|
94
85
|
}
|
|
95
|
-
/**
|
|
96
|
-
* HTML bundles served by Bun's native router.
|
|
97
|
-
*
|
|
98
|
-
* A `:param` / `*` bundle matches before `fetch`, so with a `handoff` every
|
|
99
|
-
* other route of that method is registered natively too, pointing at `fetch`;
|
|
100
|
-
* Bun's specificity (exact > param > wildcard) then picks the Elysia route over
|
|
101
|
-
* the bundle (Elysia's own router skips these bundles, see `#buildRouterUnsafe`).
|
|
102
|
-
* If any route can't be expressed in Bun's router, dynamic bundles are skipped
|
|
103
|
-
* entirely, since the bundle would shadow that route.
|
|
104
|
-
*/
|
|
105
86
|
function collectHTMLBundleRoutes(app, handoff, routes) {
|
|
106
87
|
let dynamic;
|
|
107
88
|
let bundles;
|
|
@@ -52,15 +52,6 @@ async function settleServerWebSockets(lifecycle, errors) {
|
|
|
52
52
|
} while (lifecycle.sockets.size);
|
|
53
53
|
return true;
|
|
54
54
|
}
|
|
55
|
-
/**
|
|
56
|
-
* ! This may looks like it would cause race condition, but it is not
|
|
57
|
-
* Bun is single-threaded and synchronous, so the `finally` block will
|
|
58
|
-
* always run before the next request comes in
|
|
59
|
-
* @see ../origin.ts
|
|
60
|
-
*
|
|
61
|
-
* `finally` runs on the synchronous return of `fetch` (the returned promise is
|
|
62
|
-
* not awaited), so the slot is live only for the handler's synchronous prologue
|
|
63
|
-
*/
|
|
64
55
|
const withOrigin = (fetch) => (request, server) => {
|
|
65
56
|
origin.request = request;
|
|
66
57
|
try {
|
|
@@ -92,16 +83,6 @@ function isNativePath(path) {
|
|
|
92
83
|
}
|
|
93
84
|
return true;
|
|
94
85
|
}
|
|
95
|
-
/**
|
|
96
|
-
* HTML bundles served by Bun's native router.
|
|
97
|
-
*
|
|
98
|
-
* A `:param` / `*` bundle matches before `fetch`, so with a `handoff` every
|
|
99
|
-
* other route of that method is registered natively too, pointing at `fetch`;
|
|
100
|
-
* Bun's specificity (exact > param > wildcard) then picks the Elysia route over
|
|
101
|
-
* the bundle (Elysia's own router skips these bundles, see `#buildRouterUnsafe`).
|
|
102
|
-
* If any route can't be expressed in Bun's router, dynamic bundles are skipped
|
|
103
|
-
* entirely, since the bundle would shadow that route.
|
|
104
|
-
*/
|
|
105
86
|
function collectHTMLBundleRoutes(app, handoff, routes) {
|
|
106
87
|
let dynamic;
|
|
107
88
|
let bundles;
|
package/dist/adapter/origin.js
CHANGED
|
@@ -1,63 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
3
|
//#region src/adapter/origin.ts
|
|
4
|
-
/**
|
|
5
|
-
* Provenance channel for deferred abort-signal arming.
|
|
6
|
-
*
|
|
7
|
-
* Bun materializes `Request.signal` lazily (~214ns on the first read), so
|
|
8
|
-
* Elysia only pays for it once the pipeline can actually observe an abort,
|
|
9
|
-
* that is, after it suspends. Deferring that read is only sound for the exact
|
|
10
|
-
* `Request` instance `Bun.serve` handed to `fetch`: any other request
|
|
11
|
-
* (`app.handle`, a non-Bun adapter, or a `.wrap()` HOC that substitutes the
|
|
12
|
-
* request) may carry a user-controlled signal that is already aborted at entry
|
|
13
|
-
* and therefore must be observed synchronously.
|
|
14
|
-
*
|
|
15
|
-
* The Bun adapter publishes the untouched original here for the *synchronous*
|
|
16
|
-
* prologue of `app.fetch` and, on the lane where `fetch` has no request hook
|
|
17
|
-
* to run, for the compiled route's own entry probe, which is still inside that
|
|
18
|
-
* same synchronous frame, then clears it in `finally`, so the window closes
|
|
19
|
-
* before the first suspension and cannot leak across concurrent requests.
|
|
20
|
-
* A `.wrap()` HOC that replaces the request or defers calling `next()` past a
|
|
21
|
-
* microtask simply misses the window, fetch handler falls back to eager check
|
|
22
|
-
*
|
|
23
|
-
* ## "A single shared slot that every request writes to, so a race condition?"
|
|
24
|
-
*
|
|
25
|
-
* It looks like one, but it can't be. Explained like you're five:
|
|
26
|
-
*
|
|
27
|
-
* JavaScript only ever does ONE thing at a time. When a request comes in, the
|
|
28
|
-
* adapter writes its name on the whiteboard (`origin.request = request`),
|
|
29
|
-
* walks it down the hall (`fetch(request)` the synchronous part, where the
|
|
30
|
-
* one and only identity check happens, in `fetch`'s prologue, or at the
|
|
31
|
-
* compiled route's entry when `fetch` had no hook to run first), and erases
|
|
32
|
-
* the whiteboard `finally`
|
|
33
|
-
* all in one uninterruptible turn. The next request's turn CANNOT start
|
|
34
|
-
* until this turn is completely finished, because that is how the event loop
|
|
35
|
-
* works: run-to-completion, no preemption. So request B can never see request
|
|
36
|
-
* A's name on the board, by the time B's turn starts, the board was already
|
|
37
|
-
* erased at the end of A's turn.
|
|
38
|
-
*
|
|
39
|
-
* "But the handler is async!" the `await`s inside it run LATER, in separate
|
|
40
|
-
* turns. That is fine, because the check already happened: the verdict was
|
|
41
|
-
* copied onto that request's own context (`'~sig'`) during the synchronous
|
|
42
|
-
* part, and the code that runs after an `await` only ever reads the context,
|
|
43
|
-
* never this slot.
|
|
44
|
-
*
|
|
45
|
-
* And if any assumption here ever breaks, a runtime that re-enters, a wrap
|
|
46
|
-
* that delays, a future edit that sneaks an `await` in before the check the
|
|
47
|
-
* comparison simply MISSES and the request falls back to eager arming, which
|
|
48
|
-
* is the exact pre-optimization behavior. Every failure mode makes a request
|
|
49
|
-
* slightly slower, never incorrect
|
|
50
|
-
*
|
|
51
|
-
* Adversarial proof, not just argument: `test/core/abort-race.test.ts` storms
|
|
52
|
-
* concurrent servers, in-process `handle()` calls, request-substituting and
|
|
53
|
-
* `next()`-delaying wraps at this slot and asserts zero misclassification.
|
|
54
|
-
*
|
|
55
|
-
* Never infer the Bun runtime from `fetch`'s second argument: Cloudflare
|
|
56
|
-
* Workers pass `env` there.
|
|
57
|
-
*
|
|
58
|
-
* It's insanely stupid, but it works. As Bun is single-threaded and synchronous
|
|
59
|
-
* so the `finally` block will always run before the next request comes in.
|
|
60
|
-
*/
|
|
61
4
|
const origin = { request: void 0 };
|
|
62
5
|
|
|
63
6
|
//#endregion
|
package/dist/adapter/origin.mjs
CHANGED
|
@@ -1,62 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
//#region src/adapter/origin.ts
|
|
3
|
-
/**
|
|
4
|
-
* Provenance channel for deferred abort-signal arming.
|
|
5
|
-
*
|
|
6
|
-
* Bun materializes `Request.signal` lazily (~214ns on the first read), so
|
|
7
|
-
* Elysia only pays for it once the pipeline can actually observe an abort,
|
|
8
|
-
* that is, after it suspends. Deferring that read is only sound for the exact
|
|
9
|
-
* `Request` instance `Bun.serve` handed to `fetch`: any other request
|
|
10
|
-
* (`app.handle`, a non-Bun adapter, or a `.wrap()` HOC that substitutes the
|
|
11
|
-
* request) may carry a user-controlled signal that is already aborted at entry
|
|
12
|
-
* and therefore must be observed synchronously.
|
|
13
|
-
*
|
|
14
|
-
* The Bun adapter publishes the untouched original here for the *synchronous*
|
|
15
|
-
* prologue of `app.fetch` and, on the lane where `fetch` has no request hook
|
|
16
|
-
* to run, for the compiled route's own entry probe, which is still inside that
|
|
17
|
-
* same synchronous frame, then clears it in `finally`, so the window closes
|
|
18
|
-
* before the first suspension and cannot leak across concurrent requests.
|
|
19
|
-
* A `.wrap()` HOC that replaces the request or defers calling `next()` past a
|
|
20
|
-
* microtask simply misses the window, fetch handler falls back to eager check
|
|
21
|
-
*
|
|
22
|
-
* ## "A single shared slot that every request writes to, so a race condition?"
|
|
23
|
-
*
|
|
24
|
-
* It looks like one, but it can't be. Explained like you're five:
|
|
25
|
-
*
|
|
26
|
-
* JavaScript only ever does ONE thing at a time. When a request comes in, the
|
|
27
|
-
* adapter writes its name on the whiteboard (`origin.request = request`),
|
|
28
|
-
* walks it down the hall (`fetch(request)` the synchronous part, where the
|
|
29
|
-
* one and only identity check happens, in `fetch`'s prologue, or at the
|
|
30
|
-
* compiled route's entry when `fetch` had no hook to run first), and erases
|
|
31
|
-
* the whiteboard `finally`
|
|
32
|
-
* all in one uninterruptible turn. The next request's turn CANNOT start
|
|
33
|
-
* until this turn is completely finished, because that is how the event loop
|
|
34
|
-
* works: run-to-completion, no preemption. So request B can never see request
|
|
35
|
-
* A's name on the board, by the time B's turn starts, the board was already
|
|
36
|
-
* erased at the end of A's turn.
|
|
37
|
-
*
|
|
38
|
-
* "But the handler is async!" the `await`s inside it run LATER, in separate
|
|
39
|
-
* turns. That is fine, because the check already happened: the verdict was
|
|
40
|
-
* copied onto that request's own context (`'~sig'`) during the synchronous
|
|
41
|
-
* part, and the code that runs after an `await` only ever reads the context,
|
|
42
|
-
* never this slot.
|
|
43
|
-
*
|
|
44
|
-
* And if any assumption here ever breaks, a runtime that re-enters, a wrap
|
|
45
|
-
* that delays, a future edit that sneaks an `await` in before the check the
|
|
46
|
-
* comparison simply MISSES and the request falls back to eager arming, which
|
|
47
|
-
* is the exact pre-optimization behavior. Every failure mode makes a request
|
|
48
|
-
* slightly slower, never incorrect
|
|
49
|
-
*
|
|
50
|
-
* Adversarial proof, not just argument: `test/core/abort-race.test.ts` storms
|
|
51
|
-
* concurrent servers, in-process `handle()` calls, request-substituting and
|
|
52
|
-
* `next()`-delaying wraps at this slot and asserts zero misclassification.
|
|
53
|
-
*
|
|
54
|
-
* Never infer the Bun runtime from `fetch`'s second argument: Cloudflare
|
|
55
|
-
* Workers pass `env` there.
|
|
56
|
-
*
|
|
57
|
-
* It's insanely stupid, but it works. As Bun is single-threaded and synchronous
|
|
58
|
-
* so the `finally` block will always run before the next request comes in.
|
|
59
|
-
*/
|
|
60
3
|
const origin = { request: void 0 };
|
|
61
4
|
|
|
62
5
|
//#endregion
|
package/dist/adapter/utils.js
CHANGED
|
@@ -132,7 +132,7 @@ function createStreamHandler({ mapResponse, mapCompactResponse }) {
|
|
|
132
132
|
const typedSSE = !skipFormat && "sse" in generator && generator.sse === true;
|
|
133
133
|
const sourceIterator = typeof generator.next === "function" ? generator : void 0;
|
|
134
134
|
let init = typedSSE ? void 0 : generator.next?.();
|
|
135
|
-
if (init instanceof Promise) init = await init;
|
|
135
|
+
if (init instanceof Promise || typeof init?.then === "function") init = await init;
|
|
136
136
|
const sign = set?.["~sign"];
|
|
137
137
|
if (sign) try {
|
|
138
138
|
await sign(set);
|
|
@@ -430,12 +430,6 @@ const doneResult = {
|
|
|
430
430
|
done: true,
|
|
431
431
|
value: void 0
|
|
432
432
|
};
|
|
433
|
-
/**
|
|
434
|
-
* Tee a returned `ReadableStream` so afterResponse, `defer()` and derive
|
|
435
|
-
* dispose wait for it, as for a tee'd generator. A `bytes()` stream is left
|
|
436
|
-
* alone (exact-stream contract), and so is a `Response`: reading its `.body`
|
|
437
|
-
* would move it off Bun's native send path
|
|
438
|
-
*/
|
|
439
433
|
function observeStream(source) {
|
|
440
434
|
if (source.locked || require_utils.isByteStream(source)) return;
|
|
441
435
|
const reader = source.getReader();
|
|
@@ -467,24 +461,6 @@ function observeStream(source) {
|
|
|
467
461
|
value
|
|
468
462
|
];
|
|
469
463
|
}
|
|
470
|
-
/**
|
|
471
|
-
* Split async source into `branches` independent iterators
|
|
472
|
-
*
|
|
473
|
-
* A producer drains the source ahead of consumers
|
|
474
|
-
*
|
|
475
|
-
* To prevent long/infinite stream, the unconsumed window is capped:
|
|
476
|
-
* Consumed-by-every-branch entries are trimmed off the front
|
|
477
|
-
* Producer backpressures whenever the window hits `cap` ENTRIES or
|
|
478
|
-
* `capBytes` bytes, whichever comes first
|
|
479
|
-
*
|
|
480
|
-
* Streams below both caps buffer eagerly
|
|
481
|
-
* Only streams exceeding one gate on the slowest consumer
|
|
482
|
-
*
|
|
483
|
-
* Branch 0 is the value consumer (response/client)
|
|
484
|
-
* When `return()` (client abort / early exit), source is stopped
|
|
485
|
-
* so the observer branches can still reach completion instead of spinning
|
|
486
|
-
* an infinite source
|
|
487
|
-
*/
|
|
488
464
|
function tee(source, branches = 2, cap = 64, capBytes = 1 << 22) {
|
|
489
465
|
const iterator = source[Symbol.asyncIterator]?.() ?? source[Symbol.iterator]();
|
|
490
466
|
const buffer = [];
|
package/dist/adapter/utils.mjs
CHANGED
|
@@ -132,7 +132,7 @@ function createStreamHandler({ mapResponse, mapCompactResponse }) {
|
|
|
132
132
|
const typedSSE = !skipFormat && "sse" in generator && generator.sse === true;
|
|
133
133
|
const sourceIterator = typeof generator.next === "function" ? generator : void 0;
|
|
134
134
|
let init = typedSSE ? void 0 : generator.next?.();
|
|
135
|
-
if (init instanceof Promise) init = await init;
|
|
135
|
+
if (init instanceof Promise || typeof init?.then === "function") init = await init;
|
|
136
136
|
const sign = set?.["~sign"];
|
|
137
137
|
if (sign) try {
|
|
138
138
|
await sign(set);
|
|
@@ -430,12 +430,6 @@ const doneResult = {
|
|
|
430
430
|
done: true,
|
|
431
431
|
value: void 0
|
|
432
432
|
};
|
|
433
|
-
/**
|
|
434
|
-
* Tee a returned `ReadableStream` so afterResponse, `defer()` and derive
|
|
435
|
-
* dispose wait for it, as for a tee'd generator. A `bytes()` stream is left
|
|
436
|
-
* alone (exact-stream contract), and so is a `Response`: reading its `.body`
|
|
437
|
-
* would move it off Bun's native send path
|
|
438
|
-
*/
|
|
439
433
|
function observeStream(source) {
|
|
440
434
|
if (source.locked || isByteStream(source)) return;
|
|
441
435
|
const reader = source.getReader();
|
|
@@ -467,24 +461,6 @@ function observeStream(source) {
|
|
|
467
461
|
value
|
|
468
462
|
];
|
|
469
463
|
}
|
|
470
|
-
/**
|
|
471
|
-
* Split async source into `branches` independent iterators
|
|
472
|
-
*
|
|
473
|
-
* A producer drains the source ahead of consumers
|
|
474
|
-
*
|
|
475
|
-
* To prevent long/infinite stream, the unconsumed window is capped:
|
|
476
|
-
* Consumed-by-every-branch entries are trimmed off the front
|
|
477
|
-
* Producer backpressures whenever the window hits `cap` ENTRIES or
|
|
478
|
-
* `capBytes` bytes, whichever comes first
|
|
479
|
-
*
|
|
480
|
-
* Streams below both caps buffer eagerly
|
|
481
|
-
* Only streams exceeding one gate on the slowest consumer
|
|
482
|
-
*
|
|
483
|
-
* Branch 0 is the value consumer (response/client)
|
|
484
|
-
* When `return()` (client abort / early exit), source is stopped
|
|
485
|
-
* so the observer branches can still reach completion instead of spinning
|
|
486
|
-
* an infinite source
|
|
487
|
-
*/
|
|
488
464
|
function tee(source, branches = 2, cap = 64, capBytes = 1 << 22) {
|
|
489
465
|
const iterator = source[Symbol.asyncIterator]?.() ?? source[Symbol.iterator]();
|
|
490
466
|
const buffer = [];
|
|
@@ -86,7 +86,9 @@ function mapCompactResponse(response, request, owned) {
|
|
|
86
86
|
case "String": return new Response(response, textHeaders);
|
|
87
87
|
case "Array": return Response.json(response);
|
|
88
88
|
case "ElysiaForm": return new Response(require_utils.formToFormData(response));
|
|
89
|
-
case "Object":
|
|
89
|
+
case "Object":
|
|
90
|
+
if (typeof response?.next === "function") return handleStream(response, void 0, request, void 0, owned);
|
|
91
|
+
return Response.json(response);
|
|
90
92
|
case "Number":
|
|
91
93
|
case "Boolean": return new Response(String(response));
|
|
92
94
|
case "ElysiaFile": return handleElysiaFile(response, void 0, request);
|
|
@@ -86,7 +86,9 @@ function mapCompactResponse(response, request, owned) {
|
|
|
86
86
|
case "String": return new Response(response, textHeaders);
|
|
87
87
|
case "Array": return Response.json(response);
|
|
88
88
|
case "ElysiaForm": return new Response(formToFormData(response));
|
|
89
|
-
case "Object":
|
|
89
|
+
case "Object":
|
|
90
|
+
if (typeof response?.next === "function") return handleStream(response, void 0, request, void 0, owned);
|
|
91
|
+
return Response.json(response);
|
|
90
92
|
case "Number":
|
|
91
93
|
case "Boolean": return new Response(String(response));
|
|
92
94
|
case "ElysiaFile": return handleElysiaFile(response, void 0, request);
|
package/dist/base.js
CHANGED
|
@@ -66,17 +66,11 @@ const toModel = (key, value) => {
|
|
|
66
66
|
value.$id ??= key;
|
|
67
67
|
return value;
|
|
68
68
|
};
|
|
69
|
-
const hookKeys = new Set([
|
|
70
|
-
...require_utils.schemaProperties,
|
|
71
|
-
...require_utils.eventProperties,
|
|
72
|
-
"detail",
|
|
73
|
-
"tags"
|
|
74
|
-
]);
|
|
75
69
|
const assertScope = (scope) => {
|
|
76
70
|
if (scope !== void 0 && scope !== "local" && scope !== "plugin" && scope !== "global") throw new Error(`[Elysia] Invalid hook scope ${JSON.stringify(scope)}, expected 'local', 'plugin' or 'global' (1.x 'scoped' is 'plugin')`);
|
|
77
71
|
};
|
|
78
72
|
const hasHookKeys = (value) => {
|
|
79
|
-
for (const key in value) if (
|
|
73
|
+
for (const key in value) if (require_utils.schemaProperties.has(key) || require_utils.eventProperties.has(key) || key === "detail" || key === "tags") return true;
|
|
80
74
|
return false;
|
|
81
75
|
};
|
|
82
76
|
function assertSchemaShape(hook, where) {
|
|
@@ -95,13 +89,6 @@ function assertSchemaShape(hook, where) {
|
|
|
95
89
|
throw new Error(`[Elysia] ${where}: "${key}" is not a schema — use t.Object({ ... })`);
|
|
96
90
|
}
|
|
97
91
|
}
|
|
98
|
-
/**
|
|
99
|
-
* Every key a single route must answer to, in registration order.
|
|
100
|
-
*
|
|
101
|
-
* `registerLoose` is the caller's call, not this helper's: HTTP gates it on
|
|
102
|
-
* `canRegisterLoose` because its lookup retries the trailing slash, while WS
|
|
103
|
-
* has no such retry and must own both directions outright
|
|
104
|
-
*/
|
|
105
92
|
function expandPaths(path, needsEncode, registerLoose, explicit) {
|
|
106
93
|
const variants = [path];
|
|
107
94
|
if (needsEncode) {
|
|
@@ -172,31 +159,10 @@ var Elysia = class Elysia {
|
|
|
172
159
|
get ["~programId"]() {
|
|
173
160
|
return this;
|
|
174
161
|
}
|
|
162
|
+
static #nextScopeChild;
|
|
175
163
|
constructor(config) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
*
|
|
179
|
-
* Interesting JSC internal: These `= undefined` shouldn't be removed
|
|
180
|
-
*
|
|
181
|
-
* Inline slots live inside the object cell, so JSC must pick their
|
|
182
|
-
* count at allocation before any initializer runs
|
|
183
|
-
*
|
|
184
|
-
* Its only signal is counting `this.x =` in the ctor body;
|
|
185
|
-
* class-field initializers compile into a separate hidden function
|
|
186
|
-
* the guess never sees
|
|
187
|
-
*
|
|
188
|
-
* The cell can't grow afterwards, so once inline slots run out every
|
|
189
|
-
* later property spills into the butterfly, and capacity is by n^2
|
|
190
|
-
*
|
|
191
|
-
* Fields run first before ctor (here), so they take the inline slots
|
|
192
|
-
* these assignments reserve
|
|
193
|
-
*
|
|
194
|
-
* Don't omitted field either for monomorphic access
|
|
195
|
-
*
|
|
196
|
-
* @see test/memory/instance-footprint.test.ts
|
|
197
|
-
* Est. overflow 37 -> 21 = butterfly 64 -> 32 slots = 738B -> 450B;
|
|
198
|
-
* reusing the app as its program identity brings the current cell to ~386B.
|
|
199
|
-
**/
|
|
164
|
+
const scopeChild = Elysia.#nextScopeChild;
|
|
165
|
+
Elysia.#nextScopeChild = void 0;
|
|
200
166
|
this["~config"] = config;
|
|
201
167
|
this["~ext"] = void 0;
|
|
202
168
|
this["~hookChain"] = void 0;
|
|
@@ -213,7 +179,7 @@ var Elysia = class Elysia {
|
|
|
213
179
|
this["~compilerSession"] = void 0;
|
|
214
180
|
this["~generation"] = void 0;
|
|
215
181
|
this["~introspect"] = void 0;
|
|
216
|
-
this["~scopeChild"] =
|
|
182
|
+
this["~scopeChild"] = scopeChild;
|
|
217
183
|
this["~scopeChildren"] = void 0;
|
|
218
184
|
if (config) {
|
|
219
185
|
const { prefix, name, seed, adapter } = config;
|
|
@@ -222,12 +188,6 @@ var Elysia = class Elysia {
|
|
|
222
188
|
if (adapter?.setup) this.#useFn(adapter.setup);
|
|
223
189
|
} else this["~Prefix"] = void 0;
|
|
224
190
|
}
|
|
225
|
-
/**
|
|
226
|
-
* Every registered route with its merged hooks.
|
|
227
|
-
* Development shares a frozen cache.
|
|
228
|
-
* Production rebuilds the list on every read
|
|
229
|
-
* so its metadata can be released after startup.
|
|
230
|
-
*/
|
|
231
191
|
get routes() {
|
|
232
192
|
const generation = this["~generation"];
|
|
233
193
|
if (this.#isCacheable && this.cachedGeneration === generation && this.cachedEpoch === mutationEpoch && this.cachedRoutes !== void 0) return this.cachedRoutes;
|
|
@@ -346,7 +306,7 @@ var Elysia = class Elysia {
|
|
|
346
306
|
parent,
|
|
347
307
|
refs,
|
|
348
308
|
scope,
|
|
349
|
-
owner: this,
|
|
309
|
+
owner: this["~scopeChild"] ? this : void 0,
|
|
350
310
|
origin: this.hash
|
|
351
311
|
};
|
|
352
312
|
}
|
|
@@ -369,20 +329,6 @@ var Elysia = class Elysia {
|
|
|
369
329
|
parsers[name] = fn;
|
|
370
330
|
return this;
|
|
371
331
|
}
|
|
372
|
-
/**
|
|
373
|
-
* ### setup | Life cycle event
|
|
374
|
-
* Called after server is ready for serving
|
|
375
|
-
*
|
|
376
|
-
* ---
|
|
377
|
-
* @example
|
|
378
|
-
* ```typescript
|
|
379
|
-
* new Elysia()
|
|
380
|
-
* .setup(({ server }) => {
|
|
381
|
-
* console.log("Running at ${server?.url}:${server?.port}")
|
|
382
|
-
* })
|
|
383
|
-
* .listen(3000)
|
|
384
|
-
* ```
|
|
385
|
-
*/
|
|
386
332
|
setup(handler) {
|
|
387
333
|
this.#assertMutable("setup");
|
|
388
334
|
(this.#ext.setup ??= []).push(...[].concat(handler));
|
|
@@ -482,14 +428,15 @@ var Elysia = class Elysia {
|
|
|
482
428
|
const run = arguments[2];
|
|
483
429
|
const isSchema = typeof schemaOrRun === "object";
|
|
484
430
|
const callback = isSchema ? run : schemaOrRun;
|
|
485
|
-
const
|
|
431
|
+
const config = this["~config"] || prefix ? {
|
|
486
432
|
...this["~config"],
|
|
487
433
|
name: void 0,
|
|
488
434
|
seed: void 0,
|
|
489
435
|
as: void 0,
|
|
490
436
|
prefix
|
|
491
|
-
} : void 0
|
|
492
|
-
|
|
437
|
+
} : void 0;
|
|
438
|
+
Elysia.#nextScopeChild = true;
|
|
439
|
+
const child = new Elysia(config);
|
|
493
440
|
child.scopeParent = this;
|
|
494
441
|
(this["~scopeChildren"] ??= []).push(child);
|
|
495
442
|
const src = this["~ext"];
|
|
@@ -512,7 +459,6 @@ var Elysia = class Elysia {
|
|
|
512
459
|
get #ext() {
|
|
513
460
|
return this["~ext"] ??= require_utils.nullObject();
|
|
514
461
|
}
|
|
515
|
-
/** A group child starts or takes up its parent's table, see `group()` */
|
|
516
462
|
#table(field) {
|
|
517
463
|
return this.#ext[field] ??= this.scopeParent ? this.scopeParent.#table(field) : require_utils.nullObject();
|
|
518
464
|
}
|
|
@@ -745,10 +691,6 @@ var Elysia = class Elysia {
|
|
|
745
691
|
}
|
|
746
692
|
return addedByThisCall;
|
|
747
693
|
}
|
|
748
|
-
/**
|
|
749
|
-
* Reject a second, unrelated definition of an already-registered macro.
|
|
750
|
-
* Split out of `#use` for the same reason as `#absorbExt`.
|
|
751
|
-
*/
|
|
752
694
|
#assertMacroUnique(app, addedByThisCall) {
|
|
753
695
|
const incomingMacro = app["~ext"]?.macro;
|
|
754
696
|
const existingMacro = this["~ext"]?.macro;
|
|
@@ -763,14 +705,6 @@ var Elysia = class Elysia {
|
|
|
763
705
|
throw new Error(`[Elysia] Macro "${macroName}" can be only define once`);
|
|
764
706
|
}
|
|
765
707
|
}
|
|
766
|
-
/**
|
|
767
|
-
* Merge the absorbed app's `~ext` container into this one.
|
|
768
|
-
*
|
|
769
|
-
* `#use` needed a frame wide enoughfor every local in this block + `#propagateHooks`
|
|
770
|
-
* JSC pays that width on *entry*, before a single field is read
|
|
771
|
-
*
|
|
772
|
-
* Keep the cold blocks in their own frames.
|
|
773
|
-
*/
|
|
774
708
|
#absorbExt(app, addedByThisCall) {
|
|
775
709
|
const source = app["~ext"];
|
|
776
710
|
const { decorator, store, headers, models, parser, macro, hoc, setup, cleanup, capability } = source;
|
|
@@ -857,10 +791,6 @@ var Elysia = class Elysia {
|
|
|
857
791
|
}
|
|
858
792
|
}
|
|
859
793
|
}
|
|
860
|
-
/**
|
|
861
|
-
* Republish the absorbed app's plugin/global hooks onto this instance's chain.
|
|
862
|
-
* Split out from #use, see #absorbExt
|
|
863
|
-
*/
|
|
864
794
|
#propagateHooks(app, hookChain, addedByThisCall) {
|
|
865
795
|
const nodes = useNodesBuffer;
|
|
866
796
|
nodes.length = 0;
|
|
@@ -920,7 +850,7 @@ var Elysia = class Elysia {
|
|
|
920
850
|
delete added.schema;
|
|
921
851
|
if (dropRequest) delete added.request;
|
|
922
852
|
}
|
|
923
|
-
if (require_utils.isNotEmpty(added)) this.#pushChainNode(added, scope, registration, origin, app);
|
|
853
|
+
if (require_utils.isNotEmpty(added)) this.#pushChainNode(added, scope, registration, origin, scopeChild ? app : void 0);
|
|
924
854
|
}
|
|
925
855
|
if (lifting) liftedRequests.set(this, lifting);
|
|
926
856
|
nodes.length = 0;
|
|
@@ -1090,10 +1020,6 @@ var Elysia = class Elysia {
|
|
|
1090
1020
|
if (this["~generation"] === void 0) return;
|
|
1091
1021
|
throw new Error(`[Elysia] .${api}() called after the app was sealed by its first request, listen or compile`);
|
|
1092
1022
|
}
|
|
1093
|
-
/**
|
|
1094
|
-
* `#assertMutable` for the tables a group child shares with its parents:
|
|
1095
|
-
* a child kept past its callback must not write into a sealed one
|
|
1096
|
-
*/
|
|
1097
1023
|
#assertTableMutable(api) {
|
|
1098
1024
|
for (let app = this; app; app = app.scopeParent) app.#assertMutable(api);
|
|
1099
1025
|
}
|
|
@@ -1151,9 +1077,6 @@ var Elysia = class Elysia {
|
|
|
1151
1077
|
return this;
|
|
1152
1078
|
}
|
|
1153
1079
|
}
|
|
1154
|
-
/**
|
|
1155
|
-
* Registered reusable models (via `.model()`), keyed by name.
|
|
1156
|
-
*/
|
|
1157
1080
|
get models() {
|
|
1158
1081
|
return this["~ext"]?.models ?? this.scopeParent?.models ?? require_utils.nullObject();
|
|
1159
1082
|
}
|
|
@@ -1245,10 +1168,6 @@ var Elysia = class Elysia {
|
|
|
1245
1168
|
OPTIONS: void 0
|
|
1246
1169
|
};
|
|
1247
1170
|
}
|
|
1248
|
-
/**
|
|
1249
|
-
* Force all route handlers to compile immediately (sets `precompile: true`
|
|
1250
|
-
* on the config and triggers a fresh build of the fetch handler).
|
|
1251
|
-
*/
|
|
1252
1171
|
compile() {
|
|
1253
1172
|
this["~config"] ??= require_utils.nullObject();
|
|
1254
1173
|
this["~config"].precompile = true;
|
|
@@ -1567,14 +1486,17 @@ var Elysia = class Elysia {
|
|
|
1567
1486
|
break;
|
|
1568
1487
|
}
|
|
1569
1488
|
}
|
|
1570
|
-
if (hasTypeBoxSchema) try {
|
|
1571
|
-
require_type_bridge.warmTypebox();
|
|
1572
|
-
} catch {}
|
|
1573
1489
|
let markDuplicates = false;
|
|
1490
|
+
let eager = false;
|
|
1574
1491
|
if (length) {
|
|
1575
1492
|
const programId = this["~programId"];
|
|
1576
1493
|
if (!require_compile_aot.Compiled.claim(programId, this["~aotFingerprint"] = require_compile_aot.createAotFingerprint())) require_compile_aot.Compiled.assertUncontested(programId);
|
|
1577
1494
|
markDuplicates = require_compile_aot.Compiled.hasProgram(programId) || precompile !== true && require_universal_is_production.isProduction() && !require_compile_aot.Capture.isAotBuildEnv();
|
|
1495
|
+
if (hasTypeBoxSchema) try {
|
|
1496
|
+
require_type_bridge.warmTypebox();
|
|
1497
|
+
} catch {
|
|
1498
|
+
if (!require_type_bridge.isBridgeLive() && !require_compile_aot.Compiled.hasProgram(programId)) eager = markDuplicates = true;
|
|
1499
|
+
}
|
|
1578
1500
|
}
|
|
1579
1501
|
const isLoose = this["~config"]?.strictPath !== true;
|
|
1580
1502
|
const collectExplicit = isLoose && table.hasLoose;
|
|
@@ -1624,7 +1546,7 @@ var Elysia = class Elysia {
|
|
|
1624
1546
|
}
|
|
1625
1547
|
continue;
|
|
1626
1548
|
}
|
|
1627
|
-
const lazy = !precompile && !this.compiled?.[i];
|
|
1549
|
+
const lazy = (eager ? (routeFlags & require_route_table.RouteFlag.ExactDuplicate) !== 0 : !precompile) && !this.compiled?.[i];
|
|
1628
1550
|
if (lazy) {
|
|
1629
1551
|
this.compiled ??= new Array(length);
|
|
1630
1552
|
this.jitTable = table;
|
|
@@ -1634,14 +1556,14 @@ var Elysia = class Elysia {
|
|
|
1634
1556
|
const registerLoose = isLoose && canRegisterLoose(routePath, isDynamic);
|
|
1635
1557
|
if (!isDynamic && !needsEncode && !registerLoose) {
|
|
1636
1558
|
const map = methods[routeMethod] ??= require_utils.nullObject();
|
|
1637
|
-
map[routePath] = lazy ? ~i : this.handler(i,
|
|
1559
|
+
map[routePath] = lazy ? ~i : this.handler(i, true, void 0, void 0, table);
|
|
1638
1560
|
continue;
|
|
1639
1561
|
}
|
|
1640
1562
|
const paths = expandPaths(routePath, needsEncode, registerLoose, registerLoose ? explicitPaths?.get(routeMethod) : void 0);
|
|
1641
1563
|
if (isDynamic) {
|
|
1642
1564
|
if (require_utils.isHTMLBundle(table.handler[i])) continue;
|
|
1643
1565
|
const router = this["~router"] ??= new memoirist.default({ loosePath: isLoose });
|
|
1644
|
-
const store = lazy ? i : this.handler(i,
|
|
1566
|
+
const store = lazy ? i : this.handler(i, true, void 0, void 0, table);
|
|
1645
1567
|
for (let p = 0; p < paths.length; p++) router.add(routeMethod, paths[p], store);
|
|
1646
1568
|
} else {
|
|
1647
1569
|
const map = methods[routeMethod] ??= require_utils.nullObject();
|
|
@@ -1649,7 +1571,7 @@ var Elysia = class Elysia {
|
|
|
1649
1571
|
method: routeMethod,
|
|
1650
1572
|
paths
|
|
1651
1573
|
};
|
|
1652
|
-
const handler = lazy ? ~i : this.handler(i,
|
|
1574
|
+
const handler = lazy ? ~i : this.handler(i, true, void 0, void 0, table);
|
|
1653
1575
|
for (let p = 0; p < paths.length; p++) map[paths[p]] = handler;
|
|
1654
1576
|
}
|
|
1655
1577
|
}
|
|
@@ -1680,21 +1602,6 @@ var Elysia = class Elysia {
|
|
|
1680
1602
|
if (!require_compile_aot.Capture.isAotBuildEnv()) listen(this, options, callback);
|
|
1681
1603
|
return this;
|
|
1682
1604
|
}
|
|
1683
|
-
/**
|
|
1684
|
-
* Add a higher order function over Elysia.fetch
|
|
1685
|
-
*
|
|
1686
|
-
* @example
|
|
1687
|
-
* ```ts
|
|
1688
|
-
* const ctx = new AsyncLocalStorage<{ counter: number }>()
|
|
1689
|
-
*
|
|
1690
|
-
* new Elysia()
|
|
1691
|
-
* .wrap(
|
|
1692
|
-
* (fetch) => (request, ...rest) =>
|
|
1693
|
-
* ctx.run({ counter: 0 }, () => fetch(request, ...rest))
|
|
1694
|
-
* )
|
|
1695
|
-
* .get('/', () => ctx.getStore())
|
|
1696
|
-
* ```
|
|
1697
|
-
*/
|
|
1698
1605
|
wrap(callback) {
|
|
1699
1606
|
this.#assertMutable("wrap");
|
|
1700
1607
|
if (this.fetchFn && !this.#pending) console.warn("[Elysia] .wrap() was called after the fetch handler was built");
|
|
@@ -1703,34 +1610,12 @@ var Elysia = class Elysia {
|
|
|
1703
1610
|
(ext.hocOrigin ??= []).push(this.hash ?? {});
|
|
1704
1611
|
return this;
|
|
1705
1612
|
}
|
|
1706
|
-
/**
|
|
1707
|
-
* ### cleanup | Life cycle event
|
|
1708
|
-
* Called after server stop serving request
|
|
1709
|
-
*
|
|
1710
|
-
* ---
|
|
1711
|
-
* @example
|
|
1712
|
-
* ```typescript
|
|
1713
|
-
* new Elysia()
|
|
1714
|
-
* .cleanup((app) => {
|
|
1715
|
-
* closeDatabase()
|
|
1716
|
-
* })
|
|
1717
|
-
* ```
|
|
1718
|
-
*/
|
|
1719
1613
|
cleanup(handler) {
|
|
1720
1614
|
if (this["~ext"]?.cleanupEpoch?.(handler)) return this;
|
|
1721
1615
|
this.#assertMutable("cleanup");
|
|
1722
1616
|
(this.#ext.cleanup ??= []).push(...[].concat(handler));
|
|
1723
1617
|
return this;
|
|
1724
1618
|
}
|
|
1725
|
-
/**
|
|
1726
|
-
* Stop the underlying server (if any), then run every `cleanup` handler
|
|
1727
|
-
*
|
|
1728
|
-
* Awaiting `stop()` from inside a `setup`, `cleanup` or WebSocket lifecycle
|
|
1729
|
-
* callback is only supported while that callback is still synchronous
|
|
1730
|
-
*
|
|
1731
|
-
* @param closeActiveConnections Pass `true` to terminate active
|
|
1732
|
-
* transports. Omit (or pass `false`) to drain safely.
|
|
1733
|
-
*/
|
|
1734
1619
|
stop(closeActiveConnections) {
|
|
1735
1620
|
const stop = this["~ext"]?.stop;
|
|
1736
1621
|
if (stop) return stop(closeActiveConnections);
|