@aponiajs/platform-elysia 0.3.22 → 0.5.0
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/README.md +46 -1
- package/dist/index.d.mts +11 -2
- package/dist/index.mjs +50 -2
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -15,6 +15,10 @@ The first Elysia platform slice for Aponia:
|
|
|
15
15
|
- constructor-injected controllers;
|
|
16
16
|
- Nest-style `@Module()`, `@Controller()`, route, and `@Injectable()` metadata;
|
|
17
17
|
- automatic translation of decorated controllers into native Elysia routes;
|
|
18
|
+
- Standard Schema route validation for `body`, `query`, `params`, `headers`,
|
|
19
|
+
and `response`;
|
|
20
|
+
- Nest-style request parameter decorators — `@Body()`, `@Query()`, `@Param()`,
|
|
21
|
+
`@Headers()`, `@Cookie()`, `@Req()`, `@Res()`, and `@Ctx()`;
|
|
18
22
|
- Nest-style startup logging for module initialization and route mapping;
|
|
19
23
|
- controller factories that return native Elysia plugins;
|
|
20
24
|
- `handle`, `listen`, and `close` application methods.
|
|
@@ -41,6 +45,41 @@ const application = await AponiaFactory.create(AppModule);
|
|
|
41
45
|
await application.listen(3000);
|
|
42
46
|
```
|
|
43
47
|
|
|
48
|
+
## Routes with the native Elysia context
|
|
49
|
+
|
|
50
|
+
Controllers are Nest-shaped: a route decorator declares the method, path, and
|
|
51
|
+
schema, and parameter decorators inject the request. Types come from the
|
|
52
|
+
handler's own annotations.
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { Body, Controller, Ctx, Param, Post } from "@aponiajs/common";
|
|
56
|
+
import { type ElysiaRouteContext } from "@aponiajs/platform-elysia";
|
|
57
|
+
import { z } from "zod";
|
|
58
|
+
|
|
59
|
+
const createUser = { body: z.object({ name: z.string().min(2) }) };
|
|
60
|
+
type CreateUser = z.infer<(typeof createUser)["body"]>;
|
|
61
|
+
|
|
62
|
+
@Controller("users")
|
|
63
|
+
class UserController {
|
|
64
|
+
@Post("/", createUser)
|
|
65
|
+
createUser(@Body() body: CreateUser, @Param("tenant") tenant: string) {
|
|
66
|
+
return { tenant, name: body.name };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@Post("native", createUser)
|
|
70
|
+
createNatively(@Ctx() context: ElysiaRouteContext<typeof createUser>) {
|
|
71
|
+
context.set.headers["x-created"] = "1";
|
|
72
|
+
return context.body.name === "root"
|
|
73
|
+
? context.status(403, "forbidden")
|
|
74
|
+
: { name: context.body.name };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`ElysiaRouteContext<typeof schema>` is Elysia's own context type narrowed by the
|
|
80
|
+
declared schema, so `status`, `set`, `cookie`, `store`, `redirect`, and plugin
|
|
81
|
+
decorators behave exactly as they do in a plain Elysia handler.
|
|
82
|
+
|
|
44
83
|
## Native Elysia plugins
|
|
45
84
|
|
|
46
85
|
Use existing Elysia plugins through Nest-style module imports:
|
|
@@ -50,8 +89,9 @@ bun add @elysiajs/cors @elysiajs/jwt
|
|
|
50
89
|
```
|
|
51
90
|
|
|
52
91
|
```ts
|
|
92
|
+
import { Module } from "@aponiajs/common";
|
|
93
|
+
import { ElysiaPluginModule } from "@aponiajs/platform-elysia";
|
|
53
94
|
import { cors } from "@elysiajs/cors";
|
|
54
|
-
import { jwt } from "@elysiajs/jwt";
|
|
55
95
|
|
|
56
96
|
@Module({
|
|
57
97
|
imports: [
|
|
@@ -67,6 +107,11 @@ The plugin is passed unchanged to Elysia's native `.use()` implementation. For
|
|
|
67
107
|
plugins that depend on an injectable service, use an async registration:
|
|
68
108
|
|
|
69
109
|
```ts
|
|
110
|
+
import { Module } from "@aponiajs/common";
|
|
111
|
+
import { ElysiaPluginModule } from "@aponiajs/platform-elysia";
|
|
112
|
+
import { jwt } from "@elysiajs/jwt";
|
|
113
|
+
import { ConfigModule, ConfigService } from "./config/config.module.ts";
|
|
114
|
+
|
|
70
115
|
@Module({
|
|
71
116
|
imports: [
|
|
72
117
|
ElysiaPluginModule.registerAsync({
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Constructor, ControllerDefinition, DynamicModule, LogLevel, LoggerService, ModuleDefinition, ModuleImport, Token, TokenValues } from "@aponiajs/common";
|
|
2
2
|
import "@aponiajs/core";
|
|
3
|
-
import { AnyElysia, Elysia } from "elysia";
|
|
3
|
+
import { AnyElysia, Context, Elysia, InputSchema, UnwrapRoute } from "elysia";
|
|
4
4
|
//#region src/decorated-module.d.ts
|
|
5
5
|
type AponiaRootModule = ModuleImport;
|
|
6
6
|
declare function compileRootModule(rootModule: AponiaRootModule): ModuleDefinition;
|
|
@@ -27,6 +27,15 @@ declare class AponiaFactory {
|
|
|
27
27
|
static create(rootModule: AponiaRootModule, options?: AponiaApplicationOptions): Promise<AponiaElysiaApplication>;
|
|
28
28
|
}
|
|
29
29
|
//#endregion
|
|
30
|
+
//#region src/route-context.d.ts
|
|
31
|
+
/**
|
|
32
|
+
* The native Elysia request context for a declared route schema. Handlers that
|
|
33
|
+
* take the whole context — through `@Ctx()` or a single unannotated parameter —
|
|
34
|
+
* keep `status`, `set`, `cookie`, `store`, `redirect`, and plugin decorators
|
|
35
|
+
* fully typed.
|
|
36
|
+
*/
|
|
37
|
+
type ElysiaRouteContext<TSchema extends InputSchema = {}> = Context<UnwrapRoute<TSchema, {}, string>>;
|
|
38
|
+
//#endregion
|
|
30
39
|
//#region src/controller.d.ts
|
|
31
40
|
declare const ELYSIA_CONTROLLER = "aponia.elysia.controller";
|
|
32
41
|
interface ElysiaControllerDefinition<TController, TDependencies extends readonly Token<unknown>[], TPlugin extends AnyElysia> extends ControllerDefinition {
|
|
@@ -55,4 +64,4 @@ declare class ElysiaPluginModule {
|
|
|
55
64
|
static registerAsync<const TDependencies extends readonly Token<unknown>[], const TPlugin extends NativeElysiaPlugin>(options: AsyncElysiaPluginModuleOptions<TDependencies, TPlugin>): DynamicModule;
|
|
56
65
|
}
|
|
57
66
|
//#endregion
|
|
58
|
-
export { type AponiaApplicationOptions, AponiaElysiaApplication, AponiaFactory, type AponiaRootModule, type AsyncElysiaPluginModuleOptions, type ConfiguredAponiaApplicationOptions, ELYSIA_CONTROLLER, type ElysiaControllerDefinition, ElysiaPluginModule, type ElysiaPluginModuleOptions, type NativeElysiaConfigurator, type NativeElysiaPlugin, compileRootModule, defineElysiaController };
|
|
67
|
+
export { type AponiaApplicationOptions, AponiaElysiaApplication, AponiaFactory, type AponiaRootModule, type AsyncElysiaPluginModuleOptions, type ConfiguredAponiaApplicationOptions, ELYSIA_CONTROLLER, type ElysiaControllerDefinition, ElysiaPluginModule, type ElysiaPluginModuleOptions, type ElysiaRouteContext, type NativeElysiaConfigurator, type NativeElysiaPlugin, compileRootModule, defineElysiaController };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AponiaError, Logger, Module, createToken, getConstructorDependencies, getControllerMetadata, getModuleMetadata, getRouteMetadata, provideFactory, provideValue, tokenName } from "@aponiajs/common";
|
|
1
|
+
import { AponiaError, Logger, Module, createToken, getConstructorDependencies, getControllerMetadata, getModuleMetadata, getRouteMetadata, getRouteParameterMetadata, isStandardSchema, provideFactory, provideValue, tokenName } from "@aponiajs/common";
|
|
2
2
|
import { createContainer } from "@aponiajs/core";
|
|
3
3
|
import { Elysia } from "elysia";
|
|
4
4
|
//#region src/controller.ts
|
|
@@ -125,12 +125,60 @@ function compileDecoratedController(controller) {
|
|
|
125
125
|
controller: controller.name,
|
|
126
126
|
handler: String(route.propertyKey)
|
|
127
127
|
});
|
|
128
|
-
|
|
128
|
+
const parameters = getRouteParameterMetadata(controller, route.propertyKey);
|
|
129
|
+
plugin.route(route.method, joinPaths(metadata.path, route.path), (context) => handler.call(instance, ...bindParameters(parameters, context)), toRouteHook(route.schema));
|
|
129
130
|
}
|
|
130
131
|
return plugin;
|
|
131
132
|
}
|
|
132
133
|
});
|
|
133
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Builds the handler arguments described by parameter decorators. A handler
|
|
137
|
+
* without them receives the whole context, which keeps `@Ctx()` optional for
|
|
138
|
+
* single-argument handlers.
|
|
139
|
+
*/
|
|
140
|
+
function bindParameters(parameters, context) {
|
|
141
|
+
if (parameters.length === 0) return [context];
|
|
142
|
+
const size = Math.max(...parameters.map((parameter) => parameter.index)) + 1;
|
|
143
|
+
const bound = Array.from({ length: size }, () => void 0);
|
|
144
|
+
for (const parameter of parameters) bound[parameter.index] = resolveParameter(parameter, context);
|
|
145
|
+
return bound;
|
|
146
|
+
}
|
|
147
|
+
function resolveParameter(parameter, context) {
|
|
148
|
+
const source = parameterSource(parameter, context);
|
|
149
|
+
if (parameter.property === void 0) return source;
|
|
150
|
+
if (typeof source !== "object" || source === null) return;
|
|
151
|
+
const value = source[parameter.property];
|
|
152
|
+
return parameter.kind === "cookie" ? value?.value : value;
|
|
153
|
+
}
|
|
154
|
+
function parameterSource(parameter, context) {
|
|
155
|
+
const contextRecord = context;
|
|
156
|
+
switch (parameter.kind) {
|
|
157
|
+
case "context": return context;
|
|
158
|
+
case "set": return context.set;
|
|
159
|
+
case "request": return context.request;
|
|
160
|
+
default: return contextRecord[parameter.kind];
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function toRouteHook(schema) {
|
|
164
|
+
if (!schema) return;
|
|
165
|
+
const hook = {
|
|
166
|
+
...schema.body ? { body: toElysiaSchema(schema.body) } : {},
|
|
167
|
+
...schema.query ? { query: toElysiaSchema(schema.query) } : {},
|
|
168
|
+
...schema.params ? { params: toElysiaSchema(schema.params) } : {},
|
|
169
|
+
...schema.headers ? { headers: toElysiaSchema(schema.headers) } : {},
|
|
170
|
+
...schema.response ? { response: toElysiaSchema(schema.response) } : {}
|
|
171
|
+
};
|
|
172
|
+
return Object.keys(hook).length === 0 ? void 0 : hook;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Standard Schema validators pass through unchanged. Platform-native TypeBox
|
|
176
|
+
* validators reach the platform through the neutral `NativeSchema` contract,
|
|
177
|
+
* which cannot describe TypeBox's `Kind` symbol, so they are restored here.
|
|
178
|
+
*/
|
|
179
|
+
function toElysiaSchema(validator) {
|
|
180
|
+
return isStandardSchema(validator) ? validator : validator;
|
|
181
|
+
}
|
|
134
182
|
function joinPaths(controllerPath, routePath) {
|
|
135
183
|
const segments = [controllerPath, routePath].map((path) => path.trim().replace(/^\/+|\/+$/g, "")).filter(Boolean);
|
|
136
184
|
return segments.length === 0 ? "/" : `/${segments.join("/")}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aponiajs/platform-elysia",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Minimal Elysia application platform for Aponia modules and controllers.",
|
|
5
5
|
"homepage": "https://github.com/aponiajs/aponiajs#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -33,11 +33,13 @@
|
|
|
33
33
|
"prepublishOnly": "bun run build"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@aponiajs/common": "0.
|
|
37
|
-
"@aponiajs/core": "0.
|
|
36
|
+
"@aponiajs/common": "0.5.0",
|
|
37
|
+
"@aponiajs/core": "0.5.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"
|
|
40
|
+
"arktype": "^2.2.3",
|
|
41
|
+
"elysia": "^1.4.29",
|
|
42
|
+
"zod": "^4.4.3"
|
|
41
43
|
},
|
|
42
44
|
"peerDependencies": {
|
|
43
45
|
"elysia": "^1.4.29"
|