@supacloud/elysia 0.8.1 → 0.9.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 +36 -2
- package/dist/index.d.ts +7 -0
- package/dist/index.js +24 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -91,8 +91,9 @@ write retry. Confirm the outcome using the application's durable receipt or
|
|
|
91
91
|
read-back protocol. A custom `errorMapper` can override this public envelope.
|
|
92
92
|
|
|
93
93
|
Native `Response` objects are passed through by Elysia, including JSON responses.
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
Use `validatedJsonResponse` to opt into validation when constructing a native
|
|
95
|
+
JSON response. Otherwise handlers must validate their JSON payload themselves.
|
|
96
|
+
The adapter does not consume or parse binary/streaming responses.
|
|
96
97
|
|
|
97
98
|
Jobs are executed explicitly with `executeJob(compiledModule, services, job,
|
|
98
99
|
input, requestContext)`. The asynchronous compiler-generated job scope is
|
|
@@ -101,6 +102,39 @@ fails partway through.
|
|
|
101
102
|
|
|
102
103
|
## API
|
|
103
104
|
|
|
105
|
+
### `validatedJsonResponse(validate, value, init?): Response`
|
|
106
|
+
|
|
107
|
+
Constructs a native JSON response after a synchronous, caller-owned type guard
|
|
108
|
+
validates the actual serialized JSON snapshot. The value type is inferred from
|
|
109
|
+
the guard; compatible extra fields are preserved. For a TypeBox contract, the
|
|
110
|
+
guard can delegate to `Value.Check(schema, value)` or a compiled validator.
|
|
111
|
+
No additional schema dependency is required by the adapter.
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { validatedJsonResponse } from "@supacloud/elysia";
|
|
115
|
+
import { isReportReceipt } from "./contracts";
|
|
116
|
+
|
|
117
|
+
return validatedJsonResponse(isReportReceipt, receipt, {
|
|
118
|
+
status: 201,
|
|
119
|
+
headers: { "x-request-id": requestId },
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The helper serializes once, validates that wire snapshot, and sends those same
|
|
124
|
+
bytes. Validation cannot mutate the outgoing body; it is not a transform or
|
|
125
|
+
coercion hook. Guards must be synchronous and side-effect free. Serialization
|
|
126
|
+
failures and invalid receipts throw a sanitized `ApplicationError` with HTTP 500
|
|
127
|
+
and `RESPONSE_VALIDATION_ERROR`, without retaining payloads or validator causes.
|
|
128
|
+
The application's `errorMapper` can map this to its outcome-confirmation
|
|
129
|
+
protocol. The helper never retries a command or implies rollback.
|
|
130
|
+
|
|
131
|
+
`init` uses native `Response` options. The helper explicitly rejects null-body
|
|
132
|
+
statuses 204, 205 and 304, including on runtimes that otherwise accept a body
|
|
133
|
+
with those statuses. The default content type is
|
|
134
|
+
`application/json`, and explicitly supplied headers are preserved. This helper
|
|
135
|
+
is for bounded JSON payloads, not files or streams. Existing native `Response`
|
|
136
|
+
passthrough is unchanged.
|
|
137
|
+
|
|
104
138
|
### `createApplication(options: ApplicationOptions): Elysia`
|
|
105
139
|
|
|
106
140
|
Creates the root Elysia application from compiled modules.
|
package/dist/index.d.ts
CHANGED
|
@@ -144,6 +144,13 @@ export declare class ApplicationError extends Error implements PublicApplication
|
|
|
144
144
|
readonly details?: unknown;
|
|
145
145
|
constructor(message: string, options?: ApplicationErrorOptions);
|
|
146
146
|
}
|
|
147
|
+
export type JsonResponseValidator<T> = (value: unknown) => value is T;
|
|
148
|
+
/**
|
|
149
|
+
* Validate the serialized JSON snapshot before creating a native Response.
|
|
150
|
+
* Validators must be synchronous and side-effect free; response failures do not
|
|
151
|
+
* imply that application writes were rolled back.
|
|
152
|
+
*/
|
|
153
|
+
export declare function validatedJsonResponse<T>(validate: JsonResponseValidator<T>, value: NoInfer<T>, init?: ResponseInit): Response;
|
|
147
154
|
export interface ErrorContext {
|
|
148
155
|
request: Request;
|
|
149
156
|
requestContext: unknown;
|
package/dist/index.js
CHANGED
|
@@ -405,6 +405,28 @@ class ApplicationError extends Error {
|
|
|
405
405
|
this.details = options.details;
|
|
406
406
|
}
|
|
407
407
|
}
|
|
408
|
+
function validatedJsonResponse(validate, value, init) {
|
|
409
|
+
if (init?.status === 204 || init?.status === 205 || init?.status === 304) {
|
|
410
|
+
throw new TypeError("JSON responses cannot use a null-body status");
|
|
411
|
+
}
|
|
412
|
+
let body;
|
|
413
|
+
try {
|
|
414
|
+
const serialized = JSON.stringify(value);
|
|
415
|
+
if (serialized === undefined || validate(JSON.parse(serialized)) !== true) {
|
|
416
|
+
throw new Error("Invalid JSON response");
|
|
417
|
+
}
|
|
418
|
+
body = serialized;
|
|
419
|
+
} catch {
|
|
420
|
+
throw new ApplicationError("Response validation failed", {
|
|
421
|
+
status: 500,
|
|
422
|
+
code: "RESPONSE_VALIDATION_ERROR"
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
const headers = new Headers(init?.headers);
|
|
426
|
+
if (!headers.has("content-type"))
|
|
427
|
+
headers.set("content-type", "application/json");
|
|
428
|
+
return new Response(body, { ...init, headers });
|
|
429
|
+
}
|
|
408
430
|
function safeHeaderValue(value, maxLength) {
|
|
409
431
|
if (!value || value.length > maxLength || /[\u0000-\u001f\u007f]/.test(value)) {
|
|
410
432
|
return;
|
|
@@ -807,5 +829,6 @@ export {
|
|
|
807
829
|
defaultErrorResponse,
|
|
808
830
|
executeJob,
|
|
809
831
|
requireIdempotencyKey,
|
|
810
|
-
requireTrustedIdentity
|
|
832
|
+
requireTrustedIdentity,
|
|
833
|
+
validatedJsonResponse
|
|
811
834
|
};
|
package/package.json
CHANGED