elysia 2.0.0-exp.50 → 2.0.0-exp.52
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 +26 -2
- package/dist/adapter/bun/index.mjs +26 -2
- package/dist/adapter/origin.d.ts +63 -0
- package/dist/adapter/origin.js +64 -0
- package/dist/adapter/origin.mjs +62 -0
- package/dist/base.js +283 -273
- package/dist/base.mjs +284 -274
- package/dist/compile/analysis-cache.js +0 -2
- package/dist/compile/analysis-cache.mjs +0 -2
- package/dist/compile/aot.js +2 -1
- package/dist/compile/aot.mjs +2 -1
- package/dist/compile/handler/descriptor.d.ts +3 -6
- package/dist/compile/handler/descriptor.js +5 -7
- package/dist/compile/handler/descriptor.mjs +5 -7
- package/dist/compile/handler/index.js +16 -9
- package/dist/compile/handler/index.mjs +18 -11
- package/dist/compile/handler/jit.d.ts +3 -1
- package/dist/compile/handler/jit.js +48 -35
- package/dist/compile/handler/jit.mjs +49 -37
- package/dist/compile/handler/params.js +1 -0
- package/dist/compile/handler/params.mjs +2 -1
- package/dist/compile/handler/utils.d.ts +12 -3
- package/dist/compile/handler/utils.js +24 -5
- package/dist/compile/handler/utils.mjs +24 -6
- package/dist/context.d.ts +2 -2
- package/dist/cookie/crypto.js +15 -10
- package/dist/cookie/crypto.mjs +15 -10
- package/dist/handler/fetch.js +28 -6
- package/dist/handler/fetch.mjs +28 -6
- package/dist/package.js +1 -1
- package/dist/package.mjs +1 -1
- package/dist/plugin/aot/core.js +1 -1
- package/dist/plugin/aot/core.mjs +1 -1
- package/dist/route-table.d.ts +1 -1
- package/dist/route-table.js +3 -3
- package/dist/route-table.mjs +3 -3
- package/dist/schema-snapshot.d.ts +4 -0
- package/dist/schema-snapshot.js +193 -33
- package/dist/schema-snapshot.mjs +194 -33
- package/dist/sucrose.js +64 -44
- package/dist/sucrose.mjs +64 -44
- package/dist/type/bridge-live.d.ts +2 -2
- package/dist/type/bridge-live.js +1 -0
- package/dist/type/bridge-live.mjs +2 -2
- package/dist/type/bridge.d.ts +4 -2
- package/dist/type/bridge.js +8 -0
- package/dist/type/bridge.mjs +3 -1
- package/dist/type/compat.js +1 -0
- package/dist/type/compat.mjs +2 -1
- package/dist/type/elysia/boolean-string.js +2 -1
- package/dist/type/elysia/boolean-string.mjs +2 -1
- package/dist/type/elysia/integer-string.js +4 -2
- package/dist/type/elysia/integer-string.mjs +4 -2
- package/dist/type/elysia/numeric.js +29 -3
- package/dist/type/elysia/numeric.mjs +29 -3
- package/dist/type/shared.d.ts +10 -1
- package/dist/type/shared.js +18 -0
- package/dist/type/shared.mjs +17 -1
- package/dist/type/utils.js +10 -1
- package/dist/type/utils.mjs +10 -1
- package/dist/type/validator/index.d.ts +2 -2
- package/dist/type/validator/index.js +5 -4
- package/dist/type/validator/index.mjs +7 -7
- package/dist/type/validator/validator-cache.d.ts +11 -5
- package/dist/type/validator/validator-cache.js +153 -53
- package/dist/type/validator/validator-cache.mjs +150 -53
- package/dist/types.d.ts +10 -0
- package/dist/utils.d.ts +9 -5
- package/dist/utils.js +47 -43
- package/dist/utils.mjs +47 -43
- package/dist/validator/route.js +2 -1
- package/dist/validator/route.mjs +3 -2
- package/package.json +1 -1
|
@@ -4,10 +4,34 @@ const require_constants = require('../../constants.js');
|
|
|
4
4
|
const require_utils = require('../../utils.js');
|
|
5
5
|
const require_adapter_web_standard_index = require('../web-standard/index.js');
|
|
6
6
|
const require_generation = require('../../generation.js');
|
|
7
|
+
const require_adapter_origin = require('../origin.js');
|
|
7
8
|
const require_compile_handler_index = require('../../compile/handler/index.js');
|
|
8
9
|
const require_route_table = require('../../route-table.js');
|
|
9
10
|
|
|
10
11
|
//#region src/adapter/bun/index.ts
|
|
12
|
+
/**
|
|
13
|
+
* ! This may looks like it would cause race condition, but it is not
|
|
14
|
+
* Bun is single-threaded and synchronous, so the `finally` block will
|
|
15
|
+
* always run before the next request comes in
|
|
16
|
+
* @see ../origin.ts
|
|
17
|
+
*
|
|
18
|
+
* Publish the request `Bun.serve` handed us so the pipeline can prove, before
|
|
19
|
+
* its first suspension, that it holds the untouched original and may defer
|
|
20
|
+
* materializing `request.signal`. The proof happens in `createFetchHandler`'s
|
|
21
|
+
* prologue when there is a request hook, and at the compiled route's entry
|
|
22
|
+
* probe otherwise — both inside the same synchronous frame.
|
|
23
|
+
*
|
|
24
|
+
* `finally` runs on the synchronous return of `fetch` (the returned promise is
|
|
25
|
+
* not awaited), so the slot is live only for the handler's synchronous prologue.
|
|
26
|
+
*/
|
|
27
|
+
const withOrigin = (fetch) => (request, server) => {
|
|
28
|
+
require_adapter_origin.origin.request = request;
|
|
29
|
+
try {
|
|
30
|
+
return fetch(request, server);
|
|
31
|
+
} finally {
|
|
32
|
+
require_adapter_origin.origin.request = void 0;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
11
35
|
const nativeStaticMethods = new Set([
|
|
12
36
|
"GET",
|
|
13
37
|
"POST",
|
|
@@ -124,7 +148,7 @@ const BunAdapter = require_adapter_index.createAdapter({
|
|
|
124
148
|
let built;
|
|
125
149
|
if (!needsGate) built = build();
|
|
126
150
|
if (needsGate) serve.fetch = (request, server) => ready.then(() => app.fetch(request, server));
|
|
127
|
-
else serve.fetch = built.fetch;
|
|
151
|
+
else serve.fetch = withOrigin(built.fetch);
|
|
128
152
|
const server = app.server = Bun.serve(serve);
|
|
129
153
|
const reload = () => {
|
|
130
154
|
try {
|
|
@@ -172,7 +196,7 @@ const BunAdapter = require_adapter_index.createAdapter({
|
|
|
172
196
|
const publish = () => {
|
|
173
197
|
if (app.server !== server) return;
|
|
174
198
|
built ??= build();
|
|
175
|
-
serve.fetch = built.fetch;
|
|
199
|
+
serve.fetch = withOrigin(built.fetch);
|
|
176
200
|
if (built.websocket) serve.websocket = built.websocket;
|
|
177
201
|
if (built.routes) serve.routes = built.routes[0];
|
|
178
202
|
if (needsGate || built.websocket || built.routes) reload();
|
|
@@ -3,10 +3,34 @@ import { isDynamicRegex, needEncodeRegex } from "../../constants.mjs";
|
|
|
3
3
|
import { flattenChain, getLoosePath, nullObject } from "../../utils.mjs";
|
|
4
4
|
import { WebStandardAdapter } from "../web-standard/index.mjs";
|
|
5
5
|
import { frozenRootOf, resolvedWsOf } from "../../generation.mjs";
|
|
6
|
+
import { origin } from "../origin.mjs";
|
|
6
7
|
import { buildNativeStaticResponse } from "../../compile/handler/index.mjs";
|
|
7
8
|
import { routeRow } from "../../route-table.mjs";
|
|
8
9
|
|
|
9
10
|
//#region src/adapter/bun/index.ts
|
|
11
|
+
/**
|
|
12
|
+
* ! This may looks like it would cause race condition, but it is not
|
|
13
|
+
* Bun is single-threaded and synchronous, so the `finally` block will
|
|
14
|
+
* always run before the next request comes in
|
|
15
|
+
* @see ../origin.ts
|
|
16
|
+
*
|
|
17
|
+
* Publish the request `Bun.serve` handed us so the pipeline can prove, before
|
|
18
|
+
* its first suspension, that it holds the untouched original and may defer
|
|
19
|
+
* materializing `request.signal`. The proof happens in `createFetchHandler`'s
|
|
20
|
+
* prologue when there is a request hook, and at the compiled route's entry
|
|
21
|
+
* probe otherwise — both inside the same synchronous frame.
|
|
22
|
+
*
|
|
23
|
+
* `finally` runs on the synchronous return of `fetch` (the returned promise is
|
|
24
|
+
* not awaited), so the slot is live only for the handler's synchronous prologue.
|
|
25
|
+
*/
|
|
26
|
+
const withOrigin = (fetch) => (request, server) => {
|
|
27
|
+
origin.request = request;
|
|
28
|
+
try {
|
|
29
|
+
return fetch(request, server);
|
|
30
|
+
} finally {
|
|
31
|
+
origin.request = void 0;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
10
34
|
const nativeStaticMethods = new Set([
|
|
11
35
|
"GET",
|
|
12
36
|
"POST",
|
|
@@ -123,7 +147,7 @@ const BunAdapter = createAdapter({
|
|
|
123
147
|
let built;
|
|
124
148
|
if (!needsGate) built = build();
|
|
125
149
|
if (needsGate) serve.fetch = (request, server) => ready.then(() => app.fetch(request, server));
|
|
126
|
-
else serve.fetch = built.fetch;
|
|
150
|
+
else serve.fetch = withOrigin(built.fetch);
|
|
127
151
|
const server = app.server = Bun.serve(serve);
|
|
128
152
|
const reload = () => {
|
|
129
153
|
try {
|
|
@@ -171,7 +195,7 @@ const BunAdapter = createAdapter({
|
|
|
171
195
|
const publish = () => {
|
|
172
196
|
if (app.server !== server) return;
|
|
173
197
|
built ??= build();
|
|
174
|
-
serve.fetch = built.fetch;
|
|
198
|
+
serve.fetch = withOrigin(built.fetch);
|
|
175
199
|
if (built.websocket) serve.websocket = built.websocket;
|
|
176
200
|
if (built.routes) serve.routes = built.routes[0];
|
|
177
201
|
if (needsGate || built.websocket || built.routes) reload();
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
//#region src/adapter/origin.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Provenance channel for deferred abort-signal arming.
|
|
4
|
+
*
|
|
5
|
+
* Bun materializes `Request.signal` lazily (~214ns on the first read), so
|
|
6
|
+
* Elysia only pays for it once the pipeline can actually observe an abort,
|
|
7
|
+
* that is, after it suspends. Deferring that read is only sound for the exact
|
|
8
|
+
* `Request` instance `Bun.serve` handed to `fetch`: any other request
|
|
9
|
+
* (`app.handle`, a non-Bun adapter, or a `.wrap()` HOC that substitutes the
|
|
10
|
+
* request) may carry a user-controlled signal that is already aborted at entry
|
|
11
|
+
* and therefore must be observed synchronously.
|
|
12
|
+
*
|
|
13
|
+
* The Bun adapter publishes the untouched original here for the *synchronous*
|
|
14
|
+
* prologue of `app.fetch` and, on the lane where `fetch` has no request hook
|
|
15
|
+
* to run, for the compiled route's own entry probe, which is still inside that
|
|
16
|
+
* same synchronous frame, then clears it in `finally`, so the window closes
|
|
17
|
+
* before the first suspension and cannot leak across concurrent requests.
|
|
18
|
+
* A `.wrap()` HOC that replaces the request or defers calling `next()` past a
|
|
19
|
+
* microtask simply misses the window, fetch handler falls back to eager check
|
|
20
|
+
*
|
|
21
|
+
* ## "A single shared slot that every request writes to, so a race condition?"
|
|
22
|
+
*
|
|
23
|
+
* It looks like one, but it can't be. Explained like you're five:
|
|
24
|
+
*
|
|
25
|
+
* JavaScript only ever does ONE thing at a time. When a request comes in, the
|
|
26
|
+
* adapter writes its name on the whiteboard (`origin.request = request`),
|
|
27
|
+
* walks it down the hall (`fetch(request)` the synchronous part, where the
|
|
28
|
+
* one and only identity check happens, in `fetch`'s prologue, or at the
|
|
29
|
+
* compiled route's entry when `fetch` had no hook to run first), and erases
|
|
30
|
+
* the whiteboard `finally`
|
|
31
|
+
* all in one uninterruptible turn. The next request's turn CANNOT start
|
|
32
|
+
* until this turn is completely finished, because that is how the event loop
|
|
33
|
+
* works: run-to-completion, no preemption. So request B can never see request
|
|
34
|
+
* A's name on the board, by the time B's turn starts, the board was already
|
|
35
|
+
* erased at the end of A's turn.
|
|
36
|
+
*
|
|
37
|
+
* "But the handler is async!" — the `await`s inside it run LATER, in separate
|
|
38
|
+
* turns. That is fine, because the check already happened: the verdict was
|
|
39
|
+
* copied onto that request's own context (`'~sig'`) during the synchronous
|
|
40
|
+
* part, and the code that runs after an `await` only ever reads the context,
|
|
41
|
+
* never this slot.
|
|
42
|
+
*
|
|
43
|
+
* And if any assumption here ever breaks, a runtime that re-enters, a wrap
|
|
44
|
+
* that delays, a future edit that sneaks an `await` in before the check the
|
|
45
|
+
* comparison simply MISSES and the request falls back to eager arming, which
|
|
46
|
+
* is the exact pre-optimization behavior. Every failure mode makes a request
|
|
47
|
+
* slightly slower, never incorrect
|
|
48
|
+
*
|
|
49
|
+
* Adversarial proof, not just argument: `test/core/abort-race.test.ts` storms
|
|
50
|
+
* concurrent servers, in-process `handle()` calls, request-substituting and
|
|
51
|
+
* `next()`-delaying wraps at this slot and asserts zero misclassification.
|
|
52
|
+
*
|
|
53
|
+
* Never infer the Bun runtime from `fetch`'s second argument: Cloudflare
|
|
54
|
+
* Workers pass `env` there.
|
|
55
|
+
*
|
|
56
|
+
* It's insanely stupid, but it works. As Bun is single-threaded and synchronous
|
|
57
|
+
* so the `finally` block will always run before the next request comes in.
|
|
58
|
+
*/
|
|
59
|
+
declare const origin: {
|
|
60
|
+
request: Request | undefined;
|
|
61
|
+
};
|
|
62
|
+
//#endregion
|
|
63
|
+
export { origin };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
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
|
+
const origin = { request: void 0 };
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
64
|
+
exports.origin = origin;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
//#region src/adapter/origin.ts
|
|
2
|
+
/**
|
|
3
|
+
* Provenance channel for deferred abort-signal arming.
|
|
4
|
+
*
|
|
5
|
+
* Bun materializes `Request.signal` lazily (~214ns on the first read), so
|
|
6
|
+
* Elysia only pays for it once the pipeline can actually observe an abort,
|
|
7
|
+
* that is, after it suspends. Deferring that read is only sound for the exact
|
|
8
|
+
* `Request` instance `Bun.serve` handed to `fetch`: any other request
|
|
9
|
+
* (`app.handle`, a non-Bun adapter, or a `.wrap()` HOC that substitutes the
|
|
10
|
+
* request) may carry a user-controlled signal that is already aborted at entry
|
|
11
|
+
* and therefore must be observed synchronously.
|
|
12
|
+
*
|
|
13
|
+
* The Bun adapter publishes the untouched original here for the *synchronous*
|
|
14
|
+
* prologue of `app.fetch` and, on the lane where `fetch` has no request hook
|
|
15
|
+
* to run, for the compiled route's own entry probe, which is still inside that
|
|
16
|
+
* same synchronous frame, then clears it in `finally`, so the window closes
|
|
17
|
+
* before the first suspension and cannot leak across concurrent requests.
|
|
18
|
+
* A `.wrap()` HOC that replaces the request or defers calling `next()` past a
|
|
19
|
+
* microtask simply misses the window, fetch handler falls back to eager check
|
|
20
|
+
*
|
|
21
|
+
* ## "A single shared slot that every request writes to, so a race condition?"
|
|
22
|
+
*
|
|
23
|
+
* It looks like one, but it can't be. Explained like you're five:
|
|
24
|
+
*
|
|
25
|
+
* JavaScript only ever does ONE thing at a time. When a request comes in, the
|
|
26
|
+
* adapter writes its name on the whiteboard (`origin.request = request`),
|
|
27
|
+
* walks it down the hall (`fetch(request)` the synchronous part, where the
|
|
28
|
+
* one and only identity check happens, in `fetch`'s prologue, or at the
|
|
29
|
+
* compiled route's entry when `fetch` had no hook to run first), and erases
|
|
30
|
+
* the whiteboard `finally`
|
|
31
|
+
* all in one uninterruptible turn. The next request's turn CANNOT start
|
|
32
|
+
* until this turn is completely finished, because that is how the event loop
|
|
33
|
+
* works: run-to-completion, no preemption. So request B can never see request
|
|
34
|
+
* A's name on the board, by the time B's turn starts, the board was already
|
|
35
|
+
* erased at the end of A's turn.
|
|
36
|
+
*
|
|
37
|
+
* "But the handler is async!" — the `await`s inside it run LATER, in separate
|
|
38
|
+
* turns. That is fine, because the check already happened: the verdict was
|
|
39
|
+
* copied onto that request's own context (`'~sig'`) during the synchronous
|
|
40
|
+
* part, and the code that runs after an `await` only ever reads the context,
|
|
41
|
+
* never this slot.
|
|
42
|
+
*
|
|
43
|
+
* And if any assumption here ever breaks, a runtime that re-enters, a wrap
|
|
44
|
+
* that delays, a future edit that sneaks an `await` in before the check the
|
|
45
|
+
* comparison simply MISSES and the request falls back to eager arming, which
|
|
46
|
+
* is the exact pre-optimization behavior. Every failure mode makes a request
|
|
47
|
+
* slightly slower, never incorrect
|
|
48
|
+
*
|
|
49
|
+
* Adversarial proof, not just argument: `test/core/abort-race.test.ts` storms
|
|
50
|
+
* concurrent servers, in-process `handle()` calls, request-substituting and
|
|
51
|
+
* `next()`-delaying wraps at this slot and asserts zero misclassification.
|
|
52
|
+
*
|
|
53
|
+
* Never infer the Bun runtime from `fetch`'s second argument: Cloudflare
|
|
54
|
+
* Workers pass `env` there.
|
|
55
|
+
*
|
|
56
|
+
* It's insanely stupid, but it works. As Bun is single-threaded and synchronous
|
|
57
|
+
* so the `finally` block will always run before the next request comes in.
|
|
58
|
+
*/
|
|
59
|
+
const origin = { request: void 0 };
|
|
60
|
+
|
|
61
|
+
//#endregion
|
|
62
|
+
export { origin };
|