@shivaedev/server 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -14
- package/dist/TrustedProxyRequired.d.ts +8 -0
- package/dist/TrustedProxyRequired.d.ts.map +1 -0
- package/dist/TrustedProxyRequired.js +5 -0
- package/dist/TrustedProxyRequired.js.map +1 -0
- package/dist/appHostHttp.d.ts +3 -3
- package/dist/hostChanges.d.ts +2 -1
- package/dist/hostChanges.d.ts.map +1 -1
- package/dist/hostChanges.js +39 -5
- package/dist/hostChanges.js.map +1 -1
- package/dist/hostRpc.d.ts +1 -1
- package/dist/hostRpc.d.ts.map +1 -1
- package/dist/hostRpc.js +4 -2
- package/dist/hostRpc.js.map +1 -1
- package/dist/hostRpcRoutes.d.ts.map +1 -1
- package/dist/hostRpcRoutes.js +7 -6
- package/dist/hostRpcRoutes.js.map +1 -1
- package/dist/hostTransport.d.ts +2 -1
- package/dist/hostTransport.d.ts.map +1 -1
- package/dist/hostTransport.js +2 -1
- package/dist/hostTransport.js.map +1 -1
- package/dist/node-http/subscriptionSignal.js +1 -1
- package/dist/node-http/subscriptionSignal.js.map +1 -1
- package/dist/socketRpc.d.ts +6 -0
- package/dist/socketRpc.d.ts.map +1 -0
- package/dist/socketRpc.js +27 -0
- package/dist/socketRpc.js.map +1 -0
- package/package.json +20 -20
- package/src/TrustedProxyRequired.ts +6 -0
- package/src/hostChanges.ts +56 -9
- package/src/hostRpc.ts +4 -2
- package/src/hostRpcRoutes.ts +22 -7
- package/src/hostTransport.ts +2 -3
- package/src/node-http/subscriptionSignal.ts +1 -1
- package/src/socketRpc.ts +36 -0
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ The optional modules solve different boundaries. `runtime/` carries Effect servi
|
|
|
35
35
|
|
|
36
36
|
### Host the application
|
|
37
37
|
|
|
38
|
-
`appHost` is the server entry point for an Effect app. For a PostgreSQL app, supply the native RPC group, handler and service Layers, migration loader, session policy and static asset directory. The host completes migrations before acquiring application services, starts Node HTTP with
|
|
38
|
+
`appHost` is the server entry point for an Effect app. For a PostgreSQL app, supply the native RPC group, handler and service Layers, migration loader, session policy and static asset directory. The host completes migrations before acquiring application services, starts Node HTTP with native Effect RPC over a WebSocket upgrade at `/rpc`, and provides SQL, Reactivity and `AppChanges` to those Layers.
|
|
39
39
|
|
|
40
40
|
```ts
|
|
41
41
|
import { NodeRuntime } from "@effect/platform-node";
|
|
@@ -89,7 +89,7 @@ RPC request bodies are limited to 1 MiB by default. Set `MAX_BODY_SIZE` to a byt
|
|
|
89
89
|
|
|
90
90
|
For a native Node HTTP route outside RPC, read the request body with `readBoundedBody` from [`@shivaedev/http`](https://github.com/ShivaeDev/platform/tree/main/packages/http#readme), which rejects oversized bodies with a typed 413 failure.
|
|
91
91
|
|
|
92
|
-
RPC uses `localRpcGuard` from `@shivaedev/identity/server/local.ts`: it requires a loopback peer and Host, same-origin or absent Origin, and the NDJSON content type. Session lookup uses transport headers. The host applies `RequestTracing` last to every served RPC, including loopback RPC, so it wraps authentication and handlers without a per-contract tracing declaration. Existing tracing declarations run once at that outermost boundary. With a session policy, contracts declare `Authenticated` and `MaybeAuthenticated` as needed; the host supplies their implementations. For `AuthenticatedUser`, merge `authenticatedUserLayer(sessions)` from `@shivaedev/identity/server/session.ts` beside the handlers. Declared errors retain their Schema types. Applications still own resource authorization and use `rpcBoundary` when they need to map unexpected failures.
|
|
92
|
+
Ordinary POST RPC uses `localRpcGuard` from `@shivaedev/identity/server/local.ts`: it requires a loopback peer and Host, same-origin or absent Origin, and the NDJSON content type. The WebSocket upgrade uses the same loopback, origin and trusted-proxy checks without an HTTP body content type. Session lookup uses the upgrade's transport headers; RPC message headers cannot replace its cookie or Origin. Session middleware revalidates authenticated streams at `streamRevalidationInterval` and ends revoked subscriptions with typed `Unauthorized`. The shared socket remains open; later protected calls use the same session checks. Automatic live subscriptions also recheck the session and query policy before each hint. POST RPC has no `$changes` stream. The host applies `RequestTracing` last to every served RPC, including loopback RPC, so it wraps authentication and handlers without a per-contract tracing declaration. Existing tracing declarations run once at that outermost boundary. With a session policy, contracts declare `Authenticated` and `MaybeAuthenticated` as needed; the host supplies their implementations. For `AuthenticatedUser`, merge `authenticatedUserLayer(sessions)` from `@shivaedev/identity/server/session.ts` beside the handlers. Declared errors retain their Schema types. Applications still own resource authorization and use `rpcBoundary` when they need to map unexpected failures.
|
|
93
93
|
|
|
94
94
|
Every response from the host's native RPC endpoint carries `Cache-Control: private, no-store`, including mixed public and authenticated batches, typed failures, transport rejections, body-read failures and streams. The host sets this at the outer response boundary without inspecting batch contents.
|
|
95
95
|
|
|
@@ -153,7 +153,7 @@ Both JSON and protobuf export serialization apply sensitive-key and credential-t
|
|
|
153
153
|
|
|
154
154
|
Set `config.maxConnections` on `appDatabase` or a migrated `appHost` to bound the PostgreSQL pool. The change listener reserves one connection; when transactions hold the remaining connections, another query waits until a transaction releases its connection. A daemon with long-lived RPC streams can use `config.disablePreemptiveShutdown: true` so scope closure interrupts an active HTTP RPC stream and runs its finalizer before host disposal completes, without waiting for Node's preemptive graceful-shutdown timeout. Omitting it keeps Node's native graceful-shutdown default.
|
|
155
155
|
|
|
156
|
-
For a deployment behind a proxy, set `HOST=0.0.0.0` and `TRUSTED_PROXY` to a comma-separated list of proxy peer IPs or CIDR ranges (IPv4 or IPv6), or use `config.host` and `config.trustedProxy`. Only matching peers may supply `X-Forwarded-Host` and `X-Forwarded-Proto` (`http` or `https`) for the guard's same-origin check; the proxy must overwrite both headers and preserve Origin. A single authority is required, and comma-separated forwarding chains are refused. IPv4-mapped IPv6 peers match their IPv4 address. Malformed addresses or ranges fail `appHost` startup with a typed `ConfigError`. NDJSON and the application's session Origin policy still apply. See the [deployment quickstart](https://github.com/ShivaeDev/platform/blob/main/docs/framework/quickstart.md#deploy).
|
|
156
|
+
For a deployment behind a proxy, set `HOST=0.0.0.0` and `TRUSTED_PROXY` to a comma-separated list of proxy peer IPs or CIDR ranges (IPv4 or IPv6), or use `config.host` and `config.trustedProxy`. Only matching peers may supply `X-Forwarded-Host` and `X-Forwarded-Proto` (`http` or `https`) for the guard's same-origin check; the proxy must overwrite both headers and preserve Origin. A single authority is required, and comma-separated forwarding chains are refused. IPv4-mapped IPv6 peers match their IPv4 address. A session host bound to a non-loopback address refuses to boot without `TRUSTED_PROXY` or `config.trustedProxy`, with a typed `TrustedProxyRequired` error from `@shivaedev/server/TrustedProxyRequired.ts` naming both settings and how to supply proxy peer addresses or CIDR ranges. Local loopback hosts keep working without proxy trust; `policy: "loopback"` does not use proxy trust. Malformed addresses or ranges fail `appHost` startup with a typed `ConfigError`. The starter's Railway deployment works out of the box: its web configuration supplies `TRUSTED_PROXY=100.64.0.0/10` and `AUTH_CLIENT_IP_HEADER=x-real-ip`, so matching peers supply forwarded client IPs for auth and the HTTPS authority for RPC origin checks. Set `PUBLIC_URL` to the app's HTTPS URL for secure session cookies. NDJSON and the application's session Origin policy still apply. See the [deployment quickstart](https://github.com/ShivaeDev/platform/blob/main/docs/framework/quickstart.md#deploy).
|
|
157
157
|
|
|
158
158
|
Build application handlers with `Api.application.toLayer(...)` (and `Api.application.of(...)` for an Effect-built handler map), then pass the full `Api` to the host and client. `Api.application` is the native RPC group without the reserved `$changes` stream; the host supplies that handler. The explicit `Api.omit("<namespace>.$changes")` form also works.
|
|
159
159
|
|
|
@@ -188,22 +188,16 @@ export const Server = appHost({
|
|
|
188
188
|
|
|
189
189
|
`publicConfigLayer` reads `Config.schema` from the host's native ConfigProvider when the handler Layer builds. Missing or invalid required fields fail startup with a typed `ConfigError`. An optional second argument supplies a native Config path: `publicConfigLayer(PublicConfig, "PUBLIC")` reads `PUBLIC_INSTALL_URL` with the default environment provider. Only schema-declared fields are loaded and serialized; other server values never enter this response. The generated query has no session middleware, so it can supply public configuration before sign-in. Merge it alongside authenticated contracts rather than applying authentication to the merged group.
|
|
190
190
|
|
|
191
|
-
On the client, use the same
|
|
191
|
+
On the client, use the same contract `makeClient` and shared WebSocket as other queries:
|
|
192
192
|
|
|
193
193
|
```ts
|
|
194
|
-
import {
|
|
195
|
-
import { FetchHttpClient } from "effect/http";
|
|
196
|
-
import * as AtomRpc from "effect/reactivity/AtomRpc";
|
|
197
|
-
import { RpcClient, RpcSerialization } from "effect/rpc";
|
|
198
|
-
import { bind } from "@shivaedev/contract/bind.ts";
|
|
194
|
+
import { makeClient } from "@shivaedev/contract/bind.ts";
|
|
199
195
|
import { useQuery } from "@shivaedev/react/client/resultState.ts";
|
|
200
196
|
import { PublicConfig } from "./public-config.ts";
|
|
201
197
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}) {}
|
|
206
|
-
const config = bind(PublicConfig, ConfigClient);
|
|
198
|
+
const { api: config } = makeClient(PublicConfig, {
|
|
199
|
+
url: new URL("/rpc", window.location.href).href,
|
|
200
|
+
});
|
|
207
201
|
|
|
208
202
|
export function usePublicConfig() {
|
|
209
203
|
return useQuery(config.get.query());
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
declare const TrustedProxyRequired_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 & {
|
|
2
|
+
readonly _tag: "TrustedProxyRequired";
|
|
3
|
+
} & Readonly<A>;
|
|
4
|
+
export declare class TrustedProxyRequired extends TrustedProxyRequired_base {
|
|
5
|
+
readonly message = "A non-loopback session host requires proxy trust. Set TRUSTED_PROXY to your proxy's peer IP addresses or CIDR ranges, or set config.trustedProxy. The starter's Railway configuration supplies TRUSTED_PROXY automatically.";
|
|
6
|
+
}
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=TrustedProxyRequired.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TrustedProxyRequired.d.ts","sourceRoot":"","sources":["../src/TrustedProxyRequired.ts"],"names":[],"mappings":";;;AAEA,qBAAa,oBAAqB,SAAQ,yBAAwC;IACjF,SAAkB,OAAO,iOACsM;CAC/N"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Data } from "effect";
|
|
2
|
+
export class TrustedProxyRequired extends Data.TaggedError("TrustedProxyRequired") {
|
|
3
|
+
message = "A non-loopback session host requires proxy trust. Set TRUSTED_PROXY to your proxy's peer IP addresses or CIDR ranges, or set config.trustedProxy. The starter's Railway configuration supplies TRUSTED_PROXY automatically.";
|
|
4
|
+
}
|
|
5
|
+
//# sourceMappingURL=TrustedProxyRequired.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TrustedProxyRequired.js","sourceRoot":"","sources":["../src/TrustedProxyRequired.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAE9B,MAAM,OAAO,oBAAqB,SAAQ,IAAI,CAAC,WAAW,CAAC,sBAAsB,CAAC;IAC/D,OAAO,GACxB,6NAA6N,CAAC;CAC/N"}
|
package/dist/appHostHttp.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { AppChanges, AppHttpHostOptions } from "#appHost.ts";
|
|
|
6
6
|
import type { databaseLayer } from "#databaseLayer.ts";
|
|
7
7
|
import type { HostOptions, HostPolicy, HostRouteRequirements } from "#hostOptions.ts";
|
|
8
8
|
import type { MigrationOptions } from "#MigrationOptions.ts";
|
|
9
|
-
export declare function appHostHttp<TRpcs extends Rpc.Any, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TSession extends SessionShape, TRouteError = never, TRouteRequirements = HostRouteRequirements>(options: HostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TRouteError, TRouteRequirements> & HostPolicy<TSession>, changesService: typeof AppChanges): Layer.Layer<TServices | import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/platform-node/NodeServices").NodeServices, TServiceError | import("effect/Config").ConfigError | import("effect/PlatformError").PlatformError | import("effect/http/HttpServerError").ServeError | HttpRouter.Request.Without<THandlerError> | HttpRouter.Request.Without<TRouteError>, Exclude<Exclude<Exclude<Exclude<TServiceRequirements, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<import("effect/FileSystem").FileSystem, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<import("effect/http/HttpPlatform").HttpPlatform, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<import("effect/Path").Path, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<HttpRouter.Request.Only<"GlobalRequires", Exclude<Exclude<Exclude<THandlerRequirements, import("effect/rpc/RpcSerialization").RpcSerialization>, never>, never>>, import("effect/http/HttpServerRequest").HttpServerRequest | import("effect/Scope").Scope>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/platform-node/NodeServices").NodeServices>, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<HttpRouter.Request.Only<"GlobalRequires", Exclude<Exclude<Exclude<TRouteRequirements, never>, never>, never>>, import("effect/http/HttpServerRequest").HttpServerRequest | import("effect/Scope").Scope>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/platform-node/NodeServices").NodeServices>, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<HttpRouter.Request.Only<"GlobalRequires", Exclude<Exclude<Exclude<Exclude<HttpRouter.HttpRouter, THandlers | import("@shivaedev/identity/middleware.ts").RequestTracing | Rpc.ToHandler<Extract<TRpcs, {
|
|
9
|
+
export declare function appHostHttp<TRpcs extends Rpc.Any, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TSession extends SessionShape, TRouteError = never, TRouteRequirements = HostRouteRequirements>(options: HostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TRouteError, TRouteRequirements> & HostPolicy<TSession>, changesService: typeof AppChanges): Layer.Layer<TServices | import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/platform-node/NodeServices").NodeServices, TServiceError | import("effect/Config").ConfigError | import("effect/PlatformError").PlatformError | import("effect/http/HttpServerError").ServeError | import("./TrustedProxyRequired.ts").TrustedProxyRequired | HttpRouter.Request.Without<THandlerError> | HttpRouter.Request.Without<TRouteError>, Exclude<Exclude<Exclude<Exclude<TServiceRequirements, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<import("effect/FileSystem").FileSystem, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<import("effect/http/HttpPlatform").HttpPlatform, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<import("effect/Path").Path, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<HttpRouter.Request.Only<"GlobalRequires", Exclude<Exclude<Exclude<THandlerRequirements, import("effect/rpc/RpcSerialization").RpcSerialization>, never>, never>>, import("effect/http/HttpServerRequest").HttpServerRequest | import("effect/Scope").Scope>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/platform-node/NodeServices").NodeServices>, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<HttpRouter.Request.Only<"GlobalRequires", Exclude<Exclude<Exclude<TRouteRequirements, never>, never>, never>>, import("effect/http/HttpServerRequest").HttpServerRequest | import("effect/Scope").Scope>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/platform-node/NodeServices").NodeServices>, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<HttpRouter.Request.Only<"GlobalRequires", Exclude<Exclude<Exclude<Exclude<HttpRouter.HttpRouter, THandlers | import("@shivaedev/identity/middleware.ts").RequestTracing | Rpc.ToHandler<Extract<TRpcs, {
|
|
10
10
|
readonly _tag: `${string}.$changes`;
|
|
11
11
|
}>>>, import("effect/rpc/RpcSerialization").RpcSerialization>, never>, never>>, import("effect/http/HttpServerRequest").HttpServerRequest | import("effect/Scope").Scope>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/platform-node/NodeServices").NodeServices>, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<HttpRouter.Request.Only<"GlobalRequires", Exclude<Exclude<Exclude<Exclude<import("effect/rpc/RpcSerialization").RpcSerialization, THandlers | import("@shivaedev/identity/middleware.ts").RequestTracing | Rpc.ToHandler<Extract<TRpcs, {
|
|
12
12
|
readonly _tag: `${string}.$changes`;
|
|
@@ -33,7 +33,7 @@ export declare function appHostHttp<TRpcs extends Rpc.Any, THandlers, THandlerEr
|
|
|
33
33
|
}>>>, import("effect/rpc/RpcSerialization").RpcSerialization>, never>, never>>, HttpRouter.HttpRouter>, import("effect/Scope").Scope>, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never>>;
|
|
34
34
|
export declare function migratedHost<TRpcs extends Rpc.Any, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TMigrations, TSession extends SessionShape, TRouteError = never, TRouteRequirements = HostRouteRequirements>(options: HostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TRouteError, TRouteRequirements> & HostPolicy<TSession> & {
|
|
35
35
|
readonly migrations: MigrationOptions<TMigrations>;
|
|
36
|
-
}, database: ReturnType<typeof databaseLayer<TMigrations>>, changesService: typeof AppChanges): Layer.Layer<TServices | AppChanges | import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient | import("@effect/platform-node/NodeServices").NodeServices, TServiceError | import("effect/Config").ConfigError | import("effect/sql/Migrator").MigrationError | import("effect/PlatformError").PlatformError | import("effect/http/HttpServerError").ServeError | import("effect/sql/SqlError").SqlError | HttpRouter.Request.Without<THandlerError> | HttpRouter.Request.Without<TRouteError>, Exclude<Exclude<Exclude<Exclude<Exclude<TServiceRequirements, AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<import("effect/FileSystem").FileSystem, TServices | AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<import("effect/http/HttpPlatform").HttpPlatform, TServices | AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<import("effect/Path").Path, TServices | AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<TMigrations, import("effect/Scope").Scope>, import("@effect/sql-pg/PgClient").PgClient | import("effect/sql/SqlClient").SqlClient>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<HttpRouter.Request.Only<"GlobalRequires", Exclude<Exclude<Exclude<THandlerRequirements, import("effect/rpc/RpcSerialization").RpcSerialization>, never>, never>>, import("effect/http/HttpServerRequest").HttpServerRequest | import("effect/Scope").Scope>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/platform-node/NodeServices").NodeServices>, TServices | AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<HttpRouter.Request.Only<"GlobalRequires", Exclude<Exclude<Exclude<TRouteRequirements, never>, never>, never>>, import("effect/http/HttpServerRequest").HttpServerRequest | import("effect/Scope").Scope>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/platform-node/NodeServices").NodeServices>, TServices | AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<HttpRouter.Request.Only<"GlobalRequires", Exclude<Exclude<Exclude<Exclude<HttpRouter.HttpRouter, THandlers | import("@shivaedev/identity/middleware.ts").RequestTracing | Rpc.ToHandler<Extract<TRpcs, {
|
|
36
|
+
}, database: ReturnType<typeof databaseLayer<TMigrations>>, changesService: typeof AppChanges): Layer.Layer<TServices | AppChanges | import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient | import("@effect/platform-node/NodeServices").NodeServices, TServiceError | import("effect/Config").ConfigError | import("effect/sql/Migrator").MigrationError | import("effect/PlatformError").PlatformError | import("effect/http/HttpServerError").ServeError | import("effect/sql/SqlError").SqlError | import("./TrustedProxyRequired.ts").TrustedProxyRequired | HttpRouter.Request.Without<THandlerError> | HttpRouter.Request.Without<TRouteError>, Exclude<Exclude<Exclude<Exclude<Exclude<TServiceRequirements, AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<import("effect/FileSystem").FileSystem, TServices | AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<import("effect/http/HttpPlatform").HttpPlatform, TServices | AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<import("effect/Path").Path, TServices | AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<TMigrations, import("effect/Scope").Scope>, import("@effect/sql-pg/PgClient").PgClient | import("effect/sql/SqlClient").SqlClient>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<HttpRouter.Request.Only<"GlobalRequires", Exclude<Exclude<Exclude<THandlerRequirements, import("effect/rpc/RpcSerialization").RpcSerialization>, never>, never>>, import("effect/http/HttpServerRequest").HttpServerRequest | import("effect/Scope").Scope>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/platform-node/NodeServices").NodeServices>, TServices | AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<HttpRouter.Request.Only<"GlobalRequires", Exclude<Exclude<Exclude<TRouteRequirements, never>, never>, never>>, import("effect/http/HttpServerRequest").HttpServerRequest | import("effect/Scope").Scope>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/platform-node/NodeServices").NodeServices>, TServices | AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<HttpRouter.Request.Only<"GlobalRequires", Exclude<Exclude<Exclude<Exclude<HttpRouter.HttpRouter, THandlers | import("@shivaedev/identity/middleware.ts").RequestTracing | Rpc.ToHandler<Extract<TRpcs, {
|
|
37
37
|
readonly _tag: `${string}.$changes`;
|
|
38
38
|
}>>>, import("effect/rpc/RpcSerialization").RpcSerialization>, never>, never>>, import("effect/http/HttpServerRequest").HttpServerRequest | import("effect/Scope").Scope>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/platform-node/NodeServices").NodeServices>, TServices | AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<HttpRouter.Request.Only<"GlobalRequires", Exclude<Exclude<Exclude<Exclude<import("effect/rpc/RpcSerialization").RpcSerialization, THandlers | import("@shivaedev/identity/middleware.ts").RequestTracing | Rpc.ToHandler<Extract<TRpcs, {
|
|
39
39
|
readonly _tag: `${string}.$changes`;
|
|
@@ -58,7 +58,7 @@ export declare function migratedHost<TRpcs extends Rpc.Any, THandlers, THandlerE
|
|
|
58
58
|
}>>>, import("effect/rpc/RpcSerialization").RpcSerialization>, never>, never>>, HttpRouter.HttpRouter>, import("effect/Scope").Scope>, TServices | AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<HttpRouter.Request.Without<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Rpc.ToHandler<Rpc.AddMiddleware<Rpc.AddMiddleware<TRpcs, typeof import("./hostRpc.ts").HostedReadPolicy>, typeof import("@shivaedev/identity/middleware.ts").RequestTracing>>, import("./hostRpc.ts").HostedReadPolicy>, import("effect/rpc/RpcSerialization").RpcSerialization>, import("effect/Scope").Scope>, THandlers | import("@shivaedev/identity/middleware.ts").RequestTracing | Rpc.ToHandler<Extract<TRpcs, {
|
|
59
59
|
readonly _tag: `${string}.$changes`;
|
|
60
60
|
}>>>, import("effect/rpc/RpcSerialization").RpcSerialization>, never>, never>>, HttpRouter.HttpRouter>, import("effect/Scope").Scope>, TServices | AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never>>;
|
|
61
|
-
export declare function deferredHost<TRpcs extends Rpc.Any, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TMigrations, TSession extends SessionShape, TApplicationError, TApplicationRequirements, TRouteError = never, TRouteRequirements = HostRouteRequirements>(application: Effect.Effect<AppHttpHostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TSession, never, never, never, TRouteError, TRouteRequirements>, TApplicationError, TApplicationRequirements>, database: ReturnType<typeof databaseLayer<TMigrations>>, changesService: typeof AppChanges): Layer.Layer<TServices | AppChanges | import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient | import("@effect/platform-node/NodeServices").NodeServices, TApplicationError | TServiceError | import("effect/Config").ConfigError | import("effect/sql/Migrator").MigrationError | import("effect/PlatformError").PlatformError | import("effect/http/HttpServerError").ServeError | import("effect/sql/SqlError").SqlError | HttpRouter.Request.Without<THandlerError> | HttpRouter.Request.Without<TRouteError>, Exclude<Exclude<TApplicationRequirements, import("effect/Scope").Scope>, AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient> | Exclude<Exclude<TMigrations, import("effect/Scope").Scope>, import("@effect/sql-pg/PgClient").PgClient | import("effect/sql/SqlClient").SqlClient> | Exclude<Exclude<Exclude<Exclude<Exclude<TServiceRequirements, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never>, AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<import("effect/FileSystem").FileSystem, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never>, AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<import("effect/http/HttpPlatform").HttpPlatform, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never>, AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<import("effect/Path").Path, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never>, AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<HttpRouter.Request.Only<"GlobalRequires", Exclude<Exclude<Exclude<THandlerRequirements, import("effect/rpc/RpcSerialization").RpcSerialization>, never>, never>>, import("effect/http/HttpServerRequest").HttpServerRequest | import("effect/Scope").Scope>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/platform-node/NodeServices").NodeServices>, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never>, AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<HttpRouter.Request.Only<"GlobalRequires", Exclude<Exclude<Exclude<TRouteRequirements, never>, never>, never>>, import("effect/http/HttpServerRequest").HttpServerRequest | import("effect/Scope").Scope>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/platform-node/NodeServices").NodeServices>, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never>, AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<HttpRouter.Request.Only<"GlobalRequires", Exclude<Exclude<Exclude<Exclude<HttpRouter.HttpRouter, THandlers | import("@shivaedev/identity/middleware.ts").RequestTracing | Rpc.ToHandler<Extract<TRpcs, {
|
|
61
|
+
export declare function deferredHost<TRpcs extends Rpc.Any, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TMigrations, TSession extends SessionShape, TApplicationError, TApplicationRequirements, TRouteError = never, TRouteRequirements = HostRouteRequirements>(application: Effect.Effect<AppHttpHostOptions<TRpcs, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements, TSession, never, never, never, TRouteError, TRouteRequirements>, TApplicationError, TApplicationRequirements>, database: ReturnType<typeof databaseLayer<TMigrations>>, changesService: typeof AppChanges): Layer.Layer<TServices | AppChanges | import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient | import("@effect/platform-node/NodeServices").NodeServices, TApplicationError | TServiceError | import("effect/Config").ConfigError | import("effect/sql/Migrator").MigrationError | import("effect/PlatformError").PlatformError | import("effect/http/HttpServerError").ServeError | import("effect/sql/SqlError").SqlError | import("./TrustedProxyRequired.ts").TrustedProxyRequired | HttpRouter.Request.Without<THandlerError> | HttpRouter.Request.Without<TRouteError>, Exclude<Exclude<TApplicationRequirements, import("effect/Scope").Scope>, AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient> | Exclude<Exclude<TMigrations, import("effect/Scope").Scope>, import("@effect/sql-pg/PgClient").PgClient | import("effect/sql/SqlClient").SqlClient> | Exclude<Exclude<Exclude<Exclude<Exclude<TServiceRequirements, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never>, AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<import("effect/FileSystem").FileSystem, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never>, AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<import("effect/http/HttpPlatform").HttpPlatform, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never>, AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<import("effect/Path").Path, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never>, AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<HttpRouter.Request.Only<"GlobalRequires", Exclude<Exclude<Exclude<THandlerRequirements, import("effect/rpc/RpcSerialization").RpcSerialization>, never>, never>>, import("effect/http/HttpServerRequest").HttpServerRequest | import("effect/Scope").Scope>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/platform-node/NodeServices").NodeServices>, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never>, AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<HttpRouter.Request.Only<"GlobalRequires", Exclude<Exclude<Exclude<TRouteRequirements, never>, never>, never>>, import("effect/http/HttpServerRequest").HttpServerRequest | import("effect/Scope").Scope>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/platform-node/NodeServices").NodeServices>, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never>, AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<HttpRouter.Request.Only<"GlobalRequires", Exclude<Exclude<Exclude<Exclude<HttpRouter.HttpRouter, THandlers | import("@shivaedev/identity/middleware.ts").RequestTracing | Rpc.ToHandler<Extract<TRpcs, {
|
|
62
62
|
readonly _tag: `${string}.$changes`;
|
|
63
63
|
}>>>, import("effect/rpc/RpcSerialization").RpcSerialization>, never>, never>>, import("effect/http/HttpServerRequest").HttpServerRequest | import("effect/Scope").Scope>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/platform-node/NodeServices").NodeServices>, TServices>, import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("@effect/platform-node/NodeServices").NodeServices>, never>, never>, never>, AppChanges | import("@effect/sql-pg/PgClient").PgClient | import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<HttpRouter.Request.Only<"GlobalRequires", Exclude<Exclude<Exclude<Exclude<import("effect/rpc/RpcSerialization").RpcSerialization, THandlers | import("@shivaedev/identity/middleware.ts").RequestTracing | Rpc.ToHandler<Extract<TRpcs, {
|
|
64
64
|
readonly _tag: `${string}.$changes`;
|
package/dist/hostChanges.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Layer } from "effect";
|
|
2
2
|
import type { Rpc, RpcGroup } from "effect/rpc";
|
|
3
|
+
import { type SessionPolicy, type SessionShape } from "@shivaedev/identity/server/session.ts";
|
|
3
4
|
import type { AppChanges } from "./appHost.ts";
|
|
4
|
-
export declare function hostChanges<R extends Rpc.Any>(group: RpcGroup.RpcGroup<R>, changesService: typeof AppChanges): Layer.Layer<Rpc.ToHandler<Extract<R, {
|
|
5
|
+
export declare function hostChanges<R extends Rpc.Any, TSession extends SessionShape>(group: RpcGroup.RpcGroup<R>, changesService: typeof AppChanges, sessions?: SessionPolicy<TSession>): Layer.Layer<Rpc.ToHandler<Extract<R, {
|
|
5
6
|
readonly _tag: `${string}.$changes`;
|
|
6
7
|
}>>, never, never>;
|
|
7
8
|
//# sourceMappingURL=hostChanges.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hostChanges.d.ts","sourceRoot":"","sources":["../src/hostChanges.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,EAAkB,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"hostChanges.d.ts","sourceRoot":"","sources":["../src/hostChanges.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,EAAkB,MAAM,QAAQ,CAAC;AAEhE,OAAO,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAIhD,OAAO,EAAkB,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAE9G,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,wBAAgB,WAAW,CAAC,CAAC,SAAS,GAAG,CAAC,GAAG,EAAE,QAAQ,SAAS,YAAY,EAC3E,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,EAC3B,cAAc,EAAE,OAAO,UAAU,EACjC,QAAQ,CAAC,EAAE,aAAa,CAAC,QAAQ,CAAC;mBAsCoC,GAAG,MAAM,WAAW;mBAG1F"}
|
package/dist/hostChanges.js
CHANGED
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
import { Context, Effect, Layer, Option, Stream } from "effect";
|
|
2
|
+
import { Headers } from "effect/http";
|
|
3
|
+
import { CurrentViewer } from "@shivaedev/contract/readPolicy.ts";
|
|
4
|
+
import { Unauthorized } from "@shivaedev/errors/taxonomy.ts";
|
|
5
|
+
import { resolveSession } from "@shivaedev/identity/server/session.ts";
|
|
2
6
|
import { resolveChanges } from "@shivaedev/identity/server/sessionKeys.ts";
|
|
3
|
-
export function hostChanges(group, changesService) {
|
|
7
|
+
export function hostChanges(group, changesService, sessions) {
|
|
4
8
|
return Layer.effectContext(Effect.gen(function* () {
|
|
5
9
|
const changes = yield* Effect.serviceOption(changesService);
|
|
6
10
|
const services = yield* Effect.context();
|
|
7
11
|
const handlers = new Map();
|
|
12
|
+
function subscribe(rpc, queries) {
|
|
13
|
+
return Stream.unwrap(Effect.gen(function* () {
|
|
14
|
+
const { subscription, context } = yield* resolveChanges(rpc, queries);
|
|
15
|
+
const viewer = yield* CurrentViewer;
|
|
16
|
+
const authorized = authorizeSession(sessions, rpc._tag, viewer, subscription.authorized);
|
|
17
|
+
const stream = Option.isSome(changes)
|
|
18
|
+
? changes.value.subscribe({ ...subscription, authorized }, context)
|
|
19
|
+
: Stream.concat(Stream.succeed({ _tag: "Resync" }), Stream.never);
|
|
20
|
+
if (sessions === undefined || Option.isNone(viewer)) {
|
|
21
|
+
return stream;
|
|
22
|
+
}
|
|
23
|
+
return Stream.interruptWhen(stream, sessionRevoked(sessions, rpc._tag, viewer.value.id));
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
8
26
|
for (const rpc of group.requests.values()) {
|
|
9
27
|
if (!rpc._tag.endsWith(".$changes")) {
|
|
10
28
|
continue;
|
|
@@ -14,12 +32,28 @@ export function hostChanges(group, changesService) {
|
|
|
14
32
|
handlers.set(rpc.key, provided);
|
|
15
33
|
continue;
|
|
16
34
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
35
|
+
handlers.set(rpc.key, {
|
|
36
|
+
context: services,
|
|
37
|
+
handler: ({ queries }) => subscribe(rpc, queries),
|
|
38
|
+
tag: rpc._tag,
|
|
39
|
+
});
|
|
21
40
|
}
|
|
22
41
|
return Context.makeUnsafe(handlers);
|
|
23
42
|
}));
|
|
24
43
|
}
|
|
44
|
+
function authorizeSession(sessions, tag, viewer, policy) {
|
|
45
|
+
if (sessions === undefined) {
|
|
46
|
+
return policy;
|
|
47
|
+
}
|
|
48
|
+
return resolveSession(sessions, Headers.empty, tag).pipe(Effect.map((current) => Option.isNone(viewer) || (Option.isSome(current) && current.value.user.id === viewer.value.id)), Effect.catch(() => Effect.succeed(false)), Effect.flatMap((allowed) => (allowed ? policy : Effect.succeed(false))));
|
|
49
|
+
}
|
|
50
|
+
function sessionRevoked(sessions, tag, id) {
|
|
51
|
+
return Effect.gen(function* () {
|
|
52
|
+
yield* Effect.sleep(sessions.streamRevalidationInterval ?? "30 seconds");
|
|
53
|
+
const current = yield* resolveSession(sessions, Headers.empty, tag).pipe(Effect.catchTag("AuthUnavailable", () => Effect.succeed(undefined)));
|
|
54
|
+
if (current !== undefined && (Option.isNone(current) || current.value.user.id !== id)) {
|
|
55
|
+
return yield* new Unauthorized({ message: "Authentication required" });
|
|
56
|
+
}
|
|
57
|
+
}).pipe(Effect.forever);
|
|
58
|
+
}
|
|
25
59
|
//# sourceMappingURL=hostChanges.js.map
|
package/dist/hostChanges.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hostChanges.js","sourceRoot":"","sources":["../src/hostChanges.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"hostChanges.js","sourceRoot":"","sources":["../src/hostChanges.ts"],"names":[],"mappings":"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"}
|
package/dist/hostRpc.d.ts
CHANGED
|
@@ -7,5 +7,5 @@ declare class HostedReadPolicy extends HostedReadPolicy_base {
|
|
|
7
7
|
}
|
|
8
8
|
export type { HostedReadPolicy };
|
|
9
9
|
export declare const rpcCacheControl: HttpEffect.PreResponseHandler;
|
|
10
|
-
export declare const hostRpc: <R extends Rpc.Any>(group: RpcGroup.RpcGroup<R
|
|
10
|
+
export declare const hostRpc: <R extends Rpc.Any>(group: RpcGroup.RpcGroup<R>, websocket?: boolean | undefined) => Effect.Effect<Effect.Effect<HttpServerResponse.HttpServerResponse, never, import("effect/http/HttpServerRequest").HttpServerRequest | import("effect/Scope").Scope>, never, import("effect/rpc/RpcSerialization").RpcSerialization | import("effect/Scope").Scope | Exclude<Rpc.Middleware<Rpc.AddMiddleware<Rpc.AddMiddleware<R, typeof HostedReadPolicy>, typeof import("@shivaedev/identity/middleware.ts").RequestTracing>>, HostedReadPolicy> | Exclude<Rpc.ServicesServer<Rpc.AddMiddleware<Rpc.AddMiddleware<R, typeof HostedReadPolicy>, typeof import("@shivaedev/identity/middleware.ts").RequestTracing>>, HostedReadPolicy> | Exclude<Rpc.ToHandler<Rpc.AddMiddleware<Rpc.AddMiddleware<R, typeof HostedReadPolicy>, typeof import("@shivaedev/identity/middleware.ts").RequestTracing>>, HostedReadPolicy>>;
|
|
11
11
|
//# sourceMappingURL=hostRpc.d.ts.map
|
package/dist/hostRpc.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hostRpc.d.ts","sourceRoot":"","sources":["../src/hostRpc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,KAAK,UAAU,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,KAAK,GAAG,EAAE,KAAK,QAAQ,EAAE,aAAa,EAAa,MAAM,YAAY,CAAC;AAE/E,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;;AAG7F,cAAM,gBAAiB,SAAQ,qBAE7B;CAAG;AAEL,YAAY,EAAE,gBAAgB,EAAE,CAAC;AAEjC,eAAO,MAAM,eAAe,EAAE,UAAU,CAAC,kBACoD,CAAC;AAE9F,eAAO,MAAM,OAAO,GAAgC,CAAC,SAAS,GAAG,CAAC,GAAG,
|
|
1
|
+
{"version":3,"file":"hostRpc.d.ts","sourceRoot":"","sources":["../src/hostRpc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,KAAK,UAAU,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,KAAK,GAAG,EAAE,KAAK,QAAQ,EAAE,aAAa,EAAa,MAAM,YAAY,CAAC;AAE/E,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;;AAG7F,cAAM,gBAAiB,SAAQ,qBAE7B;CAAG;AAEL,YAAY,EAAE,gBAAgB,EAAE,CAAC;AAEjC,eAAO,MAAM,eAAe,EAAE,UAAU,CAAC,kBACoD,CAAC;AAE9F,eAAO,MAAM,OAAO,GAAgC,CAAC,SAAS,GAAG,CAAC,GAAG,21BAWnE,CAAC"}
|
package/dist/hostRpc.js
CHANGED
|
@@ -9,12 +9,14 @@ class HostedReadPolicy extends RpcMiddleware.Service()("@shivaedev/server/Hosted
|
|
|
9
9
|
}) {
|
|
10
10
|
}
|
|
11
11
|
export const rpcCacheControl = (_, response) => Effect.succeed(HttpServerResponse.setHeader(response, "cache-control", "private, no-store"));
|
|
12
|
-
export const hostRpc = Effect.fnUntraced(function* (group) {
|
|
12
|
+
export const hostRpc = Effect.fnUntraced(function* (group, websocket = false) {
|
|
13
13
|
const guarded = group.middleware(HostedReadPolicy);
|
|
14
14
|
// Keep the policy inside identity middleware so it sees the request's viewer.
|
|
15
15
|
for (const rpc of guarded.requests.values()) {
|
|
16
16
|
Object.assign(rpc, { middlewares: new Set([HostedReadPolicy, ...rpc.middlewares]) });
|
|
17
17
|
}
|
|
18
|
-
|
|
18
|
+
const traced = tracedGroup(guarded);
|
|
19
|
+
const handler = websocket ? RpcServer.toHttpEffectWebsocket(traced) : RpcServer.toHttpEffect(traced);
|
|
20
|
+
return yield* handler.pipe(Effect.provideService(HostedReadPolicy, (effect, request) => Effect.flatMap(CurrentViewer, (viewer) => authorizeReads(effect, request, viewer))));
|
|
19
21
|
});
|
|
20
22
|
//# sourceMappingURL=hostRpc.js.map
|
package/dist/hostRpc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hostRpc.js","sourceRoot":"","sources":["../src/hostRpc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAmB,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAA2B,aAAa,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC/E,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAClF,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAC7F,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,MAAM,gBAAiB,SAAQ,aAAa,CAAC,OAAO,EAAoB,CAAC,oCAAoC,EAAE;IAC9G,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,iBAAiB,EAAE,eAAe,CAAC,CAAC;CACzD,CAAC;CAAG;AAIL,MAAM,CAAC,MAAM,eAAe,GAAkC,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,CAC7E,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,SAAS,CAAC,QAAQ,EAAE,eAAe,EAAE,mBAAmB,CAAC,CAAC,CAAC;AAE9F,MAAM,CAAC,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAqB,KAA2B;
|
|
1
|
+
{"version":3,"file":"hostRpc.js","sourceRoot":"","sources":["../src/hostRpc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAmB,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAA2B,aAAa,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC/E,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAClF,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAC7F,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,MAAM,gBAAiB,SAAQ,aAAa,CAAC,OAAO,EAAoB,CAAC,oCAAoC,EAAE;IAC9G,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,iBAAiB,EAAE,eAAe,CAAC,CAAC;CACzD,CAAC;CAAG;AAIL,MAAM,CAAC,MAAM,eAAe,GAAkC,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,CAC7E,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,SAAS,CAAC,QAAQ,EAAE,eAAe,EAAE,mBAAmB,CAAC,CAAC,CAAC;AAE9F,MAAM,CAAC,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAqB,KAA2B,EAAE,SAAS,GAAG,KAAK;IACpH,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;IACnD,8EAA8E;IAC9E,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;QAC7C,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,IAAI,GAAG,CAAC,CAAC,gBAAgB,EAAE,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC;IACtF,CAAC;IACD,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACpC,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IACrG,OAAO,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,CACzB,MAAM,CAAC,cAAc,CAAC,gBAAgB,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAChJ,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hostRpcRoutes.d.ts","sourceRoot":"","sources":["../src/hostRpcRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACjD,OAAO,EAAmC,UAAU,EAAyC,MAAM,aAAa,CAAC;AAEjH,OAAO,EAAE,KAAK,GAAG,EAAE,KAAK,QAAQ,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,KAAK,EAAqC,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAE3G,OAAO,EAA+C,KAAK,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAEvH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"hostRpcRoutes.d.ts","sourceRoot":"","sources":["../src/hostRpcRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACjD,OAAO,EAAmC,UAAU,EAAyC,MAAM,aAAa,CAAC;AAEjH,OAAO,EAAE,KAAK,GAAG,EAAE,KAAK,QAAQ,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,KAAK,EAAqC,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAE3G,OAAO,EAA+C,KAAK,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAEvH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAGnE,wBAAgB,aAAa,CAAC,KAAK,SAAS,GAAG,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,SAAS,YAAY,EACnH,OAAO,EAAE;IACR,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACzC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;IACjE,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC;CACxC,GAAG,UAAU,CAAC,QAAQ,CAAC,EACxB,cAAc,EAAE,OAAO,UAAU,EACjC,EAAE,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC,EAC3F,SAAS,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC;;;;;;;;0CA6CjD"}
|
package/dist/hostRpcRoutes.js
CHANGED
|
@@ -9,21 +9,22 @@ import { boundedRpcRequest } from "#boundedRpcRequest.ts";
|
|
|
9
9
|
import { hostChanges } from "#hostChanges.ts";
|
|
10
10
|
import { hostRpc, rpcCacheControl } from "#hostRpc.ts";
|
|
11
11
|
import { redactingRpcSerialization } from "#redactingRpcSerialization.ts";
|
|
12
|
+
import { socketRpc } from "#socketRpc.ts";
|
|
12
13
|
export function hostRpcRoutes(options, changesService, { bodySize, trustedProxy, nativeOrigins }, sensitive) {
|
|
13
14
|
const middleware = options.policy === "loopback"
|
|
14
15
|
? requestTracingLayer({ sensitive })
|
|
15
16
|
: Layer.mergeAll(authenticatedLayer(options.sessions), maybeAuthenticatedLayer(options.sessions), requestTracingLayer({ sensitive }));
|
|
16
17
|
return HttpRouter.use((router) => Effect.gen(function* () {
|
|
17
18
|
const wire = redactingRpcSerialization(yield* RpcSerialization.RpcSerialization, sensitive, sensitive.maskFailure);
|
|
18
|
-
const
|
|
19
|
+
const httpGroup = options.group.omit(...[...options.group.requests.keys()].filter((tag) => tag.endsWith(".$changes")));
|
|
20
|
+
const handler = yield* hostRpc(httpGroup).pipe(Effect.provideService(RpcSerialization.RpcSerialization, wire));
|
|
19
21
|
const guarded = localRpcGuard(handler, trustedProxy, nativeOrigins);
|
|
22
|
+
const websocket = yield* hostRpc(options.group, true).pipe(Effect.provideService(RpcSerialization.RpcSerialization, wire));
|
|
20
23
|
yield* router.add("OPTIONS", options.rpcPath ?? "/rpc", HttpServerResponse.text("Local same-origin requests only", {
|
|
21
24
|
status: HttpStatus.fromLiteral("Forbidden"),
|
|
22
25
|
}));
|
|
23
|
-
yield* router.add("
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
});
|
|
27
|
-
})).pipe(Layer.provide(Layer.merge(middleware, hostChanges(options.group, changesService).pipe(Layer.provideMerge(options.handlers)))), Layer.provide(RpcSerialization.layerNdjson));
|
|
26
|
+
yield* router.add("GET", options.rpcPath ?? "/rpc", socketRpc(websocket, options, trustedProxy, nativeOrigins).pipe(Effect.provideService(RpcSerialization.RpcSerialization, wire)));
|
|
27
|
+
yield* router.add("POST", options.rpcPath ?? "/rpc", (request) => guarded.pipe(Effect.provideService(HttpIncomingMessage.MaxBodySize, ByteSize.bytes(bodySize)), Effect.provideService(HttpServerRequest.HttpServerRequest, boundedRpcRequest(request)), Effect.provideService(RpcSerialization.RpcSerialization, wire), HttpEffect.withPreResponseHandler(rpcCacheControl)));
|
|
28
|
+
})).pipe(Layer.provide(Layer.merge(middleware, hostChanges(options.group, changesService, options.policy === "loopback" ? undefined : options.sessions).pipe(Layer.provideMerge(options.handlers)))), Layer.provide(RpcSerialization.layerNdjson));
|
|
28
29
|
}
|
|
29
30
|
//# sourceMappingURL=hostRpcRoutes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hostRpcRoutes.js","sourceRoot":"","sources":["../src/hostRpcRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjH,OAAO,KAAK,UAAU,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAA2B,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEvE,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAqB,MAAM,uCAAuC,CAAC;AACvH,OAAO,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAE5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEvD,OAAO,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"hostRpcRoutes.js","sourceRoot":"","sources":["../src/hostRpcRoutes.ts"],"names":[],"mappings":"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"}
|
package/dist/hostTransport.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Config, Effect } from "effect";
|
|
2
|
+
import { TrustedProxyRequired } from "#TrustedProxyRequired.ts";
|
|
2
3
|
interface Options {
|
|
3
4
|
readonly config?: {
|
|
4
5
|
readonly host?: string;
|
|
@@ -13,6 +14,6 @@ export declare const hostTransport: (options: Options) => Effect.Effect<{
|
|
|
13
14
|
host: string;
|
|
14
15
|
nativeOrigins: readonly string[];
|
|
15
16
|
trustedProxy: string | undefined;
|
|
16
|
-
}, Config.ConfigError, never>;
|
|
17
|
+
}, Config.ConfigError | TrustedProxyRequired, never>;
|
|
17
18
|
export {};
|
|
18
19
|
//# sourceMappingURL=hostTransport.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hostTransport.d.ts","sourceRoot":"","sources":["../src/hostTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAA0B,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"hostTransport.d.ts","sourceRoot":"","sources":["../src/hostTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAA0B,MAAM,QAAQ,CAAC;AAIhE,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAEhE,UAAU,OAAO;IAChB,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5G,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CACzC;AACD,eAAO,MAAM,aAAa;;;;;oDA0BxB,CAAC"}
|
package/dist/hostTransport.js
CHANGED
|
@@ -2,6 +2,7 @@ import { Config, Effect, Option, Result, Schema } from "effect";
|
|
|
2
2
|
import * as NetAddress from "effect/net/NetAddress";
|
|
3
3
|
import { TrustedProxy } from "@shivaedev/identity/server/trustedProxy.ts";
|
|
4
4
|
import { NativeOrigins } from "#nativeClientOrigins.ts";
|
|
5
|
+
import { TrustedProxyRequired } from "#TrustedProxyRequired.ts";
|
|
5
6
|
export const hostTransport = Effect.fn("Platform.hostTransport")(function* (options) {
|
|
6
7
|
const maxBodySize = options.config?.maxBodySize ?? (yield* Config.Int("MAX_BODY_SIZE").pipe(Config.withDefault(1024 * 1024)));
|
|
7
8
|
const bodySize = yield* Schema.decodeUnknownEffect(Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)))(maxBodySize).pipe(Effect.mapError((cause) => new Config.ConfigError(cause)));
|
|
@@ -18,7 +19,7 @@ export const hostTransport = Effect.fn("Platform.hostTransport")(function* (opti
|
|
|
18
19
|
onSuccess: (address) => NetAddress.isLoopback(NetAddress.toCanonical(address)),
|
|
19
20
|
});
|
|
20
21
|
if (options.policy !== "loopback" && !loopback && trustedProxy === undefined) {
|
|
21
|
-
yield* Effect.
|
|
22
|
+
return yield* Effect.fail(new TrustedProxyRequired());
|
|
22
23
|
}
|
|
23
24
|
const nativeOrigins = yield* Schema.decodeUnknownEffect(NativeOrigins)(options.nativeOrigins ?? []).pipe(Effect.mapError((cause) => new Config.ConfigError(cause)));
|
|
24
25
|
return { bodySize, host, nativeOrigins, trustedProxy };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hostTransport.js","sourceRoot":"","sources":["../src/hostTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChE,OAAO,KAAK,UAAU,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"hostTransport.js","sourceRoot":"","sources":["../src/hostTransport.ts"],"names":[],"mappings":"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"}
|
|
@@ -36,7 +36,7 @@ export function nodeSubscriptionSignal(options) {
|
|
|
36
36
|
}
|
|
37
37
|
nodeRequest.once("close", onRequestClose);
|
|
38
38
|
socket.once("close", onSocketClose);
|
|
39
|
-
if (nodeRequest.destroyed || socket.destroyed) {
|
|
39
|
+
if ((nodeRequest.destroyed && !nodeRequest.complete) || socket.destroyed) {
|
|
40
40
|
controller.abort();
|
|
41
41
|
}
|
|
42
42
|
let disposed = false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscriptionSignal.js","sourceRoot":"","sources":["../../src/node-http/subscriptionSignal.ts"],"names":[],"mappings":"AAUA,SAAS,QAAQ,CAAC,KAAc,EAAE,GAAW;IAC5C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC1F,CAAC;AACD,SAAS,iBAAiB,CAAC,KAAc;IACxC,OAAO,OAAO,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,QAAQ,IAAI,OAAO,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,UAAU,CAAC;AACvG,CAAC;AACD,SAAS,eAAe,CAAC,OAA4B;IACpD,MAAM,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC;IAC5E,OAAO,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AACjD,CAAC;AACD,gJAAgJ;AAChJ,MAAM,UAAU,sBAAsB,CAAC,OAAsC;IAC5E,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,OAAO,GAAkB,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IACnD,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACnC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;QAC5C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YAC1B,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtB,CAAC;IACF,CAAC;IACD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,eAAe,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5E,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,EAAE,OAAO,EAAE,GAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;IAC7E,CAAC;IACD,MAAM,OAAO,GAAG,WAAW,CAAC;IAC5B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAC9B,SAAS,cAAc;QACtB,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;YACvB,UAAU,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC;IACF,CAAC;IACD,SAAS,aAAa;QACrB,OAAO,UAAU,CAAC,KAAK,EAAE,CAAC;IAC3B,CAAC;IACD,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;IAC1C,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IACpC,IAAI,WAAW,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"subscriptionSignal.js","sourceRoot":"","sources":["../../src/node-http/subscriptionSignal.ts"],"names":[],"mappings":"AAUA,SAAS,QAAQ,CAAC,KAAc,EAAE,GAAW;IAC5C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC1F,CAAC;AACD,SAAS,iBAAiB,CAAC,KAAc;IACxC,OAAO,OAAO,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,QAAQ,IAAI,OAAO,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,UAAU,CAAC;AACvG,CAAC;AACD,SAAS,eAAe,CAAC,OAA4B;IACpD,MAAM,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC;IAC5E,OAAO,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AACjD,CAAC;AACD,gJAAgJ;AAChJ,MAAM,UAAU,sBAAsB,CAAC,OAAsC;IAC5E,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,OAAO,GAAkB,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IACnD,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACnC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;QAC5C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YAC1B,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtB,CAAC;IACF,CAAC;IACD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,eAAe,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5E,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,EAAE,OAAO,EAAE,GAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;IAC7E,CAAC;IACD,MAAM,OAAO,GAAG,WAAW,CAAC;IAC5B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAC9B,SAAS,cAAc;QACtB,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;YACvB,UAAU,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC;IACF,CAAC;IACD,SAAS,aAAa;QACrB,OAAO,UAAU,CAAC,KAAK,EAAE,CAAC;IAC3B,CAAC;IACD,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;IAC1C,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IACpC,IAAI,CAAC,WAAW,CAAC,SAAS,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QAC1E,UAAU,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC;IACD,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,OAAO;QACN,OAAO,EAAE,GAAG,EAAE;YACb,IAAI,QAAQ,EAAE,CAAC;gBACd,OAAO;YACR,CAAC;YACD,QAAQ,GAAG,IAAI,CAAC;YAChB,WAAW,CAAC,cAAc,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;YACpD,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;QAC/C,CAAC;QACD,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC;KAChC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Effect, type Scope } from "effect";
|
|
2
|
+
import { HttpServerRequest, HttpServerResponse } from "effect/http";
|
|
3
|
+
import { type SessionShape } from "@shivaedev/identity/server/session.ts";
|
|
4
|
+
import type { HostPolicy } from "#hostOptions.ts";
|
|
5
|
+
export declare function socketRpc<TSession extends SessionShape>(handler: Effect.Effect<HttpServerResponse.HttpServerResponse, never, HttpServerRequest.HttpServerRequest | Scope.Scope>, policy: HostPolicy<TSession>, trustedProxy: string | undefined, nativeOrigins: readonly string[]): Effect.Effect<HttpServerResponse.HttpServerResponse, never, HttpServerRequest.HttpServerRequest | Scope.Scope>;
|
|
6
|
+
//# sourceMappingURL=socketRpc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socketRpc.d.ts","sourceRoot":"","sources":["../src/socketRpc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,EAAW,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAG7E,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAC1F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,wBAAgB,SAAS,CAAC,QAAQ,SAAS,YAAY,EACtD,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,kBAAkB,EAAE,KAAK,EAAE,iBAAiB,CAAC,iBAAiB,GAAG,KAAK,CAAC,KAAK,CAAC,EACvH,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,EAC5B,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,aAAa,EAAE,SAAS,MAAM,EAAE,kHAiBhC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { Headers, HttpServerRequest, HttpServerResponse } from "effect/http";
|
|
3
|
+
import * as HttpStatus from "effect/http/HttpStatus";
|
|
4
|
+
import { localTransport } from "@shivaedev/identity/server/local.ts";
|
|
5
|
+
import { resolveSession } from "@shivaedev/identity/server/session.ts";
|
|
6
|
+
export function socketRpc(handler, policy, trustedProxy, nativeOrigins) {
|
|
7
|
+
return Effect.gen(function* () {
|
|
8
|
+
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
9
|
+
if (!localTransport(trustedProxy, nativeOrigins)(request)) {
|
|
10
|
+
return HttpServerResponse.text("Local same-origin requests only", { status: HttpStatus.fromLiteral("Forbidden") });
|
|
11
|
+
}
|
|
12
|
+
if (policy.policy !== "loopback") {
|
|
13
|
+
yield* resolveSession(policy.sessions, Headers.empty, "$connect");
|
|
14
|
+
}
|
|
15
|
+
return yield* handler;
|
|
16
|
+
}).pipe(Effect.catchTags({
|
|
17
|
+
AuthUnavailable: authUnavailable,
|
|
18
|
+
Forbidden: forbidden,
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
function authUnavailable() {
|
|
22
|
+
return Effect.succeed(HttpServerResponse.empty({ status: HttpStatus.fromLiteral("ServiceUnavailable") }));
|
|
23
|
+
}
|
|
24
|
+
function forbidden() {
|
|
25
|
+
return Effect.succeed(HttpServerResponse.empty({ status: HttpStatus.fromLiteral("Forbidden") }));
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=socketRpc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socketRpc.js","sourceRoot":"","sources":["../src/socketRpc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAc,MAAM,QAAQ,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC7E,OAAO,KAAK,UAAU,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AACrE,OAAO,EAAE,cAAc,EAAqB,MAAM,uCAAuC,CAAC;AAG1F,MAAM,UAAU,SAAS,CACxB,OAAuH,EACvH,MAA4B,EAC5B,YAAgC,EAChC,aAAgC;IAEhC,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC1B,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,iBAAiB,CAAC,iBAAiB,CAAC;QAC3D,IAAI,CAAC,cAAc,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3D,OAAO,kBAAkB,CAAC,IAAI,CAAC,iCAAiC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,WAAW,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QACpH,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YAClC,KAAK,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QACnE,CAAC;QACD,OAAO,KAAK,CAAC,CAAC,OAAO,CAAC;IACvB,CAAC,CAAC,CAAC,IAAI,CACN,MAAM,CAAC,SAAS,CAAC;QAChB,eAAe,EAAE,eAAe;QAChC,SAAS,EAAE,SAAS;KACpB,CAAC,CACF,CAAC;AACH,CAAC;AAED,SAAS,eAAe;IACvB,OAAO,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,WAAW,CAAC,oBAAoB,CAAC,EAAE,CAAC,CAAC,CAAC;AAC3G,CAAC;AACD,SAAS,SAAS;IACjB,OAAO,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,WAAW,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;AAClG,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shivaedev/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "The Effect application host: HTTP and RPC serving, health, OTLP export, database and migrations, and development mode",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"effect"
|
|
@@ -44,38 +44,38 @@
|
|
|
44
44
|
"README.md"
|
|
45
45
|
],
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@shivaedev/changes": "0.5.
|
|
48
|
-
"@shivaedev/contract": "0.
|
|
49
|
-
"@shivaedev/errors": "0.1.
|
|
50
|
-
"@shivaedev/http": "0.
|
|
51
|
-
"@shivaedev/identity": "0.
|
|
47
|
+
"@shivaedev/changes": "0.5.1",
|
|
48
|
+
"@shivaedev/contract": "0.21.0",
|
|
49
|
+
"@shivaedev/errors": "0.1.1",
|
|
50
|
+
"@shivaedev/http": "0.2.0",
|
|
51
|
+
"@shivaedev/identity": "0.4.0",
|
|
52
52
|
"@shivaedev/local-postgres": "0.7.0",
|
|
53
|
-
"@shivaedev/sql": "0.
|
|
53
|
+
"@shivaedev/sql": "0.25.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@effect/platform-node": "4.0.
|
|
57
|
-
"@effect/sql-pg": "4.0.
|
|
58
|
-
"@effect/vitest": "4.0.
|
|
59
|
-
"@shivaedev/jobs": "0.22.
|
|
60
|
-
"@shivaedev/quality": "0.
|
|
61
|
-
"@shivaedev/test": "0.10.
|
|
62
|
-
"@shivaedev/test-sql": "0.
|
|
63
|
-
"@shivaedev/test-story": "0.
|
|
56
|
+
"@effect/platform-node": "4.0.6",
|
|
57
|
+
"@effect/sql-pg": "4.0.6",
|
|
58
|
+
"@effect/vitest": "4.0.6",
|
|
59
|
+
"@shivaedev/jobs": "0.22.1",
|
|
60
|
+
"@shivaedev/quality": "0.45.0",
|
|
61
|
+
"@shivaedev/test": "0.10.1",
|
|
62
|
+
"@shivaedev/test-sql": "0.11.0",
|
|
63
|
+
"@shivaedev/test-story": "0.24.0",
|
|
64
64
|
"@types/node": "24.19.2",
|
|
65
65
|
"@types/pg": "8.20.0",
|
|
66
66
|
"better-auth": "1.7.12",
|
|
67
|
-
"effect": "4.0.
|
|
67
|
+
"effect": "4.0.6",
|
|
68
68
|
"pg": "8.23.0",
|
|
69
69
|
"pg-boss": "12.38.0",
|
|
70
70
|
"typescript": "7.0.2",
|
|
71
71
|
"vitest": "5.0.5"
|
|
72
72
|
},
|
|
73
73
|
"peerDependencies": {
|
|
74
|
-
"@effect/platform-node": "4.0.
|
|
75
|
-
"@effect/sql-pg": "4.0.
|
|
76
|
-
"@effect/vitest": "4.0.
|
|
74
|
+
"@effect/platform-node": "4.0.6",
|
|
75
|
+
"@effect/sql-pg": "4.0.6",
|
|
76
|
+
"@effect/vitest": "4.0.6",
|
|
77
77
|
"better-auth": "1.7.12",
|
|
78
|
-
"effect": "4.0.
|
|
78
|
+
"effect": "4.0.6",
|
|
79
79
|
"pg": "8.23.0"
|
|
80
80
|
},
|
|
81
81
|
"peerDependenciesMeta": {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Data } from "effect";
|
|
2
|
+
|
|
3
|
+
export class TrustedProxyRequired extends Data.TaggedError("TrustedProxyRequired") {
|
|
4
|
+
override readonly message =
|
|
5
|
+
"A non-loopback session host requires proxy trust. Set TRUSTED_PROXY to your proxy's peer IP addresses or CIDR ranges, or set config.trustedProxy. The starter's Railway configuration supplies TRUSTED_PROXY automatically.";
|
|
6
|
+
}
|
package/src/hostChanges.ts
CHANGED
|
@@ -1,15 +1,39 @@
|
|
|
1
1
|
import { Context, Effect, Layer, Option, Stream } from "effect";
|
|
2
|
+
import { Headers } from "effect/http";
|
|
2
3
|
import type { Rpc, RpcGroup } from "effect/rpc";
|
|
4
|
+
import { CurrentViewer, type Viewer } from "@shivaedev/contract/readPolicy.ts";
|
|
3
5
|
import type { QueryReference } from "@shivaedev/contract/serverMetadata.ts";
|
|
6
|
+
import { Unauthorized } from "@shivaedev/errors/taxonomy.ts";
|
|
7
|
+
import { resolveSession, type SessionPolicy, type SessionShape } from "@shivaedev/identity/server/session.ts";
|
|
4
8
|
import { resolveChanges } from "@shivaedev/identity/server/sessionKeys.ts";
|
|
5
9
|
import type { AppChanges } from "./appHost.ts";
|
|
6
10
|
|
|
7
|
-
export function hostChanges<R extends Rpc.Any
|
|
11
|
+
export function hostChanges<R extends Rpc.Any, TSession extends SessionShape>(
|
|
12
|
+
group: RpcGroup.RpcGroup<R>,
|
|
13
|
+
changesService: typeof AppChanges,
|
|
14
|
+
sessions?: SessionPolicy<TSession>,
|
|
15
|
+
) {
|
|
8
16
|
return Layer.effectContext(
|
|
9
17
|
Effect.gen(function* () {
|
|
10
18
|
const changes = yield* Effect.serviceOption(changesService);
|
|
11
19
|
const services = yield* Effect.context<never>();
|
|
12
20
|
const handlers = new Map<string, unknown>();
|
|
21
|
+
function subscribe(rpc: Rpc.Any, queries: readonly QueryReference[]) {
|
|
22
|
+
return Stream.unwrap(
|
|
23
|
+
Effect.gen(function* () {
|
|
24
|
+
const { subscription, context } = yield* resolveChanges(rpc, queries);
|
|
25
|
+
const viewer = yield* CurrentViewer;
|
|
26
|
+
const authorized = authorizeSession(sessions, rpc._tag, viewer, subscription.authorized);
|
|
27
|
+
const stream = Option.isSome(changes)
|
|
28
|
+
? changes.value.subscribe({ ...subscription, authorized }, context)
|
|
29
|
+
: Stream.concat(Stream.succeed({ _tag: "Resync" } as const), Stream.never);
|
|
30
|
+
if (sessions === undefined || Option.isNone(viewer)) {
|
|
31
|
+
return stream;
|
|
32
|
+
}
|
|
33
|
+
return Stream.interruptWhen(stream, sessionRevoked(sessions, rpc._tag, viewer.value.id));
|
|
34
|
+
}),
|
|
35
|
+
);
|
|
36
|
+
}
|
|
13
37
|
for (const rpc of group.requests.values()) {
|
|
14
38
|
if (!rpc._tag.endsWith(".$changes")) {
|
|
15
39
|
continue;
|
|
@@ -19,16 +43,39 @@ export function hostChanges<R extends Rpc.Any>(group: RpcGroup.RpcGroup<R>, chan
|
|
|
19
43
|
handlers.set(rpc.key, provided);
|
|
20
44
|
continue;
|
|
21
45
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
handlers.set(rpc.key, { context: services, handler, tag: rpc._tag });
|
|
46
|
+
handlers.set(rpc.key, {
|
|
47
|
+
context: services,
|
|
48
|
+
handler: ({ queries }: { readonly queries: readonly QueryReference[] }) => subscribe(rpc, queries),
|
|
49
|
+
tag: rpc._tag,
|
|
50
|
+
});
|
|
30
51
|
}
|
|
31
52
|
return Context.makeUnsafe<Rpc.ToHandler<Extract<R, { readonly _tag: `${string}.$changes` }>>>(handlers);
|
|
32
53
|
}),
|
|
33
54
|
);
|
|
34
55
|
}
|
|
56
|
+
|
|
57
|
+
function authorizeSession<TSession extends SessionShape>(
|
|
58
|
+
sessions: SessionPolicy<TSession> | undefined,
|
|
59
|
+
tag: string,
|
|
60
|
+
viewer: Option.Option<Viewer>,
|
|
61
|
+
policy: Effect.Effect<boolean>,
|
|
62
|
+
) {
|
|
63
|
+
if (sessions === undefined) {
|
|
64
|
+
return policy;
|
|
65
|
+
}
|
|
66
|
+
return resolveSession(sessions, Headers.empty, tag).pipe(
|
|
67
|
+
Effect.map((current) => Option.isNone(viewer) || (Option.isSome(current) && current.value.user.id === viewer.value.id)),
|
|
68
|
+
Effect.catch(() => Effect.succeed(false)),
|
|
69
|
+
Effect.flatMap((allowed) => (allowed ? policy : Effect.succeed(false))),
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function sessionRevoked<TSession extends SessionShape>(sessions: SessionPolicy<TSession>, tag: string, id: string) {
|
|
74
|
+
return Effect.gen(function* () {
|
|
75
|
+
yield* Effect.sleep(sessions.streamRevalidationInterval ?? "30 seconds");
|
|
76
|
+
const current = yield* resolveSession(sessions, Headers.empty, tag).pipe(Effect.catchTag("AuthUnavailable", () => Effect.succeed(undefined)));
|
|
77
|
+
if (current !== undefined && (Option.isNone(current) || current.value.user.id !== id)) {
|
|
78
|
+
return yield* new Unauthorized({ message: "Authentication required" });
|
|
79
|
+
}
|
|
80
|
+
}).pipe(Effect.forever);
|
|
81
|
+
}
|
package/src/hostRpc.ts
CHANGED
|
@@ -14,13 +14,15 @@ export type { HostedReadPolicy };
|
|
|
14
14
|
export const rpcCacheControl: HttpEffect.PreResponseHandler = (_, response) =>
|
|
15
15
|
Effect.succeed(HttpServerResponse.setHeader(response, "cache-control", "private, no-store"));
|
|
16
16
|
|
|
17
|
-
export const hostRpc = Effect.fnUntraced(function* <R extends Rpc.Any>(group: RpcGroup.RpcGroup<R
|
|
17
|
+
export const hostRpc = Effect.fnUntraced(function* <R extends Rpc.Any>(group: RpcGroup.RpcGroup<R>, websocket = false) {
|
|
18
18
|
const guarded = group.middleware(HostedReadPolicy);
|
|
19
19
|
// Keep the policy inside identity middleware so it sees the request's viewer.
|
|
20
20
|
for (const rpc of guarded.requests.values()) {
|
|
21
21
|
Object.assign(rpc, { middlewares: new Set([HostedReadPolicy, ...rpc.middlewares]) });
|
|
22
22
|
}
|
|
23
|
-
|
|
23
|
+
const traced = tracedGroup(guarded);
|
|
24
|
+
const handler = websocket ? RpcServer.toHttpEffectWebsocket(traced) : RpcServer.toHttpEffect(traced);
|
|
25
|
+
return yield* handler.pipe(
|
|
24
26
|
Effect.provideService(HostedReadPolicy, (effect, request) => Effect.flatMap(CurrentViewer, (viewer) => authorizeReads(effect, request, viewer))),
|
|
25
27
|
);
|
|
26
28
|
});
|
package/src/hostRpcRoutes.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { hostRpc, rpcCacheControl } from "#hostRpc.ts";
|
|
|
14
14
|
import type { hostTransport } from "#hostTransport.ts";
|
|
15
15
|
import { redactingRpcSerialization } from "#redactingRpcSerialization.ts";
|
|
16
16
|
import type { schemaSensitiveKeys } from "#schemaSensitiveKeys.ts";
|
|
17
|
+
import { socketRpc } from "#socketRpc.ts";
|
|
17
18
|
|
|
18
19
|
export function hostRpcRoutes<TRpcs extends Rpc.Any, THandlers, TError, TRequirements, TSession extends SessionShape>(
|
|
19
20
|
options: {
|
|
@@ -32,8 +33,10 @@ export function hostRpcRoutes<TRpcs extends Rpc.Any, THandlers, TError, TRequire
|
|
|
32
33
|
return HttpRouter.use((router) =>
|
|
33
34
|
Effect.gen(function* () {
|
|
34
35
|
const wire = redactingRpcSerialization(yield* RpcSerialization.RpcSerialization, sensitive, sensitive.maskFailure);
|
|
35
|
-
const
|
|
36
|
+
const httpGroup = options.group.omit(...[...options.group.requests.keys()].filter((tag) => tag.endsWith(".$changes")));
|
|
37
|
+
const handler = yield* hostRpc(httpGroup).pipe(Effect.provideService(RpcSerialization.RpcSerialization, wire));
|
|
36
38
|
const guarded = localRpcGuard(handler, trustedProxy, nativeOrigins);
|
|
39
|
+
const websocket = yield* hostRpc(options.group, true).pipe(Effect.provideService(RpcSerialization.RpcSerialization, wire));
|
|
37
40
|
yield* router.add(
|
|
38
41
|
"OPTIONS",
|
|
39
42
|
options.rpcPath ?? "/rpc",
|
|
@@ -41,17 +44,29 @@ export function hostRpcRoutes<TRpcs extends Rpc.Any, THandlers, TError, TRequire
|
|
|
41
44
|
status: HttpStatus.fromLiteral("Forbidden"),
|
|
42
45
|
}),
|
|
43
46
|
);
|
|
44
|
-
yield* router.add(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
yield* router.add(
|
|
48
|
+
"GET",
|
|
49
|
+
options.rpcPath ?? "/rpc",
|
|
50
|
+
socketRpc(websocket, options, trustedProxy, nativeOrigins).pipe(Effect.provideService(RpcSerialization.RpcSerialization, wire)),
|
|
51
|
+
);
|
|
52
|
+
yield* router.add("POST", options.rpcPath ?? "/rpc", (request) =>
|
|
53
|
+
guarded.pipe(
|
|
47
54
|
Effect.provideService(HttpIncomingMessage.MaxBodySize, ByteSize.bytes(bodySize)),
|
|
48
55
|
Effect.provideService(HttpServerRequest.HttpServerRequest, boundedRpcRequest(request)),
|
|
49
56
|
Effect.provideService(RpcSerialization.RpcSerialization, wire),
|
|
50
|
-
|
|
51
|
-
|
|
57
|
+
HttpEffect.withPreResponseHandler(rpcCacheControl),
|
|
58
|
+
),
|
|
59
|
+
);
|
|
52
60
|
}),
|
|
53
61
|
).pipe(
|
|
54
|
-
Layer.provide(
|
|
62
|
+
Layer.provide(
|
|
63
|
+
Layer.merge(
|
|
64
|
+
middleware,
|
|
65
|
+
hostChanges(options.group, changesService, options.policy === "loopback" ? undefined : options.sessions).pipe(
|
|
66
|
+
Layer.provideMerge(options.handlers),
|
|
67
|
+
),
|
|
68
|
+
),
|
|
69
|
+
),
|
|
55
70
|
Layer.provide(RpcSerialization.layerNdjson),
|
|
56
71
|
);
|
|
57
72
|
}
|
package/src/hostTransport.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Config, Effect, Option, Result, Schema } from "effect";
|
|
|
2
2
|
import * as NetAddress from "effect/net/NetAddress";
|
|
3
3
|
import { TrustedProxy } from "@shivaedev/identity/server/trustedProxy.ts";
|
|
4
4
|
import { NativeOrigins } from "#nativeClientOrigins.ts";
|
|
5
|
+
import { TrustedProxyRequired } from "#TrustedProxyRequired.ts";
|
|
5
6
|
|
|
6
7
|
interface Options {
|
|
7
8
|
readonly config?: { readonly host?: string; readonly maxBodySize?: number; readonly trustedProxy?: string };
|
|
@@ -28,9 +29,7 @@ export const hostTransport = Effect.fn("Platform.hostTransport")(function* (opti
|
|
|
28
29
|
onSuccess: (address) => NetAddress.isLoopback(NetAddress.toCanonical(address)),
|
|
29
30
|
});
|
|
30
31
|
if (options.policy !== "loopback" && !loopback && trustedProxy === undefined) {
|
|
31
|
-
yield* Effect.
|
|
32
|
-
"RPC forwarding is refused without a trusted proxy. Set TRUSTED_PROXY or config.trustedProxy for this non-loopback HOST.",
|
|
33
|
-
);
|
|
32
|
+
return yield* Effect.fail(new TrustedProxyRequired());
|
|
34
33
|
}
|
|
35
34
|
const nativeOrigins = yield* Schema.decodeUnknownEffect(NativeOrigins)(options.nativeOrigins ?? []).pipe(
|
|
36
35
|
Effect.mapError((cause) => new Config.ConfigError(cause)),
|
|
@@ -46,7 +46,7 @@ export function nodeSubscriptionSignal(options: NodeSubscriptionSignalOptions):
|
|
|
46
46
|
}
|
|
47
47
|
nodeRequest.once("close", onRequestClose);
|
|
48
48
|
socket.once("close", onSocketClose);
|
|
49
|
-
if (nodeRequest.destroyed || socket.destroyed) {
|
|
49
|
+
if ((nodeRequest.destroyed && !nodeRequest.complete) || socket.destroyed) {
|
|
50
50
|
controller.abort();
|
|
51
51
|
}
|
|
52
52
|
let disposed = false;
|
package/src/socketRpc.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Effect, type Scope } from "effect";
|
|
2
|
+
import { Headers, HttpServerRequest, HttpServerResponse } from "effect/http";
|
|
3
|
+
import * as HttpStatus from "effect/http/HttpStatus";
|
|
4
|
+
import { localTransport } from "@shivaedev/identity/server/local.ts";
|
|
5
|
+
import { resolveSession, type SessionShape } from "@shivaedev/identity/server/session.ts";
|
|
6
|
+
import type { HostPolicy } from "#hostOptions.ts";
|
|
7
|
+
|
|
8
|
+
export function socketRpc<TSession extends SessionShape>(
|
|
9
|
+
handler: Effect.Effect<HttpServerResponse.HttpServerResponse, never, HttpServerRequest.HttpServerRequest | Scope.Scope>,
|
|
10
|
+
policy: HostPolicy<TSession>,
|
|
11
|
+
trustedProxy: string | undefined,
|
|
12
|
+
nativeOrigins: readonly string[],
|
|
13
|
+
) {
|
|
14
|
+
return Effect.gen(function* () {
|
|
15
|
+
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
16
|
+
if (!localTransport(trustedProxy, nativeOrigins)(request)) {
|
|
17
|
+
return HttpServerResponse.text("Local same-origin requests only", { status: HttpStatus.fromLiteral("Forbidden") });
|
|
18
|
+
}
|
|
19
|
+
if (policy.policy !== "loopback") {
|
|
20
|
+
yield* resolveSession(policy.sessions, Headers.empty, "$connect");
|
|
21
|
+
}
|
|
22
|
+
return yield* handler;
|
|
23
|
+
}).pipe(
|
|
24
|
+
Effect.catchTags({
|
|
25
|
+
AuthUnavailable: authUnavailable,
|
|
26
|
+
Forbidden: forbidden,
|
|
27
|
+
}),
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function authUnavailable() {
|
|
32
|
+
return Effect.succeed(HttpServerResponse.empty({ status: HttpStatus.fromLiteral("ServiceUnavailable") }));
|
|
33
|
+
}
|
|
34
|
+
function forbidden() {
|
|
35
|
+
return Effect.succeed(HttpServerResponse.empty({ status: HttpStatus.fromLiteral("Forbidden") }));
|
|
36
|
+
}
|