@supacloud/elysia 0.19.0 → 0.20.1
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 +35 -26
- package/compatibility.json +3 -2
- package/dist/fixtures/webhook/contracts.d.ts +31 -31
- package/dist/http-policy-suite.d.ts +3 -27
- package/dist/http-policy.d.ts +2 -0
- package/dist/http-telemetry.d.ts +4 -17
- package/dist/index.d.ts +49 -239
- package/dist/index.js +69 -42
- package/dist/schema_contract.d.ts +18 -11
- package/dist/webhook-migration-example.d.ts +7 -20
- package/package.json +11 -7
package/README.md
CHANGED
|
@@ -8,13 +8,19 @@ is a separately authorized business command, never an assumed rollback.
|
|
|
8
8
|
|
|
9
9
|
## Compatibility and Acceptance Boundary
|
|
10
10
|
|
|
11
|
-
The
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
The Elysia peer and development dependency are pinned to `2.0.0-beta.19`.
|
|
12
|
+
`compatibility.json` records the acceptance target: Bun 1.4.2, Elysia
|
|
13
|
+
2.0.0-beta.19, `typebox` 1.3.34, `exact-mirror` 1.2.6, TypeScript CLI 7.0.2
|
|
14
|
+
and the compiler's separate TypeScript 6 semantic API 6.0.2. The matrix is a
|
|
15
|
+
required target, not proof of an execution. See the dated, commit-specific
|
|
16
|
+
[framework acceptance record](../../docs/framework-acceptance.md) for actual
|
|
17
|
+
results. The contract-upgrade gate checks both TypeScript engines. These tests
|
|
18
|
+
do not establish a wider beta version matrix or Node.js runtime compatibility.
|
|
19
|
+
|
|
20
|
+
Keep the exact Elysia peer until additional versions have their own acceptance
|
|
21
|
+
evidence. A server using this adapter still installs Elysia; application metadata
|
|
22
|
+
and business modules need not import native Elysia types. See the
|
|
23
|
+
[dependency boundary and upgrade policy](../../docs/elysia-compatibility.md).
|
|
18
24
|
|
|
19
25
|
Run `bun run test:conformance` in this package after building the local
|
|
20
26
|
`@supacloud/contracts` and `@supacloud/app` dependencies and installing this
|
|
@@ -99,9 +105,11 @@ for the evidence boundaries and upgrade policy.
|
|
|
99
105
|
|
|
100
106
|
Pass a native Elysia plugin as `http` to `createApplication`, `createTestApp`,
|
|
101
107
|
or the options argument of `createModulePlugin`. `decorate` shares existing
|
|
102
|
-
instances
|
|
103
|
-
|
|
104
|
-
|
|
108
|
+
instances. In Elysia 2, `derive` runs after validation; do not assume the Elysia 1
|
|
109
|
+
pre-validation derive/resolve split. Use `.derive("plugin", callback)` for a
|
|
110
|
+
shared derived context, or finish a guarded context plugin with `.as("plugin")`.
|
|
111
|
+
Global hooks use `.as("global")`. Local hooks retain native encapsulation and
|
|
112
|
+
do not extend the consuming routes.
|
|
105
113
|
|
|
106
114
|
```ts
|
|
107
115
|
import { Elysia, t } from "elysia";
|
|
@@ -109,10 +117,12 @@ import { createApplication } from "@supacloud/elysia";
|
|
|
109
117
|
|
|
110
118
|
const http = new Elysia({ name: "application-context" })
|
|
111
119
|
.decorate("clock", { now: () => Date.now() })
|
|
112
|
-
.derive(({ clock }) => ({ startedAt: clock.now() }))
|
|
113
120
|
.guard({ query: t.Object({ locale: t.Optional(t.String()) }) })
|
|
114
|
-
.
|
|
115
|
-
|
|
121
|
+
.derive(({ clock, query }) => ({
|
|
122
|
+
startedAt: clock.now(),
|
|
123
|
+
locale: query.locale ?? "en",
|
|
124
|
+
}))
|
|
125
|
+
.as("plugin");
|
|
116
126
|
|
|
117
127
|
const app = createApplication({
|
|
118
128
|
http,
|
|
@@ -132,13 +142,13 @@ The second `requestContext` argument contains the validated HTTP inputs and
|
|
|
132
142
|
the inferred native plugin extensions. Existing one-argument factories remain
|
|
133
143
|
valid. Its result is passed to generated request-scoped constructors and the
|
|
134
144
|
controller's `context`/`requestContext` input. It is built once per request;
|
|
135
|
-
early
|
|
145
|
+
early derived-context responses and validation failures do not construct DI scopes.
|
|
136
146
|
Request scopes are created inside the existing governed handler and released
|
|
137
147
|
after the response, including handler failures. They are not application
|
|
138
|
-
singletons or native
|
|
148
|
+
singletons or native context hooks.
|
|
139
149
|
|
|
140
|
-
Native routes added to the returned app retain the HTTP plugin's decorator
|
|
141
|
-
|
|
150
|
+
Native routes added to the returned app retain the HTTP plugin's decorator
|
|
151
|
+
and derived context types. `createModulePlugin` also preserves the concrete
|
|
142
152
|
`services` type supplied by its caller. Module service bags are attached by
|
|
143
153
|
the module-local resolver, not merged into a root `decorator.services` bag;
|
|
144
154
|
the service instances themselves remain shared. This prevents same-named
|
|
@@ -153,13 +163,13 @@ Compiled route schemas are runtime
|
|
|
153
163
|
descriptors: their body/params/query/header/cookie fields in the application-wide
|
|
154
164
|
context factory remain `unknown`-based instead of pretending to infer one
|
|
155
165
|
route's schema for every route. Use shared guards for schema-typed native
|
|
156
|
-
|
|
166
|
+
context derivation and the existing generated contracts for individual compiled routes.
|
|
157
167
|
Do not store per-request identity or transaction handles in decorated singletons.
|
|
158
168
|
|
|
159
169
|
The `http` plugin is composed into each compiled module and subsequently into
|
|
160
170
|
the root for native routes. Keep it focused on reusable context extensions;
|
|
161
171
|
register unrelated endpoints on the returned app. Hook execution is tested
|
|
162
|
-
for named/anonymous plugins and
|
|
172
|
+
for named/anonymous plugins and plugin/global hooks without duplicate work.
|
|
163
173
|
Anonymous extensions receive a stable internal plugin identity for native
|
|
164
174
|
hook deduplication; the caller's plugin configuration is not mutated.
|
|
165
175
|
This is native HTTP composition, not a replacement runtime DI container or
|
|
@@ -298,7 +308,7 @@ Runtime adapter that turns `@supacloud/compiler` output into a production-ready
|
|
|
298
308
|
## Installation
|
|
299
309
|
|
|
300
310
|
```bash
|
|
301
|
-
bun add @supacloud/elysia elysia
|
|
311
|
+
bun add --exact @supacloud/elysia elysia@2.0.0-beta.19
|
|
302
312
|
```
|
|
303
313
|
|
|
304
314
|
## Usage
|
|
@@ -383,7 +393,7 @@ read-back protocol. A custom `errorMapper` can override this public envelope.
|
|
|
383
393
|
|
|
384
394
|
Native `Response` objects are passed through by Elysia, including JSON responses.
|
|
385
395
|
Use `validatedJsonResponse` to opt into validation when constructing a native
|
|
386
|
-
JSON response. Otherwise handlers must validate their JSON
|
|
396
|
+
JSON response. Otherwise handlers must validate their JSON payloads themselves.
|
|
387
397
|
The adapter does not consume or parse binary/streaming responses.
|
|
388
398
|
|
|
389
399
|
Jobs are executed explicitly with `executeJob(compiledModule, services, job,
|
|
@@ -657,9 +667,8 @@ does not add Eden-style client inference to an existing Elysia instance; the
|
|
|
657
667
|
compiler-generated client remains the source of transport types.
|
|
658
668
|
|
|
659
669
|
Response maps may use concrete statuses, `1XX`-`5XX` families, and `default`.
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
statuses take precedence over families, which take precedence over `default`.
|
|
670
|
+
The adapter expands family/default entries to concrete validators before
|
|
671
|
+
registration. Exact statuses take precedence over families, which take precedence over `default`.
|
|
663
672
|
An actual status absent from a structured response map fails the route contract
|
|
664
673
|
before Elysia can silently accept a default `200`; binary/stream routes may
|
|
665
674
|
intentionally leave successful transport statuses unschematized when only their
|
|
@@ -700,12 +709,12 @@ const app = createApplication({
|
|
|
700
709
|
});
|
|
701
710
|
```
|
|
702
711
|
|
|
703
|
-
`identityPlugin` is an application-owned
|
|
712
|
+
`identityPlugin` is an application-owned plugin/global Elysia context plugin that verifies
|
|
704
713
|
credentials and resolves `identity`; the adapter does not trust a raw user/tenant
|
|
705
714
|
header as identity. Policy callbacks preserve its native context types.
|
|
706
715
|
|
|
707
716
|
Policies become native route-local `beforeHandle` hooks. They execute sequentially
|
|
708
|
-
after schema validation, native
|
|
717
|
+
after schema validation, native context derivation and the application request-context
|
|
709
718
|
factory, but before compiled request-scope construction and command execution.
|
|
710
719
|
Return `undefined` to continue or a `Response` to stop; thrown errors use the
|
|
711
720
|
application error mapper. Native earlier hooks can still short-circuit the request.
|
package/compatibility.json
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
export declare const WebhookInputSchema: import("
|
|
2
|
-
id: import("
|
|
3
|
-
enabled: import("
|
|
1
|
+
export declare const WebhookInputSchema: import("typebox").TObject<{
|
|
2
|
+
id: import("typebox").TString;
|
|
3
|
+
enabled: import("typebox").TBoolean;
|
|
4
4
|
}>;
|
|
5
|
-
export declare const WebhookHeadersSchema: import("
|
|
6
|
-
"idempotency-key": import("
|
|
5
|
+
export declare const WebhookHeadersSchema: import("typebox").TObject<{
|
|
6
|
+
"idempotency-key": import("typebox").TOptional<import("typebox").TString>;
|
|
7
7
|
}>;
|
|
8
8
|
export declare const decodeWebhookInput: (value: unknown) => {
|
|
9
9
|
id: string;
|
|
10
10
|
enabled: boolean;
|
|
11
11
|
};
|
|
12
|
-
export declare const WebhookReceiptSchema: import("
|
|
13
|
-
tenantId: import("
|
|
14
|
-
actorId: import("
|
|
15
|
-
command: import("
|
|
16
|
-
operationId: import("
|
|
17
|
-
dispatchKey: import("
|
|
18
|
-
status: import("
|
|
19
|
-
audit: import("
|
|
20
|
-
result: import("
|
|
21
|
-
id: import("
|
|
22
|
-
enabled: import("
|
|
12
|
+
export declare const WebhookReceiptSchema: import("typebox").TObject<{
|
|
13
|
+
tenantId: import("typebox").TString;
|
|
14
|
+
actorId: import("typebox").TString;
|
|
15
|
+
command: import("typebox").TLiteral<"webhook.update.v1">;
|
|
16
|
+
operationId: import("typebox").TString;
|
|
17
|
+
dispatchKey: import("typebox").TString;
|
|
18
|
+
status: import("typebox").TLiteral<"confirmed">;
|
|
19
|
+
audit: import("typebox").TLiteral<"complete">;
|
|
20
|
+
result: import("typebox").TObject<{
|
|
21
|
+
id: import("typebox").TString;
|
|
22
|
+
enabled: import("typebox").TBoolean;
|
|
23
23
|
}>;
|
|
24
24
|
}>;
|
|
25
|
-
export declare const WebhookKeySchema: import("
|
|
26
|
-
key: import("
|
|
25
|
+
export declare const WebhookKeySchema: import("typebox").TObject<{
|
|
26
|
+
key: import("typebox").TString;
|
|
27
27
|
}>;
|
|
28
|
-
export declare const WebhookLookupSchema: import("
|
|
29
|
-
receipt: import("
|
|
30
|
-
tenantId: import("
|
|
31
|
-
actorId: import("
|
|
32
|
-
command: import("
|
|
33
|
-
operationId: import("
|
|
34
|
-
dispatchKey: import("
|
|
35
|
-
status: import("
|
|
36
|
-
audit: import("
|
|
37
|
-
result: import("
|
|
38
|
-
id: import("
|
|
39
|
-
enabled: import("
|
|
28
|
+
export declare const WebhookLookupSchema: import("typebox").TObject<{
|
|
29
|
+
receipt: import("typebox").TUnion<[import("typebox").TObject<{
|
|
30
|
+
tenantId: import("typebox").TString;
|
|
31
|
+
actorId: import("typebox").TString;
|
|
32
|
+
command: import("typebox").TLiteral<"webhook.update.v1">;
|
|
33
|
+
operationId: import("typebox").TString;
|
|
34
|
+
dispatchKey: import("typebox").TString;
|
|
35
|
+
status: import("typebox").TLiteral<"confirmed">;
|
|
36
|
+
audit: import("typebox").TLiteral<"complete">;
|
|
37
|
+
result: import("typebox").TObject<{
|
|
38
|
+
id: import("typebox").TString;
|
|
39
|
+
enabled: import("typebox").TBoolean;
|
|
40
40
|
}>;
|
|
41
|
-
}>, import("
|
|
41
|
+
}>, import("typebox").TNull]>;
|
|
42
42
|
}>;
|
|
@@ -44,32 +44,8 @@ export declare function principalKey(context: SupAuthRequestContext): unknown[];
|
|
|
44
44
|
/** Pair the returned factory and registry; only this verifier can populate policy identity. */
|
|
45
45
|
export declare function createHttpPolicySuite(options: HttpPolicySuiteOptions): {
|
|
46
46
|
requestContext: (request: Request) => Promise<SupaCloudRequestContext | SupAuthRequestContext>;
|
|
47
|
-
httpPolicies: Readonly<Record<string, (options: unknown, route: Readonly<Pick<import("./index").CompiledRoute, "method" | "path" | "command">>) => HttpPolicy<import("elysia").
|
|
48
|
-
decorator: {};
|
|
49
|
-
store: {};
|
|
50
|
-
derive: {};
|
|
51
|
-
resolve: {};
|
|
52
|
-
}, {
|
|
47
|
+
httpPolicies: Readonly<Record<string, (options: unknown, route: Readonly<Pick<import("./index").CompiledRoute, "method" | "path" | "command">>) => HttpPolicy<import("elysia").Elysia<"", "local", import("elysia").DefaultSingleton, {
|
|
53
48
|
typebox: {};
|
|
54
|
-
error:
|
|
55
|
-
}, {
|
|
56
|
-
schema: {};
|
|
57
|
-
standaloneSchema: {};
|
|
58
|
-
macro: {};
|
|
59
|
-
macroFn: {};
|
|
60
|
-
parser: {};
|
|
61
|
-
response: {};
|
|
62
|
-
}, {}, {
|
|
63
|
-
derive: {};
|
|
64
|
-
resolve: {};
|
|
65
|
-
schema: {};
|
|
66
|
-
standaloneSchema: {};
|
|
67
|
-
response: {};
|
|
68
|
-
}, {
|
|
69
|
-
derive: {};
|
|
70
|
-
resolve: {};
|
|
71
|
-
schema: {};
|
|
72
|
-
standaloneSchema: {};
|
|
73
|
-
response: {};
|
|
74
|
-
}>>>>;
|
|
49
|
+
error: [];
|
|
50
|
+
}, import("elysia").DefaultMetadata, {}, import("elysia").DefaultEphemeral, import("elysia").DefaultEphemeral>>>>;
|
|
75
51
|
};
|
package/dist/http-policy.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ export interface HttpPolicyContext<Http extends AnyElysia = Elysia> {
|
|
|
10
10
|
}
|
|
11
11
|
export interface HttpPolicyResponseContext<Http extends AnyElysia = Elysia> extends HttpPolicyContext<Http> {
|
|
12
12
|
response: unknown;
|
|
13
|
+
/** Validate and encode before replacing a handler value with a native Response. */
|
|
14
|
+
encodeResponse?: (value: unknown) => unknown | Promise<unknown>;
|
|
13
15
|
}
|
|
14
16
|
export type HttpPolicy<Http extends AnyElysia = Elysia> = ((context: HttpPolicyContext<Http>) => void | Response | Promise<void | Response>) & {
|
|
15
17
|
/** Terminal policies (cache hits) must not skip later security checks. */
|
package/dist/http-telemetry.d.ts
CHANGED
|
@@ -10,31 +10,18 @@ export interface HttpTelemetryEvent {
|
|
|
10
10
|
}
|
|
11
11
|
export type HttpTelemetryObserver = (event: Readonly<HttpTelemetryEvent>) => void | Promise<void>;
|
|
12
12
|
/** Request tracing is best effort, not a durable business audit. */
|
|
13
|
-
export declare function createHttpTelemetry(observe: HttpTelemetryObserver): Elysia<"", {
|
|
13
|
+
export declare function createHttpTelemetry(observe: HttpTelemetryObserver): Elysia<"", "local", {
|
|
14
14
|
decorator: {};
|
|
15
15
|
store: {};
|
|
16
16
|
derive: {};
|
|
17
|
-
resolve: {};
|
|
18
17
|
}, {
|
|
19
18
|
typebox: {};
|
|
20
|
-
error:
|
|
19
|
+
error: [];
|
|
21
20
|
}, {
|
|
22
21
|
schema: {};
|
|
23
|
-
|
|
22
|
+
schemas: {};
|
|
24
23
|
macro: {};
|
|
25
24
|
macroFn: {};
|
|
26
25
|
parser: {};
|
|
27
26
|
response: {};
|
|
28
|
-
}, {},
|
|
29
|
-
derive: {};
|
|
30
|
-
resolve: {};
|
|
31
|
-
schema: {};
|
|
32
|
-
standaloneSchema: {};
|
|
33
|
-
response: {};
|
|
34
|
-
}, {
|
|
35
|
-
derive: {};
|
|
36
|
-
resolve: {};
|
|
37
|
-
schema: {};
|
|
38
|
-
standaloneSchema: {};
|
|
39
|
-
response: {};
|
|
40
|
-
}>;
|
|
27
|
+
}, {}, import("elysia").DefaultEphemeral, import("elysia").DefaultEphemeral>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Elysia, type AnyElysia, type
|
|
1
|
+
import { Elysia, type AnyElysia, type Context, type StatusMap } from "elysia";
|
|
2
2
|
import type { CommandRuntimeAudit, CommandRuntimeAuthorizer, CommandRuntimeGovernance, CommandRuntimeInvocation, CommandRuntimeMiddleware } from "@supacloud/app";
|
|
3
3
|
import { type CommandPreview } from "@supacloud/contracts";
|
|
4
4
|
import { type HttpPolicyRegistry } from "./http-policy";
|
|
@@ -236,71 +236,11 @@ export interface ErrorContext {
|
|
|
236
236
|
frameworkCode: string | number | undefined;
|
|
237
237
|
}
|
|
238
238
|
export type ErrorMapper = (error: unknown, context: ErrorContext) => Response | undefined | Promise<Response | undefined>;
|
|
239
|
-
|
|
240
|
-
decorator:
|
|
241
|
-
store:
|
|
242
|
-
derive:
|
|
243
|
-
|
|
244
|
-
}, {
|
|
245
|
-
typebox: {};
|
|
246
|
-
error: {};
|
|
247
|
-
} & Http["~Definitions"], {
|
|
248
|
-
schema: {};
|
|
249
|
-
standaloneSchema: {};
|
|
250
|
-
macro: {};
|
|
251
|
-
macroFn: {};
|
|
252
|
-
parser: {};
|
|
253
|
-
response: {};
|
|
254
|
-
} & Http["~Metadata"], {} & Http["~Routes"], {
|
|
255
|
-
derive: {};
|
|
256
|
-
resolve: {};
|
|
257
|
-
schema: {};
|
|
258
|
-
standaloneSchema: {};
|
|
259
|
-
response: {};
|
|
260
|
-
}, {
|
|
261
|
-
derive: {};
|
|
262
|
-
resolve: {};
|
|
263
|
-
schema: {};
|
|
264
|
-
standaloneSchema: {};
|
|
265
|
-
response: {};
|
|
266
|
-
} & {
|
|
267
|
-
derive: {} & ({
|
|
268
|
-
derive: {};
|
|
269
|
-
resolve: {};
|
|
270
|
-
schema: {};
|
|
271
|
-
standaloneSchema: {};
|
|
272
|
-
response: {};
|
|
273
|
-
} & Http["~Ephemeral"])["derive"];
|
|
274
|
-
resolve: {} & ({
|
|
275
|
-
derive: {};
|
|
276
|
-
resolve: {};
|
|
277
|
-
schema: {};
|
|
278
|
-
standaloneSchema: {};
|
|
279
|
-
response: {};
|
|
280
|
-
} & Http["~Ephemeral"])["resolve"];
|
|
281
|
-
schema: import("elysia").MergeSchema<({
|
|
282
|
-
derive: {};
|
|
283
|
-
resolve: {};
|
|
284
|
-
schema: {};
|
|
285
|
-
standaloneSchema: {};
|
|
286
|
-
response: {};
|
|
287
|
-
} & Http["~Ephemeral"])["schema"], {}, "">;
|
|
288
|
-
standaloneSchema: ({
|
|
289
|
-
derive: {};
|
|
290
|
-
resolve: {};
|
|
291
|
-
schema: {};
|
|
292
|
-
standaloneSchema: {};
|
|
293
|
-
response: {};
|
|
294
|
-
} & Http["~Ephemeral"])["standaloneSchema"] & {};
|
|
295
|
-
response: ({
|
|
296
|
-
derive: {};
|
|
297
|
-
resolve: {};
|
|
298
|
-
schema: {};
|
|
299
|
-
standaloneSchema: {};
|
|
300
|
-
response: {};
|
|
301
|
-
} & Http["~Ephemeral"])["response"] & {};
|
|
302
|
-
}>;
|
|
303
|
-
export type ApplicationHttpContext<Http extends AnyElysia = Elysia> = Omit<InferContext<ReturnType<typeof mountHttp<Http>>>, "body" | "params" | "query" | "headers" | "cookie"> & Pick<HttpContext, "body" | "params" | "query" | "headers" | "cookie">;
|
|
239
|
+
export type ApplicationHttpContext<Http extends AnyElysia = Elysia> = Omit<Context<{}, {
|
|
240
|
+
decorator: Http["~Singleton"]["decorator"];
|
|
241
|
+
store: Http["~Singleton"]["store"];
|
|
242
|
+
derive: Http["~Singleton"]["derive"] & Http["~Ephemeral"]["derive"];
|
|
243
|
+
}>, "body" | "params" | "query" | "headers" | "cookie"> & Pick<HttpContext, "body" | "params" | "query" | "headers" | "cookie">;
|
|
304
244
|
export type HttpRequestContextFactory<Http extends AnyElysia = Elysia, Value = unknown> = (request: Request, context: ApplicationHttpContext<Http>) => Value | Promise<Value>;
|
|
305
245
|
export interface ApplicationOptions<Http extends AnyElysia = Elysia, RequestContext = unknown> {
|
|
306
246
|
name?: string;
|
|
@@ -396,69 +336,25 @@ interface HttpContext {
|
|
|
396
336
|
* and passed to the controller input as `scope`. Request-scoped controllers
|
|
397
337
|
* are looked up on that scope, everything else on `services`.
|
|
398
338
|
*/
|
|
399
|
-
export declare function createModulePlugin<const Services extends Record<string, unknown>, const Http extends AnyElysia = Elysia>(compiled: CompiledModule, services: Services, ctxFactory?: HttpRequestContextFactory<Http>, options?: Pick<ApplicationOptions<Http>, "http" | "httpPolicies" | "commandGovernance" | "commandExecutor" | "errorMapper" | "onExecution" | "normalize">, imported?: Record<string, Record<string, unknown>>):
|
|
339
|
+
export declare function createModulePlugin<const Services extends Record<string, unknown>, const Http extends AnyElysia = Elysia>(compiled: CompiledModule, services: Services, ctxFactory?: HttpRequestContextFactory<Http>, options?: Pick<ApplicationOptions<Http>, "http" | "httpPolicies" | "commandGovernance" | "commandExecutor" | "errorMapper" | "onExecution" | "normalize">, imported?: Record<string, Record<string, unknown>>): import("elysia/types").LocalHookReturn<"", "local", {
|
|
400
340
|
decorator: {} & Http["~Singleton"]["decorator"];
|
|
401
341
|
store: ({} & Http["~Singleton"]["store"] extends infer T_1 ? { [K_1 in keyof T_1]: T_1[K_1]; } : never) extends infer T ? { [K in keyof T]: T[K]; } : never;
|
|
402
342
|
derive: {} & Http["~Singleton"]["derive"];
|
|
403
|
-
resolve: {} & Http["~Singleton"]["resolve"];
|
|
404
343
|
}, {
|
|
405
|
-
typebox: {};
|
|
406
|
-
error:
|
|
407
|
-
} & Http["~Definitions"], {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
response: {};
|
|
420
|
-
}, {
|
|
421
|
-
derive: {} & ({
|
|
422
|
-
derive: {};
|
|
423
|
-
resolve: {};
|
|
424
|
-
schema: {};
|
|
425
|
-
standaloneSchema: {};
|
|
426
|
-
response: {};
|
|
427
|
-
} & Http["~Ephemeral"])["derive"];
|
|
428
|
-
resolve: ({
|
|
429
|
-
derive: {};
|
|
430
|
-
resolve: {};
|
|
431
|
-
schema: {};
|
|
432
|
-
standaloneSchema: {};
|
|
433
|
-
response: {};
|
|
434
|
-
} & Http["~Ephemeral"])["resolve"] & {
|
|
435
|
-
readonly services: Services;
|
|
436
|
-
readonly requestContext: unknown;
|
|
437
|
-
};
|
|
438
|
-
schema: {} & import("elysia").MergeSchema<({
|
|
439
|
-
derive: {};
|
|
440
|
-
resolve: {};
|
|
441
|
-
schema: {};
|
|
442
|
-
standaloneSchema: {};
|
|
443
|
-
response: {};
|
|
444
|
-
} & Http["~Ephemeral"])["schema"], {}, "">;
|
|
445
|
-
standaloneSchema: {} & ({
|
|
446
|
-
derive: {};
|
|
447
|
-
resolve: {};
|
|
448
|
-
schema: {};
|
|
449
|
-
standaloneSchema: {};
|
|
450
|
-
response: {};
|
|
451
|
-
} & Http["~Ephemeral"])["standaloneSchema"];
|
|
452
|
-
response: import("elysia/types").UnionResponseStatus<{} & ({
|
|
453
|
-
derive: {};
|
|
454
|
-
resolve: {};
|
|
455
|
-
schema: {};
|
|
456
|
-
standaloneSchema: {};
|
|
457
|
-
response: {};
|
|
458
|
-
} & Http["~Ephemeral"])["response"], import("elysia").ExtractErrorFromHandle<{
|
|
459
|
-
readonly services: Services;
|
|
460
|
-
readonly requestContext: unknown;
|
|
461
|
-
}>>;
|
|
344
|
+
typebox: {} & Http["~Definitions"]["typebox"];
|
|
345
|
+
error: [...Http["~Definitions"]["error"]];
|
|
346
|
+
}, import("elysia").DefaultMetadata & Http["~Metadata"], import("elysia/types").ResolveRouteErrors<{}, [...Http["~Definitions"]["error"], ...Http["~Ephemeral"]["error"]]> & Http["~Routes"], import("elysia").DefaultEphemeral, {
|
|
347
|
+
derive: {} & Http["~Ephemeral"]["derive"];
|
|
348
|
+
schema: {} & import("elysia/types").MergeSchema<{} & Http["~Ephemeral"]["schema"], {}, "", false>;
|
|
349
|
+
schemas: {} & Http["~Ephemeral"]["schemas"];
|
|
350
|
+
response: {} & Http["~Ephemeral"]["response"];
|
|
351
|
+
error: [...Http["~Ephemeral"]["error"]];
|
|
352
|
+
}, import("elysia/types").ExtractErrorFromHandle<{
|
|
353
|
+
readonly services: Services;
|
|
354
|
+
readonly requestContext: unknown;
|
|
355
|
+
}>, {
|
|
356
|
+
readonly services: Services;
|
|
357
|
+
readonly requestContext: unknown;
|
|
462
358
|
}>;
|
|
463
359
|
/**
|
|
464
360
|
* Execute one compiler-emitted Job descriptor.
|
|
@@ -476,134 +372,48 @@ export declare function defaultErrorResponse(error: unknown, frameworkCode?: str
|
|
|
476
372
|
* `createServices` receives `deps` plus the services of all previously
|
|
477
373
|
* created modules, keyed by module name.
|
|
478
374
|
*/
|
|
479
|
-
export declare function createApplication<const Http extends AnyElysia = Elysia>(options: ApplicationOptions<Http>): Elysia<"", {
|
|
375
|
+
export declare function createApplication<const Http extends AnyElysia = Elysia>(options: ApplicationOptions<Http>): Elysia<"", "local", {
|
|
480
376
|
decorator: {} & Http["~Singleton"]["decorator"];
|
|
481
377
|
store: ({} & Http["~Singleton"]["store"] extends infer T_1 ? { [K_1 in keyof T_1]: T_1[K_1]; } : never) extends infer T ? { [K in keyof T]: T[K]; } : never;
|
|
482
|
-
derive: {} & Http["~Singleton"]["derive"];
|
|
483
|
-
resolve: {} & Http["~Singleton"]["resolve"];
|
|
378
|
+
derive: {} & Http["~Singleton"]["derive"] & Http["~Ephemeral"]["derive"];
|
|
484
379
|
}, {
|
|
485
|
-
typebox: {};
|
|
486
|
-
error:
|
|
487
|
-
}
|
|
488
|
-
schema: {}
|
|
489
|
-
|
|
490
|
-
macro:
|
|
491
|
-
macroFn:
|
|
492
|
-
parser:
|
|
493
|
-
response: {}
|
|
494
|
-
}
|
|
380
|
+
typebox: {} & Http["~Definitions"]["typebox"];
|
|
381
|
+
error: [...Http["~Definitions"]["error"], ...Http["~Ephemeral"]["error"]];
|
|
382
|
+
}, import("elysia").DefaultMetadata & {
|
|
383
|
+
schema: import("elysia/types").MergeSchema<import("elysia/types").MergeSchema<{} & Http["~Ephemeral"]["schema"], {}, "", false>, (import("elysia").DefaultMetadata & Http["~Metadata"])["schema"], "", false>;
|
|
384
|
+
schemas: (import("elysia").DefaultMetadata & Http["~Metadata"])["schemas"] & {} & Http["~Ephemeral"]["schemas"];
|
|
385
|
+
macro: (import("elysia").DefaultMetadata & Http["~Metadata"])["macro"];
|
|
386
|
+
macroFn: (import("elysia").DefaultMetadata & Http["~Metadata"])["macroFn"];
|
|
387
|
+
parser: (import("elysia").DefaultMetadata & Http["~Metadata"])["parser"];
|
|
388
|
+
response: import("elysia/types").UnionResponseStatus<(import("elysia").DefaultMetadata & Http["~Metadata"])["response"], import("elysia/types").UnionResponseStatus<{} & Http["~Ephemeral"]["response"], {}>>;
|
|
389
|
+
}, import("elysia/types").ResolveRouteErrors<{}, [...Http["~Definitions"]["error"], ...Http["~Ephemeral"]["error"]]> & Http["~Routes"], import("elysia").DefaultEphemeral, {
|
|
495
390
|
derive: {};
|
|
496
|
-
resolve: {};
|
|
497
391
|
schema: {};
|
|
498
|
-
|
|
392
|
+
schemas: {};
|
|
499
393
|
response: {};
|
|
500
|
-
|
|
501
|
-
derive: {};
|
|
502
|
-
resolve: {};
|
|
503
|
-
schema: {};
|
|
504
|
-
standaloneSchema: {};
|
|
505
|
-
response: {};
|
|
506
|
-
} & {
|
|
507
|
-
derive: {} & ({
|
|
508
|
-
derive: {};
|
|
509
|
-
resolve: {};
|
|
510
|
-
schema: {};
|
|
511
|
-
standaloneSchema: {};
|
|
512
|
-
response: {};
|
|
513
|
-
} & Http["~Ephemeral"])["derive"];
|
|
514
|
-
resolve: {} & ({
|
|
515
|
-
derive: {};
|
|
516
|
-
resolve: {};
|
|
517
|
-
schema: {};
|
|
518
|
-
standaloneSchema: {};
|
|
519
|
-
response: {};
|
|
520
|
-
} & Http["~Ephemeral"])["resolve"];
|
|
521
|
-
schema: import("elysia").MergeSchema<({
|
|
522
|
-
derive: {};
|
|
523
|
-
resolve: {};
|
|
524
|
-
schema: {};
|
|
525
|
-
standaloneSchema: {};
|
|
526
|
-
response: {};
|
|
527
|
-
} & Http["~Ephemeral"])["schema"], {}, "">;
|
|
528
|
-
standaloneSchema: ({
|
|
529
|
-
derive: {};
|
|
530
|
-
resolve: {};
|
|
531
|
-
schema: {};
|
|
532
|
-
standaloneSchema: {};
|
|
533
|
-
response: {};
|
|
534
|
-
} & Http["~Ephemeral"])["standaloneSchema"] & {};
|
|
535
|
-
response: ({
|
|
536
|
-
derive: {};
|
|
537
|
-
resolve: {};
|
|
538
|
-
schema: {};
|
|
539
|
-
standaloneSchema: {};
|
|
540
|
-
response: {};
|
|
541
|
-
} & Http["~Ephemeral"])["response"] & {};
|
|
394
|
+
error: [];
|
|
542
395
|
}>;
|
|
543
396
|
/** Semantic alias of createApplication for readable tests. */
|
|
544
|
-
export declare function createTestApp<const Http extends AnyElysia = Elysia>(options: ApplicationOptions<Http>): Elysia<"", {
|
|
397
|
+
export declare function createTestApp<const Http extends AnyElysia = Elysia>(options: ApplicationOptions<Http>): Elysia<"", "local", {
|
|
545
398
|
decorator: {} & Http["~Singleton"]["decorator"];
|
|
546
399
|
store: ({} & Http["~Singleton"]["store"] extends infer T_1 ? { [K_1 in keyof T_1]: T_1[K_1]; } : never) extends infer T ? { [K in keyof T]: T[K]; } : never;
|
|
547
|
-
derive: {} & Http["~Singleton"]["derive"];
|
|
548
|
-
resolve: {} & Http["~Singleton"]["resolve"];
|
|
549
|
-
}, {
|
|
550
|
-
typebox: {};
|
|
551
|
-
error: {};
|
|
552
|
-
} & Http["~Definitions"], {
|
|
553
|
-
schema: {};
|
|
554
|
-
standaloneSchema: {};
|
|
555
|
-
macro: {};
|
|
556
|
-
macroFn: {};
|
|
557
|
-
parser: {};
|
|
558
|
-
response: {};
|
|
559
|
-
} & Http["~Metadata"], {} & Http["~Routes"], {
|
|
560
|
-
derive: {};
|
|
561
|
-
resolve: {};
|
|
562
|
-
schema: {};
|
|
563
|
-
standaloneSchema: {};
|
|
564
|
-
response: {};
|
|
400
|
+
derive: {} & Http["~Singleton"]["derive"] & Http["~Ephemeral"]["derive"];
|
|
565
401
|
}, {
|
|
402
|
+
typebox: {} & Http["~Definitions"]["typebox"];
|
|
403
|
+
error: [...Http["~Definitions"]["error"], ...Http["~Ephemeral"]["error"]];
|
|
404
|
+
}, import("elysia").DefaultMetadata & {
|
|
405
|
+
schema: import("elysia/types").MergeSchema<import("elysia/types").MergeSchema<{} & Http["~Ephemeral"]["schema"], {}, "", false>, (import("elysia").DefaultMetadata & Http["~Metadata"])["schema"], "", false>;
|
|
406
|
+
schemas: (import("elysia").DefaultMetadata & Http["~Metadata"])["schemas"] & {} & Http["~Ephemeral"]["schemas"];
|
|
407
|
+
macro: (import("elysia").DefaultMetadata & Http["~Metadata"])["macro"];
|
|
408
|
+
macroFn: (import("elysia").DefaultMetadata & Http["~Metadata"])["macroFn"];
|
|
409
|
+
parser: (import("elysia").DefaultMetadata & Http["~Metadata"])["parser"];
|
|
410
|
+
response: import("elysia/types").UnionResponseStatus<(import("elysia").DefaultMetadata & Http["~Metadata"])["response"], import("elysia/types").UnionResponseStatus<{} & Http["~Ephemeral"]["response"], {}>>;
|
|
411
|
+
}, import("elysia/types").ResolveRouteErrors<{}, [...Http["~Definitions"]["error"], ...Http["~Ephemeral"]["error"]]> & Http["~Routes"], import("elysia").DefaultEphemeral, {
|
|
566
412
|
derive: {};
|
|
567
|
-
resolve: {};
|
|
568
413
|
schema: {};
|
|
569
|
-
|
|
414
|
+
schemas: {};
|
|
570
415
|
response: {};
|
|
571
|
-
|
|
572
|
-
derive: {} & ({
|
|
573
|
-
derive: {};
|
|
574
|
-
resolve: {};
|
|
575
|
-
schema: {};
|
|
576
|
-
standaloneSchema: {};
|
|
577
|
-
response: {};
|
|
578
|
-
} & Http["~Ephemeral"])["derive"];
|
|
579
|
-
resolve: {} & ({
|
|
580
|
-
derive: {};
|
|
581
|
-
resolve: {};
|
|
582
|
-
schema: {};
|
|
583
|
-
standaloneSchema: {};
|
|
584
|
-
response: {};
|
|
585
|
-
} & Http["~Ephemeral"])["resolve"];
|
|
586
|
-
schema: import("elysia").MergeSchema<({
|
|
587
|
-
derive: {};
|
|
588
|
-
resolve: {};
|
|
589
|
-
schema: {};
|
|
590
|
-
standaloneSchema: {};
|
|
591
|
-
response: {};
|
|
592
|
-
} & Http["~Ephemeral"])["schema"], {}, "">;
|
|
593
|
-
standaloneSchema: ({
|
|
594
|
-
derive: {};
|
|
595
|
-
resolve: {};
|
|
596
|
-
schema: {};
|
|
597
|
-
standaloneSchema: {};
|
|
598
|
-
response: {};
|
|
599
|
-
} & Http["~Ephemeral"])["standaloneSchema"] & {};
|
|
600
|
-
response: ({
|
|
601
|
-
derive: {};
|
|
602
|
-
resolve: {};
|
|
603
|
-
schema: {};
|
|
604
|
-
standaloneSchema: {};
|
|
605
|
-
response: {};
|
|
606
|
-
} & Http["~Ephemeral"])["response"] & {};
|
|
416
|
+
error: [];
|
|
607
417
|
}>;
|
|
608
418
|
export { createMemorySandbox, } from "./memory";
|
|
609
419
|
export type { HandleLike as MemoryHandleLike, MemoryDatabase, MemorySandbox, MemorySandboxOptions, MemoryStorage, MemoryStorageObject, } from "./memory";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import { Elysia as Elysia3 } from "elysia";
|
|
2
|
+
import { Elysia as Elysia3, setupTypebox, Validator as Validator2 } from "elysia";
|
|
3
3
|
import { decodeCommandPreview } from "@supacloud/contracts";
|
|
4
4
|
|
|
5
5
|
// src/command-errors.ts
|
|
@@ -83,15 +83,15 @@ function httpRequestId(request) {
|
|
|
83
83
|
}
|
|
84
84
|
function createHttpTelemetry(observe) {
|
|
85
85
|
const starts = new WeakMap;
|
|
86
|
-
return new Elysia().
|
|
86
|
+
return new Elysia().request(({ request, set }) => {
|
|
87
87
|
const supplied = request.headers.get("x-request-id");
|
|
88
88
|
const requestId = supplied && /^[A-Za-z0-9._:-]{1,128}$/.test(supplied) ? supplied : crypto.randomUUID();
|
|
89
89
|
starts.set(request, { time: performance.now(), requestId });
|
|
90
90
|
requestIds.set(request, requestId);
|
|
91
91
|
set.headers["x-request-id"] = requestId;
|
|
92
|
-
}).
|
|
92
|
+
}).error(() => {
|
|
93
93
|
return;
|
|
94
|
-
}).
|
|
94
|
+
}).afterResponse(async ({ request, responseValue: response, set, route }) => {
|
|
95
95
|
const start = starts.get(request);
|
|
96
96
|
if (!start)
|
|
97
97
|
return;
|
|
@@ -193,7 +193,7 @@ function unauthenticated() {
|
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
// src/http-response-cache.ts
|
|
196
|
-
import {
|
|
196
|
+
import { WebStandardAdapter } from "elysia/adapter/web-standard";
|
|
197
197
|
function mayStore({ http, response }) {
|
|
198
198
|
if (response === null || typeof response !== "object" || response instanceof Response || !Array.isArray(response) && Object.getPrototypeOf(response) !== Object.prototype)
|
|
199
199
|
return false;
|
|
@@ -277,7 +277,10 @@ function createCachePolicy(options) {
|
|
|
277
277
|
policy.mapResponse = async (context) => {
|
|
278
278
|
if (!misses.has(context.http.request) || !mayStore(context))
|
|
279
279
|
return;
|
|
280
|
-
const
|
|
280
|
+
const value = context.encodeResponse ? await context.encodeResponse(context.response) : context.response;
|
|
281
|
+
const response = await WebStandardAdapter.response.map(value, context.http.set, context.http.request);
|
|
282
|
+
if (!(response instanceof Response))
|
|
283
|
+
throw new Error("HTTP response mapping did not return a Response");
|
|
281
284
|
const body = boundedBody(response.clone(), options.maxBodyBytes).catch(() => {
|
|
282
285
|
try {
|
|
283
286
|
options.onWriteError();
|
|
@@ -695,9 +698,10 @@ function executionTrace(context) {
|
|
|
695
698
|
|
|
696
699
|
// src/schema_contract.ts
|
|
697
700
|
import {
|
|
698
|
-
|
|
701
|
+
Validator,
|
|
702
|
+
t,
|
|
699
703
|
StatusMap as StatusMap2,
|
|
700
|
-
|
|
704
|
+
ElysiaStatus
|
|
701
705
|
} from "elysia";
|
|
702
706
|
function defineRouteContract(schemas) {
|
|
703
707
|
return Object.freeze({ ...schemas });
|
|
@@ -708,7 +712,7 @@ var RESPONSE_STATUS_MAX = 599;
|
|
|
708
712
|
var RESPONSE_FAMILY_PATTERN = /^([1-5])(?:xx|XX)$/;
|
|
709
713
|
var RESPONSE_STATUS_PATTERN = /^[1-5]\d{2}$/;
|
|
710
714
|
function isSchema(value) {
|
|
711
|
-
return value !== null && typeof value === "object" && ((TYPEBOX_KIND in value) || ("~standard" in value));
|
|
715
|
+
return value !== null && typeof value === "object" && ((TYPEBOX_KIND in value) || ("~kind" in value) || ("~standard" in value));
|
|
712
716
|
}
|
|
713
717
|
function isJsonSchemaObject(value) {
|
|
714
718
|
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
@@ -769,8 +773,8 @@ function responseStatusDeclared(responses, status) {
|
|
|
769
773
|
function responseStatusOf(value, configuredStatus) {
|
|
770
774
|
if (value instanceof Response)
|
|
771
775
|
return value.status;
|
|
772
|
-
if (value instanceof
|
|
773
|
-
const code = value.
|
|
776
|
+
if (value instanceof ElysiaStatus) {
|
|
777
|
+
const code = value.status;
|
|
774
778
|
if (typeof code === "number" && Number.isInteger(code) && code >= RESPONSE_STATUS_MIN && code <= RESPONSE_STATUS_MAX) {
|
|
775
779
|
return code;
|
|
776
780
|
}
|
|
@@ -884,7 +888,7 @@ function registerElysiaRoute(app, route) {
|
|
|
884
888
|
}
|
|
885
889
|
return value;
|
|
886
890
|
};
|
|
887
|
-
app.
|
|
891
|
+
app.method(route.method, route.path, toElysiaRouteSchema(route.contract), handler);
|
|
888
892
|
return app;
|
|
889
893
|
}
|
|
890
894
|
|
|
@@ -896,13 +900,19 @@ class SchemaContractError extends Error {
|
|
|
896
900
|
}
|
|
897
901
|
}
|
|
898
902
|
function createSchemaDecoder(schema, options = {}) {
|
|
899
|
-
const validator =
|
|
900
|
-
|
|
903
|
+
const validator = Validator.create(schema, {
|
|
904
|
+
...options.coerce ? { coerces: [[[
|
|
905
|
+
["number", (value) => t.Numeric(value)],
|
|
906
|
+
["integer", (value) => t.IntegerString(value)],
|
|
907
|
+
["boolean", (value) => t.BooleanString(value)]
|
|
908
|
+
]]] } : {},
|
|
901
909
|
normalize: options.normalize ?? true
|
|
902
910
|
});
|
|
903
911
|
return (value) => {
|
|
904
912
|
try {
|
|
905
|
-
|
|
913
|
+
if (validator.isAsync)
|
|
914
|
+
throw new SchemaContractError;
|
|
915
|
+
return validator.FromSync(value);
|
|
906
916
|
} catch {
|
|
907
917
|
throw new SchemaContractError;
|
|
908
918
|
}
|
|
@@ -1008,12 +1018,12 @@ function createDocumentationPlugin(options = {}) {
|
|
|
1008
1018
|
reserve(specPath);
|
|
1009
1019
|
reserve(uiPath);
|
|
1010
1020
|
const title = options.openApi.title ?? "OpenAPI Documentation";
|
|
1011
|
-
app.get(specPath,
|
|
1021
|
+
app.get(specPath, {
|
|
1012
1022
|
detail: { hide: true }
|
|
1013
|
-
});
|
|
1014
|
-
app.get(uiPath, () => new Response(renderViewerPage(title, specPath, "json"), {
|
|
1023
|
+
}, async () => jsonResponse(await resolveSource(options.openApi.document)));
|
|
1024
|
+
app.get(uiPath, { detail: { hide: true } }, () => new Response(renderViewerPage(title, specPath, "json"), {
|
|
1015
1025
|
headers: { "content-type": "text/html; charset=utf-8" }
|
|
1016
|
-
})
|
|
1026
|
+
}));
|
|
1017
1027
|
}
|
|
1018
1028
|
if (options.graphql) {
|
|
1019
1029
|
const schemaPath = normalizePath(options.graphql.schemaPath, "/graphql/schema.graphql");
|
|
@@ -1021,12 +1031,12 @@ function createDocumentationPlugin(options = {}) {
|
|
|
1021
1031
|
reserve(schemaPath);
|
|
1022
1032
|
reserve(uiPath);
|
|
1023
1033
|
const title = options.graphql.title ?? "GraphQL Schema";
|
|
1024
|
-
app.get(schemaPath,
|
|
1034
|
+
app.get(schemaPath, {
|
|
1025
1035
|
detail: { hide: true }
|
|
1026
|
-
});
|
|
1027
|
-
app.get(uiPath, () => new Response(renderViewerPage(title, schemaPath, "text"), {
|
|
1036
|
+
}, async () => textResponse(await resolveSource(options.graphql.schema)));
|
|
1037
|
+
app.get(uiPath, { detail: { hide: true } }, () => new Response(renderViewerPage(title, schemaPath, "text"), {
|
|
1028
1038
|
headers: { "content-type": "text/html; charset=utf-8" }
|
|
1029
|
-
})
|
|
1039
|
+
}));
|
|
1030
1040
|
}
|
|
1031
1041
|
return app;
|
|
1032
1042
|
}
|
|
@@ -1832,6 +1842,7 @@ function createWorker(options) {
|
|
|
1832
1842
|
}
|
|
1833
1843
|
|
|
1834
1844
|
// src/index.ts
|
|
1845
|
+
setupTypebox();
|
|
1835
1846
|
var VERIFIED_JWT_SUBJECT_HEADER = "x-supacloud-jwt-sub";
|
|
1836
1847
|
var EXECUTION_ID_HEADER = "x-sb-execution-id";
|
|
1837
1848
|
var IDEMPOTENCY_KEY_HEADER = "idempotency-key";
|
|
@@ -1974,7 +1985,7 @@ function contextPlugin(http) {
|
|
|
1974
1985
|
seed = ++nextHttpPluginId;
|
|
1975
1986
|
httpPluginIds.set(http, seed);
|
|
1976
1987
|
}
|
|
1977
|
-
return new Elysia3({ name: "supacloud:http-context", seed }).use(http ?? new Elysia3).as("
|
|
1988
|
+
return new Elysia3({ name: "supacloud:http-context", seed }).use(http ?? new Elysia3).as("plugin");
|
|
1978
1989
|
}
|
|
1979
1990
|
function mountHttp(http, name, normalize = true) {
|
|
1980
1991
|
return new Elysia3({ name, normalize }).use(contextPlugin(http));
|
|
@@ -2363,7 +2374,7 @@ function createModulePlugin(compiled, services, ctxFactory = defaultRequestConte
|
|
|
2363
2374
|
const requestContexts = new WeakMap;
|
|
2364
2375
|
const governanceExecutor = options.commandGovernance ? createCommandExecutor(options.commandGovernance, options.onExecution) : undefined;
|
|
2365
2376
|
const commandExecutor = options.commandExecutor && governanceExecutor ? composeCommandExecutors(options.commandExecutor, governanceExecutor) : options.commandExecutor ?? governanceExecutor;
|
|
2366
|
-
const plugin = mountHttp(options.http, `supacloud:${compiled.name}`, options.normalize ?? true).
|
|
2377
|
+
const plugin = mountHttp(options.http, `supacloud:${compiled.name}`, options.normalize ?? true).derive(async (context) => {
|
|
2367
2378
|
const requestContext = await ctxFactory(context.request, context);
|
|
2368
2379
|
requestContexts.set(context.request, requestContext);
|
|
2369
2380
|
return { services, requestContext };
|
|
@@ -2372,7 +2383,7 @@ function createModulePlugin(compiled, services, ctxFactory = defaultRequestConte
|
|
|
2372
2383
|
const requestScopes = new WeakMap;
|
|
2373
2384
|
if (compiled.destroyRequestScope) {
|
|
2374
2385
|
const destroyRequestScope = compiled.destroyRequestScope;
|
|
2375
|
-
plugin.
|
|
2386
|
+
plugin.afterResponse(async ({ request }) => {
|
|
2376
2387
|
const scope = requestScopes.get(request);
|
|
2377
2388
|
if (!scope)
|
|
2378
2389
|
return;
|
|
@@ -2384,7 +2395,8 @@ function createModulePlugin(compiled, services, ctxFactory = defaultRequestConte
|
|
|
2384
2395
|
}
|
|
2385
2396
|
});
|
|
2386
2397
|
}
|
|
2387
|
-
plugin.
|
|
2398
|
+
plugin.error(async ({ error, request }) => {
|
|
2399
|
+
const code = error instanceof Error && "code" in error && typeof error.code === "string" ? error.code.toUpperCase().replaceAll("-", "_") : "UNKNOWN";
|
|
2388
2400
|
const context = {
|
|
2389
2401
|
request,
|
|
2390
2402
|
requestContext: requestContexts.get(request),
|
|
@@ -2399,12 +2411,14 @@ function createModulePlugin(compiled, services, ctxFactory = defaultRequestConte
|
|
|
2399
2411
|
const path = joinPaths(controller.path, route.path);
|
|
2400
2412
|
const documentation = route.data?.openapi;
|
|
2401
2413
|
const hidden = documentation !== null && typeof documentation === "object" && "hide" in documentation && documentation.hide === true;
|
|
2414
|
+
const tags = documentation !== null && typeof documentation === "object" && "tags" in documentation && Array.isArray(documentation.tags) ? documentation.tags.filter((tag) => typeof tag === "string") : undefined;
|
|
2402
2415
|
const schema = {
|
|
2403
2416
|
...toElysiaRouteSchema(route),
|
|
2404
2417
|
...route.parse === "none" ? { parse: "none" } : {},
|
|
2405
|
-
...route.title || hidden ? { detail: {
|
|
2418
|
+
...route.title || hidden || tags ? { detail: {
|
|
2406
2419
|
...route.title ? { summary: route.title } : {},
|
|
2407
|
-
...hidden ? { hide: true } : {}
|
|
2420
|
+
...hidden ? { hide: true } : {},
|
|
2421
|
+
...tags ? { tags } : {}
|
|
2408
2422
|
} } : {}
|
|
2409
2423
|
};
|
|
2410
2424
|
const policies = compileHttpPolicies(route, path, options.httpPolicies);
|
|
@@ -2428,15 +2442,28 @@ function createModulePlugin(compiled, services, ctxFactory = defaultRequestConte
|
|
|
2428
2442
|
const responsePolicies = policies.filter((policy) => policy.afterResponse);
|
|
2429
2443
|
const mappingPolicies = policies.filter((policy) => policy.mapResponse);
|
|
2430
2444
|
if (mappingPolicies.length > 0) {
|
|
2445
|
+
const responseValidators = Validator2.response(schema.response, { normalize: options.normalize ?? true });
|
|
2431
2446
|
Object.assign(schema, {
|
|
2432
2447
|
mapResponse: async (context) => {
|
|
2433
|
-
let response = context.
|
|
2448
|
+
let response = context.responseValue;
|
|
2434
2449
|
let mapped;
|
|
2435
2450
|
for (const policy of mappingPolicies) {
|
|
2436
2451
|
const result = await policy.mapResponse({
|
|
2437
2452
|
http: context,
|
|
2438
2453
|
requestContext: requestContexts.get(context.request),
|
|
2439
|
-
response
|
|
2454
|
+
response,
|
|
2455
|
+
encodeResponse: async (value) => {
|
|
2456
|
+
const validator = responseValidators?.[responseStatusOf(value, context.set.status)];
|
|
2457
|
+
if (!validator)
|
|
2458
|
+
return value;
|
|
2459
|
+
if (!("EncodeFrom" in validator) || typeof validator.EncodeFrom !== "function") {
|
|
2460
|
+
throw new ApplicationError("Response validation failed", { code: "RESPONSE_VALIDATION_FAILED" });
|
|
2461
|
+
}
|
|
2462
|
+
if ("mayReturnPromise" in validator && validator.mayReturnPromise && "From" in validator && typeof validator.From === "function") {
|
|
2463
|
+
return await Reflect.apply(validator.From, validator, [value, "response", true]);
|
|
2464
|
+
}
|
|
2465
|
+
return await Reflect.apply(validator.EncodeFrom, validator, [value, "response"]);
|
|
2466
|
+
}
|
|
2440
2467
|
});
|
|
2441
2468
|
if (result !== undefined) {
|
|
2442
2469
|
if (!(result instanceof Response))
|
|
@@ -2456,7 +2483,7 @@ function createModulePlugin(compiled, services, ctxFactory = defaultRequestConte
|
|
|
2456
2483
|
await policy.afterResponse({
|
|
2457
2484
|
http: context,
|
|
2458
2485
|
requestContext: requestContexts.get(context.request),
|
|
2459
|
-
response: context.
|
|
2486
|
+
response: context.responseValue
|
|
2460
2487
|
});
|
|
2461
2488
|
}
|
|
2462
2489
|
}
|
|
@@ -2555,25 +2582,25 @@ function createModulePlugin(compiled, services, ctxFactory = defaultRequestConte
|
|
|
2555
2582
|
};
|
|
2556
2583
|
switch (route.method) {
|
|
2557
2584
|
case "GET":
|
|
2558
|
-
routeRegistrar.get(path,
|
|
2585
|
+
routeRegistrar.get(path, schema, handler);
|
|
2559
2586
|
break;
|
|
2560
2587
|
case "POST":
|
|
2561
|
-
routeRegistrar.post(path,
|
|
2588
|
+
routeRegistrar.post(path, schema, handler);
|
|
2562
2589
|
break;
|
|
2563
2590
|
case "PUT":
|
|
2564
|
-
routeRegistrar.put(path,
|
|
2591
|
+
routeRegistrar.put(path, schema, handler);
|
|
2565
2592
|
break;
|
|
2566
2593
|
case "PATCH":
|
|
2567
|
-
routeRegistrar.patch(path,
|
|
2594
|
+
routeRegistrar.patch(path, schema, handler);
|
|
2568
2595
|
break;
|
|
2569
2596
|
case "DELETE":
|
|
2570
|
-
routeRegistrar.delete(path,
|
|
2597
|
+
routeRegistrar.delete(path, schema, handler);
|
|
2571
2598
|
break;
|
|
2572
2599
|
case "HEAD":
|
|
2573
|
-
routeRegistrar.head(path,
|
|
2600
|
+
routeRegistrar.head(path, schema, handler);
|
|
2574
2601
|
break;
|
|
2575
2602
|
case "OPTIONS":
|
|
2576
|
-
routeRegistrar.options(path,
|
|
2603
|
+
routeRegistrar.options(path, schema, handler);
|
|
2577
2604
|
break;
|
|
2578
2605
|
}
|
|
2579
2606
|
}
|
|
@@ -2675,7 +2702,7 @@ function isPublicApplicationError(error) {
|
|
|
2675
2702
|
return error.expose === true && typeof error.status === "number" && Number.isInteger(error.status) && error.status >= 400 && error.status <= 599 && typeof error.code === "string" && error.code.length > 0;
|
|
2676
2703
|
}
|
|
2677
2704
|
function createApplication(options) {
|
|
2678
|
-
const app = new Elysia3({ name: options.name ?? "supacloud:app", normalize: options.normalize ?? true });
|
|
2705
|
+
const app = new Elysia3({ name: options.name ?? "supacloud:app", normalize: options.normalize ?? true }).use(contextPlugin(options.http).as("global"));
|
|
2679
2706
|
if (options.documentation)
|
|
2680
2707
|
app.use(createDocumentationPlugin(options.documentation));
|
|
2681
2708
|
const configuredContextFactory = options.requestContext ?? defaultRequestContext;
|
|
@@ -2692,9 +2719,9 @@ function createApplication(options) {
|
|
|
2692
2719
|
for (const module of options.modules ?? []) {
|
|
2693
2720
|
const services = module.createServices(options.deps ?? {}, imported);
|
|
2694
2721
|
imported[module.name] = services;
|
|
2695
|
-
app.use(createModulePlugin(module, services, ctxFactory, options, imported));
|
|
2722
|
+
app.use(createModulePlugin(module, services, ctxFactory, { ...options, http: undefined }, imported));
|
|
2696
2723
|
}
|
|
2697
|
-
return app
|
|
2724
|
+
return app;
|
|
2698
2725
|
}
|
|
2699
2726
|
function createTestApp(options) {
|
|
2700
2727
|
return createApplication(options);
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { StatusMap,
|
|
1
|
+
import { StatusMap, ElysiaStatus, type Cookie, type AnyElysia, type InputSchema } from "elysia";
|
|
2
|
+
import type { HTTPMethod, MaybePromise } from "elysia/types";
|
|
3
|
+
import type { TSchema, StaticDecode as UnwrapSchema } from "typebox";
|
|
2
4
|
import type { ResponseMapSelector as AppResponseMapSelector, RouteContractSchemas as AppRouteContractSchemas } from "@supacloud/app";
|
|
3
5
|
/** Response selectors are owned by @supacloud/app and re-exported by the adapter. */
|
|
4
6
|
export type ResponseMapSelector = AppResponseMapSelector;
|
|
@@ -40,17 +42,20 @@ type FamilyResponseCodes<Value> = Value extends Readonly<Record<string | number,
|
|
|
40
42
|
}[keyof Value & string] : never;
|
|
41
43
|
type ResponseStatusCodes<Value> = NumericResponseCodes<Value> | FamilyResponseCodes<Value> | ([DefaultResponseKey<Value>] extends [never] ? never : AllHttpStatusCodes);
|
|
42
44
|
type DecodedSchema<Value> = Value extends TSchema ? UnwrapSchema<Value> : unknown;
|
|
43
|
-
type
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
type StatusResponse<Schemas extends RouteContractSchemas> = [
|
|
46
|
+
DeclaredResponses<Schemas>
|
|
47
|
+
] extends [never] ? never : {
|
|
48
|
+
[Code in ResponseStatusCodes<DeclaredResponses<Schemas>>]: ElysiaStatus<Code, DecodedSchema<ResponseSchemaForCode<DeclaredResponses<Schemas>, Code>>>;
|
|
49
|
+
}[ResponseStatusCodes<DeclaredResponses<Schemas>>];
|
|
47
50
|
/**
|
|
48
51
|
* A plain handler return is sent as the default HTTP 200 response. When a
|
|
49
52
|
* status map declares several payloads, only the schema selected for 200 is
|
|
50
53
|
* valid here; every other payload must use `status(code, value)` so its
|
|
51
54
|
* status and schema stay coupled at runtime.
|
|
52
55
|
*/
|
|
53
|
-
type PlainResponse<Schemas extends RouteContractSchemas> =
|
|
56
|
+
type PlainResponse<Schemas extends RouteContractSchemas> = [
|
|
57
|
+
DeclaredResponses<Schemas>
|
|
58
|
+
] extends [never] ? DecodedSchema<LegacyResponse<Schemas>> : AllowsPlainSuccessResponse<DeclaredResponses<Schemas>> extends true ? DecodedSchema<ResponseSchemaForCode<DeclaredResponses<Schemas>, 200>> : never;
|
|
54
59
|
type ElysiaRouteOutput<Schemas extends RouteContractSchemas> = PlainResponse<Schemas> | StatusResponse<Schemas> | Response | void;
|
|
55
60
|
/** Raw schema shape consumed by Elysia's route registration API. */
|
|
56
61
|
export interface ElysiaRouteSchema<Schemas extends RouteContractSchemas> {
|
|
@@ -71,9 +76,11 @@ type DecodedField<Schemas extends RouteContractSchemas, Key extends keyof RouteC
|
|
|
71
76
|
type DecodedCookie<Schemas extends RouteContractSchemas> = DecodedField<Schemas, "cookie"> extends infer Value ? Value extends Record<string, unknown> ? Record<string, Cookie<unknown>> & {
|
|
72
77
|
[Key in keyof Value]-?: Cookie<Value[Key]>;
|
|
73
78
|
} : Record<string, Cookie<unknown>> : Record<string, Cookie<unknown>>;
|
|
74
|
-
type StatusCode<Schemas extends RouteContractSchemas> =
|
|
75
|
-
|
|
76
|
-
|
|
79
|
+
type StatusCode<Schemas extends RouteContractSchemas> = [
|
|
80
|
+
DeclaredResponses<Schemas>
|
|
81
|
+
] extends [never] ? never : ResponseStatusCodes<DeclaredResponses<Schemas>>;
|
|
82
|
+
type StatusResponseValue<Schemas extends RouteContractSchemas, Code extends number> = DecodedSchema<ResponseSchemaForCode<DeclaredResponses<Schemas>, Code>>;
|
|
83
|
+
type RouteStatus<Schemas extends RouteContractSchemas> = [StatusCode<Schemas>] extends [never] ? <const Code extends number, const Value>(code: Code, response: Value) => ElysiaStatus<Code, Value> : <const Code extends StatusCode<Schemas>, const Value extends StatusResponseValue<Schemas, Code>>(code: Code, response: Value) => ElysiaStatus<Code, Value>;
|
|
77
84
|
/** Elysia-compatible decoded context inferred from one route contract and path. */
|
|
78
85
|
export type ElysiaRouteContext<Schemas extends RouteContractSchemas, Path extends string = ""> = {
|
|
79
86
|
body: DecodedField<Schemas, "body">;
|
|
@@ -147,8 +154,8 @@ export declare function defineJsonContract<const Input extends TSchema, const Re
|
|
|
147
154
|
url: string;
|
|
148
155
|
body?: unknown;
|
|
149
156
|
}, options?: SchemaDecoderOptions): Readonly<{
|
|
150
|
-
input: (value: unknown) =>
|
|
151
|
-
result: (value: unknown) =>
|
|
157
|
+
input: (value: unknown) => import("typebox").StaticType<[], "Decode", {}, {}, Input>;
|
|
158
|
+
result: (value: unknown) => import("typebox").StaticType<[], "Decode", {}, {}, Result>;
|
|
152
159
|
request: (input: NoInfer<UnwrapSchema<Input>>) => {
|
|
153
160
|
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
154
161
|
url: string;
|
|
@@ -18,39 +18,26 @@ export declare function createWebhookMigrationExample(options: {
|
|
|
18
18
|
authorize(identity: CommandIdentity, transaction: CommandTransaction): Promise<CommandAuthorization>;
|
|
19
19
|
writeAudit?(transaction: CommandTransaction): Promise<void>;
|
|
20
20
|
}): {
|
|
21
|
-
app: import("elysia").
|
|
21
|
+
app: import("elysia").Elysia<"", "local", {
|
|
22
22
|
decorator: {};
|
|
23
23
|
store: {};
|
|
24
24
|
derive: {};
|
|
25
|
-
resolve: {};
|
|
26
25
|
}, {
|
|
27
26
|
typebox: {};
|
|
28
|
-
error:
|
|
29
|
-
}, {
|
|
27
|
+
error: [];
|
|
28
|
+
}, import("elysia").DefaultMetadata & {
|
|
30
29
|
schema: {};
|
|
31
|
-
|
|
30
|
+
schemas: {};
|
|
32
31
|
macro: {};
|
|
33
32
|
macroFn: {};
|
|
34
33
|
parser: {};
|
|
35
34
|
response: {};
|
|
36
|
-
}, {}, {
|
|
37
|
-
derive: {};
|
|
38
|
-
resolve: {};
|
|
39
|
-
schema: {};
|
|
40
|
-
standaloneSchema: {};
|
|
41
|
-
response: {};
|
|
42
|
-
}, {
|
|
43
|
-
derive: {};
|
|
44
|
-
resolve: {};
|
|
45
|
-
schema: {};
|
|
46
|
-
standaloneSchema: {};
|
|
47
|
-
response: {};
|
|
48
|
-
} & {
|
|
35
|
+
}, {}, import("elysia").DefaultEphemeral, {
|
|
49
36
|
derive: {};
|
|
50
|
-
resolve: {};
|
|
51
37
|
schema: {};
|
|
52
|
-
|
|
38
|
+
schemas: {};
|
|
53
39
|
response: {};
|
|
40
|
+
error: [];
|
|
54
41
|
}>;
|
|
55
42
|
modules: import("./fixtures/webhook-generated/application").CompiledApplicationModule[];
|
|
56
43
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supacloud/elysia",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.1",
|
|
4
4
|
"description": "Elysia runtime adapter for SupaCloud compiled modules: application/request scopes, route registration and validation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -47,23 +47,27 @@
|
|
|
47
47
|
"directory": "packages/elysia"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@supacloud/app": "0.
|
|
50
|
+
"@supacloud/app": "0.18.0",
|
|
51
51
|
"@supacloud/contracts": "0.5.0",
|
|
52
|
-
"
|
|
52
|
+
"exact-mirror": "1.2.6",
|
|
53
|
+
"jose": "^6.2.11",
|
|
54
|
+
"typebox": "1.3.34"
|
|
53
55
|
},
|
|
54
56
|
"overrides": {
|
|
57
|
+
"@supacloud/delivery": "0.2.0",
|
|
55
58
|
"@supacloud/contracts": "0.5.0",
|
|
56
59
|
"@supacloud/commands": "0.7.0"
|
|
57
60
|
},
|
|
58
61
|
"peerDependencies": {
|
|
59
|
-
"elysia": "
|
|
62
|
+
"elysia": "2.0.0-beta.19"
|
|
60
63
|
},
|
|
61
64
|
"devDependencies": {
|
|
62
|
-
"@supacloud/compiler": "0.
|
|
65
|
+
"@supacloud/compiler": "0.27.2",
|
|
66
|
+
"@supacloud/delivery": "0.2.0",
|
|
63
67
|
"@supacloud/commands": "0.7.0",
|
|
64
|
-
"@supacloud/db": "0.
|
|
68
|
+
"@supacloud/db": "0.9.0",
|
|
65
69
|
"@types/bun": "^1.4.2",
|
|
66
|
-
"elysia": "
|
|
70
|
+
"elysia": "2.0.0-beta.19",
|
|
67
71
|
"typescript": "^7.0.2"
|
|
68
72
|
}
|
|
69
73
|
}
|