@shivaedev/platform 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +32 -4
- package/dist/rpc/identity.d.ts +7 -0
- package/dist/rpc/identity.d.ts.map +1 -1
- package/dist/rpc/identity.js +2 -0
- package/dist/rpc/identity.js.map +1 -1
- package/dist/rpc/middleware.d.ts +4 -1
- package/dist/rpc/middleware.d.ts.map +1 -1
- package/dist/rpc/middleware.js +4 -0
- package/dist/rpc/middleware.js.map +1 -1
- package/dist/server/rpc-server/session.d.ts +6 -1
- package/dist/server/rpc-server/session.d.ts.map +1 -1
- package/dist/server/rpc-server/session.js +13 -2
- package/dist/server/rpc-server/session.js.map +1 -1
- package/package.json +2 -2
- package/src/rpc/identity.ts +5 -0
- package/src/rpc/middleware.ts +9 -1
- package/src/server/rpc-server/session.ts +22 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.0 - 2026-10-16
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `SessionUser` and `SessionUserValue` expose the signed-in user’s id, name and email beside the unchanged `Identity`. `AuthenticatedUser` and `authenticatedUserLayer` provide both services from a `UserSessionShape` provider, with typed authentication failures and request-local profiles.
|
|
8
|
+
|
|
3
9
|
## 0.8.0 - 2026-10-14
|
|
4
10
|
|
|
5
11
|
### Breaking
|
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ The listener uses `HOST` or `127.0.0.1`, uses `PORT` or 3000, and reads `DATABAS
|
|
|
65
65
|
|
|
66
66
|
RPC request bodies are limited to 1 MiB by default. Set `MAX_BODY_SIZE` to a byte count or override it with `config.maxBodySize`, a nonnegative safe integer. An oversized NDJSON body returns a 4xx before any RPC handler runs; real HTTP tests cover fixed-length and chunked requests, byte counting, and configuration precedence. This limit applies to the host's RPC route.
|
|
67
67
|
|
|
68
|
-
RPC uses the existing `localRpcGuard`: it requires a loopback peer and Host, same-origin or absent Origin, and the NDJSON content type. Session lookup uses transport headers. With a session policy, contracts declare `Authenticated`, `MaybeAuthenticated` and `RequestTracing` as needed; the host supplies their implementations. Declared errors retain their Schema types. Applications still own resource authorization and use `rpcBoundary` when they need to map unexpected failures.
|
|
68
|
+
RPC uses the existing `localRpcGuard`: it requires a loopback peer and Host, same-origin or absent Origin, and the NDJSON content type. Session lookup uses transport headers. With a session policy, contracts declare `Authenticated`, `MaybeAuthenticated` and `RequestTracing` as needed; the host supplies their implementations. For `AuthenticatedUser`, merge `authenticatedUserLayer(sessions)` beside the handlers as shown below. Declared errors retain their Schema types. Applications still own resource authorization and use `rpcBoundary` when they need to map unexpected failures.
|
|
69
69
|
|
|
70
70
|
For local tools, use `policy: "loopback"` with contracts that do not use session middleware to serve RPC without a session provider. The guard requires a loopback peer and Host, absent or same-origin Origin, and NDJSON content type. This policy does not use `TRUSTED_PROXY` or `config.trustedProxy`. Set `rpcPath` to choose another native RPC route; `/rpc` remains the default.
|
|
71
71
|
|
|
@@ -129,6 +129,34 @@ export const sessions = {
|
|
|
129
129
|
|
|
130
130
|
The application's `auth` owns its storage and session configuration. A null lookup becomes a missing session. A thrown or rejected lookup is logged in redacted form and fails with the generic `AuthUnavailable` message. It remains a failure even on an RPC with optional authentication.
|
|
131
131
|
|
|
132
|
+
### Reading the signed-in user
|
|
133
|
+
|
|
134
|
+
When a handler needs the current user's name and email, declare `AuthenticatedUser` and read `SessionUser`. `Identity` keeps its `{ id }` shape; the new middleware supplies both services for that invocation. Its provider must return `user: { id, name, email }`, described by `UserSessionShape`.
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
import { Effect, Layer, Schema } from "effect";
|
|
138
|
+
import { Rpc, RpcGroup } from "effect/rpc";
|
|
139
|
+
import { SessionUser } from "@shivaedev/platform/rpc/identity.ts";
|
|
140
|
+
import { AuthenticatedUser } from "@shivaedev/platform/rpc/middleware.ts";
|
|
141
|
+
import { authenticatedUserLayer } from "@shivaedev/platform/rpc-server/session.ts";
|
|
142
|
+
|
|
143
|
+
export const Account = RpcGroup.make(
|
|
144
|
+
Rpc.make("Whoami", {
|
|
145
|
+
success: Schema.Struct({ id: Schema.String, name: Schema.String, email: Schema.String }),
|
|
146
|
+
}),
|
|
147
|
+
).middleware(AuthenticatedUser);
|
|
148
|
+
|
|
149
|
+
export const AccountHandlers = Account.toLayer({
|
|
150
|
+
Whoami: () => Effect.map(SessionUser, (user) => user),
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
export const AccountServer = Layer.merge(AccountHandlers, authenticatedUserLayer(sessions));
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Here `sessions` is the application's policy from the previous example, with a Better Auth lookup that returns all three user fields. Merge `authenticatedUserLayer(sessions)` alongside the handler Layer when using `appHost`; the host continues to supply its id-only and optional middleware. A missing session fails with typed `Unauthorized`; rejected origins fail with `Forbidden`, and provider failures remain `AuthUnavailable`. The profile contains only id, name and email, without exposing the rest of the provider's user record. Handlers do not query the provider's user table.
|
|
157
|
+
|
|
158
|
+
The starter's `notes.whoami` query returns this profile, and its note handlers use `SessionUser.id` for ownership. Its PostgreSQL/HTTP test checks concurrent users, anonymous requests and sign-out revocation.
|
|
159
|
+
|
|
132
160
|
### Transport headers and request ids
|
|
133
161
|
|
|
134
162
|
Session resolution reads `HttpServerRequest`: the HTTP POST or WebSocket upgrade request. Credentials and Origin supplied inside an RPC message cannot replace those transport headers. An application-owned middleware can use `transportHeaders(rpcHeaders)` for the same source of headers.
|
|
@@ -368,10 +396,10 @@ Import the module that defines the name; there is no package-root entry. Paths b
|
|
|
368
396
|
| `appHost.ts` | `appHost({ group, handlers, services, migrations?, sessions?, policy?, rpcPath?, assets?, routes?, config? })`, `appDatabase({ migrations, config? })`, `AppChanges`, `AppHostOptions`, `AppHttpHostOptions`, `AppLocalHostOptions`, `AppDatabaseOptions`: scoped HTTP and PostgreSQL composition with loopback defaults and optional proxy trust. |
|
|
369
397
|
| `errors/taxonomy.ts` | `NotFound`, `Unauthorized`, `Forbidden`, `BadRequest`, `Conflict`, `PreconditionFailed`, `TooManyRequests`, `AuthUnavailable`, `InternalServerError`, `ServiceUnavailable`: Schema tagged error classes with a `message`; `BadRequest` and `Conflict` also accept an optional `field`. |
|
|
370
398
|
| `errors/rejected-field.ts` | `rejectedField(unknown): Option<RejectedField>`; `RejectedField` is `{ field: string, message: string }`. |
|
|
371
|
-
| `rpc/identity.ts` | `RequestId`: string service. `Identity`: `IdentityValue`, `{ id: string }`. `OptionalIdentity`: `Option<IdentityValue>`. |
|
|
372
|
-
| `rpc/middleware.ts` | `RequestTracing`, `Authenticated`, `MaybeAuthenticated`: native RPC middleware declarations. |
|
|
399
|
+
| `rpc/identity.ts` | `RequestId`: string service. `Identity`: `IdentityValue`, `{ id: string }`. `OptionalIdentity`: `Option<IdentityValue>`. `SessionUser`: `SessionUserValue`, `{ id, name, email }`. |
|
|
400
|
+
| `rpc/middleware.ts` | `RequestTracing`, `Authenticated`, `AuthenticatedUser`, `MaybeAuthenticated`: native RPC middleware declarations. |
|
|
373
401
|
| `rpc-server/boundary.ts` | `rpcBoundary(schema, { unexpected, message? })`; `RpcBoundaryOptions<Error>`: declared failures pass through, unexpected failures become one declared rejection. |
|
|
374
|
-
| `rpc-server/session.ts` | `authenticatedLayer(policy)`, `maybeAuthenticatedLayer(policy)`, `resolveSession(policy, headers, rpc)`; `SessionShape`, `SessionProvider<Session>`, `SessionPolicy<Session>`. |
|
|
402
|
+
| `rpc-server/session.ts` | `authenticatedLayer(policy)`, `maybeAuthenticatedLayer(policy)`, `resolveSession(policy, headers, rpc)`; `SessionShape`, `SessionProvider<Session>`, `SessionPolicy<Session>`. `authenticatedUserLayer(policy)` requires `UserSessionShape` and implements `AuthenticatedUser`. |
|
|
375
403
|
| `rpc-server/adapters/better-auth-sessions.ts` | `betterAuthSessions(getSession)`; `GetSession<Session>` is a Promise lookup returning `Session \| null`. |
|
|
376
404
|
| `rpc-server/origin.ts` | `trustedOrigins({ allow, missing })`, `originRequest(headers, rpc)`; `OriginPolicy`, `OriginRequest`, `TrustedOriginsOptions`. |
|
|
377
405
|
| `rpc-server/local.ts` | `localRpcGuard(effect)`: HTTP RPC guard using native request and serialization services; `loopbackOnly(handler)`, `sameOrigin(request)`. |
|
package/dist/rpc/identity.d.ts
CHANGED
|
@@ -11,5 +11,12 @@ export declare class Identity extends Identity_base {
|
|
|
11
11
|
declare const OptionalIdentity_base: Context.ServiceClass<OptionalIdentity, "@shivaedev/platform/rpc/OptionalIdentity", Option.Option<IdentityValue>>;
|
|
12
12
|
export declare class OptionalIdentity extends OptionalIdentity_base {
|
|
13
13
|
}
|
|
14
|
+
export interface SessionUserValue extends IdentityValue {
|
|
15
|
+
readonly email: string;
|
|
16
|
+
readonly name: string;
|
|
17
|
+
}
|
|
18
|
+
declare const SessionUser_base: Context.ServiceClass<SessionUser, "@shivaedev/platform/rpc/SessionUser", SessionUserValue>;
|
|
19
|
+
export declare class SessionUser extends SessionUser_base {
|
|
20
|
+
}
|
|
14
21
|
export {};
|
|
15
22
|
//# sourceMappingURL=identity.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/rpc/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;;AAC9C,qBAAa,SAAU,SAAQ,cAAyE;CAAG;AAC3G,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACpB;;AACD,qBAAa,QAAS,SAAQ,aAA8E;CAAG;;AAC/G,qBAAa,gBAAiB,SAAQ,qBAA6G;CAAG"}
|
|
1
|
+
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/rpc/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;;AAC9C,qBAAa,SAAU,SAAQ,cAAyE;CAAG;AAC3G,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACpB;;AACD,qBAAa,QAAS,SAAQ,aAA8E;CAAG;;AAC/G,qBAAa,gBAAiB,SAAQ,qBAA6G;CAAG;AACtJ,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACtD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;;AACD,qBAAa,WAAY,SAAQ,gBAAuF;CAAG"}
|
package/dist/rpc/identity.js
CHANGED
|
@@ -5,4 +5,6 @@ export class Identity extends Context.Service()("@shivaedev/platform/rpc/Identit
|
|
|
5
5
|
}
|
|
6
6
|
export class OptionalIdentity extends Context.Service()("@shivaedev/platform/rpc/OptionalIdentity") {
|
|
7
7
|
}
|
|
8
|
+
export class SessionUser extends Context.Service()("@shivaedev/platform/rpc/SessionUser") {
|
|
9
|
+
}
|
|
8
10
|
//# sourceMappingURL=identity.js.map
|
package/dist/rpc/identity.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/rpc/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAe,MAAM,QAAQ,CAAC;AAC9C,MAAM,OAAO,SAAU,SAAQ,OAAO,CAAC,OAAO,EAAqB,CAAC,mCAAmC,CAAC;CAAG;AAI3G,MAAM,OAAO,QAAS,SAAQ,OAAO,CAAC,OAAO,EAA2B,CAAC,kCAAkC,CAAC;CAAG;AAC/G,MAAM,OAAO,gBAAiB,SAAQ,OAAO,CAAC,OAAO,EAAkD,CAAC,0CAA0C,CAAC;CAAG"}
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/rpc/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAe,MAAM,QAAQ,CAAC;AAC9C,MAAM,OAAO,SAAU,SAAQ,OAAO,CAAC,OAAO,EAAqB,CAAC,mCAAmC,CAAC;CAAG;AAI3G,MAAM,OAAO,QAAS,SAAQ,OAAO,CAAC,OAAO,EAA2B,CAAC,kCAAkC,CAAC;CAAG;AAC/G,MAAM,OAAO,gBAAiB,SAAQ,OAAO,CAAC,OAAO,EAAkD,CAAC,0CAA0C,CAAC;CAAG;AAKtJ,MAAM,OAAO,WAAY,SAAQ,OAAO,CAAC,OAAO,EAAiC,CAAC,qCAAqC,CAAC;CAAG"}
|
package/dist/rpc/middleware.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
2
|
import { RpcMiddleware } from "effect/rpc";
|
|
3
3
|
import { AuthUnavailable, Forbidden, Unauthorized } from "#errors/taxonomy.ts";
|
|
4
|
-
import type { Identity, OptionalIdentity, RequestId } from "./identity.ts";
|
|
4
|
+
import type { Identity, OptionalIdentity, RequestId, SessionUser } from "./identity.ts";
|
|
5
5
|
declare const RequestTracing_base: RpcMiddleware.ServiceClass<RequestTracing, "@shivaedev/platform/rpc/RequestTracing", RequestId, Schema.Never, never, never, false>;
|
|
6
6
|
export declare class RequestTracing extends RequestTracing_base {
|
|
7
7
|
}
|
|
@@ -11,5 +11,8 @@ export declare class Authenticated extends Authenticated_base {
|
|
|
11
11
|
declare const MaybeAuthenticated_base: RpcMiddleware.ServiceClass<MaybeAuthenticated, "@shivaedev/platform/rpc/MaybeAuthenticated", OptionalIdentity, Schema.Union<readonly [typeof AuthUnavailable, typeof Forbidden]>, never, never, false>;
|
|
12
12
|
export declare class MaybeAuthenticated extends MaybeAuthenticated_base {
|
|
13
13
|
}
|
|
14
|
+
declare const AuthenticatedUser_base: RpcMiddleware.ServiceClass<AuthenticatedUser, "@shivaedev/platform/rpc/AuthenticatedUser", Identity | SessionUser, Schema.Union<readonly [typeof Unauthorized, typeof AuthUnavailable, typeof Forbidden]>, never, never, false>;
|
|
15
|
+
export declare class AuthenticatedUser extends AuthenticatedUser_base {
|
|
16
|
+
}
|
|
14
17
|
export {};
|
|
15
18
|
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/rpc/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;;
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/rpc/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;;AACxF,qBAAa,cAAe,SAAQ,mBAKS;CAAG;;AAChD,qBAAa,aAAc,SAAQ,kBAOjC;CAAG;;AACL,qBAAa,kBAAmB,SAAQ,uBAKgE;CAAG;;AAC3G,qBAAa,iBAAkB,SAAQ,sBAOrC;CAAG"}
|
package/dist/rpc/middleware.js
CHANGED
|
@@ -9,4 +9,8 @@ export class Authenticated extends RpcMiddleware.Service()("@shivaedev/platform/
|
|
|
9
9
|
}
|
|
10
10
|
export class MaybeAuthenticated extends RpcMiddleware.Service()("@shivaedev/platform/rpc/MaybeAuthenticated", { error: Schema.Union([AuthUnavailable, Forbidden]) }) {
|
|
11
11
|
}
|
|
12
|
+
export class AuthenticatedUser extends RpcMiddleware.Service()("@shivaedev/platform/rpc/AuthenticatedUser", {
|
|
13
|
+
error: Schema.Union([Unauthorized, AuthUnavailable, Forbidden]),
|
|
14
|
+
}) {
|
|
15
|
+
}
|
|
12
16
|
//# sourceMappingURL=middleware.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/rpc/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAE/E,MAAM,OAAO,cAAe,SAAQ,aAAa,CAAC,OAAO,EAKtD,CAAC,wCAAwC,CAAC;CAAG;AAChD,MAAM,OAAO,aAAc,SAAQ,aAAa,CAAC,OAAO,EAKrD,CAAC,uCAAuC,EAAE;IAC5C,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,EAAE,eAAe,EAAE,SAAS,CAAC,CAAC;CAC/D,CAAC;CAAG;AACL,MAAM,OAAO,kBAAmB,SAAQ,aAAa,CAAC,OAAO,EAK1D,CAAC,4CAA4C,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC;CAAG"}
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/rpc/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAE/E,MAAM,OAAO,cAAe,SAAQ,aAAa,CAAC,OAAO,EAKtD,CAAC,wCAAwC,CAAC;CAAG;AAChD,MAAM,OAAO,aAAc,SAAQ,aAAa,CAAC,OAAO,EAKrD,CAAC,uCAAuC,EAAE;IAC5C,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,EAAE,eAAe,EAAE,SAAS,CAAC,CAAC;CAC/D,CAAC;CAAG;AACL,MAAM,OAAO,kBAAmB,SAAQ,aAAa,CAAC,OAAO,EAK1D,CAAC,4CAA4C,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC;CAAG;AAC3G,MAAM,OAAO,iBAAkB,SAAQ,aAAa,CAAC,OAAO,EAKzD,CAAC,2CAA2C,EAAE;IAChD,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,EAAE,eAAe,EAAE,SAAS,CAAC,CAAC;CAC/D,CAAC;CAAG"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Effect, Layer, Option } from "effect";
|
|
2
2
|
import type { Headers } from "effect/http";
|
|
3
3
|
import { type AuthUnavailable, Forbidden } from "#errors/taxonomy.ts";
|
|
4
|
-
import {
|
|
4
|
+
import { type SessionUserValue } from "#rpc/identity.ts";
|
|
5
|
+
import { Authenticated, AuthenticatedUser, MaybeAuthenticated } from "#rpc/middleware.ts";
|
|
5
6
|
import { type OriginPolicy } from "./origin.ts";
|
|
6
7
|
export interface SessionShape {
|
|
7
8
|
readonly user: {
|
|
@@ -18,4 +19,8 @@ export interface SessionPolicy<TSession extends SessionShape> {
|
|
|
18
19
|
export declare const resolveSession: <TSession extends SessionShape>(policy: SessionPolicy<TSession>, headers: Headers.Headers, rpc: string) => Effect.Effect<Option.Option<TSession>, AuthUnavailable | Forbidden>;
|
|
19
20
|
export declare function authenticatedLayer<TSession extends SessionShape>(policy: SessionPolicy<TSession>): Layer.Layer<Authenticated>;
|
|
20
21
|
export declare function maybeAuthenticatedLayer<TSession extends SessionShape>(policy: SessionPolicy<TSession>): Layer.Layer<MaybeAuthenticated>;
|
|
22
|
+
export interface UserSessionShape extends SessionShape {
|
|
23
|
+
readonly user: SessionUserValue;
|
|
24
|
+
}
|
|
25
|
+
export declare function authenticatedUserLayer<TSession extends UserSessionShape>(policy: SessionPolicy<TSession>): Layer.Layer<AuthenticatedUser>;
|
|
21
26
|
//# sourceMappingURL=session.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/server/rpc-server/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,KAAK,eAAe,EAAE,SAAS,EAAgB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/server/rpc-server/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,KAAK,eAAe,EAAE,SAAS,EAAgB,MAAM,qBAAqB,CAAC;AACpF,OAAO,EAA2C,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAClG,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC1F,OAAO,EAAE,KAAK,YAAY,EAAiB,MAAM,aAAa,CAAC;AAE/D,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,IAAI,EAAE;QACd,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;KACpB,CAAC;CACF;AACD,MAAM,WAAW,eAAe,CAAC,QAAQ,SAAS,YAAY;IAC7D,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC,CAAC;CACpG;AACD,MAAM,WAAW,aAAa,CAAC,QAAQ,SAAS,YAAY;IAC3D,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC;CAC7C;AACD,eAAO,MAAM,cAAc,EAAE,CAAC,QAAQ,SAAS,YAAY,EAC1D,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,EAC/B,OAAO,EAAE,OAAO,CAAC,OAAO,EACxB,GAAG,EAAE,MAAM,KACP,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,eAAe,GAAG,SAAS,CAQrE,CAAC;AAIH,wBAAgB,kBAAkB,CAAC,QAAQ,SAAS,YAAY,EAAE,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,CAW7H;AACD,wBAAgB,uBAAuB,CAAC,QAAQ,SAAS,YAAY,EAAE,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAWvI;AACD,MAAM,WAAW,gBAAiB,SAAQ,YAAY;IACrD,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;CAChC;AACD,wBAAgB,sBAAsB,CAAC,QAAQ,SAAS,gBAAgB,EAAE,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAgBzI"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Effect, Layer, Option } from "effect";
|
|
2
2
|
import { Forbidden, Unauthorized } from "#errors/taxonomy.ts";
|
|
3
|
-
import { Identity, OptionalIdentity } from "#rpc/identity.ts";
|
|
4
|
-
import { Authenticated, MaybeAuthenticated } from "#rpc/middleware.ts";
|
|
3
|
+
import { Identity, OptionalIdentity, SessionUser } from "#rpc/identity.ts";
|
|
4
|
+
import { Authenticated, AuthenticatedUser, MaybeAuthenticated } from "#rpc/middleware.ts";
|
|
5
5
|
import { originRequest } from "./origin.js";
|
|
6
6
|
import { transportHeaders } from "./transportHeaders.js";
|
|
7
7
|
export const resolveSession = Effect.fn("PlatformRpc.resolveSession")(function* (policy, headers, rpc) {
|
|
@@ -34,4 +34,15 @@ export function maybeAuthenticatedLayer(policy) {
|
|
|
34
34
|
return yield* effect.pipe(Effect.provideService(OptionalIdentity, Option.some({ id })), Effect.annotateLogs({ userId: id }));
|
|
35
35
|
}));
|
|
36
36
|
}
|
|
37
|
+
export function authenticatedUserLayer(policy) {
|
|
38
|
+
return Layer.succeed(AuthenticatedUser, (effect, { headers, rpc }) => Effect.gen(function* () {
|
|
39
|
+
const session = yield* resolveSession(policy, headers, rpc._tag);
|
|
40
|
+
if (Option.isNone(session)) {
|
|
41
|
+
return yield* new Unauthorized({ message: "Authentication required" });
|
|
42
|
+
}
|
|
43
|
+
const { name, email } = session.value.user;
|
|
44
|
+
const id = yield* identified(session.value.user.id);
|
|
45
|
+
return yield* effect.pipe(Effect.provideService(Identity, { id }), Effect.provideService(SessionUser, { email, id, name }), Effect.annotateLogs({ userId: id }));
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
37
48
|
//# sourceMappingURL=session.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/server/rpc-server/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE/C,OAAO,EAAwB,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACpF,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/server/rpc-server/session.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shivaedev/platform",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Opinionated Effect application infrastructure for ShivaeDev services",
|
|
5
5
|
"homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/platform#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -327,7 +327,7 @@
|
|
|
327
327
|
"dependencies": {
|
|
328
328
|
"@shivaedev/effect-contract": "0.5.0",
|
|
329
329
|
"@shivaedev/effect-sql": "0.3.0",
|
|
330
|
-
"@shivaedev/types": "0.2.
|
|
330
|
+
"@shivaedev/types": "0.2.1"
|
|
331
331
|
},
|
|
332
332
|
"devDependencies": {
|
|
333
333
|
"@effect/platform-node": "4.0.4",
|
package/src/rpc/identity.ts
CHANGED
|
@@ -5,3 +5,8 @@ export interface IdentityValue {
|
|
|
5
5
|
}
|
|
6
6
|
export class Identity extends Context.Service<Identity, IdentityValue>()("@shivaedev/platform/rpc/Identity") {}
|
|
7
7
|
export class OptionalIdentity extends Context.Service<OptionalIdentity, Option.Option<IdentityValue>>()("@shivaedev/platform/rpc/OptionalIdentity") {}
|
|
8
|
+
export interface SessionUserValue extends IdentityValue {
|
|
9
|
+
readonly email: string;
|
|
10
|
+
readonly name: string;
|
|
11
|
+
}
|
|
12
|
+
export class SessionUser extends Context.Service<SessionUser, SessionUserValue>()("@shivaedev/platform/rpc/SessionUser") {}
|
package/src/rpc/middleware.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
2
|
import { RpcMiddleware } from "effect/rpc";
|
|
3
3
|
import { AuthUnavailable, Forbidden, Unauthorized } from "#errors/taxonomy.ts";
|
|
4
|
-
import type { Identity, OptionalIdentity, RequestId } from "./identity.ts";
|
|
4
|
+
import type { Identity, OptionalIdentity, RequestId, SessionUser } from "./identity.ts";
|
|
5
5
|
export class RequestTracing extends RpcMiddleware.Service<
|
|
6
6
|
RequestTracing,
|
|
7
7
|
{
|
|
@@ -22,3 +22,11 @@ export class MaybeAuthenticated extends RpcMiddleware.Service<
|
|
|
22
22
|
provides: OptionalIdentity;
|
|
23
23
|
}
|
|
24
24
|
>()("@shivaedev/platform/rpc/MaybeAuthenticated", { error: Schema.Union([AuthUnavailable, Forbidden]) }) {}
|
|
25
|
+
export class AuthenticatedUser extends RpcMiddleware.Service<
|
|
26
|
+
AuthenticatedUser,
|
|
27
|
+
{
|
|
28
|
+
provides: Identity | SessionUser;
|
|
29
|
+
}
|
|
30
|
+
>()("@shivaedev/platform/rpc/AuthenticatedUser", {
|
|
31
|
+
error: Schema.Union([Unauthorized, AuthUnavailable, Forbidden]),
|
|
32
|
+
}) {}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Effect, Layer, Option } from "effect";
|
|
2
2
|
import type { Headers } from "effect/http";
|
|
3
3
|
import { type AuthUnavailable, Forbidden, Unauthorized } from "#errors/taxonomy.ts";
|
|
4
|
-
import { Identity, OptionalIdentity } from "#rpc/identity.ts";
|
|
5
|
-
import { Authenticated, MaybeAuthenticated } from "#rpc/middleware.ts";
|
|
4
|
+
import { Identity, OptionalIdentity, SessionUser, type SessionUserValue } from "#rpc/identity.ts";
|
|
5
|
+
import { Authenticated, AuthenticatedUser, MaybeAuthenticated } from "#rpc/middleware.ts";
|
|
6
6
|
import { type OriginPolicy, originRequest } from "./origin.ts";
|
|
7
7
|
import { transportHeaders } from "./transportHeaders.ts";
|
|
8
8
|
export interface SessionShape {
|
|
@@ -57,3 +57,23 @@ export function maybeAuthenticatedLayer<TSession extends SessionShape>(policy: S
|
|
|
57
57
|
}),
|
|
58
58
|
);
|
|
59
59
|
}
|
|
60
|
+
export interface UserSessionShape extends SessionShape {
|
|
61
|
+
readonly user: SessionUserValue;
|
|
62
|
+
}
|
|
63
|
+
export function authenticatedUserLayer<TSession extends UserSessionShape>(policy: SessionPolicy<TSession>): Layer.Layer<AuthenticatedUser> {
|
|
64
|
+
return Layer.succeed(AuthenticatedUser, (effect, { headers, rpc }) =>
|
|
65
|
+
Effect.gen(function* () {
|
|
66
|
+
const session = yield* resolveSession(policy, headers, rpc._tag);
|
|
67
|
+
if (Option.isNone(session)) {
|
|
68
|
+
return yield* new Unauthorized({ message: "Authentication required" });
|
|
69
|
+
}
|
|
70
|
+
const { name, email } = session.value.user;
|
|
71
|
+
const id = yield* identified(session.value.user.id);
|
|
72
|
+
return yield* effect.pipe(
|
|
73
|
+
Effect.provideService(Identity, { id }),
|
|
74
|
+
Effect.provideService(SessionUser, { email, id, name }),
|
|
75
|
+
Effect.annotateLogs({ userId: id }),
|
|
76
|
+
);
|
|
77
|
+
}),
|
|
78
|
+
);
|
|
79
|
+
}
|