@shivaedev/platform 0.9.0 → 0.11.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/CHANGELOG.md +13 -0
- package/README.md +17 -0
- package/dist/server/appHost.d.ts +7 -7
- package/dist/server/appHost.d.ts.map +1 -1
- package/dist/server/appHost.js.map +1 -1
- package/dist/server/appHostHttp.d.ts.map +1 -1
- package/dist/server/appHostHttp.js +3 -2
- package/dist/server/appHostHttp.js.map +1 -1
- package/dist/server/boundedBody.d.ts +14 -0
- package/dist/server/boundedBody.d.ts.map +1 -0
- package/dist/server/boundedBody.js +36 -0
- package/dist/server/boundedBody.js.map +1 -0
- package/dist/server/boundedRpcRequest.d.ts.map +1 -1
- package/dist/server/boundedRpcRequest.js +7 -26
- package/dist/server/boundedRpcRequest.js.map +1 -1
- package/dist/server/hostOptions.d.ts +4 -2
- package/dist/server/hostOptions.d.ts.map +1 -1
- package/package.json +8 -2
- package/src/server/appHost.ts +39 -31
- package/src/server/appHostHttp.ts +3 -2
- package/src/server/boundedBody.ts +45 -0
- package/src/server/boundedRpcRequest.ts +10 -29
- package/src/server/hostOptions.ts +5 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.11.0 - 2026-10-16
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Export `readBoundedBody` and `RequestBodyTooLarge` from `@shivaedev/platform/boundedBody.ts`. Native Node HTTP routes can apply Effect's `HttpIncomingMessage.MaxBodySize` and return 413 for oversized content lengths or chunked bodies while keeping the connection usable and preserving accepted bytes.
|
|
8
|
+
|
|
9
|
+
## 0.10.0 - 2026-10-16
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `appHost` and its options types accept an omitted `services` Layer for apps without services of their own. Annotated options require a Layer when they declare service outputs, errors or requirements. Existing service Layers keep their behavior, and migrated hosts still provide database services to handlers.
|
|
14
|
+
- Signed-in handlers can read the current user's id, name and email: declare `AuthenticatedUser`, read `SessionUser`, and merge `authenticatedUserLayer(sessions)` beside the handlers. `Identity` keeps its `{ id }` shape.
|
|
15
|
+
|
|
3
16
|
## 0.9.0 - 2026-10-16
|
|
4
17
|
|
|
5
18
|
### Added
|
package/README.md
CHANGED
|
@@ -59,12 +59,29 @@ NodeRuntime.runMain(Layer.launch(Server));
|
|
|
59
59
|
|
|
60
60
|
`app.ts` exports the application's contracts, native Layers, numbered `Migrator.Loader` and `SessionPolicy`. The host needs the optional peers `@effect/platform-node` and `@effect/sql-pg` at the workspace's Effect version; install both for this entry point. The `errors/`, `node-http/`, `rpc/`, `rpc-server/` and `runtime/` entries still work without them.
|
|
61
61
|
|
|
62
|
+
Omit `services` when the app has no service Layer of its own. The host uses `Layer.empty`; handler requirements remain explicit, and a migrated host still provides its database services to handlers. Annotated options that declare service outputs, errors or requirements must supply a `services` Layer.
|
|
63
|
+
|
|
62
64
|
`assets` accepts a directory string or native `HttpStaticServer.layer` options. For a browser app whose routes must also work on direct navigation or reload, use `{ root: "dist", spa: true }` or merge `HttpStaticServer.layer({ root: "dist", spa: true })` from `effect/http` alongside the handler Layer. Native route Layers can register additional endpoints in that same composition; `routes` also accepts a native `Layer<never, never, HttpRouter>` for endpoints on the same listener. The [starter](https://github.com/ShivaeDev/platform/tree/main/examples/starter) uses it for static assets and Better Auth at `/api/auth/*`, with provider-owned cookies and the host's RPC session middleware.
|
|
63
65
|
|
|
64
66
|
The listener uses `HOST` or `127.0.0.1`, uses `PORT` or 3000, and reads `DATABASE_URL`. Set overrides in the one `config` object: `host`, `port` (0 selects an ephemeral port), `trustedProxy`, `maxBodySize`, `databaseUrl` (an Effect `Redacted<string>`), `applicationName`, and optional `changes`. Migration options accept `table` and `lockTimeout` (default `effect_sql_migrations` and five seconds). Native Layer failures remain in the build error channel; missing application dependencies remain in the requirement type.
|
|
65
67
|
|
|
66
68
|
RPC request bodies are limited to 1 MiB by default. Set `MAX_BODY_SIZE` to a byte count or override it with `config.maxBodySize`, a nonnegative safe integer. An oversized NDJSON body returns a 4xx before any RPC handler runs; real HTTP tests cover fixed-length and chunked requests, byte counting, and configuration precedence. This limit applies to the host's RPC route.
|
|
67
69
|
|
|
70
|
+
For a native Node HTTP route, use `readBoundedBody` from `@shivaedev/platform/boundedBody.ts` with Effect's `HttpIncomingMessage.MaxBodySize`. It returns an `ArrayBuffer` without decoding the bytes. Oversized `content-length` headers are rejected before reading; chunked overflow drains the remaining body and preserves the connection for another request. `RequestBodyTooLarge` is a typed failure that implements Effect's `HttpServerRespondable` protocol with status 413; other read errors remain native `HttpServerError` failures. Consume the body once through this reader and decode the result in the application.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { ByteSize, Effect } from "effect";
|
|
74
|
+
import { HttpIncomingMessage, HttpRouter, HttpServerResponse } from "effect/http";
|
|
75
|
+
import { readBoundedBody } from "@shivaedev/platform/boundedBody.ts";
|
|
76
|
+
|
|
77
|
+
const routes = HttpRouter.add("POST", "/webhook", (request) =>
|
|
78
|
+
readBoundedBody(request).pipe(
|
|
79
|
+
Effect.map((body) => HttpServerResponse.uint8Array(new Uint8Array(body))),
|
|
80
|
+
Effect.provideService(HttpIncomingMessage.MaxBodySize, ByteSize.bytes(64 * 1024)),
|
|
81
|
+
),
|
|
82
|
+
);
|
|
83
|
+
```
|
|
84
|
+
|
|
68
85
|
RPC uses the existing `localRpcGuard`: it requires a loopback peer and Host, same-origin or absent Origin, and the NDJSON content type. Session lookup uses transport headers. With a session policy, contracts declare `Authenticated`, `MaybeAuthenticated` and `RequestTracing` as needed; the host supplies their implementations. For `AuthenticatedUser`, merge `authenticatedUserLayer(sessions)` beside the handlers as shown below. Declared errors retain their Schema types. Applications still own resource authorization and use `rpcBoundary` when they need to map unexpected failures.
|
|
69
86
|
|
|
70
87
|
For local tools, use `policy: "loopback"` with contracts that do not use session middleware to serve RPC without a session provider. The guard requires a loopback peer and Host, absent or same-origin Origin, and NDJSON content type. This policy does not use `TRUSTED_PROXY` or `config.trustedProxy`. Set `rpcPath` to choose another native RPC route; `/rpc` remains the default.
|
package/dist/server/appHost.d.ts
CHANGED
|
@@ -37,25 +37,25 @@ export interface AppDatabaseOptions<R = never> {
|
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
export declare function appDatabase<R>(options: AppDatabaseOptions<R>): import("effect/Layer").Layer<AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient, import("effect/Config").ConfigError | Migrator.MigrationError | import("effect/sql/SqlError").SqlError, Exclude<Exclude<R, import("effect/Scope").Scope>, import("@effect/sql-pg/PgClient").PgClient | import("effect/sql/SqlClient").SqlClient>>;
|
|
40
|
-
export
|
|
40
|
+
export type AppHostOptions<TRpcs extends Rpc.Any, THandlers, THandlerError, THandlerRequirements, TServices = never, TServiceError = never, TServiceRequirements = never, TMigrations = never, TSession extends SessionShape = SessionShape> = HostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements> & {
|
|
41
41
|
readonly migrations: AppDatabaseOptions<TMigrations>["migrations"];
|
|
42
42
|
readonly policy?: undefined;
|
|
43
43
|
readonly sessions: SessionPolicy<TSession>;
|
|
44
|
-
}
|
|
45
|
-
export type AppLocalHostOptions<TRpcs extends Rpc.Any, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TMigrations> = HostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements> & {
|
|
44
|
+
};
|
|
45
|
+
export type AppLocalHostOptions<TRpcs extends Rpc.Any, THandlers, THandlerError, THandlerRequirements, TServices = never, TServiceError = never, TServiceRequirements = never, TMigrations = never> = HostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements> & {
|
|
46
46
|
readonly migrations: AppDatabaseOptions<TMigrations>["migrations"];
|
|
47
47
|
readonly policy: "loopback";
|
|
48
48
|
readonly sessions?: never;
|
|
49
49
|
};
|
|
50
|
-
export type AppHttpHostOptions<TRpcs extends Rpc.Any, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TSession extends SessionShape> = HostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements> & HostPolicy<TSession> & {
|
|
50
|
+
export type AppHttpHostOptions<TRpcs extends Rpc.Any, THandlers, THandlerError, THandlerRequirements, TServices = never, TServiceError = never, TServiceRequirements = never, TSession extends SessionShape = SessionShape> = HostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements> & HostPolicy<TSession> & {
|
|
51
51
|
readonly migrations?: undefined;
|
|
52
52
|
};
|
|
53
|
-
export declare function appHost<TRpcs extends Rpc.Any, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TMigrations, TSession extends SessionShape>(options: AppHostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TMigrations, TSession> | AppLocalHostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TMigrations>): ReturnType<typeof migratedHost<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TMigrations, TSession>>;
|
|
54
|
-
export declare function appHost<TRpcs extends Rpc.Any, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements>(options: HostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements> & {
|
|
53
|
+
export declare function appHost<TRpcs extends Rpc.Any, THandlers, THandlerError, THandlerRequirements, TServices = never, TServiceError = never, TServiceRequirements = never, TMigrations = never, TSession extends SessionShape = SessionShape>(options: AppHostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TMigrations, TSession> | AppLocalHostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TMigrations>): ReturnType<typeof migratedHost<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TMigrations, TSession>>;
|
|
54
|
+
export declare function appHost<TRpcs extends Rpc.Any, THandlers, THandlerError, THandlerRequirements, TServices = never, TServiceError = never, TServiceRequirements = never>(options: HostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements> & {
|
|
55
55
|
readonly policy: "loopback";
|
|
56
56
|
readonly sessions?: never;
|
|
57
57
|
readonly migrations?: undefined;
|
|
58
58
|
}): ReturnType<typeof appHostHttp<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, SessionShape>>;
|
|
59
|
-
export declare function appHost<TRpcs extends Rpc.Any, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TSession extends SessionShape>(options: AppHttpHostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TSession>): ReturnType<typeof appHostHttp<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TSession>>;
|
|
59
|
+
export declare function appHost<TRpcs extends Rpc.Any, THandlers, THandlerError, THandlerRequirements, TServices = never, TServiceError = never, TServiceRequirements = never, TSession extends SessionShape = SessionShape>(options: AppHttpHostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TSession>): ReturnType<typeof appHostHttp<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TSession>>;
|
|
60
60
|
export {};
|
|
61
61
|
//# sourceMappingURL=appHost.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appHost.d.ts","sourceRoot":"","sources":["../../src/server/appHost.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAE,KAAK,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAC5E,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0CAA0C,CAAC;AACvF,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAE7D,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;;;;;;;;;;;;;;;;;AAEhE,qBAAa,UAAW,SAAQ,eAAsG;CAAG;AACzI,MAAM,WAAW,kBAAkB,CAAC,CAAC,GAAG,KAAK;IAC5C,QAAQ,CAAC,MAAM,CAAC,EAAE;QACjB,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACjD,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;QAClC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;QACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,QAAQ,GAAG,QAAQ,CAAC,CAAC;KACrE,CAAC;IACF,QAAQ,CAAC,UAAU,EAAE;QACpB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACpC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC;KACtC,CAAC;CACF;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,0aAE5D;AAED,MAAM,
|
|
1
|
+
{"version":3,"file":"appHost.d.ts","sourceRoot":"","sources":["../../src/server/appHost.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appHost.js","sourceRoot":"","sources":["../../src/server/appHost.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAA6C,MAAM,QAAQ,CAAC;AAM5E,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,MAAM,OAAO,UAAW,SAAQ,OAAO,CAAC,OAAO,EAAqD,CAAC,gCAAgC,CAAC;CAAG;AAezI,MAAM,UAAU,WAAW,CAAI,OAA8B;IAC5D,OAAO,aAAa,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC3C,CAAC;
|
|
1
|
+
{"version":3,"file":"appHost.js","sourceRoot":"","sources":["../../src/server/appHost.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAA6C,MAAM,QAAQ,CAAC;AAM5E,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,MAAM,OAAO,UAAW,SAAQ,OAAO,CAAC,OAAO,EAAqD,CAAC,gCAAgC,CAAC;CAAG;AAezI,MAAM,UAAU,WAAW,CAAI,OAA8B;IAC5D,OAAO,aAAa,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC3C,CAAC;AA2FD,MAAM,UAAU,OAAO,CAWtB,OAGsI;IAEtI,OAAO,OAAO,CAAC,UAAU,KAAK,SAAS;QACtC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC;QACtB,CAAC,CAAC,YAAY,CAAC,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,EAAE,WAAW,CAAC,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;AAC9H,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appHostHttp.d.ts","sourceRoot":"","sources":["../../src/server/appHostHttp.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAY,MAAM,EAAU,KAAK,EAAkB,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAuB,UAAU,EAAuC,MAAM,aAAa,CAAC;AACnG,OAAO,EAAE,KAAK,GAAG,EAAE,gBAAgB,EAAa,MAAM,YAAY,CAAC;AAEnE,OAAO,EAA+C,KAAK,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAGxG,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAExE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAChE,wBAAgB,WAAW,CAC1B,KAAK,SAAS,GAAG,CAAC,GAAG,EACrB,SAAS,EACT,aAAa,EACb,oBAAoB,EACpB,SAAS,EACT,aAAa,EACb,oBAAoB,EACpB,QAAQ,SAAS,YAAY,EAE7B,OAAO,EAAE,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,oBAAoB,EAAE,SAAS,EAAE,aAAa,EAAE,oBAAoB,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,u3QA0DlJ;AAED,wBAAgB,YAAY,CAC3B,KAAK,SAAS,GAAG,CAAC,GAAG,EACrB,SAAS,EACT,aAAa,EACb,oBAAoB,EACpB,SAAS,EACT,aAAa,EACb,oBAAoB,EACpB,WAAW,EACX,QAAQ,SAAS,YAAY,EAE7B,OAAO,EACJ,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,oBAAoB,EAAE,SAAS,EAAE,aAAa,EAAE,oBAAoB,EAAE,WAAW,EAAE,QAAQ,CAAC,GAC5I,mBAAmB,CAAC,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,oBAAoB,EAAE,SAAS,EAAE,aAAa,EAAE,oBAAoB,EAAE,WAAW,CAAC,EAC1I,QAAQ,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,WAAW,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"appHostHttp.d.ts","sourceRoot":"","sources":["../../src/server/appHostHttp.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAY,MAAM,EAAU,KAAK,EAAkB,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAuB,UAAU,EAAuC,MAAM,aAAa,CAAC;AACnG,OAAO,EAAE,KAAK,GAAG,EAAE,gBAAgB,EAAa,MAAM,YAAY,CAAC;AAEnE,OAAO,EAA+C,KAAK,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAGxG,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAExE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAChE,wBAAgB,WAAW,CAC1B,KAAK,SAAS,GAAG,CAAC,GAAG,EACrB,SAAS,EACT,aAAa,EACb,oBAAoB,EACpB,SAAS,EACT,aAAa,EACb,oBAAoB,EACpB,QAAQ,SAAS,YAAY,EAE7B,OAAO,EAAE,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,oBAAoB,EAAE,SAAS,EAAE,aAAa,EAAE,oBAAoB,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,u3QA0DlJ;AAED,wBAAgB,YAAY,CAC3B,KAAK,SAAS,GAAG,CAAC,GAAG,EACrB,SAAS,EACT,aAAa,EACb,oBAAoB,EACpB,SAAS,EACT,aAAa,EACb,oBAAoB,EACpB,WAAW,EACX,QAAQ,SAAS,YAAY,EAE7B,OAAO,EACJ,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,oBAAoB,EAAE,SAAS,EAAE,aAAa,EAAE,oBAAoB,EAAE,WAAW,EAAE,QAAQ,CAAC,GAC5I,mBAAmB,CAAC,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,oBAAoB,EAAE,SAAS,EAAE,aAAa,EAAE,oBAAoB,EAAE,WAAW,CAAC,EAC1I,QAAQ,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,WAAW,CAAC,CAAC,26YAIvD"}
|
|
@@ -30,7 +30,7 @@ export function appHostHttp(options) {
|
|
|
30
30
|
const assets = options.assets === undefined
|
|
31
31
|
? Layer.empty
|
|
32
32
|
: HttpStaticServer.layer(typeof options.assets === "string" ? { root: options.assets } : options.assets);
|
|
33
|
-
const application = options.services;
|
|
33
|
+
const application = Layer.mergeAll(options.services ?? Layer.empty);
|
|
34
34
|
const server = Layer.unwrap(Effect.gen(function* () {
|
|
35
35
|
const port = options.config?.port ?? (yield* Config.Int("PORT").pipe(Config.withDefault(3000)));
|
|
36
36
|
const host = options.config?.host ?? (yield* Config.String("HOST").pipe(Config.withDefault("127.0.0.1")));
|
|
@@ -46,6 +46,7 @@ export function appHostHttp(options) {
|
|
|
46
46
|
}));
|
|
47
47
|
}
|
|
48
48
|
export function migratedHost(options, database) {
|
|
49
|
-
|
|
49
|
+
const services = Layer.mergeAll(options.services ?? Layer.empty);
|
|
50
|
+
return appHostHttp({ ...options, services: services.pipe(Layer.provideMerge(database)) });
|
|
50
51
|
}
|
|
51
52
|
//# sourceMappingURL=appHostHttp.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appHostHttp.js","sourceRoot":"","sources":["../../src/server/appHostHttp.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"appHostHttp.js","sourceRoot":"","sources":["../../src/server/appHostHttp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { HttpServerError, type HttpServerRequest, HttpServerRespondable, HttpServerResponse } from "effect/http";
|
|
3
|
+
declare const RequestBodyTooLarge_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
4
|
+
readonly _tag: "RequestBodyTooLarge";
|
|
5
|
+
} & Readonly<A>;
|
|
6
|
+
export declare class RequestBodyTooLarge extends RequestBodyTooLarge_base<{
|
|
7
|
+
readonly request: HttpServerRequest.HttpServerRequest;
|
|
8
|
+
readonly limit: bigint;
|
|
9
|
+
}> implements HttpServerRespondable.Respondable {
|
|
10
|
+
[HttpServerRespondable.symbol](): Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>;
|
|
11
|
+
}
|
|
12
|
+
export declare const readBoundedBody: (request: HttpServerRequest.HttpServerRequest) => Effect.Effect<ArrayBuffer, HttpServerError.HttpServerError | RequestBodyTooLarge, never>;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=boundedBody.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boundedBody.d.ts","sourceRoot":"","sources":["../../src/server/boundedBody.ts"],"names":[],"mappings":"AACA,OAAO,EAAQ,MAAM,EAAU,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAuB,eAAe,EAAE,KAAK,iBAAiB,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;;;;AAEtI,qBAAa,mBACZ,SAAQ,yBAAwC;IAC/C,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC,iBAAiB,CAAC;IACtD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB,CACD,YAAW,qBAAqB,CAAC,WAAW;IAE5C,CAAC,qBAAqB,CAAC,MAAM,CAAC,uEAE7B;CACD;AAED,eAAO,MAAM,eAAe,4IA4B1B,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { NodeHttpServerRequest, NodeStream } from "@effect/platform-node";
|
|
2
|
+
import { Data, Effect, Stream } from "effect";
|
|
3
|
+
import { HttpIncomingMessage, HttpServerError, HttpServerRespondable, HttpServerResponse } from "effect/http";
|
|
4
|
+
export class RequestBodyTooLarge extends Data.TaggedError("RequestBodyTooLarge") {
|
|
5
|
+
[HttpServerRespondable.symbol]() {
|
|
6
|
+
return Effect.succeed(HttpServerResponse.empty({ status: 413 }));
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export const readBoundedBody = Effect.fnUntraced(function* (request) {
|
|
10
|
+
const incoming = NodeHttpServerRequest.toIncomingMessage(request);
|
|
11
|
+
const limit = yield* HttpIncomingMessage.MaxBodySize;
|
|
12
|
+
const read = Effect.gen(function* () {
|
|
13
|
+
const length = request.headers["content-length"];
|
|
14
|
+
if (limit !== undefined && length !== undefined && BigInt(length) > limit) {
|
|
15
|
+
return yield* new RequestBodyTooLarge({ limit, request });
|
|
16
|
+
}
|
|
17
|
+
const chunks = [];
|
|
18
|
+
let size = 0n;
|
|
19
|
+
yield* NodeStream.fromReadable({
|
|
20
|
+
closeOnDone: false,
|
|
21
|
+
evaluate: () => incoming,
|
|
22
|
+
onError: (cause) => new HttpServerError.HttpServerError({ reason: new HttpServerError.RequestParseError({ cause, request }) }),
|
|
23
|
+
}).pipe(Stream.runForEach((chunk) => {
|
|
24
|
+
size += BigInt(chunk.byteLength);
|
|
25
|
+
if (limit !== undefined && size > limit) {
|
|
26
|
+
return Effect.fail(new RequestBodyTooLarge({ limit, request }));
|
|
27
|
+
}
|
|
28
|
+
chunks.push(chunk);
|
|
29
|
+
return Effect.void;
|
|
30
|
+
}));
|
|
31
|
+
return Uint8Array.from(Buffer.concat(chunks)).buffer;
|
|
32
|
+
});
|
|
33
|
+
// Drain unread bytes without destroying the socket so the server can send a response.
|
|
34
|
+
return yield* read.pipe(Effect.onError(() => Effect.sync(() => incoming.resume())));
|
|
35
|
+
});
|
|
36
|
+
//# sourceMappingURL=boundedBody.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boundedBody.js","sourceRoot":"","sources":["../../src/server/boundedBody.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAA0B,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtI,MAAM,OAAO,mBACZ,SAAQ,IAAI,CAAC,WAAW,CAAC,qBAAqB,CAG5C;IAGF,CAAC,qBAAqB,CAAC,MAAM,CAAC;QAC7B,OAAO,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;IAClE,CAAC;CACD;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,OAA4C;IACvG,MAAM,QAAQ,GAAG,qBAAqB,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAClE,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,mBAAmB,CAAC,WAAW,CAAC;IACrD,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAChC,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QACjD,IAAI,KAAK,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,KAAK,EAAE,CAAC;YAC3E,OAAO,KAAK,CAAC,CAAC,IAAI,mBAAmB,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,MAAM,MAAM,GAAiB,EAAE,CAAC;QAChC,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,KAAK,CAAC,CAAC,UAAU,CAAC,YAAY,CAA8C;YAC3E,WAAW,EAAE,KAAK;YAClB,QAAQ,EAAE,GAAG,EAAE,CAAC,QAAQ;YACxB,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,eAAe,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,IAAI,eAAe,CAAC,iBAAiB,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;SAC9H,CAAC,CAAC,IAAI,CACN,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,EAAE,EAAE;YAC3B,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YACjC,IAAI,KAAK,KAAK,SAAS,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;gBACzC,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,mBAAmB,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;YACjE,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnB,OAAO,MAAM,CAAC,IAAI,CAAC;QACpB,CAAC,CAAC,CACF,CAAC;QACF,OAAO,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IACtD,CAAC,CAAC,CAAC;IACH,sFAAsF;IACtF,OAAO,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;AACrF,CAAC,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"boundedRpcRequest.d.ts","sourceRoot":"","sources":["../../src/server/boundedRpcRequest.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"boundedRpcRequest.d.ts","sourceRoot":"","sources":["../../src/server/boundedRpcRequest.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGtE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,CAAC,iBAAiB,GAAG,iBAAiB,CAAC,iBAAiB,CAuBnH"}
|
|
@@ -1,30 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { HttpServerError } from "effect/http";
|
|
3
|
+
import { readBoundedBody } from "./boundedBody.js";
|
|
4
4
|
export function boundedRpcRequest(request) {
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const text = Effect.gen(function* () {
|
|
10
|
-
const limit = yield* HttpIncomingMessage.MaxBodySize;
|
|
11
|
-
const chunks = [];
|
|
12
|
-
let size = 0n;
|
|
13
|
-
yield* NodeStream.fromReadable({
|
|
14
|
-
closeOnDone: false,
|
|
15
|
-
evaluate: () => incoming,
|
|
16
|
-
onError: error,
|
|
17
|
-
}).pipe(Stream.runForEach((chunk) => {
|
|
18
|
-
size += BigInt(chunk.byteLength);
|
|
19
|
-
if (limit !== undefined && size > limit) {
|
|
20
|
-
return Effect.fail(error(new Error("RPC body exceeds MAX_BODY_SIZE")));
|
|
21
|
-
}
|
|
22
|
-
chunks.push(chunk);
|
|
23
|
-
return Effect.void;
|
|
24
|
-
}));
|
|
25
|
-
return Buffer.concat(chunks).toString("utf8");
|
|
26
|
-
}).pipe(Effect.onError(() => Effect.sync(() => incoming.resume())), Effect.cached, Effect.runSync);
|
|
27
|
-
const arrayBuffer = Effect.map(text, (body) => new TextEncoder().encode(body).buffer);
|
|
5
|
+
const arrayBuffer = readBoundedBody(request).pipe(Effect.mapError((cause) => cause._tag === "RequestBodyTooLarge"
|
|
6
|
+
? new HttpServerError.HttpServerError({ reason: new HttpServerError.RequestParseError({ cause, request }) })
|
|
7
|
+
: cause), Effect.cached, Effect.runSync);
|
|
8
|
+
const text = Effect.map(arrayBuffer, (body) => new TextDecoder().decode(body));
|
|
28
9
|
// The native text reader destroys the HTTP socket on overflow, preventing a 4xx response.
|
|
29
10
|
return new Proxy(request, {
|
|
30
11
|
get: (target, key, receiver) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"boundedRpcRequest.js","sourceRoot":"","sources":["../../src/server/boundedRpcRequest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"boundedRpcRequest.js","sourceRoot":"","sources":["../../src/server/boundedRpcRequest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,eAAe,EAA0B,MAAM,aAAa,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,UAAU,iBAAiB,CAAC,OAA4C;IAC7E,MAAM,WAAW,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC,IAAI,CAChD,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CACzB,KAAK,CAAC,IAAI,KAAK,qBAAqB;QACnC,CAAC,CAAC,IAAI,eAAe,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,IAAI,eAAe,CAAC,iBAAiB,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QAC5G,CAAC,CAAC,KAAK,CACR,EACD,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,OAAO,CACd,CAAC;IACF,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/E,0FAA0F;IAC1F,OAAO,IAAI,KAAK,CAAC,OAAO,EAAE;QACzB,GAAG,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE;YAC9B,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;gBACpB,OAAO,IAAI,CAAC;YACb,CAAC;YACD,IAAI,GAAG,KAAK,aAAa,EAAE,CAAC;gBAC3B,OAAO,WAAW,CAAC;YACpB,CAAC;YACD,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC3C,CAAC;KACD,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -3,7 +3,7 @@ import type { HttpRouter, HttpStaticServer } from "effect/http";
|
|
|
3
3
|
import type { Rpc, RpcGroup } from "effect/rpc";
|
|
4
4
|
import type { SessionPolicy, SessionShape } from "#rpc-server/session.ts";
|
|
5
5
|
import type { AppDatabaseOptions } from "./appHost.ts";
|
|
6
|
-
export
|
|
6
|
+
export type HostOptions<TRpcs extends Rpc.Any, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements> = {
|
|
7
7
|
readonly assets?: string | Parameters<typeof HttpStaticServer.layer>[0];
|
|
8
8
|
readonly config?: AppDatabaseOptions["config"] & {
|
|
9
9
|
readonly port?: number;
|
|
@@ -16,8 +16,10 @@ export interface HostOptions<TRpcs extends Rpc.Any, THandlers, THandlerError, TH
|
|
|
16
16
|
readonly handlers: Layer.Layer<THandlers, THandlerError, THandlerRequirements>;
|
|
17
17
|
readonly routes?: Layer.Layer<never, never, HttpRouter.HttpRouter | HttpRouter.Request<"Error", unknown>>;
|
|
18
18
|
readonly rpcPath?: HttpRouter.PathInput;
|
|
19
|
+
readonly services?: Layer.Layer<TServices, TServiceError, TServiceRequirements>;
|
|
20
|
+
} & ([TServices | TServiceError | TServiceRequirements] extends [never] ? unknown : {
|
|
19
21
|
readonly services: Layer.Layer<TServices, TServiceError, TServiceRequirements>;
|
|
20
|
-
}
|
|
22
|
+
});
|
|
21
23
|
export type HostPolicy<TSession extends SessionShape> = {
|
|
22
24
|
readonly policy?: undefined;
|
|
23
25
|
readonly sessions: SessionPolicy<TSession>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hostOptions.d.ts","sourceRoot":"","sources":["../../src/server/hostOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,MAAM,
|
|
1
|
+
{"version":3,"file":"hostOptions.d.ts","sourceRoot":"","sources":["../../src/server/hostOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,MAAM,MAAM,WAAW,CAAC,KAAK,SAAS,GAAG,CAAC,GAAG,EAAE,SAAS,EAAE,aAAa,EAAE,oBAAoB,EAAE,SAAS,EAAE,aAAa,EAAE,oBAAoB,IAAI;IAChJ,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,QAAQ,CAAC,MAAM,CAAC,EAAE,kBAAkB,CAAC,QAAQ,CAAC,GAAG;QAChD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAC9B,QAAQ,CAAC,yBAAyB,CAAC,EAAE,OAAO,CAAC;KAC7C,CAAC;IACF,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACzC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,aAAa,EAAE,oBAAoB,CAAC,CAAC;IAC/E,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1G,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,aAAa,EAAE,oBAAoB,CAAC,CAAC;CAChF,GAAG,CAAC,CAAC,SAAS,GAAG,aAAa,GAAG,oBAAoB,CAAC,SAAS,CAAC,KAAK,CAAC,GACpE,OAAO,GACP;IAAE,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,aAAa,EAAE,oBAAoB,CAAC,CAAA;CAAE,CAAC,CAAC;AAEvF,MAAM,MAAM,UAAU,CAAC,QAAQ,SAAS,YAAY,IACjD;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAA;CAAE,GAC3E;IAAE,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shivaedev/platform",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Opinionated Effect application infrastructure for ShivaeDev services",
|
|
5
5
|
"homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/platform#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -168,6 +168,12 @@
|
|
|
168
168
|
}
|
|
169
169
|
},
|
|
170
170
|
"exports": {
|
|
171
|
+
"./boundedBody.ts": {
|
|
172
|
+
"types": "./dist/server/boundedBody.d.ts",
|
|
173
|
+
"source": "./src/server/boundedBody.ts",
|
|
174
|
+
"import": "./dist/server/boundedBody.js",
|
|
175
|
+
"default": "./dist/server/boundedBody.js"
|
|
176
|
+
},
|
|
171
177
|
"./appHost.ts": {
|
|
172
178
|
"types": "./dist/server/appHost.d.ts",
|
|
173
179
|
"source": "./src/server/appHost.ts",
|
|
@@ -341,7 +347,7 @@
|
|
|
341
347
|
"@prisma-next/target-postgres": "0.16.0",
|
|
342
348
|
"@shivaedev/effect-prisma": "0.9.0",
|
|
343
349
|
"@shivaedev/effect-trpc": "0.7.0",
|
|
344
|
-
"@shivaedev/quality": "0.
|
|
350
|
+
"@shivaedev/quality": "0.21.1",
|
|
345
351
|
"@trpc/client": "11.19.0",
|
|
346
352
|
"@trpc/server": "11.19.0",
|
|
347
353
|
"@types/node": "24.19.1",
|
package/src/server/appHost.ts
CHANGED
|
@@ -27,31 +27,31 @@ export function appDatabase<R>(options: AppDatabaseOptions<R>) {
|
|
|
27
27
|
return databaseLayer(options, AppChanges);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
export
|
|
30
|
+
export type AppHostOptions<
|
|
31
31
|
TRpcs extends Rpc.Any,
|
|
32
32
|
THandlers,
|
|
33
33
|
THandlerError,
|
|
34
34
|
THandlerRequirements,
|
|
35
|
-
TServices,
|
|
36
|
-
TServiceError,
|
|
37
|
-
TServiceRequirements,
|
|
38
|
-
TMigrations,
|
|
39
|
-
TSession extends SessionShape,
|
|
40
|
-
>
|
|
35
|
+
TServices = never,
|
|
36
|
+
TServiceError = never,
|
|
37
|
+
TServiceRequirements = never,
|
|
38
|
+
TMigrations = never,
|
|
39
|
+
TSession extends SessionShape = SessionShape,
|
|
40
|
+
> = HostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements> & {
|
|
41
41
|
readonly migrations: AppDatabaseOptions<TMigrations>["migrations"];
|
|
42
42
|
readonly policy?: undefined;
|
|
43
43
|
readonly sessions: SessionPolicy<TSession>;
|
|
44
|
-
}
|
|
44
|
+
};
|
|
45
45
|
|
|
46
46
|
export type AppLocalHostOptions<
|
|
47
47
|
TRpcs extends Rpc.Any,
|
|
48
48
|
THandlers,
|
|
49
49
|
THandlerError,
|
|
50
50
|
THandlerRequirements,
|
|
51
|
-
TServices,
|
|
52
|
-
TServiceError,
|
|
53
|
-
TServiceRequirements,
|
|
54
|
-
TMigrations,
|
|
51
|
+
TServices = never,
|
|
52
|
+
TServiceError = never,
|
|
53
|
+
TServiceRequirements = never,
|
|
54
|
+
TMigrations = never,
|
|
55
55
|
> = HostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements> & {
|
|
56
56
|
readonly migrations: AppDatabaseOptions<TMigrations>["migrations"];
|
|
57
57
|
readonly policy: "loopback";
|
|
@@ -63,10 +63,10 @@ export type AppHttpHostOptions<
|
|
|
63
63
|
THandlers,
|
|
64
64
|
THandlerError,
|
|
65
65
|
THandlerRequirements,
|
|
66
|
-
TServices,
|
|
67
|
-
TServiceError,
|
|
68
|
-
TServiceRequirements,
|
|
69
|
-
TSession extends SessionShape,
|
|
66
|
+
TServices = never,
|
|
67
|
+
TServiceError = never,
|
|
68
|
+
TServiceRequirements = never,
|
|
69
|
+
TSession extends SessionShape = SessionShape,
|
|
70
70
|
> = HostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements> &
|
|
71
71
|
HostPolicy<TSession> & { readonly migrations?: undefined };
|
|
72
72
|
|
|
@@ -75,11 +75,11 @@ export function appHost<
|
|
|
75
75
|
THandlers,
|
|
76
76
|
THandlerError,
|
|
77
77
|
THandlerRequirements,
|
|
78
|
-
TServices,
|
|
79
|
-
TServiceError,
|
|
80
|
-
TServiceRequirements,
|
|
81
|
-
TMigrations,
|
|
82
|
-
TSession extends SessionShape,
|
|
78
|
+
TServices = never,
|
|
79
|
+
TServiceError = never,
|
|
80
|
+
TServiceRequirements = never,
|
|
81
|
+
TMigrations = never,
|
|
82
|
+
TSession extends SessionShape = SessionShape,
|
|
83
83
|
>(
|
|
84
84
|
options:
|
|
85
85
|
| AppHostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TMigrations, TSession>
|
|
@@ -87,7 +87,15 @@ export function appHost<
|
|
|
87
87
|
): ReturnType<
|
|
88
88
|
typeof migratedHost<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TMigrations, TSession>
|
|
89
89
|
>;
|
|
90
|
-
export function appHost<
|
|
90
|
+
export function appHost<
|
|
91
|
+
TRpcs extends Rpc.Any,
|
|
92
|
+
THandlers,
|
|
93
|
+
THandlerError,
|
|
94
|
+
THandlerRequirements,
|
|
95
|
+
TServices = never,
|
|
96
|
+
TServiceError = never,
|
|
97
|
+
TServiceRequirements = never,
|
|
98
|
+
>(
|
|
91
99
|
options: HostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements> & {
|
|
92
100
|
readonly policy: "loopback";
|
|
93
101
|
readonly sessions?: never;
|
|
@@ -101,10 +109,10 @@ export function appHost<
|
|
|
101
109
|
THandlers,
|
|
102
110
|
THandlerError,
|
|
103
111
|
THandlerRequirements,
|
|
104
|
-
TServices,
|
|
105
|
-
TServiceError,
|
|
106
|
-
TServiceRequirements,
|
|
107
|
-
TSession extends SessionShape,
|
|
112
|
+
TServices = never,
|
|
113
|
+
TServiceError = never,
|
|
114
|
+
TServiceRequirements = never,
|
|
115
|
+
TSession extends SessionShape = SessionShape,
|
|
108
116
|
>(
|
|
109
117
|
options: AppHttpHostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TSession>,
|
|
110
118
|
): ReturnType<typeof appHostHttp<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TSession>>;
|
|
@@ -113,11 +121,11 @@ export function appHost<
|
|
|
113
121
|
THandlers,
|
|
114
122
|
THandlerError,
|
|
115
123
|
THandlerRequirements,
|
|
116
|
-
TServices,
|
|
117
|
-
TServiceError,
|
|
118
|
-
TServiceRequirements,
|
|
119
|
-
TMigrations,
|
|
120
|
-
TSession extends SessionShape,
|
|
124
|
+
TServices = never,
|
|
125
|
+
TServiceError = never,
|
|
126
|
+
TServiceRequirements = never,
|
|
127
|
+
TMigrations = never,
|
|
128
|
+
TSession extends SessionShape = SessionShape,
|
|
121
129
|
>(
|
|
122
130
|
options:
|
|
123
131
|
| AppHostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TMigrations, TSession>
|
|
@@ -58,7 +58,7 @@ export function appHostHttp<
|
|
|
58
58
|
options.assets === undefined
|
|
59
59
|
? Layer.empty
|
|
60
60
|
: HttpStaticServer.layer(typeof options.assets === "string" ? { root: options.assets } : options.assets);
|
|
61
|
-
const application = options.services;
|
|
61
|
+
const application = Layer.mergeAll(options.services ?? Layer.empty);
|
|
62
62
|
const server = Layer.unwrap(
|
|
63
63
|
Effect.gen(function* () {
|
|
64
64
|
const port = options.config?.port ?? (yield* Config.Int("PORT").pipe(Config.withDefault(3000)));
|
|
@@ -97,5 +97,6 @@ export function migratedHost<
|
|
|
97
97
|
| AppLocalHostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TMigrations>,
|
|
98
98
|
database: ReturnType<typeof databaseLayer<TMigrations>>,
|
|
99
99
|
) {
|
|
100
|
-
|
|
100
|
+
const services = Layer.mergeAll(options.services ?? Layer.empty);
|
|
101
|
+
return appHostHttp({ ...options, services: services.pipe(Layer.provideMerge(database)) });
|
|
101
102
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { NodeHttpServerRequest, NodeStream } from "@effect/platform-node";
|
|
2
|
+
import { Data, Effect, Stream } from "effect";
|
|
3
|
+
import { HttpIncomingMessage, HttpServerError, type HttpServerRequest, HttpServerRespondable, HttpServerResponse } from "effect/http";
|
|
4
|
+
|
|
5
|
+
export class RequestBodyTooLarge
|
|
6
|
+
extends Data.TaggedError("RequestBodyTooLarge")<{
|
|
7
|
+
readonly request: HttpServerRequest.HttpServerRequest;
|
|
8
|
+
readonly limit: bigint;
|
|
9
|
+
}>
|
|
10
|
+
implements HttpServerRespondable.Respondable
|
|
11
|
+
{
|
|
12
|
+
[HttpServerRespondable.symbol]() {
|
|
13
|
+
return Effect.succeed(HttpServerResponse.empty({ status: 413 }));
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const readBoundedBody = Effect.fnUntraced(function* (request: HttpServerRequest.HttpServerRequest) {
|
|
18
|
+
const incoming = NodeHttpServerRequest.toIncomingMessage(request);
|
|
19
|
+
const limit = yield* HttpIncomingMessage.MaxBodySize;
|
|
20
|
+
const read = Effect.gen(function* () {
|
|
21
|
+
const length = request.headers["content-length"];
|
|
22
|
+
if (limit !== undefined && length !== undefined && BigInt(length) > limit) {
|
|
23
|
+
return yield* new RequestBodyTooLarge({ limit, request });
|
|
24
|
+
}
|
|
25
|
+
const chunks: Uint8Array[] = [];
|
|
26
|
+
let size = 0n;
|
|
27
|
+
yield* NodeStream.fromReadable<Uint8Array, HttpServerError.HttpServerError>({
|
|
28
|
+
closeOnDone: false,
|
|
29
|
+
evaluate: () => incoming,
|
|
30
|
+
onError: (cause) => new HttpServerError.HttpServerError({ reason: new HttpServerError.RequestParseError({ cause, request }) }),
|
|
31
|
+
}).pipe(
|
|
32
|
+
Stream.runForEach((chunk) => {
|
|
33
|
+
size += BigInt(chunk.byteLength);
|
|
34
|
+
if (limit !== undefined && size > limit) {
|
|
35
|
+
return Effect.fail(new RequestBodyTooLarge({ limit, request }));
|
|
36
|
+
}
|
|
37
|
+
chunks.push(chunk);
|
|
38
|
+
return Effect.void;
|
|
39
|
+
}),
|
|
40
|
+
);
|
|
41
|
+
return Uint8Array.from(Buffer.concat(chunks)).buffer;
|
|
42
|
+
});
|
|
43
|
+
// Drain unread bytes without destroying the socket so the server can send a response.
|
|
44
|
+
return yield* read.pipe(Effect.onError(() => Effect.sync(() => incoming.resume())));
|
|
45
|
+
});
|
|
@@ -1,37 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { HttpServerError, type HttpServerRequest } from "effect/http";
|
|
3
|
+
import { readBoundedBody } from "./boundedBody.ts";
|
|
4
4
|
|
|
5
5
|
export function boundedRpcRequest(request: HttpServerRequest.HttpServerRequest): HttpServerRequest.HttpServerRequest {
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const chunks: Uint8Array[] = [];
|
|
13
|
-
let size = 0n;
|
|
14
|
-
yield* NodeStream.fromReadable<Uint8Array, HttpServerError.HttpServerError>({
|
|
15
|
-
closeOnDone: false,
|
|
16
|
-
evaluate: () => incoming,
|
|
17
|
-
onError: error,
|
|
18
|
-
}).pipe(
|
|
19
|
-
Stream.runForEach((chunk) => {
|
|
20
|
-
size += BigInt(chunk.byteLength);
|
|
21
|
-
if (limit !== undefined && size > limit) {
|
|
22
|
-
return Effect.fail(error(new Error("RPC body exceeds MAX_BODY_SIZE")));
|
|
23
|
-
}
|
|
24
|
-
chunks.push(chunk);
|
|
25
|
-
return Effect.void;
|
|
26
|
-
}),
|
|
27
|
-
);
|
|
28
|
-
return Buffer.concat(chunks).toString("utf8");
|
|
29
|
-
}).pipe(
|
|
30
|
-
Effect.onError(() => Effect.sync(() => incoming.resume())),
|
|
6
|
+
const arrayBuffer = readBoundedBody(request).pipe(
|
|
7
|
+
Effect.mapError((cause) =>
|
|
8
|
+
cause._tag === "RequestBodyTooLarge"
|
|
9
|
+
? new HttpServerError.HttpServerError({ reason: new HttpServerError.RequestParseError({ cause, request }) })
|
|
10
|
+
: cause,
|
|
11
|
+
),
|
|
31
12
|
Effect.cached,
|
|
32
13
|
Effect.runSync,
|
|
33
14
|
);
|
|
34
|
-
const
|
|
15
|
+
const text = Effect.map(arrayBuffer, (body) => new TextDecoder().decode(body));
|
|
35
16
|
// The native text reader destroys the HTTP socket on overflow, preventing a 4xx response.
|
|
36
17
|
return new Proxy(request, {
|
|
37
18
|
get: (target, key, receiver) => {
|
|
@@ -4,7 +4,7 @@ import type { Rpc, RpcGroup } from "effect/rpc";
|
|
|
4
4
|
import type { SessionPolicy, SessionShape } from "#rpc-server/session.ts";
|
|
5
5
|
import type { AppDatabaseOptions } from "./appHost.ts";
|
|
6
6
|
|
|
7
|
-
export
|
|
7
|
+
export type HostOptions<TRpcs extends Rpc.Any, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements> = {
|
|
8
8
|
readonly assets?: string | Parameters<typeof HttpStaticServer.layer>[0];
|
|
9
9
|
readonly config?: AppDatabaseOptions["config"] & {
|
|
10
10
|
readonly port?: number;
|
|
@@ -17,8 +17,10 @@ export interface HostOptions<TRpcs extends Rpc.Any, THandlers, THandlerError, TH
|
|
|
17
17
|
readonly handlers: Layer.Layer<THandlers, THandlerError, THandlerRequirements>;
|
|
18
18
|
readonly routes?: Layer.Layer<never, never, HttpRouter.HttpRouter | HttpRouter.Request<"Error", unknown>>;
|
|
19
19
|
readonly rpcPath?: HttpRouter.PathInput;
|
|
20
|
-
readonly services
|
|
21
|
-
}
|
|
20
|
+
readonly services?: Layer.Layer<TServices, TServiceError, TServiceRequirements>;
|
|
21
|
+
} & ([TServices | TServiceError | TServiceRequirements] extends [never]
|
|
22
|
+
? unknown
|
|
23
|
+
: { readonly services: Layer.Layer<TServices, TServiceError, TServiceRequirements> });
|
|
22
24
|
|
|
23
25
|
export type HostPolicy<TSession extends SessionShape> =
|
|
24
26
|
| { readonly policy?: undefined; readonly sessions: SessionPolicy<TSession> }
|