@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 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 NDJSON RPC at `/rpc`, and provides SQL, Reactivity and `AppChanges` to those Layers.
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 native `AtomRpc.Service`, NDJSON HTTP protocol and contract `bind` as other queries:
191
+ On the client, use the same contract `makeClient` and shared WebSocket as other queries:
192
192
 
193
193
  ```ts
194
- import { Layer } from "effect";
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
- class ConfigClient extends AtomRpc.Service<ConfigClient>()("app/ConfigClient", {
203
- group: PublicConfig,
204
- protocol: RpcClient.layerProtocolHttp({ url: "/rpc" }).pipe(Layer.provide([FetchHttpClient.layer, RpcSerialization.layerNdjson])),
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"}
@@ -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`;
@@ -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;AAChE,OAAO,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGhD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,wBAAgB,WAAW,CAAC,CAAC,SAAS,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,UAAU;mBAwBtC,GAAG,MAAM,WAAW;mBAG1F"}
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"}
@@ -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
- function handler({ queries }) {
18
- return Stream.unwrap(Effect.map(resolveChanges(rpc, queries), ({ subscription, context }) => Option.isSome(changes) ? changes.value.subscribe(subscription, context) : Stream.never));
19
- }
20
- handlers.set(rpc.key, { context: services, handler, tag: rpc._tag });
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
@@ -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;AAGhE,OAAO,EAAE,cAAc,EAAE,MAAM,2CAA2C,CAAC;AAG3E,MAAM,UAAU,WAAW,CAAoB,KAA2B,EAAE,cAAiC;IAC5G,OAAO,KAAK,CAAC,aAAa,CACzB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACnB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,EAAS,CAAC;QAChD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAmB,CAAC;QAC5C,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YAC3C,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;gBACrC,SAAS;YACV,CAAC;YACD,MAAM,QAAQ,GAAG,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACjD,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBACtD,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;gBAChC,SAAS;YACV,CAAC;YACD,SAAS,OAAO,CAAC,EAAE,OAAO,EAAmD;gBAC5E,OAAO,MAAM,CAAC,MAAM,CACnB,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE,YAAY,EAAE,OAAO,EAAE,EAAE,EAAE,CACtE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CACtF,CACD,CAAC;YACH,CAAC;YACD,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QACtE,CAAC;QACD,OAAO,OAAO,CAAC,UAAU,CAAqE,QAAQ,CAAC,CAAC;IACzG,CAAC,CAAC,CACF,CAAC;AACH,CAAC"}
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;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,OAAO,EAAE,aAAa,EAAe,MAAM,mCAAmC,CAAC;AAE/E,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAyC,MAAM,uCAAuC,CAAC;AAC9G,OAAO,EAAE,cAAc,EAAE,MAAM,2CAA2C,CAAC;AAG3E,MAAM,UAAU,WAAW,CAC1B,KAA2B,EAC3B,cAAiC,EACjC,QAAkC;IAElC,OAAO,KAAK,CAAC,aAAa,CACzB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACnB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,EAAS,CAAC;QAChD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAmB,CAAC;QAC5C,SAAS,SAAS,CAAC,GAAY,EAAE,OAAkC;YAClE,OAAO,MAAM,CAAC,MAAM,CACnB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;gBACnB,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;gBACtE,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,aAAa,CAAC;gBACpC,MAAM,UAAU,GAAG,gBAAgB,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,UAAU,CAAC,CAAC;gBACzF,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;oBACpC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,GAAG,YAAY,EAAE,UAAU,EAAE,EAAE,OAAO,CAAC;oBACnE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAW,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC5E,IAAI,QAAQ,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;oBACrD,OAAO,MAAM,CAAC;gBACf,CAAC;gBACD,OAAO,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,cAAc,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;YAC1F,CAAC,CAAC,CACF,CAAC;QACH,CAAC;QACD,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YAC3C,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;gBACrC,SAAS;YACV,CAAC;YACD,MAAM,QAAQ,GAAG,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACjD,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBACtD,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;gBAChC,SAAS;YACV,CAAC;YACD,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE;gBACrB,OAAO,EAAE,QAAQ;gBACjB,OAAO,EAAE,CAAC,EAAE,OAAO,EAAmD,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC;gBAClG,GAAG,EAAE,GAAG,CAAC,IAAI;aACb,CAAC,CAAC;QACJ,CAAC;QACD,OAAO,OAAO,CAAC,UAAU,CAAqE,QAAQ,CAAC,CAAC;IACzG,CAAC,CAAC,CACF,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CACxB,QAA6C,EAC7C,GAAW,EACX,MAA6B,EAC7B,MAA8B;IAE9B,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,MAAM,CAAC;IACf,CAAC;IACD,OAAO,cAAc,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,IAAI,CACvD,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EACvH,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EACzC,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CACvE,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAgC,QAAiC,EAAE,GAAW,EAAE,EAAU;IAChH,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC1B,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,0BAA0B,IAAI,YAAY,CAAC,CAAC;QACzE,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAC9I,IAAI,OAAO,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;YACvF,OAAO,KAAK,CAAC,CAAC,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAC,CAAC;QACxE,CAAC;IACF,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AACzB,CAAC"}
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>) => 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>>;
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
@@ -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,0zBASnE,CAAC"}
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
- return yield* RpcServer.toHttpEffect(tracedGroup(guarded)).pipe(Effect.provideService(HostedReadPolicy, (effect, request) => Effect.flatMap(CurrentViewer, (viewer) => authorizeReads(effect, request, viewer))));
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
@@ -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;IACjG,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,OAAO,KAAK,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAC9D,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
+ {"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;AAEnE,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;;;;;;;;0CA+BjD"}
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"}
@@ -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 handler = yield* hostRpc(options.group).pipe(Effect.provideService(RpcSerialization.RpcSerialization, wire));
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("POST", options.rpcPath ?? "/rpc", (request) => {
24
- HttpEffect.appendPreResponseHandlerUnsafe(request, rpcCacheControl);
25
- return guarded.pipe(Effect.provideService(HttpIncomingMessage.MaxBodySize, ByteSize.bytes(bodySize)), Effect.provideService(HttpServerRequest.HttpServerRequest, boundedRpcRequest(request)), Effect.provideService(RpcSerialization.RpcSerialization, wire));
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":"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"}
1
+ {"version":3,"file":"hostRpcRoutes.js","sourceRoot":"","sources":["../src/hostRpcRoutes.ts"],"names":[],"mappings":"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"}
@@ -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;AAKhE,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;;;;;6BA4BxB,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"}
@@ -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.logWarning("RPC forwarding is refused without a trusted proxy. Set TRUSTED_PROXY or config.trustedProxy for this non-loopback HOST.");
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":"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"}
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;QAC/C,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"}
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.1",
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.0",
48
- "@shivaedev/contract": "0.19.1",
49
- "@shivaedev/errors": "0.1.0",
50
- "@shivaedev/http": "0.1.1",
51
- "@shivaedev/identity": "0.3.1",
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.24.0"
53
+ "@shivaedev/sql": "0.25.0"
54
54
  },
55
55
  "devDependencies": {
56
- "@effect/platform-node": "4.0.5",
57
- "@effect/sql-pg": "4.0.5",
58
- "@effect/vitest": "4.0.5",
59
- "@shivaedev/jobs": "0.22.0",
60
- "@shivaedev/quality": "0.44.0",
61
- "@shivaedev/test": "0.10.0",
62
- "@shivaedev/test-sql": "0.10.2",
63
- "@shivaedev/test-story": "0.22.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.5",
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.5",
75
- "@effect/sql-pg": "4.0.5",
76
- "@effect/vitest": "4.0.5",
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.5",
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
+ }
@@ -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>(group: RpcGroup.RpcGroup<R>, changesService: typeof AppChanges) {
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
- function handler({ queries }: { readonly queries: readonly QueryReference[] }) {
23
- return Stream.unwrap(
24
- Effect.map(resolveChanges(rpc, queries), ({ subscription, context }) =>
25
- Option.isSome(changes) ? changes.value.subscribe(subscription, context) : Stream.never,
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
- return yield* RpcServer.toHttpEffect(tracedGroup(guarded)).pipe(
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
  });
@@ -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 handler = yield* hostRpc(options.group).pipe(Effect.provideService(RpcSerialization.RpcSerialization, wire));
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("POST", options.rpcPath ?? "/rpc", (request) => {
45
- HttpEffect.appendPreResponseHandlerUnsafe(request, rpcCacheControl);
46
- return guarded.pipe(
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(Layer.merge(middleware, hostChanges(options.group, changesService).pipe(Layer.provideMerge(options.handlers)))),
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
  }
@@ -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.logWarning(
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;
@@ -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
+ }