@shivaedev/platform 0.17.0 → 0.18.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 +22 -0
- package/README.md +130 -12
- package/dist/rpc/identity.d.ts +8 -2
- 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/rpc/publicConfig.d.ts +3 -0
- package/dist/rpc/publicConfig.d.ts.map +1 -0
- package/dist/rpc/publicConfig.js +8 -0
- package/dist/rpc/publicConfig.js.map +1 -0
- package/dist/server/better-auth/mount.d.ts +61 -0
- package/dist/server/better-auth/mount.d.ts.map +1 -0
- package/dist/server/better-auth/mount.js +59 -0
- package/dist/server/better-auth/mount.js.map +1 -0
- package/dist/server/better-auth/testing.d.ts +50 -0
- package/dist/server/better-auth/testing.d.ts.map +1 -0
- package/dist/server/better-auth/testing.js +18 -0
- package/dist/server/better-auth/testing.js.map +1 -0
- package/dist/server/databaseLayer.d.ts +2 -1
- package/dist/server/databaseLayer.d.ts.map +1 -1
- package/dist/server/databaseLayer.js +2 -0
- package/dist/server/databaseLayer.js.map +1 -1
- package/dist/server/rpc-server/adapters/betterAuthSessions.d.ts +3 -1
- package/dist/server/rpc-server/adapters/betterAuthSessions.d.ts.map +1 -1
- package/dist/server/rpc-server/adapters/betterAuthSessions.js +13 -22
- package/dist/server/rpc-server/adapters/betterAuthSessions.js.map +1 -1
- package/dist/server/rpc-server/publicConfigLayer.d.ts +4 -0
- package/dist/server/rpc-server/publicConfigLayer.d.ts.map +1 -0
- package/dist/server/rpc-server/publicConfigLayer.js +8 -0
- package/dist/server/rpc-server/publicConfigLayer.js.map +1 -0
- package/dist/server/rpc-server/session.d.ts +6 -2
- package/dist/server/rpc-server/session.d.ts.map +1 -1
- package/dist/server/rpc-server/session.js +16 -6
- package/dist/server/rpc-server/session.js.map +1 -1
- package/package.json +10 -6
- package/src/rpc/identity.ts +8 -2
- package/src/rpc/middleware.ts +7 -1
- package/src/rpc/publicConfig.ts +9 -0
- package/src/server/better-auth/mount.ts +68 -0
- package/src/server/better-auth/testing.ts +21 -0
- package/src/server/databaseLayer.ts +4 -1
- package/src/server/rpc-server/adapters/betterAuthSessions.ts +19 -23
- package/src/server/rpc-server/publicConfigLayer.ts +11 -0
- package/src/server/rpc-server/session.ts +38 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.18.0 - 2026-10-18
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- effect-sql exports `clientOptions(options, { snakeCase? })` and `SqlNameTransforms` from `clientOptions.ts`. New clients configured with this helper use Effect SQL's native camelCase-to-snake_case query names and snake_case-to-camelCase results by default. Pass `{ snakeCase: false }` to preserve native names, or supply native transform functions in the options.
|
|
8
|
+
- Platform's app database and app host configuration accept native `transformQueryNames` and `transformResultNames`, so applications can use effect-sql's client options without replacing the host's database layer.
|
|
9
|
+
- Declare public client configuration once with `publicConfig(schema)` from `rpc/publicConfig.ts`. Load it at host startup with `publicConfigLayer` from `server/rpc-server/publicConfigLayer.ts` and read the generated contract through native atoms and effect-react's `useQuery`. Only declared fields reach the client; missing or invalid required values fail startup with a typed `ConfigError`.
|
|
10
|
+
- `@shivaedev/platform/better-auth.ts` exposes `betterAuthMount(options)`, returning a typed Better Auth instance and native `/api/auth/*` route Layer with a 503 fallback, trusted-peer forwarded-IP handling and Config-controlled rate limiting. Applications supply their Better Auth options and RPC session policy.
|
|
11
|
+
- `authDatabase(url)` scopes the Better Auth PostgreSQL pool, logs idle-client errors at Debug and waits for clients to disconnect on release. The entry uses the existing Better Auth integration and the optional `pg` peer.
|
|
12
|
+
- Platform exports `authTestUtils(options?)` and `signedInUser(auth, fields)` from `server/better-auth/testing.ts` for typed Better Auth test utilities and signed-in story preconditions with user fields and roles, without OAuth or password hashing.
|
|
13
|
+
- Test-story exports `seedCookies(cookies)` and `DomCookie` from `seedCookies.ts` to seed happy-dom's cookie container, including HttpOnly session cookies accepted by a real HTTP host.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Newly generated applications configure snake_case SQL columns through effect-sql. The starter migrates existing note ownership and word-count columns while retaining camelCase model fields.
|
|
18
|
+
- Import `betterAuthMount` and `authDatabase` from `@shivaedev/platform/server/better-auth/mount.ts`, the module's own path under the package's wildcard export. The separate `@shivaedev/platform/better-auth.ts` entry is removed.
|
|
19
|
+
|
|
20
|
+
### Breaking
|
|
21
|
+
|
|
22
|
+
- Provider accounts load when a handler asks for them. Replace `SessionUser.providerAccounts` array reads with `yield* (yield* SessionUserWithProviderAccounts).providerAccounts`, declare `AuthenticatedUserWithProviderAccounts` on the RPC, and install `authenticatedUserWithProviderAccountsLayer(sessions)`. Add `AuthUnavailable` and `Unauthorized` to the RPC's error schema because account lookup failures now fail the reading handler. Base `SessionUserValue` no longer has `providerAccounts`.
|
|
23
|
+
- A custom session provider supplies `user.providerAccounts` as `Effect<readonly ProviderAccountValue[], AuthUnavailable | Unauthorized>`, described by the new `ProviderAccountsSessionShape`, instead of an array. The request boundary memoizes this Effect and projects its results to provider and account IDs. `betterAuthSessions(getSession, getProviderAccounts)` supplies this shape; without a lookup its session user has no accounts field. `SessionUserWithProviderAccountsValue` describes the accounts-capable handler profile. Identity-only requests do not call the lookup, and repeated reads share one lookup per request.
|
|
24
|
+
|
|
3
25
|
## 0.17.0 - 2026-10-18
|
|
4
26
|
|
|
5
27
|
### Added
|
package/README.md
CHANGED
|
@@ -144,6 +144,65 @@ For a deployment behind a proxy, set `HOST=0.0.0.0` and `TRUSTED_PROXY` to a com
|
|
|
144
144
|
|
|
145
145
|
A subscription handler reads `AppChanges` and returns `changes.stream(keys)` after its authorization check. Writes use `transact` and `invalidateOnCommit` from effect-sql: local streams receive committed invalidations and rollback emits none. For delivery between processes, set `config.changes: { channel: "app_changes" }`; the host binds PostgreSQL LISTEN/NOTIFY to the same SQL pool and resyncs streams when its listener recovers. The native heartbeat and retry options may be supplied in `changes`. A worker can provide `appDatabase({ migrations, config })` to its service Layer to share this database and delivery composition without starting HTTP.
|
|
146
146
|
|
|
147
|
+
### Public client configuration
|
|
148
|
+
|
|
149
|
+
Use one `publicConfig` declaration per application as the one path for server configuration that a client needs. Declare only public fields in a shared Schema; the declaration creates a standard effect-contract query, so applications write no config RPC. Keep private configuration in server modules.
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
import { Schema } from "effect";
|
|
153
|
+
import { publicConfig } from "@shivaedev/platform/rpc/publicConfig.ts";
|
|
154
|
+
|
|
155
|
+
export const PublicConfig = publicConfig(
|
|
156
|
+
Schema.Struct({ installUrl: Schema.URL }).pipe(
|
|
157
|
+
Schema.encodeKeys({ installUrl: "INSTALL_URL" }),
|
|
158
|
+
),
|
|
159
|
+
);
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
On the server, merge the declaration into the application's group and its generated handler Layer into the handlers:
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
import { Layer } from "effect";
|
|
166
|
+
import { appHost } from "@shivaedev/platform/server/appHost.ts";
|
|
167
|
+
import { publicConfigLayer } from "@shivaedev/platform/server/rpc-server/publicConfigLayer.ts";
|
|
168
|
+
import { Api, Handlers, sessions } from "./app.ts";
|
|
169
|
+
import { PublicConfig } from "./public-config.ts";
|
|
170
|
+
|
|
171
|
+
export const Server = appHost({
|
|
172
|
+
group: Api.merge(PublicConfig),
|
|
173
|
+
handlers: Layer.merge(Handlers, publicConfigLayer(PublicConfig)),
|
|
174
|
+
sessions,
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`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.
|
|
179
|
+
|
|
180
|
+
On the client, use the same native `AtomRpc.Service`, NDJSON HTTP protocol and effect-contract `bind` as other queries:
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
import { Layer } from "effect";
|
|
184
|
+
import { FetchHttpClient } from "effect/http";
|
|
185
|
+
import * as AtomRpc from "effect/reactivity/AtomRpc";
|
|
186
|
+
import { RpcClient, RpcSerialization } from "effect/rpc";
|
|
187
|
+
import { bind } from "@shivaedev/effect-contract/bind.ts";
|
|
188
|
+
import { useQuery } from "@shivaedev/effect-react/client/resultState.ts";
|
|
189
|
+
import { PublicConfig } from "./public-config.ts";
|
|
190
|
+
|
|
191
|
+
class ConfigClient extends AtomRpc.Service<ConfigClient>()("app/ConfigClient", {
|
|
192
|
+
group: PublicConfig,
|
|
193
|
+
protocol: RpcClient.layerProtocolHttp({ url: "/rpc" }).pipe(
|
|
194
|
+
Layer.provide([FetchHttpClient.layer, RpcSerialization.layerNdjson]),
|
|
195
|
+
),
|
|
196
|
+
}) {}
|
|
197
|
+
const config = bind(PublicConfig, ConfigClient);
|
|
198
|
+
|
|
199
|
+
export function usePublicConfig() {
|
|
200
|
+
return useQuery(config.get.query());
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Render inside the application's native registry provider. The query's `data` is an `Option<{ readonly installUrl: URL }>`: the same codec validates server configuration and decodes the RPC response on the client. Public configuration uses the existing atom registry and query lifecycle.
|
|
205
|
+
|
|
147
206
|
### 1. Once per application: contracts and session policy
|
|
148
207
|
|
|
149
208
|
Declare the RPC group in a shared module:
|
|
@@ -153,7 +212,7 @@ import { Effect, Schema } from "effect";
|
|
|
153
212
|
import { Rpc, RpcGroup } from "effect/rpc";
|
|
154
213
|
import { Forbidden } from "@shivaedev/platform/errors/taxonomy.ts";
|
|
155
214
|
import { Identity } from "@shivaedev/platform/rpc/identity.ts";
|
|
156
|
-
import { Authenticated
|
|
215
|
+
import { Authenticated } from "@shivaedev/platform/rpc/middleware.ts";
|
|
157
216
|
|
|
158
217
|
export const Account = RpcGroup.make(
|
|
159
218
|
Rpc.make("ReadOwn", {
|
|
@@ -161,7 +220,7 @@ export const Account = RpcGroup.make(
|
|
|
161
220
|
payload: { userId: Schema.String },
|
|
162
221
|
success: Schema.String,
|
|
163
222
|
}),
|
|
164
|
-
).middleware(Authenticated)
|
|
223
|
+
).middleware(Authenticated);
|
|
165
224
|
|
|
166
225
|
export const Handlers = Account.toLayer({
|
|
167
226
|
ReadOwn: ({ userId }) =>
|
|
@@ -222,9 +281,14 @@ Here `sessions` is the application's policy from the previous example, with a Be
|
|
|
222
281
|
|
|
223
282
|
#### Reading provider account IDs
|
|
224
283
|
|
|
225
|
-
|
|
284
|
+
Pass Better Auth's account lookup as the second argument to `betterAuthSessions`. Declare `AuthenticatedUserWithProviderAccounts` on account-reading RPCs and merge `authenticatedUserWithProviderAccountsLayer(sessions)` alongside their handlers. This boundary requires a provider whose user has a `providerAccounts` Effect; a provider without the second lookup does not satisfy that type.
|
|
226
285
|
|
|
227
286
|
```ts
|
|
287
|
+
import { SessionUserWithProviderAccounts } from "@shivaedev/platform/rpc/identity.ts";
|
|
288
|
+
import { AuthenticatedUserWithProviderAccounts } from "@shivaedev/platform/rpc/middleware.ts";
|
|
289
|
+
import { AuthUnavailable, Unauthorized } from "@shivaedev/platform/errors/taxonomy.ts";
|
|
290
|
+
import { authenticatedUserWithProviderAccountsLayer } from "@shivaedev/platform/server/rpc-server/session.ts";
|
|
291
|
+
|
|
228
292
|
const sessions = {
|
|
229
293
|
origin: trustedOrigins({ allow: ["https://app.example"], missing: "reject" }),
|
|
230
294
|
provider: betterAuthSessions(
|
|
@@ -233,14 +297,18 @@ const sessions = {
|
|
|
233
297
|
),
|
|
234
298
|
};
|
|
235
299
|
|
|
236
|
-
const githubAccount = Effect.
|
|
237
|
-
user
|
|
238
|
-
|
|
300
|
+
const githubAccount = Effect.gen(function* () {
|
|
301
|
+
const user = yield* SessionUserWithProviderAccounts;
|
|
302
|
+
const accounts = yield* user.providerAccounts;
|
|
303
|
+
return accounts.find((account) => account.providerId === "github");
|
|
304
|
+
});
|
|
239
305
|
```
|
|
240
306
|
|
|
241
|
-
|
|
307
|
+
Declare `Schema.Union([AuthUnavailable, Unauthorized])` in the account-reading RPC's `error` schema: lookup failures now come from the handler's Effect. An outage fails with `AuthUnavailable`; a session that expires before the lookup fails with `Unauthorized`. Neither failure exposes provider exception details.
|
|
242
308
|
|
|
243
|
-
|
|
309
|
+
The lookup uses the same transport headers and runs only when the handler evaluates `user.providerAccounts`. The boundary memoizes its result in that invocation's Effect context, including concurrent reads, without sharing it across requests or users. Identity-only handlers never run the accounts lookup. `SessionUserWithProviderAccountsValue.providerAccounts` is a required `Effect<readonly ProviderAccountValue[], AuthUnavailable | Unauthorized>`. Each result contains only `{ providerId: string, accountId: string }`; for example, GitHub's numeric user ID is the string `"123"`, not Better Auth's account-row id. `SessionUser` and `SessionUserValue` contain the base profile without accounts.
|
|
310
|
+
|
|
311
|
+
A custom session provider supplies `user.providerAccounts` as a lazy Effect with that same result and failure type, described by `ProviderAccountsSessionShape`. The boundary owns request-local memoization and the reduction to public IDs; custom providers need not cache or project the returned account objects.
|
|
244
312
|
|
|
245
313
|
### Requiring an admin
|
|
246
314
|
|
|
@@ -441,6 +509,53 @@ export async function consumeSubscription(
|
|
|
441
509
|
|
|
442
510
|
The signal aborts on an additional signal, socket close, incomplete request close or an already destroyed connection. Normal completion of a request does not abort a continuing response. Without an explicit `nodeRequest`, the helper can read one from an srvx Web request's `runtime.node.req`. `dispose()` removes Node listeners and can be called repeatedly.
|
|
443
511
|
|
|
512
|
+
### Mount Better Auth
|
|
513
|
+
|
|
514
|
+
`betterAuthMount(options)` returns the Better Auth instance and a native route Layer for `/api/auth/*`. It accepts Better Auth options, including application plugins and session policy. Merge `routes` into the HTTP host and use `auth.api.getSession` with `betterAuthSessions` for the application's RPC session provider.
|
|
515
|
+
|
|
516
|
+
```ts
|
|
517
|
+
import { Config, Effect } from "effect";
|
|
518
|
+
import { authDatabase, betterAuthMount } from "@shivaedev/platform/server/better-auth/mount.ts";
|
|
519
|
+
|
|
520
|
+
const authentication = Effect.gen(function* () {
|
|
521
|
+
const database = yield* authDatabase(yield* Config.Redacted("DATABASE_URL"));
|
|
522
|
+
return yield* betterAuthMount({
|
|
523
|
+
database,
|
|
524
|
+
emailAndPassword: { enabled: true },
|
|
525
|
+
session: { cookieCache: { enabled: false } },
|
|
526
|
+
});
|
|
527
|
+
});
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
Run this Effect in the host's scope. The pool logs idle-client errors at Debug and replaces failed connections on the next query; closing the scope disconnects its clients. A rejected auth handler returns HTTP 503 with `Authentication is temporarily unavailable`.
|
|
531
|
+
|
|
532
|
+
The mount reads `BETTER_AUTH_SECRET` unless options supply `secret`. `PUBLIC_URL` supplies the default base URL, trusted origin and HTTPS secure-cookie setting; without it, the defaults accept loopback HTTP. Application options can override those defaults. RPC Origin policy remains an application choice.
|
|
533
|
+
|
|
534
|
+
`AUTH_RATE_LIMIT_ENABLED` is a Config boolean that defaults to true and takes precedence over `options.rateLimit.enabled`; other Better Auth rate-limit options pass through. `TRUSTED_PROXY` supplies the same address and CIDR policy used by the host. An untrusted transport peer loses `x-forwarded-for` and the configured client-IP header before Better Auth sees the request. `AUTH_CLIENT_IP_HEADER` defaults to `x-forwarded-for`. With no trusted proxy, Better Auth ignores forwarded IPs and shares a rate-limit bucket per path.
|
|
535
|
+
|
|
536
|
+
### Signed-in story preconditions
|
|
537
|
+
|
|
538
|
+
Use `authTestUtils()` from `@shivaedev/platform/server/better-auth/testing.ts` in a test-only Better Auth instance with the host's database, secret and cookie policy. The wrapper preserves the plugin's typed `$context.test` under `exactOptionalPropertyTypes`. Keep the plugin out of production auth options.
|
|
539
|
+
|
|
540
|
+
```ts
|
|
541
|
+
import { betterAuth } from "better-auth";
|
|
542
|
+
import { authTestUtils, signedInUser } from "@shivaedev/platform/server/better-auth/testing.ts";
|
|
543
|
+
|
|
544
|
+
const testAuth = betterAuth({
|
|
545
|
+
...options,
|
|
546
|
+
plugins: [...(options.plugins ?? []), authTestUtils()],
|
|
547
|
+
});
|
|
548
|
+
const { user, headers, cookies } = await signedInUser(testAuth, {
|
|
549
|
+
email: "member@example.test",
|
|
550
|
+
name: "Member",
|
|
551
|
+
role: "user,admin",
|
|
552
|
+
});
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
`options` is the application's auth configuration. `signedInUser` saves the supplied user fields and mints one session through Better Auth's `testUtils`, without OAuth or password hashing. It returns Better Auth's login result: the user, session, signed request headers, browser cookies and token. Roles require the application's role plugin, such as Better Auth's `admin()`. Send `headers` to the real host, or pass `cookies` to test-story's `seedCookies` in a DOM story after setting its host location with `domFetch`. Keep a real sign-in story for each sign-in flow; feature stories start from the minted precondition.
|
|
556
|
+
|
|
557
|
+
With database-backed sessions and cookie caching disabled, revocation rejects that same minted session on the next protected HTTP or RPC request. Cookie caching and secondary storage can change revocation timing.
|
|
558
|
+
|
|
444
559
|
### Optional Better Auth storage on Effect Prisma
|
|
445
560
|
|
|
446
561
|
When Better Auth's rows belong to an Effect Prisma database, give its adapter the same database service and runtime:
|
|
@@ -514,13 +629,13 @@ Import the module that defines the name; there is no package-root entry. Paths b
|
|
|
514
629
|
| --- | --- |
|
|
515
630
|
| `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`. |
|
|
516
631
|
| `errors/rejectedField.ts` | `rejectedField(unknown)`: the field and message of a field rejection. |
|
|
517
|
-
| `rpc/identity.ts` | `RequestId`, `Identity`, `OptionalIdentity`, `SessionUser`: request-local services. |
|
|
518
|
-
| `rpc/middleware.ts` | `RequestTracing`, `Authenticated`, `AuthenticatedUser`, `MaybeAuthenticated`: native RPC middleware declarations. |
|
|
632
|
+
| `rpc/identity.ts` | `RequestId`, `Identity`, `OptionalIdentity`, `SessionUser`, `SessionUserWithProviderAccounts`: request-local services. |
|
|
633
|
+
| `rpc/middleware.ts` | `RequestTracing`, `Authenticated`, `AuthenticatedUser`, `AuthenticatedUserWithProviderAccounts`, `MaybeAuthenticated`: native RPC middleware declarations. |
|
|
519
634
|
| `rpc/requireAdmin.ts` | `requireAdmin`: the signed-in user with an admin role. |
|
|
520
635
|
| `server/appHost.ts` | `appHost`, `appDatabase`, `AppChanges`: scoped HTTP and PostgreSQL composition with loopback defaults and optional proxy trust. |
|
|
521
636
|
| `server/boundedBody.ts` | `readBoundedBody`, `RequestBodyTooLarge`: bounded body bytes and a typed HTTP 413 failure. |
|
|
522
637
|
| `server/otlpTracing.ts` | `otlpTracing`: opt-in Effect OTLP trace export for worker Layers. |
|
|
523
|
-
| `server/rpc-server/session.ts` | `authenticatedLayer`, `maybeAuthenticatedLayer`, `authenticatedUserLayer`, `withSessionUser`, `resolveSession`: session middleware implementations and the identity boundary. |
|
|
638
|
+
| `server/rpc-server/session.ts` | `authenticatedLayer`, `maybeAuthenticatedLayer`, `authenticatedUserLayer`, `authenticatedUserWithProviderAccountsLayer`, `withSessionUser`, `resolveSession`: session middleware implementations and the identity boundary. |
|
|
524
639
|
| `server/rpc-server/adapters/betterAuthSessions.ts` | `betterAuthSessions(getSession, getProviderAccounts?)`: a session provider from Better Auth lookups. |
|
|
525
640
|
| `server/rpc-server/origin.ts` | `trustedOrigins({ allow, missing })`: an exact Origin allowlist. |
|
|
526
641
|
| `server/rpc-server/local.ts` | `localRpcGuard`, `loopbackOnly`, `sameOrigin`, `trustedProxyPeer`, `trustedProxyRanges`: loopback and proxy guards. |
|
|
@@ -530,6 +645,8 @@ Import the module that defines the name; there is no package-root entry. Paths b
|
|
|
530
645
|
| `server/rpc-server/redact.ts`, `server/rpc-server/redactCause.ts`, `server/rpc-server/sensitive.ts` | `redact`, `redactCause`, `redactDefect`, `redactingErrorReporter`, `isSensitiveKey`, `redactText`: diagnostic redaction. |
|
|
531
646
|
| `server/runtime/makePlatform.ts` | `makePlatformRuntime(layer, { developmentCacheKey? })`. |
|
|
532
647
|
| `server/node-http/subscriptionSignal.ts` | `nodeSubscriptionSignal({ nodeRequest?, request?, signals? })`, returning `{ signal, dispose }`. |
|
|
648
|
+
| `server/better-auth/mount.ts` | `betterAuthMount(options)`, `authDatabase(url)`: native auth routes and a scoped PostgreSQL pool. |
|
|
649
|
+
| `server/better-auth/testing.ts` | `authTestUtils(options?)`, `signedInUser(auth, fields)`: test-only typed Better Auth helpers and minted user sessions. |
|
|
533
650
|
| `server/better-auth/adapter.ts` | `effectPrismaAdapter(Database, runtime, options?)`: a Better Auth adapter over Effect Prisma; `modelName` maps model names. |
|
|
534
651
|
| `server/testing/makePlatformIt.ts` | `makePlatformIt(Database)({ adapter, createCaller, layer, extend? })`. |
|
|
535
652
|
|
|
@@ -547,13 +664,14 @@ Install the optional peers for the selected integration:
|
|
|
547
664
|
| --- | --- |
|
|
548
665
|
| `server/boundedBody.ts` | `@effect/platform-node` |
|
|
549
666
|
| `server/appHost.ts` | `@effect/platform-node`, `@effect/sql-pg` |
|
|
667
|
+
| `server/better-auth/mount.ts` | `better-auth`, `pg` |
|
|
550
668
|
| `server/better-auth/adapter.ts` | `better-auth`, `@shivaedev/effect-prisma`, `@prisma-next/sql-orm-client` |
|
|
551
669
|
| `server/testing/makePlatformIt.ts` | `@effect/vitest`, `@shivaedev/effect-prisma`, `@shivaedev/effect-trpc`, `@trpc/server` |
|
|
552
670
|
|
|
553
671
|
- The package needs Node 24 or newer.
|
|
554
672
|
- Keep the auth provider's configuration, cookie settings and resource authorization in the application. The session adapter takes a session lookup and an optional provider-account lookup and returns a session result; it does not write provider refresh cookies into RPC responses.
|
|
555
673
|
- Without an `HttpServerRequest`, `transportHeaders` falls back to the supplied RPC headers. An application using another transport must decide what headers it can trust.
|
|
556
|
-
- `appHost` adds tracing last on its server-side copy of the group. When composing a native RPC server directly, add `RequestTracing` last on the group
|
|
674
|
+
- `appHost` adds tracing last on its server-side copy of the group. When composing a native RPC server directly, add `RequestTracing` last on the group so it wraps authentication too; see the [native-server example](https://github.com/ShivaeDev/platform/blob/main/docs/framework/request-context.md#request-id-identity-and-logging). Middleware outside that tracing boundary is responsible for its own diagnostics.
|
|
557
675
|
- Redaction recognizes common shapes. Keep secrets out of messages and extend the key predicate for application-specific sensitive fields.
|
|
558
676
|
- A development cache key is ignored when `NODE_ENV` is `production`. The application owns runtime disposal.
|
|
559
677
|
- The Better Auth storage adapter's default model mapping capitalizes the model name. Supply `modelName` for a generated schema with another naming convention. Native Better Auth joins (`advanced.database.joins`) are rejected; leave that option off so Better Auth reads related rows with separate queries.
|
package/dist/rpc/identity.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Context, type Option } from "effect";
|
|
1
|
+
import { Context, type Effect, type Option } from "effect";
|
|
2
|
+
import type { AuthUnavailable, Unauthorized } from "#errors/taxonomy.ts";
|
|
2
3
|
declare const RequestId_base: Context.ServiceClass<RequestId, "@shivaedev/platform/rpc/RequestId", string>;
|
|
3
4
|
export declare class RequestId extends RequestId_base {
|
|
4
5
|
}
|
|
@@ -18,11 +19,16 @@ export interface ProviderAccountValue {
|
|
|
18
19
|
export interface SessionUserValue extends IdentityValue {
|
|
19
20
|
readonly email: string;
|
|
20
21
|
readonly name: string;
|
|
21
|
-
readonly providerAccounts?: readonly ProviderAccountValue[];
|
|
22
22
|
readonly role?: string | null | undefined;
|
|
23
23
|
}
|
|
24
24
|
declare const SessionUser_base: Context.ServiceClass<SessionUser, "@shivaedev/platform/rpc/SessionUser", SessionUserValue>;
|
|
25
25
|
export declare class SessionUser extends SessionUser_base {
|
|
26
26
|
}
|
|
27
|
+
export interface SessionUserWithProviderAccountsValue extends SessionUserValue {
|
|
28
|
+
readonly providerAccounts: Effect.Effect<readonly ProviderAccountValue[], AuthUnavailable | Unauthorized>;
|
|
29
|
+
}
|
|
30
|
+
declare const SessionUserWithProviderAccounts_base: Context.ServiceClass<SessionUserWithProviderAccounts, "@shivaedev/platform/rpc/SessionUserWithProviderAccounts", SessionUserWithProviderAccountsValue>;
|
|
31
|
+
export declare class SessionUserWithProviderAccounts extends SessionUserWithProviderAccounts_base {
|
|
32
|
+
}
|
|
27
33
|
export {};
|
|
28
34
|
//# 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;;
|
|
1
|
+
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/rpc/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;;AACzE,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,oBAAoB;IACpC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC5B;AACD,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACtD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CAC1C;;AACD,qBAAa,WAAY,SAAQ,gBAAuF;CAAG;AAC3H,MAAM,WAAW,oCAAqC,SAAQ,gBAAgB;IAC7E,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,oBAAoB,EAAE,EAAE,eAAe,GAAG,YAAY,CAAC,CAAC;CAC1G;;AACD,qBAAa,+BAAgC,SAAQ,oCAEpD;CAAG"}
|
package/dist/rpc/identity.js
CHANGED
|
@@ -7,4 +7,6 @@ export class OptionalIdentity extends Context.Service()("@shivaedev/platform/rpc
|
|
|
7
7
|
}
|
|
8
8
|
export class SessionUser extends Context.Service()("@shivaedev/platform/rpc/SessionUser") {
|
|
9
9
|
}
|
|
10
|
+
export class SessionUserWithProviderAccounts extends Context.Service()("@shivaedev/platform/rpc/SessionUserWithProviderAccounts") {
|
|
11
|
+
}
|
|
10
12
|
//# 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,
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/rpc/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAA4B,MAAM,QAAQ,CAAC;AAE3D,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;AAUtJ,MAAM,OAAO,WAAY,SAAQ,OAAO,CAAC,OAAO,EAAiC,CAAC,qCAAqC,CAAC;CAAG;AAI3H,MAAM,OAAO,+BAAgC,SAAQ,OAAO,CAAC,OAAO,EAAyE,CAC5I,yDAAyD,CACzD;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, SessionUser } from "./identity.ts";
|
|
4
|
+
import type { Identity, OptionalIdentity, RequestId, SessionUser, SessionUserWithProviderAccounts } 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
|
}
|
|
@@ -14,5 +14,8 @@ export declare class MaybeAuthenticated extends MaybeAuthenticated_base {
|
|
|
14
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
15
|
export declare class AuthenticatedUser extends AuthenticatedUser_base {
|
|
16
16
|
}
|
|
17
|
+
declare const AuthenticatedUserWithProviderAccounts_base: RpcMiddleware.ServiceClass<AuthenticatedUserWithProviderAccounts, "@shivaedev/platform/rpc/AuthenticatedUserWithProviderAccounts", Identity | SessionUser | SessionUserWithProviderAccounts, Schema.Union<readonly [typeof Unauthorized, typeof AuthUnavailable, typeof Forbidden]>, never, never, false>;
|
|
18
|
+
export declare class AuthenticatedUserWithProviderAccounts extends AuthenticatedUserWithProviderAccounts_base {
|
|
19
|
+
}
|
|
17
20
|
export {};
|
|
18
21
|
//# 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,WAAW,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,+BAA+B,EAAE,MAAM,eAAe,CAAC;;AACzH,qBAAa,cAAe,SAAQ,mBAKS;CAAG;;AAChD,qBAAa,aAAc,SAAQ,kBAOjC;CAAG;;AACL,qBAAa,kBAAmB,SAAQ,uBAKgE;CAAG;;AAC3G,qBAAa,iBAAkB,SAAQ,sBAOrC;CAAG;;AACL,qBAAa,qCAAsC,SAAQ,0CAKzD;CAAG"}
|
package/dist/rpc/middleware.js
CHANGED
|
@@ -13,4 +13,8 @@ export class AuthenticatedUser extends RpcMiddleware.Service()("@shivaedev/platf
|
|
|
13
13
|
error: Schema.Union([Unauthorized, AuthUnavailable, Forbidden]),
|
|
14
14
|
}) {
|
|
15
15
|
}
|
|
16
|
+
export class AuthenticatedUserWithProviderAccounts extends RpcMiddleware.Service()("@shivaedev/platform/rpc/AuthenticatedUserWithProviderAccounts", {
|
|
17
|
+
error: Schema.Union([Unauthorized, AuthUnavailable, Forbidden]),
|
|
18
|
+
}) {
|
|
19
|
+
}
|
|
16
20
|
//# 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;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
|
+
{"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;AACL,MAAM,OAAO,qCAAsC,SAAQ,aAAa,CAAC,OAAO,EAG7E,CAAC,+DAA+D,EAAE;IACpE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,EAAE,eAAe,EAAE,SAAS,CAAC,CAAC;CAC/D,CAAC;CAAG"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { Schema } from "effect";
|
|
2
|
+
export declare function publicConfig<T, E>(schema: Schema.Codec<T, E>): import("@shivaedev/effect-contract/contract.ts").Contract<"platform.publicConfig", readonly [NoInfer<import("@shivaedev/effect-contract/operation.ts").Query<"get", Schema.Void, Schema.Codec<T, E, never, never>, {}>>], readonly [], import("effect/rpc/Rpc").Rpc<"platform.publicConfig.get", Schema.Void, Schema.Codec<T, E, never, never>, Schema.Never, never, never>, readonly []>;
|
|
3
|
+
//# sourceMappingURL=publicConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publicConfig.d.ts","sourceRoot":"","sources":["../../src/rpc/publicConfig.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIrC,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,6XAI5D"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { contract } from "@shivaedev/effect-contract/contract.ts";
|
|
2
|
+
import { query } from "@shivaedev/effect-contract/operation.ts";
|
|
3
|
+
export function publicConfig(schema) {
|
|
4
|
+
return contract("platform.publicConfig", {
|
|
5
|
+
queries: [query("get", { reads: () => [], success: schema })],
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=publicConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publicConfig.js","sourceRoot":"","sources":["../../src/rpc/publicConfig.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,wCAAwC,CAAC;AAClE,OAAO,EAAE,KAAK,EAAE,MAAM,yCAAyC,CAAC;AAEhE,MAAM,UAAU,YAAY,CAAO,MAA0B;IAC5D,OAAO,QAAQ,CAAC,uBAAuB,EAAE;QACxC,OAAO,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;KAC7D,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type BetterAuthOptions } from "better-auth";
|
|
2
|
+
import { Config, Effect, Redacted } from "effect";
|
|
3
|
+
import { HttpRouter } from "effect/http";
|
|
4
|
+
import { Pool } from "pg";
|
|
5
|
+
export declare const betterAuthMount: <const TOptions extends BetterAuthOptions>(options: TOptions) => Effect.Effect<{
|
|
6
|
+
auth: import("better-auth").Auth<TOptions & {
|
|
7
|
+
advanced: {
|
|
8
|
+
disableCSRFCheck?: boolean | undefined;
|
|
9
|
+
disableOriginCheck?: boolean | undefined;
|
|
10
|
+
crossSubDomainCookies?: {
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
additionalCookies?: string[];
|
|
13
|
+
domain?: string;
|
|
14
|
+
} | undefined;
|
|
15
|
+
cookies?: {
|
|
16
|
+
[key: string]: {
|
|
17
|
+
name?: string;
|
|
18
|
+
attributes?: import("better-auth").CookieOptions;
|
|
19
|
+
};
|
|
20
|
+
} | undefined;
|
|
21
|
+
defaultCookieAttributes?: import("better-auth").CookieOptions | undefined;
|
|
22
|
+
cookiePrefix?: string | undefined;
|
|
23
|
+
database?: {
|
|
24
|
+
defaultFindManyLimit?: number;
|
|
25
|
+
generateId?: import("better-auth").GenerateIdFn | false | "serial" | "uuid";
|
|
26
|
+
joins?: boolean;
|
|
27
|
+
validateSchema?: boolean;
|
|
28
|
+
} | undefined;
|
|
29
|
+
trustedProxyHeaders?: boolean | undefined;
|
|
30
|
+
backgroundTasks?: {
|
|
31
|
+
handler: (promise: Promise<unknown>) => void;
|
|
32
|
+
};
|
|
33
|
+
skipTrailingSlashes?: boolean;
|
|
34
|
+
useSecureCookies: boolean | undefined;
|
|
35
|
+
ipAddress: {
|
|
36
|
+
disableIpTracking?: boolean;
|
|
37
|
+
ipv6Subnet?: number;
|
|
38
|
+
ipAddressHeaders: string[];
|
|
39
|
+
trustedProxies: string[];
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
baseURL: import("better-auth").BaseURLConfig;
|
|
43
|
+
rateLimit: {
|
|
44
|
+
window?: number | undefined;
|
|
45
|
+
max?: number | undefined;
|
|
46
|
+
modelName?: "rateLimit" | import("better-auth").LiteralString;
|
|
47
|
+
fields?: Partial<Record<"count" | "key" | "lastRequest", string>>;
|
|
48
|
+
customRules?: {
|
|
49
|
+
[key: string]: import("better-auth").BetterAuthRateLimitRule | false | ((request: Request, currentRule: import("better-auth").BetterAuthRateLimitRule) => import("better-auth").Awaitable<false | import("better-auth").BetterAuthRateLimitRule>);
|
|
50
|
+
} | undefined;
|
|
51
|
+
storage?: ("memory" | "database" | "secondary-storage") | undefined;
|
|
52
|
+
customStorage?: import("better-auth").BetterAuthRateLimitStorage;
|
|
53
|
+
enabled: boolean;
|
|
54
|
+
};
|
|
55
|
+
secret: string;
|
|
56
|
+
trustedOrigins: string[] | ((request?: Request | undefined) => import("better-auth").Awaitable<(string | undefined | null)[]>);
|
|
57
|
+
}>;
|
|
58
|
+
routes: import("effect/Layer").Layer<never, never, HttpRouter.HttpRouter>;
|
|
59
|
+
}, Config.ConfigError, never>;
|
|
60
|
+
export declare const authDatabase: (url: Redacted.Redacted<string>) => Effect.Effect<Pool, never, import("effect/Scope").Scope>;
|
|
61
|
+
//# sourceMappingURL=mount.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../../src/server/better-auth/mount.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAc,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAU,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAyC,MAAM,aAAa,CAAC;AAChF,OAAO,EAAE,IAAI,EAAmB,MAAM,IAAI,CAAC;AAG3C,eAAO,MAAM,eAAe,SAA0D,QAAQ,SAAS,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BAsCtH,CAAC;AAEH,eAAO,MAAM,YAAY,8FAqBvB,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { betterAuth } from "better-auth";
|
|
2
|
+
import { Config, Effect, Option, Redacted } from "effect";
|
|
3
|
+
import { HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/http";
|
|
4
|
+
import { Pool } from "pg";
|
|
5
|
+
import { trustedProxyPeer, trustedProxyRanges } from "#server/rpc-server/local.ts";
|
|
6
|
+
export const betterAuthMount = Effect.fn("Platform.betterAuthMount")(function* (options) {
|
|
7
|
+
const secret = options.secret ?? Redacted.value(yield* Config.Redacted("BETTER_AUTH_SECRET"));
|
|
8
|
+
const publicUrl = yield* Config.URL("PUBLIC_URL").pipe(Config.option);
|
|
9
|
+
const origin = Option.map(publicUrl, (url) => url.origin);
|
|
10
|
+
const trustedProxy = Option.getOrUndefined(yield* Config.String("TRUSTED_PROXY").pipe(Config.option));
|
|
11
|
+
const clientIpHeader = yield* Config.String("AUTH_CLIENT_IP_HEADER").pipe(Config.withDefault("x-forwarded-for"));
|
|
12
|
+
const rateLimitEnabled = yield* Config.Boolean("AUTH_RATE_LIMIT_ENABLED").pipe(Config.withDefault(true));
|
|
13
|
+
const isTrustedProxy = trustedProxyPeer(trustedProxy);
|
|
14
|
+
const trustedProxies = trustedProxyRanges(trustedProxy);
|
|
15
|
+
const auth = betterAuth({
|
|
16
|
+
...options,
|
|
17
|
+
advanced: {
|
|
18
|
+
useSecureCookies: Option.isSome(publicUrl) && publicUrl.value.protocol === "https:",
|
|
19
|
+
...options.advanced,
|
|
20
|
+
ipAddress: {
|
|
21
|
+
...options.advanced?.ipAddress,
|
|
22
|
+
ipAddressHeaders: trustedProxies.length === 0 ? [] : [clientIpHeader],
|
|
23
|
+
trustedProxies,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
"baseURL": options.baseURL ?? Option.getOrElse(origin, () => ({ allowedHosts: ["127.0.0.1:*"], protocol: "http" })),
|
|
27
|
+
rateLimit: { ...options.rateLimit, enabled: rateLimitEnabled },
|
|
28
|
+
secret,
|
|
29
|
+
trustedOrigins: options.trustedOrigins ?? [Option.getOrElse(origin, () => "http://127.0.0.1:*")],
|
|
30
|
+
});
|
|
31
|
+
const routes = HttpRouter.use((router) => router.add("*", "/api/auth/*", (request) => Effect.gen(function* () {
|
|
32
|
+
const web = yield* HttpServerRequest.toWeb(request);
|
|
33
|
+
if (!Option.exists(request.remoteAddress, isTrustedProxy)) {
|
|
34
|
+
web.headers.delete("x-forwarded-for");
|
|
35
|
+
web.headers.delete(clientIpHeader);
|
|
36
|
+
}
|
|
37
|
+
return HttpServerResponse.fromWeb(yield* Effect.tryPromise(() => auth.handler(web)));
|
|
38
|
+
}).pipe(Effect.catch(() => Effect.succeed(HttpServerResponse.text("Authentication is temporarily unavailable", { status: 503 }))))));
|
|
39
|
+
return { auth, routes };
|
|
40
|
+
});
|
|
41
|
+
export const authDatabase = Effect.fn("Platform.authDatabase")(function* (url) {
|
|
42
|
+
const clients = new Set();
|
|
43
|
+
const run = Effect.runForkWith(yield* Effect.context());
|
|
44
|
+
return yield* Effect.acquireRelease(Effect.sync(() => {
|
|
45
|
+
const pool = new Pool({ connectionString: Redacted.value(url) });
|
|
46
|
+
pool.on("connect", (client) => clients.add(client));
|
|
47
|
+
pool.on("remove", (client) => clients.delete(client));
|
|
48
|
+
pool.on("error", (error) => {
|
|
49
|
+
run(Effect.logDebug({ event: "auth_database_idle_client_failed", message: error.message }));
|
|
50
|
+
});
|
|
51
|
+
return pool;
|
|
52
|
+
}), (pool) => Effect.promise(async () => {
|
|
53
|
+
// pg-pool resolves end before its clients finish disconnecting.
|
|
54
|
+
const disconnected = [...clients].map((client) => new Promise((resolve) => client.once("end", resolve)));
|
|
55
|
+
await pool.end();
|
|
56
|
+
await Promise.all(disconnected);
|
|
57
|
+
}));
|
|
58
|
+
});
|
|
59
|
+
//# sourceMappingURL=mount.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mount.js","sourceRoot":"","sources":["../../../src/server/better-auth/mount.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,UAAU,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,EAAE,IAAI,EAAmB,MAAM,IAAI,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEnF,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,EAAE,CAAC,0BAA0B,CAAC,CAAC,QAAQ,CAAC,EAA4C,OAAiB;IAC1I,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC,CAAC;IAC9F,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACtE,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1D,MAAM,YAAY,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACtG,MAAM,cAAc,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,iBAAiB,CAAC,CAAC,CAAC;IACjH,MAAM,gBAAgB,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,yBAAyB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IACzG,MAAM,cAAc,GAAG,gBAAgB,CAAC,YAAY,CAAC,CAAC;IACtD,MAAM,cAAc,GAAG,kBAAkB,CAAC,YAAY,CAAC,CAAC;IACxD,MAAM,IAAI,GAAG,UAAU,CAAC;QACvB,GAAG,OAAO;QACV,QAAQ,EAAE;YACT,gBAAgB,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,KAAK,CAAC,QAAQ,KAAK,QAAQ;YACnF,GAAG,OAAO,CAAC,QAAQ;YACnB,SAAS,EAAE;gBACV,GAAG,OAAO,CAAC,QAAQ,EAAE,SAAS;gBAC9B,gBAAgB,EAAE,cAAc,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC;gBACrE,cAAc;aACd;SACD;QACD,SAAS,EAAE,OAAO,CAAC,OAAO,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,aAAa,CAAC,EAAE,QAAQ,EAAE,MAAe,EAAE,CAAC,CAAC;QAC5H,SAAS,EAAE,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,gBAAgB,EAAE;QAC9D,MAAM;QACN,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,oBAAoB,CAAC,CAAC;KAChG,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CACxC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,aAAa,EAAE,CAAC,OAAO,EAAE,EAAE,CAC1C,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACnB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACpD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,cAAc,CAAC,EAAE,CAAC;YAC3D,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;YACtC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;QACpC,CAAC;QACD,OAAO,kBAAkB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACtF,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,IAAI,CAAC,2CAA2C,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAClI,CACD,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AACzB,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,EAAE,CAAC,uBAAuB,CAAC,CAAC,QAAQ,CAAC,EAAE,GAA8B;IACvG,MAAM,OAAO,GAAG,IAAI,GAAG,EAAc,CAAC;IACtC,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,EAAS,CAAC,CAAC;IAC/D,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,cAAc,CAClC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;QAChB,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,gBAAgB,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACjE,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;QACpD,IAAI,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QACtD,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YAC1B,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,kCAAkC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC7F,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACb,CAAC,CAAC,EACF,CAAC,IAAI,EAAE,EAAE,CACR,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE;QACzB,gEAAgE;QAChE,MAAM,YAAY,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;QAC/G,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;QACjB,MAAM,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACjC,CAAC,CAAC,CACH,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { testUtils } from "better-auth/plugins";
|
|
2
|
+
export declare function authTestUtils(options?: Parameters<typeof testUtils>[0]): {
|
|
3
|
+
id: "test-utils";
|
|
4
|
+
version: string;
|
|
5
|
+
options: import("better-auth/plugins").TestUtilsOptions;
|
|
6
|
+
init(context: Parameters<(ctx: import("better-auth").AuthContext) => {
|
|
7
|
+
context: {
|
|
8
|
+
test: import("better-auth/plugins").TestHelpers;
|
|
9
|
+
};
|
|
10
|
+
options: {
|
|
11
|
+
databaseHooks: {
|
|
12
|
+
verification: {
|
|
13
|
+
create: {
|
|
14
|
+
after(verification: {
|
|
15
|
+
identifier: string;
|
|
16
|
+
value: string;
|
|
17
|
+
} | null): Promise<void>;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
} | undefined;
|
|
22
|
+
}>[0]): {
|
|
23
|
+
context: {
|
|
24
|
+
test: import("better-auth/plugins").TestHelpers;
|
|
25
|
+
};
|
|
26
|
+
options?: never;
|
|
27
|
+
} | {
|
|
28
|
+
context: {
|
|
29
|
+
test: import("better-auth/plugins").TestHelpers;
|
|
30
|
+
};
|
|
31
|
+
options: {
|
|
32
|
+
databaseHooks: {
|
|
33
|
+
verification: {
|
|
34
|
+
create: {
|
|
35
|
+
after(verification: {
|
|
36
|
+
identifier: string;
|
|
37
|
+
value: string;
|
|
38
|
+
} | null): Promise<void>;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
type TestContext = ReturnType<ReturnType<typeof authTestUtils>["init"]>["context"];
|
|
46
|
+
export declare function signedInUser(auth: {
|
|
47
|
+
readonly $context: Promise<TestContext>;
|
|
48
|
+
}, fields: Parameters<TestContext["test"]["createUser"]>[0]): Promise<import("better-auth/plugins").LoginResult>;
|
|
49
|
+
export {};
|
|
50
|
+
//# sourceMappingURL=testing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../../src/server/better-auth/testing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC;;;;kBAIvD,UAAU;;;;;;;;;;;;;;;;MAAoB,CAAC,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;EAMhD;AAED,KAAK,WAAW,GAAG,UAAU,CAAC,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AAEnF,wBAAsB,YAAY,CAAC,IAAI,EAAE;IAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;CAAE,EAAE,MAAM,EAAE,UAAU,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,sDAI7I"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { testUtils } from "better-auth/plugins";
|
|
2
|
+
export function authTestUtils(options) {
|
|
3
|
+
const plugin = testUtils(options);
|
|
4
|
+
return {
|
|
5
|
+
...plugin,
|
|
6
|
+
init(context) {
|
|
7
|
+
const result = plugin.init(context);
|
|
8
|
+
// Remove this wrapper once Better Auth's testUtils omits undefined options.
|
|
9
|
+
return result.options === undefined ? { context: result.context } : { context: result.context, options: result.options };
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export async function signedInUser(auth, fields) {
|
|
14
|
+
const { test } = await auth.$context;
|
|
15
|
+
const user = await test.saveUser(test.createUser(fields));
|
|
16
|
+
return test.login({ userId: user.id });
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.js","sourceRoot":"","sources":["../../../src/server/better-auth/testing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,MAAM,UAAU,aAAa,CAAC,OAAyC;IACtE,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;IAClC,OAAO;QACN,GAAG,MAAM;QACT,IAAI,CAAC,OAA0C;YAC9C,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACpC,4EAA4E;YAC5E,OAAO,MAAM,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;QAC1H,CAAC;KACD,CAAC;AACH,CAAC;AAID,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAiD,EAAE,MAAwD;IAC7I,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC;IACrC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1D,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;AACxC,CAAC"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as PgClient from "@effect/sql-pg/PgClient";
|
|
2
2
|
import { Config, Layer, type Redacted } from "effect";
|
|
3
3
|
import * as Reactivity from "effect/reactivity/Reactivity";
|
|
4
|
+
import type { SqlNameTransforms } from "@shivaedev/effect-sql/clientOptions.ts";
|
|
4
5
|
import { type PostgresChangesOptions } from "@shivaedev/effect-sql/postgresChanges.ts";
|
|
5
6
|
import type { AppChanges, AppDatabaseOptions } from "#server/appHost.ts";
|
|
6
|
-
export interface DatabaseConfig {
|
|
7
|
+
export interface DatabaseConfig extends SqlNameTransforms {
|
|
7
8
|
readonly applicationName?: string;
|
|
8
9
|
readonly changes?: Omit<PostgresChangesOptions, "client" | "resync">;
|
|
9
10
|
readonly databaseUrl?: Redacted.Redacted<string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"databaseLayer.d.ts","sourceRoot":"","sources":["../../src/server/databaseLayer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAU,KAAK,EAAE,KAAK,QAAQ,EAAY,MAAM,QAAQ,CAAC;AACxE,OAAO,KAAK,UAAU,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"databaseLayer.d.ts","sourceRoot":"","sources":["../../src/server/databaseLayer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAU,KAAK,EAAE,KAAK,QAAQ,EAAY,MAAM,QAAQ,CAAC;AACxE,OAAO,KAAK,UAAU,MAAM,8BAA8B,CAAC;AAG3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAEhF,OAAO,EAAE,KAAK,sBAAsB,EAAmB,MAAM,0CAA0C,CAAC;AACxG,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAGzE,MAAM,WAAW,cAAe,SAAQ,iBAAiB;IACxD,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,QAAQ,GAAG,QAAQ,CAAC,CAAC;IACrE,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,UAAU,+UA4BjG"}
|
|
@@ -10,6 +10,8 @@ export function databaseLayer(options, changesService) {
|
|
|
10
10
|
const pool = Layer.unwrap(Effect.gen(function* () {
|
|
11
11
|
const url = options.config?.databaseUrl ?? (yield* Config.Redacted("DATABASE_URL"));
|
|
12
12
|
return PgClient.layer({
|
|
13
|
+
transformQueryNames: options.config?.transformQueryNames,
|
|
14
|
+
transformResultNames: options.config?.transformResultNames,
|
|
13
15
|
url,
|
|
14
16
|
...databaseAddress(url),
|
|
15
17
|
...(options.config?.maxConnections === undefined ? {} : { maxConnections: options.config.maxConnections }),
|