elysia 2.0.0-beta.31 → 2.0.0-beta.32
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/base.d.ts +18 -18
- package/dist/base.js +2 -2
- package/dist/base.mjs +2 -2
- package/dist/compile/aot-capture.js +1 -1
- package/dist/compile/aot-capture.mjs +1 -1
- package/dist/compile/aot.d.ts +4 -5
- package/dist/compile/aot.js +11 -12
- package/dist/compile/aot.mjs +11 -12
- package/dist/compile/handler/jit.js +6 -4
- package/dist/compile/handler/jit.mjs +6 -4
- package/dist/compile/handler/utils.d.ts +1 -1
- package/dist/compile/handler/utils.js +2 -2
- package/dist/compile/handler/utils.mjs +2 -2
- package/dist/error.d.ts +55 -50
- package/dist/handler/error.d.ts +7 -8
- package/dist/handler/error.js +26 -10
- package/dist/handler/error.mjs +27 -12
- package/dist/plugin/aot/core.d.ts +15 -3
- package/dist/plugin/aot/core.js +147 -83
- package/dist/plugin/aot/core.mjs +148 -85
- package/dist/plugin/aot/source.d.ts +6 -1
- package/dist/plugin/aot/source.js +32 -10
- package/dist/plugin/aot/source.mjs +32 -12
- package/dist/type/bridge-live.d.ts +1 -1
- package/dist/type/bridge-live.js +1 -1
- package/dist/type/bridge-live.mjs +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/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-ops.d.ts +2 -2
- package/dist/type/typebox-value-ops.js +2 -2
- package/dist/type/typebox-value-ops.mjs +2 -2
- package/dist/type/typebox-value.d.ts +6 -6
- package/dist/types.d.ts +70 -64
- package/dist/ws/route.js +6 -4
- package/dist/ws/route.mjs +7 -5
- package/package.json +1 -1
package/dist/handler/error.js
CHANGED
|
@@ -60,19 +60,28 @@ function statusFallbackBody(error, status, mask = "Internal Server Error", asDet
|
|
|
60
60
|
const message = error.message ?? "";
|
|
61
61
|
return asDetail && message === "" && !Object.hasOwn(error, "message") ? void 0 : message;
|
|
62
62
|
}
|
|
63
|
-
|
|
63
|
+
function problemOf(self, type, detail, status, claimsProblem, members) {
|
|
64
64
|
const code = claimsProblem ? self.code : void 0;
|
|
65
65
|
return new require_error.ElysiaStatus(status, require_error.problemBody({
|
|
66
66
|
type: typeof type === "string" ? type : "about:blank",
|
|
67
67
|
...typeof code === "string" ? { code } : {},
|
|
68
|
+
...members,
|
|
68
69
|
...detail === void 0 ? {} : { detail },
|
|
69
70
|
status
|
|
70
71
|
}), { "content-type": require_error.PROBLEM_JSON });
|
|
71
|
-
}
|
|
72
|
-
|
|
72
|
+
}
|
|
73
|
+
function extensionOf(self) {
|
|
74
|
+
const extension = self.extension;
|
|
75
|
+
const members = typeof extension === "function" && extension.call(self);
|
|
76
|
+
if (!require_utils.isPlainObject(members)) return;
|
|
77
|
+
const served = require_utils.nullObject();
|
|
78
|
+
for (const [key, value] of Object.entries(members)) if (typeof value !== "function" && key !== "type" && key !== "code" && key !== "status" && (key !== "detail" || typeof value === "string")) served[key] = value;
|
|
79
|
+
return served;
|
|
80
|
+
}
|
|
81
|
+
function readAnnotation(self, key, claimsProblem) {
|
|
73
82
|
const annotation = self[key];
|
|
74
|
-
return typeof annotation === "function" ? claimsProblem ? annotation.call(self) : void 0 : annotation;
|
|
75
|
-
}
|
|
83
|
+
return typeof annotation === "function" ? claimsProblem && key === "value" ? annotation.call(self) : void 0 : annotation;
|
|
84
|
+
}
|
|
76
85
|
function fallbackErrorResponse(context, error, mapResponse) {
|
|
77
86
|
if (error instanceof require_error.ElysiaStatus || error instanceof Response) return mapResponse(error, context.set, context);
|
|
78
87
|
const self = error ?? {};
|
|
@@ -93,26 +102,28 @@ function fallbackErrorResponse(context, error, mapResponse) {
|
|
|
93
102
|
if (error?.message != null && (context.set.status === void 0 || context.set.status === 200)) context.set.status = 500;
|
|
94
103
|
return mapResponse(require_error.internalServerErrorResponse(error), context.set, context);
|
|
95
104
|
};
|
|
105
|
+
let members;
|
|
96
106
|
const serveMessage = () => {
|
|
97
107
|
if (!claimsProblem) return legacy();
|
|
98
108
|
mergeHeaders();
|
|
99
|
-
return mapResponse(problemOf(self, type, statusFallbackBody(error, served, require_error.titleOf(served), true), served, claimsProblem), context.set, context);
|
|
109
|
+
return mapResponse(problemOf(self, type, members?.detail ?? statusFallbackBody(error, served, require_error.titleOf(served), true), served, claimsProblem, members), context.set, context);
|
|
100
110
|
};
|
|
101
111
|
const serveAnnotation = (key) => {
|
|
102
112
|
let annotation;
|
|
103
113
|
try {
|
|
104
114
|
annotation = readAnnotation(self, key, claimsProblem);
|
|
115
|
+
if (key === "detail" && claimsProblem) members = extensionOf(self);
|
|
105
116
|
} catch (cause) {
|
|
106
117
|
return failed(cause);
|
|
107
118
|
}
|
|
108
119
|
if (annotation === void 0) return key === "value" ? serveAnnotation("detail") : serveMessage();
|
|
109
|
-
if (annotation instanceof Promise) return annotation.then((resolved) => {
|
|
110
|
-
if (resolved === void 0) return
|
|
120
|
+
if (key === "value" && annotation instanceof Promise) return annotation.then((resolved) => {
|
|
121
|
+
if (resolved === void 0) return serveAnnotation("detail");
|
|
111
122
|
mergeHeaders();
|
|
112
|
-
return mapResponse(
|
|
123
|
+
return mapResponse(resolved, context.set, context);
|
|
113
124
|
}, failed);
|
|
114
125
|
mergeHeaders();
|
|
115
|
-
return mapResponse(key === "value" ? annotation : problemOf(self, type, annotation, served, claimsProblem), context.set, context);
|
|
126
|
+
return mapResponse(key === "value" ? annotation : problemOf(self, type, annotation, served, claimsProblem, members), context.set, context);
|
|
116
127
|
};
|
|
117
128
|
if (error instanceof require_error.ValidationError) return mapResponse(error.type === "response" ? require_error.internalServerErrorResponse(void 0) : new require_error.ElysiaStatus(422, require_error.problemBody({
|
|
118
129
|
type: "validation",
|
|
@@ -141,6 +152,10 @@ function createErrorHandler(onErrors, mapResponse, allowUnsafe = false) {
|
|
|
141
152
|
return fallbackResponse(context, error, mapWith(sign));
|
|
142
153
|
};
|
|
143
154
|
const respond = (context, error, result, sign) => {
|
|
155
|
+
if (result instanceof Error) {
|
|
156
|
+
context.set.status = result.status || 500;
|
|
157
|
+
return serveUnhandled(context, result, sign);
|
|
158
|
+
}
|
|
144
159
|
if (result instanceof require_error.ElysiaStatus || result instanceof Response) context.set.status = result.status;
|
|
145
160
|
else if (context.set.status === void 0 || context.set.status === 200) context.set.status = 500;
|
|
146
161
|
return mapWith(sign)(adoptErrorType(result, error), context.set, context);
|
|
@@ -171,6 +186,7 @@ function createErrorHandler(onErrors, mapResponse, allowUnsafe = false) {
|
|
|
171
186
|
exports.adoptErrorType = adoptErrorType;
|
|
172
187
|
exports.claimsProblemType = claimsProblemType;
|
|
173
188
|
exports.createErrorHandler = createErrorHandler;
|
|
189
|
+
exports.extensionOf = extensionOf;
|
|
174
190
|
exports.fallbackResponse = fallbackResponse;
|
|
175
191
|
exports.readAnnotation = readAnnotation;
|
|
176
192
|
exports.resolveStatus = resolveStatus;
|
package/dist/handler/error.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import { StatusMap } from "../constants.mjs";
|
|
3
|
-
import { isNotEmpty } from "../utils.mjs";
|
|
3
|
+
import { isNotEmpty, isPlainObject, nullObject } from "../utils.mjs";
|
|
4
4
|
import { isProduction } from "../universal/is-production.mjs";
|
|
5
5
|
import { ElysiaStatus, HTTPError, NotFound, PROBLEM_JSON, ValidationError, internalServerErrorResponse, problemBody, titleOf } from "../error.mjs";
|
|
6
6
|
import { materializeSetHeaders } from "../adapter/utils.mjs";
|
|
@@ -60,19 +60,28 @@ function statusFallbackBody(error, status, mask = "Internal Server Error", asDet
|
|
|
60
60
|
const message = error.message ?? "";
|
|
61
61
|
return asDetail && message === "" && !Object.hasOwn(error, "message") ? void 0 : message;
|
|
62
62
|
}
|
|
63
|
-
|
|
63
|
+
function problemOf(self, type, detail, status, claimsProblem, members) {
|
|
64
64
|
const code = claimsProblem ? self.code : void 0;
|
|
65
65
|
return new ElysiaStatus(status, problemBody({
|
|
66
66
|
type: typeof type === "string" ? type : "about:blank",
|
|
67
67
|
...typeof code === "string" ? { code } : {},
|
|
68
|
+
...members,
|
|
68
69
|
...detail === void 0 ? {} : { detail },
|
|
69
70
|
status
|
|
70
71
|
}), { "content-type": PROBLEM_JSON });
|
|
71
|
-
}
|
|
72
|
-
|
|
72
|
+
}
|
|
73
|
+
function extensionOf(self) {
|
|
74
|
+
const extension = self.extension;
|
|
75
|
+
const members = typeof extension === "function" && extension.call(self);
|
|
76
|
+
if (!isPlainObject(members)) return;
|
|
77
|
+
const served = nullObject();
|
|
78
|
+
for (const [key, value] of Object.entries(members)) if (typeof value !== "function" && key !== "type" && key !== "code" && key !== "status" && (key !== "detail" || typeof value === "string")) served[key] = value;
|
|
79
|
+
return served;
|
|
80
|
+
}
|
|
81
|
+
function readAnnotation(self, key, claimsProblem) {
|
|
73
82
|
const annotation = self[key];
|
|
74
|
-
return typeof annotation === "function" ? claimsProblem ? annotation.call(self) : void 0 : annotation;
|
|
75
|
-
}
|
|
83
|
+
return typeof annotation === "function" ? claimsProblem && key === "value" ? annotation.call(self) : void 0 : annotation;
|
|
84
|
+
}
|
|
76
85
|
function fallbackErrorResponse(context, error, mapResponse) {
|
|
77
86
|
if (error instanceof ElysiaStatus || error instanceof Response) return mapResponse(error, context.set, context);
|
|
78
87
|
const self = error ?? {};
|
|
@@ -93,26 +102,28 @@ function fallbackErrorResponse(context, error, mapResponse) {
|
|
|
93
102
|
if (error?.message != null && (context.set.status === void 0 || context.set.status === 200)) context.set.status = 500;
|
|
94
103
|
return mapResponse(internalServerErrorResponse(error), context.set, context);
|
|
95
104
|
};
|
|
105
|
+
let members;
|
|
96
106
|
const serveMessage = () => {
|
|
97
107
|
if (!claimsProblem) return legacy();
|
|
98
108
|
mergeHeaders();
|
|
99
|
-
return mapResponse(problemOf(self, type, statusFallbackBody(error, served, titleOf(served), true), served, claimsProblem), context.set, context);
|
|
109
|
+
return mapResponse(problemOf(self, type, members?.detail ?? statusFallbackBody(error, served, titleOf(served), true), served, claimsProblem, members), context.set, context);
|
|
100
110
|
};
|
|
101
111
|
const serveAnnotation = (key) => {
|
|
102
112
|
let annotation;
|
|
103
113
|
try {
|
|
104
114
|
annotation = readAnnotation(self, key, claimsProblem);
|
|
115
|
+
if (key === "detail" && claimsProblem) members = extensionOf(self);
|
|
105
116
|
} catch (cause) {
|
|
106
117
|
return failed(cause);
|
|
107
118
|
}
|
|
108
119
|
if (annotation === void 0) return key === "value" ? serveAnnotation("detail") : serveMessage();
|
|
109
|
-
if (annotation instanceof Promise) return annotation.then((resolved) => {
|
|
110
|
-
if (resolved === void 0) return
|
|
120
|
+
if (key === "value" && annotation instanceof Promise) return annotation.then((resolved) => {
|
|
121
|
+
if (resolved === void 0) return serveAnnotation("detail");
|
|
111
122
|
mergeHeaders();
|
|
112
|
-
return mapResponse(
|
|
123
|
+
return mapResponse(resolved, context.set, context);
|
|
113
124
|
}, failed);
|
|
114
125
|
mergeHeaders();
|
|
115
|
-
return mapResponse(key === "value" ? annotation : problemOf(self, type, annotation, served, claimsProblem), context.set, context);
|
|
126
|
+
return mapResponse(key === "value" ? annotation : problemOf(self, type, annotation, served, claimsProblem, members), context.set, context);
|
|
116
127
|
};
|
|
117
128
|
if (error instanceof ValidationError) return mapResponse(error.type === "response" ? internalServerErrorResponse(void 0) : new ElysiaStatus(422, problemBody({
|
|
118
129
|
type: "validation",
|
|
@@ -141,6 +152,10 @@ function createErrorHandler(onErrors, mapResponse, allowUnsafe = false) {
|
|
|
141
152
|
return fallbackResponse(context, error, mapWith(sign));
|
|
142
153
|
};
|
|
143
154
|
const respond = (context, error, result, sign) => {
|
|
155
|
+
if (result instanceof Error) {
|
|
156
|
+
context.set.status = result.status || 500;
|
|
157
|
+
return serveUnhandled(context, result, sign);
|
|
158
|
+
}
|
|
144
159
|
if (result instanceof ElysiaStatus || result instanceof Response) context.set.status = result.status;
|
|
145
160
|
else if (context.set.status === void 0 || context.set.status === 200) context.set.status = 500;
|
|
146
161
|
return mapWith(sign)(adoptErrorType(result, error), context.set, context);
|
|
@@ -168,4 +183,4 @@ function createErrorHandler(onErrors, mapResponse, allowUnsafe = false) {
|
|
|
168
183
|
}
|
|
169
184
|
|
|
170
185
|
//#endregion
|
|
171
|
-
export { adoptErrorType, claimsProblemType, createErrorHandler, fallbackResponse, readAnnotation, resolveStatus, statusFallbackBody };
|
|
186
|
+
export { adoptErrorType, claimsProblemType, createErrorHandler, extensionOf, fallbackResponse, readAnnotation, resolveStatus, statusFallbackBody };
|
|
@@ -100,7 +100,7 @@ interface ElysiaAotOptions {
|
|
|
100
100
|
}
|
|
101
101
|
declare const resolveEntry: (entry: string) => string;
|
|
102
102
|
/** @internal Resolve the package condition used to evaluate an AOT entry. */
|
|
103
|
-
declare const resolveEntryModuleKind: (entry: string) => "
|
|
103
|
+
declare const resolveEntryModuleKind: (entry: string) => "esm" | "cjs";
|
|
104
104
|
/** Runtime handler-JIT module a strip build can replace with a throwing stub. */
|
|
105
105
|
interface StubPlan {
|
|
106
106
|
/** Stub the internal handler codegen module. */
|
|
@@ -225,7 +225,19 @@ declare function planFromReport(strip: boolean | 'auto', report: JITProbeResult,
|
|
|
225
225
|
* outside the handler JIT, so it emits no `cc` alias — stubbing on that
|
|
226
226
|
* alias alone would hand `src/ws/route.ts` a throwing `parseCookieRaw`
|
|
227
227
|
*/
|
|
228
|
-
wsCookie?: boolean, /** `exact-mirror` resolves from the elysia package at build time. */exactMirror?: boolean): {
|
|
228
|
+
wsCookie?: boolean, /** `exact-mirror` resolves from the elysia package at build time. */exactMirror?: boolean, /** Manifest key, names the app in a multi-app build's errors */key?: string): {
|
|
229
|
+
plan: StubPlan;
|
|
230
|
+
mode: BridgeMode;
|
|
231
|
+
};
|
|
232
|
+
/**
|
|
233
|
+
* Fold the per-app plans of a multi-app build into the one shared bundle: a
|
|
234
|
+
* module is stubbed only when every app can drop it, and the TypeBox wiring
|
|
235
|
+
* follows the least sealed app
|
|
236
|
+
*/
|
|
237
|
+
declare function mergePlans(plans: {
|
|
238
|
+
plan: StubPlan;
|
|
239
|
+
mode: BridgeMode;
|
|
240
|
+
}[], exactMirror: boolean): {
|
|
229
241
|
plan: StubPlan;
|
|
230
242
|
mode: BridgeMode;
|
|
231
243
|
};
|
|
@@ -281,4 +293,4 @@ declare function generateCompiledArtifacts(file: string, options?: ElysiaAotOpti
|
|
|
281
293
|
declare const SOURCE_REGEX: RegExp;
|
|
282
294
|
declare const realPath: (path: string) => string;
|
|
283
295
|
//#endregion
|
|
284
|
-
export { ADAPTER_BUN_FILTER, ADAPTER_CONSTANTS_FILTER, BridgeMode, CompiledArtifacts, ELYSIA_MODULE_FILTER, EXACT_MIRROR_REQUIRE_FILTER, ElysiaAotOptions, IS_PRODUCTION_FILTER, NO_STUB, OVERRIDE_MAP, SOURCE_REGEX, STUB_SOURCES, StubPlan, TYPEBOX_REQUIRE_FILTER, TYPEBOX_SYSTEM_LITE_FILTER, TYPEBOX_TYPE_FILTER, adapterConstantsSource, alignStubExtensions, bunAdapterStubSource, generateCompiledArtifacts, generateCompiledArtifactsIsolated, generateVirtualType, makeIsElysiaModule, planFromReport, realPath, resolveElysiaRoot, resolveEntry, resolveEntryModuleKind, resolveExactMirror, rewriteIsProductionCalls, siblingModuleExt, validatorSlotSetsMatch };
|
|
296
|
+
export { ADAPTER_BUN_FILTER, ADAPTER_CONSTANTS_FILTER, BridgeMode, CompiledArtifacts, ELYSIA_MODULE_FILTER, EXACT_MIRROR_REQUIRE_FILTER, ElysiaAotOptions, IS_PRODUCTION_FILTER, NO_STUB, OVERRIDE_MAP, SOURCE_REGEX, STUB_SOURCES, StubPlan, TYPEBOX_REQUIRE_FILTER, TYPEBOX_SYSTEM_LITE_FILTER, TYPEBOX_TYPE_FILTER, adapterConstantsSource, alignStubExtensions, bunAdapterStubSource, generateCompiledArtifacts, generateCompiledArtifactsIsolated, generateVirtualType, makeIsElysiaModule, mergePlans, planFromReport, realPath, resolveElysiaRoot, resolveEntry, resolveEntryModuleKind, resolveExactMirror, rewriteIsProductionCalls, siblingModuleExt, validatorSlotSetsMatch };
|
package/dist/plugin/aot/core.js
CHANGED
|
@@ -3,6 +3,7 @@ const require_runtime = require('../../_virtual/_rolldown/runtime.js');
|
|
|
3
3
|
const require_compile_handler_params = require('../../compile/handler/params.js');
|
|
4
4
|
const require_compile_handler_frozen_validator = require('../../compile/handler/frozen-validator.js');
|
|
5
5
|
const require_compile_handler_index = require('../../compile/handler/index.js');
|
|
6
|
+
const require_compile_aot_capture = require('../../compile/aot-capture.js');
|
|
6
7
|
const require_plugin_aot_source = require('./source.js');
|
|
7
8
|
let node_fs = require("node:fs");
|
|
8
9
|
let node_module = require("node:module");
|
|
@@ -57,9 +58,9 @@ const NO_STUB = {
|
|
|
57
58
|
adapter: false,
|
|
58
59
|
isProduction: false
|
|
59
60
|
};
|
|
60
|
-
function planFromReport(strip, report, hasWS, mayTrace, aliases, allBridgeFree, zeroCapture, adapterStub = false, productionStub = true, wsCookie = false, exactMirror = false) {
|
|
61
|
+
function planFromReport(strip, report, hasWS, mayTrace, aliases, allBridgeFree, zeroCapture, adapterStub = false, productionStub = true, wsCookie = false, exactMirror = false, key = "") {
|
|
61
62
|
const jit = report.jit;
|
|
62
|
-
if (strip === true && !jit) throw new Error(`[elysia-aot] strip: true requires every route to be covered by the AOT handler manifest, but handler JIT is still reachable (${report.reasons.join(", ") || "unknown"}). Use strip: 'auto' to skip stubbing when the app is not fully precompiled.`);
|
|
63
|
+
if (strip === true && !jit) throw new Error(`[elysia-aot] strip: true requires every route${key && ` of app "${key}"`} to be covered by the AOT handler manifest, but handler JIT is still reachable (${report.reasons.join(", ") || "unknown"}). Use strip: 'auto' to skip stubbing when the app is not fully precompiled.`);
|
|
63
64
|
const mode = !jit ? "off" : allBridgeFree ? "sealed" : hasWS && zeroCapture ? "off" : "wired";
|
|
64
65
|
return {
|
|
65
66
|
plan: {
|
|
@@ -70,16 +71,51 @@ function planFromReport(strip, report, hasWS, mayTrace, aliases, allBridgeFree,
|
|
|
70
71
|
cookie: jit && !aliases.has("cc") && !wsCookie,
|
|
71
72
|
trace: !aliases.has("tr") && (jit || !mayTrace),
|
|
72
73
|
sucrose: jit,
|
|
73
|
-
|
|
74
|
-
bridge: mode === "wired",
|
|
75
|
-
typeboxValue: mode !== "sealed",
|
|
76
|
-
exactMirror: exactMirror && mode !== "sealed",
|
|
74
|
+
...bridgePlan(mode, exactMirror),
|
|
77
75
|
adapter: adapterStub,
|
|
78
76
|
isProduction: productionStub
|
|
79
77
|
},
|
|
80
78
|
mode
|
|
81
79
|
};
|
|
82
80
|
}
|
|
81
|
+
const bridgePlan = (mode, exactMirror) => ({
|
|
82
|
+
compat: mode !== "off",
|
|
83
|
+
bridge: mode === "wired",
|
|
84
|
+
typeboxValue: mode !== "sealed",
|
|
85
|
+
exactMirror: exactMirror && mode !== "sealed"
|
|
86
|
+
});
|
|
87
|
+
const MODE_RANK = {
|
|
88
|
+
off: 0,
|
|
89
|
+
wired: 1,
|
|
90
|
+
sealed: 2
|
|
91
|
+
};
|
|
92
|
+
const REMOVAL_FLAGS = {
|
|
93
|
+
jit: 0,
|
|
94
|
+
staticClone: 0,
|
|
95
|
+
ws: 0,
|
|
96
|
+
reconstruct: 0,
|
|
97
|
+
cookie: 0,
|
|
98
|
+
trace: 0,
|
|
99
|
+
sucrose: 0
|
|
100
|
+
};
|
|
101
|
+
function mergePlans(plans, exactMirror) {
|
|
102
|
+
const plan = { ...plans[0].plan };
|
|
103
|
+
let mode = plans[0].mode;
|
|
104
|
+
for (const each of plans) {
|
|
105
|
+
if (MODE_RANK[each.mode] < MODE_RANK[mode]) mode = each.mode;
|
|
106
|
+
for (const key in REMOVAL_FLAGS) {
|
|
107
|
+
const flag = key;
|
|
108
|
+
plan[flag] &&= each.plan[flag];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
plan: {
|
|
113
|
+
...plan,
|
|
114
|
+
...bridgePlan(mode, exactMirror)
|
|
115
|
+
},
|
|
116
|
+
mode
|
|
117
|
+
};
|
|
118
|
+
}
|
|
83
119
|
function adapterConstantsSource(target) {
|
|
84
120
|
if (target === "bun") return "import { BunAdapter } from './bun/index'\nexport const defaultAdapter = BunAdapter\n";
|
|
85
121
|
return "import { WebStandardAdapter } from './web-standard/index'\nexport const defaultAdapter = WebStandardAdapter\n";
|
|
@@ -411,13 +447,6 @@ function validatorSlotSetsMatch(expected, captured) {
|
|
|
411
447
|
for (const key of expectedKeys) if (!capturedKeys.has(key)) return false;
|
|
412
448
|
return true;
|
|
413
449
|
}
|
|
414
|
-
const assertNoMount = (app, entry) => {
|
|
415
|
-
const routes = app["~routes"];
|
|
416
|
-
const mounted = [];
|
|
417
|
-
for (const route of routes) if (route[2]?.["~mount"] && !mounted.includes(route[1])) mounted.push(route[1]);
|
|
418
|
-
if (!mounted.length) return;
|
|
419
|
-
throw new Error(`[elysia-aot] "${entry}" mounts a sub-app (${mounted.join(", ")}). AOT cannot compile a mounted sub-app. Compose with .use() instead of .mount(), or disable AOT plugin`);
|
|
420
|
-
};
|
|
421
450
|
async function generateCompiledArtifacts(file, options, moduleCondition = resolveEntryModuleKind(file)) {
|
|
422
451
|
if (moduleCondition !== loadedModuleCondition) throw new Error(`[elysia-aot] entry uses the ${moduleCondition === "cjs" ? "CommonJS \"require\"" : "ESM \"import\""} package condition, but the AOT build plugin was loaded through ${loadedModuleCondition === "cjs" ? "CommonJS \"require\"" : "ESM \"import\""}; entry: ${JSON.stringify(resolveEntry(file))}. Load the plugin through ${moduleCondition === "cjs" ? "require()" : "import"} so capture and generated registration share one Elysia module instance.`);
|
|
423
452
|
const previousAotBuild = process.env.ELYSIA_AOT_BUILD;
|
|
@@ -432,6 +461,8 @@ async function generateCompiledArtifacts(file, options, moduleCondition = resolv
|
|
|
432
461
|
_importedEntries.add(entryReal);
|
|
433
462
|
const mod = await import(entry);
|
|
434
463
|
const isApp = (value) => typeof value?.compile === "function";
|
|
464
|
+
await Promise.allSettled(Object.values(mod).filter(isApp).map((app) => app.modules));
|
|
465
|
+
require_compile_aot_capture.abortCapture();
|
|
435
466
|
let app = isApp(mod.app) ? mod.app : mod.default;
|
|
436
467
|
if (!isApp(app)) {
|
|
437
468
|
const names = Object.keys(mod).filter((name) => isApp(mod[name]));
|
|
@@ -439,8 +470,22 @@ async function generateCompiledArtifacts(file, options, moduleCondition = resolv
|
|
|
439
470
|
app = names[0] ? mod[names[0]] : void 0;
|
|
440
471
|
}
|
|
441
472
|
if (!isApp(app)) throw new Error(`[elysia-aot] "${entry}" must export an Elysia app`);
|
|
442
|
-
const
|
|
443
|
-
|
|
473
|
+
const primary = app;
|
|
474
|
+
const apps = [primary];
|
|
475
|
+
const exportOf = new Map([[require_plugin_aot_source.manifestKey(primary), Object.keys(mod).find((name) => mod[name] === app)]]);
|
|
476
|
+
for (const name of Object.keys(mod)) {
|
|
477
|
+
const other = mod[name];
|
|
478
|
+
const key = isApp(other) && !apps.includes(other) && require_plugin_aot_source.manifestKey(other);
|
|
479
|
+
if (!key || !other["~routes"].length) continue;
|
|
480
|
+
if (exportOf.has(key)) throw new Error(`[elysia-aot] "${entry}" exports two Elysia apps named "${key}" (${exportOf.get(key)}, ${name}). Each AOT manifest is keyed by app name, rename one`);
|
|
481
|
+
exportOf.set(key, name);
|
|
482
|
+
apps.push(other);
|
|
483
|
+
}
|
|
484
|
+
if (apps.length > 1 && !require_plugin_aot_source.manifestKey(primary)) {
|
|
485
|
+
const unnamed = exportOf.get("");
|
|
486
|
+
const named = [...exportOf.values()].slice(1).map((name) => `\`${name}\``).join(", ");
|
|
487
|
+
throw new Error(`[elysia-aot] "${entry}" exports \`${unnamed}\` with no name next to named Elysia apps (${named}): name it (new Elysia({ name: '${unnamed}' })), or stop exporting ${named}`);
|
|
488
|
+
}
|
|
444
489
|
const sourceOptions = {
|
|
445
490
|
moduleCondition,
|
|
446
491
|
registerFrom: options?.registerFrom,
|
|
@@ -452,8 +497,90 @@ async function generateCompiledArtifacts(file, options, moduleCondition = resolv
|
|
|
452
497
|
const adapterStub = options?.target === "bun" ? "bun" : options?.target === "node" || options?.target === "workerd" ? "web-standard" : false;
|
|
453
498
|
const productionStub = options?.production !== false;
|
|
454
499
|
const exactMirror = resolveExactMirror(entry);
|
|
500
|
+
const analyze = (typedApp, artifacts) => {
|
|
501
|
+
const report = require_plugin_aot_source.replayStubbability(typedApp, artifacts.handlers, options?.target);
|
|
502
|
+
const aliases = /* @__PURE__ */ new Set();
|
|
503
|
+
for (const handler of artifacts.handlers) if (handler.alias) for (const name of handler.alias.split(",")) aliases.add(require_compile_handler_params.isResponseParam(name) ? "va" : name);
|
|
504
|
+
const history = typedApp["~routes"];
|
|
505
|
+
const hasWS = !!typedApp["~hasWS"] || history.some((route) => route[0] === "WS");
|
|
506
|
+
const mayTrace = !!typedApp["~hasTrace"] || history.some((route) => (route?.[4])?.trace?.length || route?.[2]?.["~mount"]);
|
|
507
|
+
const expectedSlots = [];
|
|
508
|
+
const declaredTypeBoxSlots = /* @__PURE__ */ new Set();
|
|
509
|
+
let routesForbidSeal = false;
|
|
510
|
+
let wsCookie = false;
|
|
511
|
+
for (const route of history) {
|
|
512
|
+
const method = route[0];
|
|
513
|
+
const path = route[1];
|
|
514
|
+
const expectTypeBoxSlot = (slot) => {
|
|
515
|
+
const identity = {
|
|
516
|
+
method,
|
|
517
|
+
path,
|
|
518
|
+
slot
|
|
519
|
+
};
|
|
520
|
+
const key = validatorSlotKey(identity);
|
|
521
|
+
if (declaredTypeBoxSlots.has(key)) routesForbidSeal = true;
|
|
522
|
+
else {
|
|
523
|
+
declaredTypeBoxSlots.add(key);
|
|
524
|
+
expectedSlots.push(identity);
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
const [, , , instance, hook, appHook, inheritedChain, macroScope] = route;
|
|
528
|
+
const hooks = require_compile_handler_index.composeRouteHook(instance, hook, appHook, inheritedChain, typedApp, macroScope);
|
|
529
|
+
require_compile_handler_index.runIntrospect(hooks);
|
|
530
|
+
if (method === "WS" && (hook?.cookie !== void 0 || hooks?.cookie !== void 0)) wsCookie = true;
|
|
531
|
+
if (!hooks) continue;
|
|
532
|
+
let routeHasTypeBoxDirectSlot = false;
|
|
533
|
+
const classify = (schema, slot) => {
|
|
534
|
+
if (schema === void 0) {
|
|
535
|
+
routesForbidSeal = true;
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
if (require_compile_handler_frozen_validator.isStandardSchema(schema)) return;
|
|
539
|
+
routeHasTypeBoxDirectSlot = true;
|
|
540
|
+
expectTypeBoxSlot(slot);
|
|
541
|
+
};
|
|
542
|
+
for (const slot of require_compile_handler_frozen_validator.REQUEST_SLOTS) {
|
|
543
|
+
const value = hooks[slot];
|
|
544
|
+
if (value !== void 0) classify(require_compile_handler_frozen_validator.resolveModelRef(value, typedApp), slot);
|
|
545
|
+
}
|
|
546
|
+
if (hooks.response !== void 0) {
|
|
547
|
+
const response = require_compile_handler_frozen_validator.resolveModelRef(hooks.response, typedApp);
|
|
548
|
+
if (require_compile_handler_frozen_validator.isResponseMap(response)) for (const status in response) classify(require_compile_handler_frozen_validator.resolveModelRef(response[status], typedApp), `response:${status}`);
|
|
549
|
+
else classify(response, "response:200");
|
|
550
|
+
}
|
|
551
|
+
const mergeSchemas = hooks.schemas;
|
|
552
|
+
if (Array.isArray(mergeSchemas) && mergeSchemas.length > 0) {
|
|
553
|
+
if (routeHasTypeBoxDirectSlot || !require_compile_handler_frozen_validator.mergeSchemasAllStandard(mergeSchemas)) routesForbidSeal = true;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
if (typedApp["~config"]?.normalize === "typebox") routesForbidSeal = true;
|
|
557
|
+
const validatorSlotsMatch = validatorSlotSetsMatch(expectedSlots, artifacts.validators);
|
|
558
|
+
const hasReplayableCapture = artifacts.handlers.some((h) => h.code) || artifacts.validators.length > 0;
|
|
559
|
+
const plan = planFromReport(strip, report, hasWS, mayTrace, aliases, hasReplayableCapture && !routesForbidSeal && validatorSlotsMatch && artifacts.validators.every((v) => v.bridgeFree === true), !hasReplayableCapture, adapterStub, productionStub, wsCookie, exactMirror, artifacts.key);
|
|
560
|
+
if (options?.target === "workerd") {
|
|
561
|
+
if (!report.jit) throw new Error(`[elysia-aot] target 'workerd' but handler JIT is still reachable${artifacts.key && ` in app "${artifacts.key}"`} (${report.reasons.join(", ") || "unknown"}). Every route must be captured into the AOT manifest.`);
|
|
562
|
+
if (!validatorSlotsMatch) console.warn("[elysia-aot] target 'workerd': captured validator slots do not exactly match declared slots; unfrozen slots compile at runtime and will fail on workerd.");
|
|
563
|
+
}
|
|
564
|
+
return plan;
|
|
565
|
+
};
|
|
566
|
+
const captured = [];
|
|
567
|
+
const plans = [];
|
|
568
|
+
for (const each of apps) {
|
|
569
|
+
let artifacts;
|
|
570
|
+
try {
|
|
571
|
+
artifacts = await require_plugin_aot_source.captureArtifacts(each, sourceOptions);
|
|
572
|
+
} catch (error) {
|
|
573
|
+
if (each === primary) throw error;
|
|
574
|
+
const key = require_plugin_aot_source.manifestKey(each);
|
|
575
|
+
console.warn(`[elysia-aot] export \`${exportOf.get(key)}\` (app "${key}") gets no AOT manifest, its capture failed: ${error.message}`);
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
captured.push(artifacts);
|
|
579
|
+
if (strip !== false) plans.push(analyze(each, artifacts));
|
|
580
|
+
}
|
|
581
|
+
const source = captured.length > 1 ? require_plugin_aot_source.emitModule(captured, captured[0].fingerprint, sourceOptions) : captured[0].source;
|
|
455
582
|
if (strip === false) return {
|
|
456
|
-
source
|
|
583
|
+
source,
|
|
457
584
|
stub: {
|
|
458
585
|
...NO_STUB,
|
|
459
586
|
typeboxValue: true,
|
|
@@ -463,76 +590,12 @@ async function generateCompiledArtifacts(file, options, moduleCondition = resolv
|
|
|
463
590
|
},
|
|
464
591
|
mode: "off"
|
|
465
592
|
};
|
|
466
|
-
const
|
|
467
|
-
const report = require_plugin_aot_source.replayStubbability(typedApp, artifacts.handlers, options?.target);
|
|
468
|
-
const aliases = /* @__PURE__ */ new Set();
|
|
469
|
-
for (const handler of artifacts.handlers) if (handler.alias) for (const name of handler.alias.split(",")) aliases.add(require_compile_handler_params.isResponseParam(name) ? "va" : name);
|
|
470
|
-
const history = typedApp["~routes"];
|
|
471
|
-
const hasWS = !!typedApp["~hasWS"] || history.some((route) => route[0] === "WS");
|
|
472
|
-
const mayTrace = !!typedApp["~hasTrace"] || history.some((route) => (route?.[4])?.trace?.length || route?.[2]?.["~mount"]);
|
|
473
|
-
const expectedSlots = [];
|
|
474
|
-
const declaredTypeBoxSlots = /* @__PURE__ */ new Set();
|
|
475
|
-
let routesForbidSeal = false;
|
|
476
|
-
let wsCookie = false;
|
|
477
|
-
for (const route of history) {
|
|
478
|
-
const method = route[0];
|
|
479
|
-
const path = route[1];
|
|
480
|
-
const expectTypeBoxSlot = (slot) => {
|
|
481
|
-
const identity = {
|
|
482
|
-
method,
|
|
483
|
-
path,
|
|
484
|
-
slot
|
|
485
|
-
};
|
|
486
|
-
const key = validatorSlotKey(identity);
|
|
487
|
-
if (declaredTypeBoxSlots.has(key)) routesForbidSeal = true;
|
|
488
|
-
else {
|
|
489
|
-
declaredTypeBoxSlots.add(key);
|
|
490
|
-
expectedSlots.push(identity);
|
|
491
|
-
}
|
|
492
|
-
};
|
|
493
|
-
const [, , , instance, hook, appHook, inheritedChain, macroScope] = route;
|
|
494
|
-
const hooks = require_compile_handler_index.composeRouteHook(instance, hook, appHook, inheritedChain, typedApp, macroScope);
|
|
495
|
-
require_compile_handler_index.runIntrospect(hooks);
|
|
496
|
-
if (method === "WS" && (hook?.cookie !== void 0 || hooks?.cookie !== void 0)) wsCookie = true;
|
|
497
|
-
if (!hooks) continue;
|
|
498
|
-
let routeHasTypeBoxDirectSlot = false;
|
|
499
|
-
const classify = (schema, slot) => {
|
|
500
|
-
if (schema === void 0) {
|
|
501
|
-
routesForbidSeal = true;
|
|
502
|
-
return;
|
|
503
|
-
}
|
|
504
|
-
if (require_compile_handler_frozen_validator.isStandardSchema(schema)) return;
|
|
505
|
-
routeHasTypeBoxDirectSlot = true;
|
|
506
|
-
expectTypeBoxSlot(slot);
|
|
507
|
-
};
|
|
508
|
-
for (const slot of require_compile_handler_frozen_validator.REQUEST_SLOTS) {
|
|
509
|
-
const value = hooks[slot];
|
|
510
|
-
if (value !== void 0) classify(require_compile_handler_frozen_validator.resolveModelRef(value, typedApp), slot);
|
|
511
|
-
}
|
|
512
|
-
if (hooks.response !== void 0) {
|
|
513
|
-
const response = require_compile_handler_frozen_validator.resolveModelRef(hooks.response, typedApp);
|
|
514
|
-
if (require_compile_handler_frozen_validator.isResponseMap(response)) for (const status in response) classify(require_compile_handler_frozen_validator.resolveModelRef(response[status], typedApp), `response:${status}`);
|
|
515
|
-
else classify(response, "response:200");
|
|
516
|
-
}
|
|
517
|
-
const mergeSchemas = hooks.schemas;
|
|
518
|
-
if (Array.isArray(mergeSchemas) && mergeSchemas.length > 0) {
|
|
519
|
-
if (routeHasTypeBoxDirectSlot || !require_compile_handler_frozen_validator.mergeSchemasAllStandard(mergeSchemas)) routesForbidSeal = true;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
if (typedApp["~config"]?.normalize === "typebox") routesForbidSeal = true;
|
|
523
|
-
const validatorSlotsMatch = validatorSlotSetsMatch(expectedSlots, artifacts.validators);
|
|
524
|
-
const hasReplayableCapture = artifacts.handlers.some((h) => h.code) || artifacts.validators.length > 0;
|
|
525
|
-
const { plan: stub, mode } = planFromReport(strip, report, hasWS, mayTrace, aliases, hasReplayableCapture && !routesForbidSeal && validatorSlotsMatch && artifacts.validators.every((v) => v.bridgeFree === true), !hasReplayableCapture, adapterStub, productionStub, wsCookie, exactMirror);
|
|
526
|
-
if (options?.target === "workerd") {
|
|
527
|
-
if (!report.jit) throw new Error(`[elysia-aot] target 'workerd' but handler JIT is still reachable (${report.reasons.join(", ") || "unknown"}). Every route must be captured into the AOT manifest.`);
|
|
528
|
-
if (!validatorSlotsMatch) console.warn("[elysia-aot] target 'workerd': captured validator slots do not exactly match declared slots; unfrozen slots compile at runtime and will fail on workerd.");
|
|
529
|
-
}
|
|
530
|
-
const virtualType = mode === "off" ? void 0 : await generateVirtualType(moduleCondition);
|
|
593
|
+
const { plan: stub, mode } = mergePlans(plans, exactMirror);
|
|
531
594
|
return {
|
|
532
|
-
source
|
|
595
|
+
source,
|
|
533
596
|
stub,
|
|
534
597
|
mode,
|
|
535
|
-
virtualType
|
|
598
|
+
virtualType: mode === "off" ? void 0 : await generateVirtualType(moduleCondition)
|
|
536
599
|
};
|
|
537
600
|
} finally {
|
|
538
601
|
if (previousAotBuild === void 0) delete process.env.ELYSIA_AOT_BUILD;
|
|
@@ -570,6 +633,7 @@ exports.generateCompiledArtifacts = generateCompiledArtifacts;
|
|
|
570
633
|
exports.generateCompiledArtifactsIsolated = generateCompiledArtifactsIsolated;
|
|
571
634
|
exports.generateVirtualType = generateVirtualType;
|
|
572
635
|
exports.makeIsElysiaModule = makeIsElysiaModule;
|
|
636
|
+
exports.mergePlans = mergePlans;
|
|
573
637
|
exports.planFromReport = planFromReport;
|
|
574
638
|
exports.realPath = realPath;
|
|
575
639
|
exports.resolveElysiaRoot = resolveElysiaRoot;
|