@shivaedev/platform 0.14.4 → 0.16.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 +107 -51
- package/dist/rpc/identity.d.ts +6 -0
- package/dist/rpc/identity.d.ts.map +1 -1
- package/dist/rpc/identity.js.map +1 -1
- package/dist/rpc/requireAdmin.d.ts +5 -0
- package/dist/rpc/requireAdmin.d.ts.map +1 -0
- package/dist/rpc/requireAdmin.js +14 -0
- package/dist/rpc/requireAdmin.js.map +1 -0
- package/dist/server/MigrationOptions.d.ts +11 -0
- package/dist/server/MigrationOptions.d.ts.map +1 -0
- package/dist/server/MigrationOptions.js +2 -0
- package/dist/server/MigrationOptions.js.map +1 -0
- package/dist/server/appHost.d.ts +5 -10
- package/dist/server/appHost.d.ts.map +1 -1
- package/dist/server/appHost.js.map +1 -1
- package/dist/server/appHostHttp.d.ts +7 -7
- package/dist/server/appHostHttp.d.ts.map +1 -1
- package/dist/server/appHostHttp.js +8 -7
- package/dist/server/appHostHttp.js.map +1 -1
- package/dist/server/better-auth/adapter.d.ts +1 -1
- package/dist/server/better-auth/adapter.d.ts.map +1 -1
- package/dist/server/databaseLayer.d.ts.map +1 -1
- package/dist/server/databaseLayer.js +18 -4
- package/dist/server/databaseLayer.js.map +1 -1
- package/dist/server/hostOptions.d.ts +1 -1
- package/dist/server/hostOptions.d.ts.map +1 -1
- package/dist/server/otlpHttpClient.js +1 -1
- package/dist/server/otlpHttpClient.js.map +1 -1
- package/dist/server/otlpTracing.js +2 -2
- package/dist/server/otlpTracing.js.map +1 -1
- package/dist/server/redactOtlpUrl.js +1 -1
- package/dist/server/redactOtlpUrl.js.map +1 -1
- package/dist/server/rpc-server/adapters/betterAuthSessions.d.ts +9 -1
- package/dist/server/rpc-server/adapters/betterAuthSessions.d.ts.map +1 -1
- package/dist/server/rpc-server/adapters/betterAuthSessions.js +14 -3
- package/dist/server/rpc-server/adapters/betterAuthSessions.js.map +1 -1
- package/dist/server/rpc-server/session.d.ts +3 -2
- package/dist/server/rpc-server/session.d.ts.map +1 -1
- package/dist/server/rpc-server/session.js +17 -6
- package/dist/server/rpc-server/session.js.map +1 -1
- package/dist/server/safeOtlpSerialization.js +2 -2
- package/dist/server/safeOtlpSerialization.js.map +1 -1
- package/dist/server/testing/makePlatformIt.d.ts +1 -1
- package/dist/server/testing/makePlatformIt.d.ts.map +1 -1
- package/dist/server/testing/makePlatformIt.js +1 -1
- package/dist/server/testing/makePlatformIt.js.map +1 -1
- package/dist/server/testing/types.d.ts +3 -3
- package/dist/server/testing/types.d.ts.map +1 -1
- package/dist/server/tracedGroup.d.ts +4 -0
- package/dist/server/tracedGroup.d.ts.map +1 -0
- package/dist/server/tracedGroup.js +13 -0
- package/dist/server/tracedGroup.js.map +1 -0
- package/package.json +15 -343
- package/src/rpc/identity.ts +6 -0
- package/src/rpc/requireAdmin.ts +14 -0
- package/src/server/MigrationOptions.ts +11 -0
- package/src/server/appHost.ts +4 -9
- package/src/server/appHostHttp.ts +11 -10
- package/src/server/better-auth/adapter.ts +1 -1
- package/src/server/databaseLayer.ts +22 -6
- package/src/server/hostOptions.ts +1 -1
- package/src/server/otlpHttpClient.ts +1 -1
- package/src/server/otlpTracing.ts +2 -2
- package/src/server/redactOtlpUrl.ts +1 -1
- package/src/server/rpc-server/adapters/betterAuthSessions.ts +25 -6
- package/src/server/rpc-server/session.ts +17 -7
- package/src/server/safeOtlpSerialization.ts +2 -2
- package/src/server/testing/makePlatformIt.ts +4 -4
- package/src/server/testing/types.ts +3 -3
- package/src/server/tracedGroup.ts +14 -0
package/src/server/appHost.ts
CHANGED
|
@@ -1,21 +1,16 @@
|
|
|
1
|
-
import { Context, type
|
|
1
|
+
import { Context, type Effect } from "effect";
|
|
2
2
|
import type { Rpc } from "effect/rpc";
|
|
3
|
-
import type { Migrator } from "effect/sql";
|
|
4
3
|
import type { makeLiveServer } from "@shivaedev/effect-contract/makeLiveServer.ts";
|
|
5
|
-
import type { SessionPolicy, SessionShape } from "#rpc-server/session.ts";
|
|
6
4
|
import { appHostHttp, deferredHost, migratedHost } from "#server/appHostHttp.ts";
|
|
7
5
|
import { type DatabaseConfig, databaseLayer } from "#server/databaseLayer.ts";
|
|
8
6
|
import type { HostOptions, HostPolicy } from "#server/hostOptions.ts";
|
|
7
|
+
import type { MigrationOptions } from "#server/MigrationOptions.ts";
|
|
8
|
+
import type { SessionPolicy, SessionShape } from "#server/rpc-server/session.ts";
|
|
9
9
|
|
|
10
10
|
export class AppChanges extends Context.Service<AppChanges, Effect.Success<typeof makeLiveServer>>()("@shivaedev/platform/AppChanges") {}
|
|
11
11
|
export interface AppDatabaseOptions<R = never> {
|
|
12
12
|
readonly config?: DatabaseConfig;
|
|
13
|
-
readonly migrations:
|
|
14
|
-
readonly loader: Migrator.Loader<R>;
|
|
15
|
-
readonly mode?: "migrate" | "check";
|
|
16
|
-
readonly table?: string;
|
|
17
|
-
readonly lockTimeout?: Duration.Input;
|
|
18
|
-
};
|
|
13
|
+
readonly migrations: MigrationOptions<R>;
|
|
19
14
|
}
|
|
20
15
|
|
|
21
16
|
export function appDatabase<R>(options: AppDatabaseOptions<R>) {
|
|
@@ -3,17 +3,18 @@ import { NodeHttpServer, NodeServices } from "@effect/platform-node";
|
|
|
3
3
|
import { ByteSize, Config, Effect, Layer, Option, Schema } from "effect";
|
|
4
4
|
import { HttpIncomingMessage, HttpRouter, HttpServerRequest, HttpStaticServer } from "effect/http";
|
|
5
5
|
import { type Rpc, RpcSerialization, RpcServer } from "effect/rpc";
|
|
6
|
-
import type { AppHostOptions, AppHttpHostOptions, AppLocalHostOptions } from "#appHost.ts";
|
|
7
|
-
import { boundedRpcRequest } from "#boundedRpcRequest.ts";
|
|
8
|
-
import type { databaseLayer } from "#databaseLayer.ts";
|
|
9
|
-
import type { HostOptions, HostPolicy } from "#hostOptions.ts";
|
|
10
|
-
import { otlpTracing } from "#otlpTracing.ts";
|
|
11
6
|
import type { Authenticated, MaybeAuthenticated, RequestTracing } from "#rpc/middleware.ts";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { TrustedProxy } from "#rpc-server/trustedProxy.ts";
|
|
7
|
+
import type { AppHostOptions, AppHttpHostOptions, AppLocalHostOptions } from "#server/appHost.ts";
|
|
8
|
+
import { boundedRpcRequest } from "#server/boundedRpcRequest.ts";
|
|
9
|
+
import type { databaseLayer } from "#server/databaseLayer.ts";
|
|
16
10
|
import { healthRoute } from "#server/healthRoute.ts";
|
|
11
|
+
import type { HostOptions, HostPolicy } from "#server/hostOptions.ts";
|
|
12
|
+
import { otlpTracing } from "#server/otlpTracing.ts";
|
|
13
|
+
import { localRpcGuard } from "#server/rpc-server/local.ts";
|
|
14
|
+
import { authenticatedLayer, maybeAuthenticatedLayer, type SessionShape } from "#server/rpc-server/session.ts";
|
|
15
|
+
import { requestTracingLayer } from "#server/rpc-server/tracing.ts";
|
|
16
|
+
import { TrustedProxy } from "#server/rpc-server/trustedProxy.ts";
|
|
17
|
+
import { tracedGroup } from "#server/tracedGroup.ts";
|
|
17
18
|
export function appHostHttp<
|
|
18
19
|
TRpcs extends Rpc.Any,
|
|
19
20
|
THandlers,
|
|
@@ -46,7 +47,7 @@ export function appHostHttp<
|
|
|
46
47
|
const routes = HttpRouter.use((router) =>
|
|
47
48
|
Effect.gen(function* () {
|
|
48
49
|
const wire = yield* RpcSerialization.RpcSerialization;
|
|
49
|
-
const handler = yield* RpcServer.toHttpEffect(options.group);
|
|
50
|
+
const handler = yield* RpcServer.toHttpEffect(tracedGroup(options.group));
|
|
50
51
|
const guarded = localRpcGuard(handler, trustedProxy);
|
|
51
52
|
yield* router.add("POST", options.rpcPath ?? "/rpc", (request) =>
|
|
52
53
|
guarded.pipe(
|
|
@@ -2,7 +2,7 @@ import type { BetterAuthOptions, DBAdapterInstance } from "better-auth";
|
|
|
2
2
|
import { createAdapterFactory } from "better-auth/adapters";
|
|
3
3
|
import { Effect } from "effect";
|
|
4
4
|
import type { AnyDatabase } from "@shivaedev/effect-prisma/databaseTypes.ts";
|
|
5
|
-
import type { PlatformRuntime } from "#runtime/types.ts";
|
|
5
|
+
import type { PlatformRuntime } from "#server/runtime/types.ts";
|
|
6
6
|
import { type DynamicRelation, refineRelation } from "./relation.ts";
|
|
7
7
|
import { makeRowAdapter, type RelationQuery } from "./rowAdapter.ts";
|
|
8
8
|
export interface EffectPrismaAdapterOptions {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as PgClient from "@effect/sql-pg/PgClient";
|
|
2
|
-
import { Config, Effect, Layer, type Redacted } from "effect";
|
|
2
|
+
import { Config, Effect, Layer, type Redacted, Schedule } from "effect";
|
|
3
3
|
import * as Reactivity from "effect/reactivity/Reactivity";
|
|
4
4
|
import { makeLiveServer } from "@shivaedev/effect-contract/makeLiveServer.ts";
|
|
5
5
|
import { assertPostgresMigrations } from "@shivaedev/effect-sql/assertPostgresMigrations.ts";
|
|
@@ -27,11 +27,7 @@ export function databaseLayer<R>(options: AppDatabaseOptions<R>, changesService:
|
|
|
27
27
|
});
|
|
28
28
|
}),
|
|
29
29
|
);
|
|
30
|
-
const prepared = Layer.effectDiscard(
|
|
31
|
-
options.migrations.mode === "check"
|
|
32
|
-
? assertPostgresMigrations(options.migrations)
|
|
33
|
-
: migratePostgres({ ...options.migrations, lockTimeout: options.migrations.lockTimeout ?? "5 seconds" }),
|
|
34
|
-
).pipe(Layer.provideMerge(pool));
|
|
30
|
+
const prepared = Layer.effectDiscard(prepareMigrations(options.migrations)).pipe(Layer.provideMerge(pool));
|
|
35
31
|
const live = Layer.effect(changesService, makeLiveServer).pipe(Layer.provideMerge(Reactivity.layer));
|
|
36
32
|
const base = Layer.merge(prepared, live);
|
|
37
33
|
const changes = options.config?.changes;
|
|
@@ -46,3 +42,23 @@ export function databaseLayer<R>(options: AppDatabaseOptions<R>, changesService:
|
|
|
46
42
|
}),
|
|
47
43
|
).pipe(Layer.provideMerge(base));
|
|
48
44
|
}
|
|
45
|
+
|
|
46
|
+
function prepareMigrations<R>(migrations: AppDatabaseOptions<R>["migrations"]) {
|
|
47
|
+
switch (migrations.mode) {
|
|
48
|
+
case "check":
|
|
49
|
+
return assertPostgresMigrations(migrations);
|
|
50
|
+
case "wait":
|
|
51
|
+
return waitForMigrations(migrations);
|
|
52
|
+
default:
|
|
53
|
+
return migratePostgres({ ...migrations, lockTimeout: migrations.lockTimeout ?? "5 seconds" });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function waitForMigrations<R>(migrations: AppDatabaseOptions<R>["migrations"]) {
|
|
58
|
+
return assertPostgresMigrations(migrations).pipe(
|
|
59
|
+
Effect.retry({
|
|
60
|
+
schedule: Schedule.max([Schedule.spaced(migrations.pollInterval ?? "1 second"), Schedule.during(migrations.waitTimeout ?? "5 minutes")]),
|
|
61
|
+
while: (error) => error._tag === "MigrationError" && error.kind === "BadState",
|
|
62
|
+
}),
|
|
63
|
+
);
|
|
64
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Layer } from "effect";
|
|
2
2
|
import type { HttpRouter, HttpStaticServer } from "effect/http";
|
|
3
3
|
import type { Rpc, RpcGroup } from "effect/rpc";
|
|
4
|
-
import type { SessionPolicy, SessionShape } from "#rpc-server/session.ts";
|
|
4
|
+
import type { SessionPolicy, SessionShape } from "#server/rpc-server/session.ts";
|
|
5
5
|
import type { AppDatabaseOptions } from "./appHost.ts";
|
|
6
6
|
|
|
7
7
|
export type HostOptions<TRpcs extends Rpc.Any, THandlers, THandlerError, THandlerRequirements, TServices, TServiceError, TServiceRequirements> = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Effect, Layer } from "effect";
|
|
2
2
|
import { FetchHttpClient, HttpClient, HttpClientError, type HttpClientRequest } from "effect/http";
|
|
3
|
-
import { redactOtlpText } from "#safeOtlpSerialization.ts";
|
|
3
|
+
import { redactOtlpText } from "#server/safeOtlpSerialization.ts";
|
|
4
4
|
|
|
5
5
|
function timeoutError(request: HttpClientRequest.HttpClientRequest) {
|
|
6
6
|
return new HttpClientError.HttpClientError({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Config, Effect, Layer, Schema } from "effect";
|
|
2
2
|
import { OtlpSerialization, OtlpTracer } from "effect/observability";
|
|
3
|
-
import { otlpHttpClient } from "#otlpHttpClient.ts";
|
|
4
|
-
import { safeOtlpSerialization } from "#safeOtlpSerialization.ts";
|
|
3
|
+
import { otlpHttpClient } from "#server/otlpHttpClient.ts";
|
|
4
|
+
import { safeOtlpSerialization } from "#server/safeOtlpSerialization.ts";
|
|
5
5
|
|
|
6
6
|
export const otlpTracing: Layer.Layer<never, Config.ConfigError> = Layer.unwrap(
|
|
7
7
|
Effect.gen(function* () {
|
|
@@ -1,20 +1,39 @@
|
|
|
1
1
|
import { Effect, Option } from "effect";
|
|
2
2
|
import type { Headers } from "effect/http";
|
|
3
3
|
import { AuthUnavailable } from "#errors/taxonomy.ts";
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
4
|
+
import type { ProviderAccountValue } from "#rpc/identity.ts";
|
|
5
|
+
import { redact } from "#server/rpc-server/redact.ts";
|
|
6
|
+
import type { SessionProvider, SessionShape } from "#server/rpc-server/session.ts";
|
|
6
7
|
export type GetSession<TSession extends SessionShape> = (headers: globalThis.Headers) => Promise<TSession | null>;
|
|
8
|
+
export type GetProviderAccounts = (headers: globalThis.Headers) => Promise<readonly ProviderAccountValue[]>;
|
|
9
|
+
export type SessionWithProviderAccounts<TSession extends SessionShape> = TSession & {
|
|
10
|
+
readonly user: TSession["user"] & { readonly providerAccounts: readonly ProviderAccountValue[] };
|
|
11
|
+
};
|
|
7
12
|
function unavailable(cause: unknown) {
|
|
8
13
|
return Effect.andThen(
|
|
9
14
|
Effect.logError("Authentication provider failed", redact(cause)),
|
|
10
15
|
Effect.fail(new AuthUnavailable({ message: "Authentication is temporarily unavailable" })),
|
|
11
16
|
);
|
|
12
17
|
}
|
|
13
|
-
export function betterAuthSessions<TSession extends SessionShape>(getSession: GetSession<TSession>): SessionProvider<TSession
|
|
18
|
+
export function betterAuthSessions<TSession extends SessionShape>(getSession: GetSession<TSession>): SessionProvider<TSession>;
|
|
19
|
+
export function betterAuthSessions<TSession extends SessionShape>(
|
|
20
|
+
getSession: GetSession<TSession>,
|
|
21
|
+
getProviderAccounts: GetProviderAccounts,
|
|
22
|
+
): SessionProvider<SessionWithProviderAccounts<TSession>>;
|
|
23
|
+
export function betterAuthSessions<TSession extends SessionShape>(getSession: GetSession<TSession>, getProviderAccounts?: GetProviderAccounts) {
|
|
14
24
|
const betterAuthSession = Effect.fn("PlatformRpc.betterAuthSession")(function* (headers: Headers.Headers) {
|
|
15
|
-
const session = yield* Effect.tryPromise(() =>
|
|
16
|
-
|
|
17
|
-
|
|
25
|
+
const session = yield* Effect.tryPromise(async () => {
|
|
26
|
+
const transport = new globalThis.Headers(headers);
|
|
27
|
+
const resolved = await getSession(transport);
|
|
28
|
+
if (resolved === null || getProviderAccounts === undefined) {
|
|
29
|
+
return resolved;
|
|
30
|
+
}
|
|
31
|
+
const accounts = await getProviderAccounts(transport);
|
|
32
|
+
return {
|
|
33
|
+
...resolved,
|
|
34
|
+
user: { ...resolved.user, providerAccounts: accounts.map(({ accountId, providerId }) => ({ accountId, providerId })) },
|
|
35
|
+
};
|
|
36
|
+
}).pipe(Effect.catch((error) => unavailable(error.cause)));
|
|
18
37
|
return Option.fromNullishOr(session);
|
|
19
38
|
});
|
|
20
39
|
return { get: betterAuthSession };
|
|
@@ -60,20 +60,30 @@ export function maybeAuthenticatedLayer<TSession extends SessionShape>(policy: S
|
|
|
60
60
|
export interface UserSessionShape extends SessionShape {
|
|
61
61
|
readonly user: SessionUserValue;
|
|
62
62
|
}
|
|
63
|
-
export function
|
|
64
|
-
return
|
|
63
|
+
export function withSessionUser<TSession extends UserSessionShape>(policy: SessionPolicy<TSession>, headers: Headers.Headers, operation: string) {
|
|
64
|
+
return <A, E, R>(effect: Effect.Effect<A, E, R>) =>
|
|
65
65
|
Effect.gen(function* () {
|
|
66
|
-
const session = yield* resolveSession(policy, headers,
|
|
66
|
+
const session = yield* resolveSession(policy, headers, operation);
|
|
67
67
|
if (Option.isNone(session)) {
|
|
68
68
|
return yield* new Unauthorized({ message: "Authentication required" });
|
|
69
69
|
}
|
|
70
|
-
const { name, email } = session.value.user;
|
|
70
|
+
const { name, email, role, providerAccounts } = session.value.user;
|
|
71
71
|
const id = yield* identified(session.value.user.id);
|
|
72
72
|
return yield* effect.pipe(
|
|
73
73
|
Effect.provideService(Identity, { id }),
|
|
74
|
-
Effect.provideService(SessionUser, {
|
|
74
|
+
Effect.provideService(SessionUser, {
|
|
75
|
+
email,
|
|
76
|
+
id,
|
|
77
|
+
name,
|
|
78
|
+
...(role === undefined ? {} : { role }),
|
|
79
|
+
...(providerAccounts === undefined
|
|
80
|
+
? {}
|
|
81
|
+
: { providerAccounts: providerAccounts.map(({ accountId, providerId }) => ({ accountId, providerId })) }),
|
|
82
|
+
}),
|
|
75
83
|
Effect.annotateLogs({ userId: id }),
|
|
76
84
|
);
|
|
77
|
-
})
|
|
78
|
-
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
export function authenticatedUserLayer<TSession extends UserSessionShape>(policy: SessionPolicy<TSession>): Layer.Layer<AuthenticatedUser> {
|
|
88
|
+
return Layer.succeed(AuthenticatedUser, (effect, { headers, rpc }) => effect.pipe(withSessionUser(policy, headers, rpc._tag)));
|
|
79
89
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Effect, Layer } from "effect";
|
|
2
2
|
import type { OtlpResource } from "effect/observability";
|
|
3
3
|
import { OtlpSerialization } from "effect/observability";
|
|
4
|
-
import { redactOtlpUrl } from "#redactOtlpUrl.ts";
|
|
5
|
-
import { isSensitiveKey, REDACTED, redactText } from "#rpc-server/sensitive.ts";
|
|
4
|
+
import { redactOtlpUrl } from "#server/redactOtlpUrl.ts";
|
|
5
|
+
import { isSensitiveKey, REDACTED, redactText } from "#server/rpc-server/sensitive.ts";
|
|
6
6
|
|
|
7
7
|
function sensitive(key: string) {
|
|
8
8
|
return isSensitiveKey(key) || ["db.user", "db.user.name", "db.connection_string"].includes(key.toLowerCase());
|
|
@@ -2,10 +2,10 @@ import type { TestContext } from "@effect/vitest";
|
|
|
2
2
|
import { Effect, type Layer } from "effect";
|
|
3
3
|
import type { AnyDatabase } from "@shivaedev/effect-prisma/databaseTypes.ts";
|
|
4
4
|
import { withTestTransaction } from "@shivaedev/effect-prisma/testing/transaction.ts";
|
|
5
|
-
import type { EffectTRPCAdapter } from "@shivaedev/effect-trpc/adapter.ts";
|
|
6
|
-
import type { EffectCallerFactory } from "@shivaedev/effect-trpc/testing/caller.ts";
|
|
7
|
-
import type { CallerOptions, CallerResult } from "@shivaedev/effect-trpc/testing/types.ts";
|
|
8
|
-
import { makeTrpcHarnessIt } from "@shivaedev/effect-trpc/testing/vitest.ts";
|
|
5
|
+
import type { EffectTRPCAdapter } from "@shivaedev/effect-trpc/server/adapter.ts";
|
|
6
|
+
import type { EffectCallerFactory } from "@shivaedev/effect-trpc/server/testing/caller.ts";
|
|
7
|
+
import type { CallerOptions, CallerResult } from "@shivaedev/effect-trpc/server/testing/types.ts";
|
|
8
|
+
import { makeTrpcHarnessIt } from "@shivaedev/effect-trpc/server/testing/vitest.ts";
|
|
9
9
|
import type { Bivariant } from "@shivaedev/types/bivariant.ts";
|
|
10
10
|
import type { MakePlatformItOptions, PlatformHarness, PlatformIt } from "./types.ts";
|
|
11
11
|
|
|
@@ -2,9 +2,9 @@ import type { TestContext, Vitest } from "@effect/vitest";
|
|
|
2
2
|
import type { Effect, Layer } from "effect";
|
|
3
3
|
import type { AnyDatabase } from "@shivaedev/effect-prisma/databaseTypes.ts";
|
|
4
4
|
import type { DatabaseService } from "@shivaedev/effect-prisma/testing/types.ts";
|
|
5
|
-
import type { EffectTRPCAdapter } from "@shivaedev/effect-trpc/adapter.ts";
|
|
6
|
-
import type { EffectCallerFactory } from "@shivaedev/effect-trpc/testing/caller.ts";
|
|
7
|
-
import type { CallerOptions, CallerResult, TrpcHarnessTest, TrpcHarnessTester } from "@shivaedev/effect-trpc/testing/types.ts";
|
|
5
|
+
import type { EffectTRPCAdapter } from "@shivaedev/effect-trpc/server/adapter.ts";
|
|
6
|
+
import type { EffectCallerFactory } from "@shivaedev/effect-trpc/server/testing/caller.ts";
|
|
7
|
+
import type { CallerOptions, CallerResult, TrpcHarnessTest, TrpcHarnessTester } from "@shivaedev/effect-trpc/server/testing/types.ts";
|
|
8
8
|
export type PlatformHarness<TDatabase extends AnyDatabase, TOptions, TCaller, TExtension> = Readonly<{
|
|
9
9
|
db: DatabaseService<TDatabase>;
|
|
10
10
|
trpc: EffectCallerFactory<TOptions, TCaller>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Rpc, RpcGroup } from "effect/rpc";
|
|
2
|
+
import { RequestTracing } from "#rpc/middleware.ts";
|
|
3
|
+
|
|
4
|
+
export function tracedGroup<R extends Rpc.Any>(group: RpcGroup.RpcGroup<R>) {
|
|
5
|
+
const traced = group.middleware(RequestTracing);
|
|
6
|
+
for (const rpc of traced.requests.values()) {
|
|
7
|
+
const middlewares = new Set(rpc.middlewares);
|
|
8
|
+
middlewares.delete(RequestTracing);
|
|
9
|
+
middlewares.add(RequestTracing);
|
|
10
|
+
// Native middleware() clones each RPC but cannot move an existing middleware to the outermost position.
|
|
11
|
+
Object.assign(rpc, { middlewares });
|
|
12
|
+
}
|
|
13
|
+
return traced;
|
|
14
|
+
}
|