@shivaedev/server 0.9.3 → 0.11.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 +46 -0
- package/README.md +24 -19
- package/dist/appDatabase.d.ts +14 -0
- package/dist/appDatabase.d.ts.map +1 -0
- package/dist/{databaseLayer.js → appDatabase.js} +6 -6
- package/dist/appDatabase.js.map +1 -0
- package/dist/appHost.d.ts +1 -14
- package/dist/appHost.d.ts.map +1 -1
- package/dist/appHost.js +5 -10
- package/dist/appHost.js.map +1 -1
- package/dist/appHostHttp.d.ts +29 -29
- package/dist/appHostHttp.d.ts.map +1 -1
- package/dist/appHostHttp.js +7 -7
- package/dist/appHostHttp.js.map +1 -1
- package/dist/bearerSocketRpc.d.ts +5 -0
- package/dist/bearerSocketRpc.d.ts.map +1 -0
- package/dist/bearerSocketRpc.js +69 -0
- package/dist/bearerSocketRpc.js.map +1 -0
- package/dist/hostChanges.d.ts +2 -3
- package/dist/hostChanges.d.ts.map +1 -1
- package/dist/hostChanges.js +9 -14
- package/dist/hostChanges.js.map +1 -1
- package/dist/hostRpcRoutes.d.ts +2 -3
- package/dist/hostRpcRoutes.d.ts.map +1 -1
- package/dist/hostRpcRoutes.js +4 -2
- package/dist/hostRpcRoutes.js.map +1 -1
- package/dist/otlpHttpClient.js +2 -2
- package/dist/otlpHttpClient.js.map +1 -1
- package/dist/redactingDiagnostics.d.ts +0 -2
- package/dist/redactingDiagnostics.d.ts.map +1 -1
- package/dist/redactingDiagnostics.js +1 -4
- package/dist/redactingDiagnostics.js.map +1 -1
- package/dist/redactingTracer.d.ts.map +1 -1
- package/dist/redactingTracer.js +6 -5
- package/dist/redactingTracer.js.map +1 -1
- package/dist/safeOtlpSerialization.d.ts +0 -2
- package/dist/safeOtlpSerialization.d.ts.map +1 -1
- package/dist/safeOtlpSerialization.js +5 -12
- package/dist/safeOtlpSerialization.js.map +1 -1
- package/dist/setupDatabases.d.ts +1 -1
- package/dist/setupDatabases.d.ts.map +1 -1
- package/dist/setupDatabases.js +1 -1
- package/dist/setupDatabases.js.map +1 -1
- package/dist/socketRpc.d.ts.map +1 -1
- package/dist/socketRpc.js +6 -4
- package/dist/socketRpc.js.map +1 -1
- package/package.json +11 -17
- package/src/{databaseLayer.ts → appDatabase.ts} +6 -6
- package/src/appHost.ts +5 -11
- package/src/appHostHttp.ts +8 -11
- package/src/bearerSocketRpc.ts +80 -0
- package/src/hostChanges.ts +12 -22
- package/src/hostRpcRoutes.ts +4 -6
- package/src/otlpHttpClient.ts +2 -2
- package/src/redactingDiagnostics.ts +1 -5
- package/src/redactingTracer.ts +7 -5
- package/src/safeOtlpSerialization.ts +5 -12
- package/src/setupDatabases.ts +1 -1
- package/src/socketRpc.ts +6 -6
- package/dist/databaseLayer.d.ts +0 -14
- package/dist/databaseLayer.d.ts.map +0 -1
- package/dist/databaseLayer.js.map +0 -1
- package/dist/redactOtlpUrl.d.ts +0 -2
- package/dist/redactOtlpUrl.d.ts.map +0 -1
- package/dist/redactOtlpUrl.js +0 -41
- package/dist/redactOtlpUrl.js.map +0 -1
- package/dist/schemaSensitiveAst.d.ts +0 -7
- package/dist/schemaSensitiveAst.d.ts.map +0 -1
- package/dist/schemaSensitiveAst.js +0 -102
- package/dist/schemaSensitiveAst.js.map +0 -1
- package/dist/schemaSensitiveKeys.d.ts +0 -6
- package/dist/schemaSensitiveKeys.d.ts.map +0 -1
- package/dist/schemaSensitiveKeys.js +0 -47
- package/dist/schemaSensitiveKeys.js.map +0 -1
- package/src/redactOtlpUrl.ts +0 -48
- package/src/schemaSensitiveAst.ts +0 -106
- package/src/schemaSensitiveKeys.ts +0 -49
package/src/appHost.ts
CHANGED
|
@@ -1,25 +1,19 @@
|
|
|
1
1
|
import * as NodeCrypto from "@effect/platform-node/NodeCrypto";
|
|
2
|
-
import {
|
|
2
|
+
import { Layer } from "effect";
|
|
3
3
|
import type { Rpc } from "effect/rpc";
|
|
4
|
-
import type { makeLiveServer } from "@shivaedev/contract/makeLiveServer.ts";
|
|
5
4
|
import type { SessionPolicy, SessionShape } from "@shivaedev/identity/server/session.ts";
|
|
6
5
|
import type { AppHostFactory } from "#AppHostFactory.ts";
|
|
7
6
|
import type { AppHostInput, HostPreflight } from "#AppHostInput.ts";
|
|
7
|
+
import { appDatabase, type DatabaseConfig } from "#appDatabase.ts";
|
|
8
8
|
import { appHostHttp, deferredHost, migratedHost } from "#appHostHttp.ts";
|
|
9
|
-
import { type DatabaseConfig, databaseLayer } from "#databaseLayer.ts";
|
|
10
9
|
import type { HostCoreOptions, HostPolicy, HostRouteRequirements, HostRoutes } from "#hostOptions.ts";
|
|
11
10
|
import type { MigrationOptions } from "#MigrationOptions.ts";
|
|
12
11
|
|
|
13
|
-
export class AppChanges extends Context.Service<AppChanges, Effect.Success<typeof makeLiveServer>>()("@shivaedev/server/AppChanges") {}
|
|
14
12
|
export interface AppDatabaseOptions<R = never, E = never> {
|
|
15
13
|
readonly config?: DatabaseConfig;
|
|
16
14
|
readonly migrations: MigrationOptions<R, E>;
|
|
17
15
|
}
|
|
18
16
|
|
|
19
|
-
export function appDatabase<R, E = never>(options: AppDatabaseOptions<R, E>) {
|
|
20
|
-
return databaseLayer(options, AppChanges);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
17
|
export type AppHostOptions<
|
|
24
18
|
TRpcs extends Rpc.Any,
|
|
25
19
|
THandlers,
|
|
@@ -125,14 +119,14 @@ export const appHost: AppHostFactory = <
|
|
|
125
119
|
>,
|
|
126
120
|
) => {
|
|
127
121
|
if ("application" in options) {
|
|
128
|
-
return deferredHost(options.application, appDatabase(options)
|
|
122
|
+
return deferredHost(options.application, appDatabase(options)).pipe(
|
|
129
123
|
Layer.provideMerge(options.preflight ?? Layer.empty),
|
|
130
124
|
Layer.provide(NodeCrypto.layer),
|
|
131
125
|
);
|
|
132
126
|
}
|
|
133
127
|
return options.migrations === undefined
|
|
134
|
-
? appHostHttp(options
|
|
135
|
-
: migratedHost({ ...options, migrations: options.migrations }, appDatabase({ ...options, migrations: options.migrations })
|
|
128
|
+
? appHostHttp(options).pipe(Layer.provideMerge(options.preflight ?? Layer.empty), Layer.provide(NodeCrypto.layer))
|
|
129
|
+
: migratedHost({ ...options, migrations: options.migrations }, appDatabase({ ...options, migrations: options.migrations })).pipe(
|
|
136
130
|
Layer.provideMerge(options.preflight ?? Layer.empty),
|
|
137
131
|
Layer.provide(NodeCrypto.layer),
|
|
138
132
|
);
|
package/src/appHostHttp.ts
CHANGED
|
@@ -2,9 +2,10 @@ import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer";
|
|
|
2
2
|
import { Effect, Layer } from "effect";
|
|
3
3
|
import { HttpRouter, HttpServer, HttpStaticServer } from "effect/http";
|
|
4
4
|
import type { Rpc } from "effect/rpc";
|
|
5
|
+
import { schemaSensitiveKeys } from "@shivaedev/errors/schemaSensitiveKeys.ts";
|
|
5
6
|
import type { SessionShape } from "@shivaedev/identity/server/session.ts";
|
|
6
|
-
import type {
|
|
7
|
-
import type {
|
|
7
|
+
import type { appDatabase } from "#appDatabase.ts";
|
|
8
|
+
import type { AppHttpHostOptions } from "#appHost.ts";
|
|
8
9
|
import { healthRoute } from "#healthRoute.ts";
|
|
9
10
|
import { hostApplicationRoutes } from "#hostApplicationRoutes.ts";
|
|
10
11
|
import type { HostOptions, HostPolicy, HostRouteRequirements } from "#hostOptions.ts";
|
|
@@ -16,7 +17,6 @@ import { nodeHttpServer } from "#nodeHttpServer.ts";
|
|
|
16
17
|
import { otlpTracing } from "#otlpTracing.ts";
|
|
17
18
|
import { RedactionPolicy } from "#RedactionPolicy.ts";
|
|
18
19
|
import { redactingDiagnostics } from "#redactingDiagnostics.ts";
|
|
19
|
-
import { schemaSensitiveKeys } from "#schemaSensitiveKeys.ts";
|
|
20
20
|
|
|
21
21
|
export function appHostHttp<
|
|
22
22
|
TRpcs extends Rpc.Any,
|
|
@@ -42,14 +42,13 @@ export function appHostHttp<
|
|
|
42
42
|
TRouteRequirements
|
|
43
43
|
> &
|
|
44
44
|
HostPolicy<TSession>,
|
|
45
|
-
changesService: typeof AppChanges,
|
|
46
45
|
) {
|
|
47
46
|
const sensitive = schemaSensitiveKeys(options.group, options.config?.sensitiveKeys);
|
|
48
47
|
return Layer.unwrap(
|
|
49
48
|
Effect.gen(function* () {
|
|
50
49
|
const transport = yield* hostTransport(options);
|
|
51
50
|
const { bodySize, host, trustedProxy, nativeOrigins } = transport;
|
|
52
|
-
const routes = hostRpcRoutes(options,
|
|
51
|
+
const routes = hostRpcRoutes(options, transport, sensitive);
|
|
53
52
|
const assets =
|
|
54
53
|
options.assets === undefined
|
|
55
54
|
? Layer.empty
|
|
@@ -98,11 +97,10 @@ export function migratedHost<
|
|
|
98
97
|
HostPolicy<TSession> & {
|
|
99
98
|
readonly migrations: MigrationOptions<TMigrations, TMigrationError>;
|
|
100
99
|
},
|
|
101
|
-
database: ReturnType<typeof
|
|
102
|
-
changesService: typeof AppChanges,
|
|
100
|
+
database: ReturnType<typeof appDatabase<TMigrations, TMigrationError>>,
|
|
103
101
|
) {
|
|
104
102
|
const services = Layer.mergeAll(options.services ?? Layer.empty);
|
|
105
|
-
return appHostHttp({ ...options, services: services.pipe(Layer.provideMerge(database)) }
|
|
103
|
+
return appHostHttp({ ...options, services: services.pipe(Layer.provideMerge(database)) });
|
|
106
104
|
}
|
|
107
105
|
|
|
108
106
|
export function deferredHost<
|
|
@@ -140,8 +138,7 @@ export function deferredHost<
|
|
|
140
138
|
TApplicationError,
|
|
141
139
|
TApplicationRequirements
|
|
142
140
|
>,
|
|
143
|
-
database: ReturnType<typeof
|
|
144
|
-
changesService: typeof AppChanges,
|
|
141
|
+
database: ReturnType<typeof appDatabase<TMigrations, TMigrationError>>,
|
|
145
142
|
) {
|
|
146
|
-
return Layer.unwrap(Effect.map(application, (options) => appHostHttp(options
|
|
143
|
+
return Layer.unwrap(Effect.map(application, (options) => appHostHttp(options))).pipe(Layer.provideMerge(database));
|
|
147
144
|
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Effect, Option, Schema, type Scope } from "effect";
|
|
2
|
+
import { Headers, HttpServerRequest, HttpServerResponse } from "effect/http";
|
|
3
|
+
import * as Socket from "effect/socket/Socket";
|
|
4
|
+
import { SocketAuthentication } from "@shivaedev/contract/socketAuthentication.ts";
|
|
5
|
+
import { Unauthorized } from "@shivaedev/errors/taxonomy.ts";
|
|
6
|
+
import { recheckSession } from "@shivaedev/identity/server/recheckSession.ts";
|
|
7
|
+
import { resolveSession, type SessionPolicy, type SessionShape } from "@shivaedev/identity/server/session.ts";
|
|
8
|
+
import { makeSocketSession, SocketSession } from "@shivaedev/identity/server/socketSession.ts";
|
|
9
|
+
|
|
10
|
+
export const bearerSocketRpc = Effect.fnUntraced(function* <TSession extends SessionShape>(
|
|
11
|
+
handler: Effect.Effect<HttpServerResponse.HttpServerResponse, never, HttpServerRequest.HttpServerRequest | Scope.Scope>,
|
|
12
|
+
policy: SessionPolicy<TSession>,
|
|
13
|
+
) {
|
|
14
|
+
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
15
|
+
const socket = yield* Effect.orDie(request.upgrade);
|
|
16
|
+
const reader = yield* Effect.orDie(socket.reader);
|
|
17
|
+
const writer = yield* socket.writer;
|
|
18
|
+
function close(code: number, reason: string) {
|
|
19
|
+
return writer.write(new Socket.CloseEvent(code, reason)).pipe(Effect.as(HttpServerResponse.empty()));
|
|
20
|
+
}
|
|
21
|
+
function unauthorized() {
|
|
22
|
+
return close(1008, "Authentication required");
|
|
23
|
+
}
|
|
24
|
+
function unavailable() {
|
|
25
|
+
return close(1011, "Authentication unavailable");
|
|
26
|
+
}
|
|
27
|
+
return yield* Effect.gen(function* () {
|
|
28
|
+
const frames = yield* reader.pull.pipe(Effect.timeout("5 seconds"));
|
|
29
|
+
const frame = frames[0];
|
|
30
|
+
const auth =
|
|
31
|
+
typeof frame === "string" && frame.length <= 8192
|
|
32
|
+
? Schema.decodeUnknownOption(Schema.fromJsonString(SocketAuthentication))(frame)
|
|
33
|
+
: Option.none();
|
|
34
|
+
if (Option.isNone(auth)) {
|
|
35
|
+
return yield* new Unauthorized({ message: "Authentication required" });
|
|
36
|
+
}
|
|
37
|
+
const authenticated = request.modify({ headers: Headers.set(request.headers, "authorization", `Bearer ${auth.value.token}`) });
|
|
38
|
+
let buffered = frames.slice(1);
|
|
39
|
+
const authenticatedSocket: Socket.Socket = {
|
|
40
|
+
...socket,
|
|
41
|
+
reader: Effect.succeed({
|
|
42
|
+
...reader,
|
|
43
|
+
pull: Effect.suspend(() => {
|
|
44
|
+
if (buffered.length === 0) {
|
|
45
|
+
return reader.pull;
|
|
46
|
+
}
|
|
47
|
+
const [first, ...rest] = buffered;
|
|
48
|
+
buffered = [];
|
|
49
|
+
return first === undefined ? reader.pull : Effect.succeed([first, ...rest] as const);
|
|
50
|
+
}),
|
|
51
|
+
}),
|
|
52
|
+
};
|
|
53
|
+
const transport = new Proxy(authenticated, {
|
|
54
|
+
get: (target, key, receiver) => (key === "upgrade" ? Effect.succeed(authenticatedSocket) : Reflect.get(target, key, receiver)),
|
|
55
|
+
});
|
|
56
|
+
return yield* Effect.gen(function* () {
|
|
57
|
+
const initial = yield* resolveSession(policy, Headers.empty, "$connect");
|
|
58
|
+
if (Option.isNone(initial)) {
|
|
59
|
+
return yield* new Unauthorized({ message: "Authentication required" });
|
|
60
|
+
}
|
|
61
|
+
const session = yield* makeSocketSession(policy.streamRevalidationInterval ?? "30 seconds");
|
|
62
|
+
const check = recheckSession(policy, Headers.empty, "$connect").pipe(
|
|
63
|
+
Effect.flatMap((current) =>
|
|
64
|
+
Option.isSome(current) && current.value.user.id === initial.value.user.id
|
|
65
|
+
? Effect.void
|
|
66
|
+
: Effect.fail(new Unauthorized({ message: "Authentication required" })),
|
|
67
|
+
),
|
|
68
|
+
);
|
|
69
|
+
return yield* Effect.raceFirst(handler, session.monitor(check)).pipe(Effect.provideService(SocketSession, session));
|
|
70
|
+
}).pipe(Effect.provideService(HttpServerRequest.HttpServerRequest, transport));
|
|
71
|
+
}).pipe(
|
|
72
|
+
Effect.catchTags({
|
|
73
|
+
AuthUnavailable: unavailable,
|
|
74
|
+
Forbidden: unauthorized,
|
|
75
|
+
TimeoutError: unauthorized,
|
|
76
|
+
Unauthorized: unauthorized,
|
|
77
|
+
}),
|
|
78
|
+
Effect.catchTag("SocketError", () => Effect.succeed(HttpServerResponse.empty())),
|
|
79
|
+
);
|
|
80
|
+
});
|
package/src/hostChanges.ts
CHANGED
|
@@ -1,44 +1,43 @@
|
|
|
1
1
|
import { Context, Effect, Layer, Option, Stream } from "effect";
|
|
2
2
|
import { Headers } from "effect/http";
|
|
3
|
-
import
|
|
3
|
+
import { Rpc, type RpcGroup } from "effect/rpc";
|
|
4
|
+
import { LiveServer } from "@shivaedev/contract/LiveServer.ts";
|
|
4
5
|
import { CurrentViewer, type Viewer } from "@shivaedev/contract/readPolicy.ts";
|
|
5
6
|
import type { QueryReference } from "@shivaedev/contract/serverMetadata.ts";
|
|
6
7
|
import { Unauthorized } from "@shivaedev/errors/taxonomy.ts";
|
|
7
8
|
import { recheckSession } from "@shivaedev/identity/server/recheckSession.ts";
|
|
9
|
+
import { revalidateStream } from "@shivaedev/identity/server/revalidateStream.ts";
|
|
8
10
|
import type { SessionPolicy, SessionShape } from "@shivaedev/identity/server/session.ts";
|
|
9
11
|
import { resolveChanges } from "@shivaedev/identity/server/sessionKeys.ts";
|
|
10
|
-
import { revalidateSession } from "@shivaedev/identity/server/socketSession.ts";
|
|
11
|
-
import type { AppChanges } from "#appHost.ts";
|
|
12
12
|
import { rerunQuery } from "#rerunQuery.ts";
|
|
13
13
|
|
|
14
|
-
export function hostChanges<R extends Rpc.Any, TSession extends SessionShape>(
|
|
15
|
-
group: RpcGroup.RpcGroup<R>,
|
|
16
|
-
changesService: typeof AppChanges,
|
|
17
|
-
sessions?: SessionPolicy<TSession>,
|
|
18
|
-
) {
|
|
14
|
+
export function hostChanges<R extends Rpc.Any, TSession extends SessionShape>(group: RpcGroup.RpcGroup<R>, sessions?: SessionPolicy<TSession>) {
|
|
19
15
|
return Layer.effectContext(
|
|
20
16
|
Effect.gen(function* () {
|
|
21
|
-
const changes = yield* Effect.serviceOption(
|
|
17
|
+
const changes = yield* Effect.serviceOption(LiveServer);
|
|
22
18
|
const services = yield* Effect.context<never>();
|
|
23
19
|
const handlers = new Map<string, unknown>();
|
|
24
|
-
function subscribe(rpc: Rpc.
|
|
20
|
+
function subscribe(rpc: Rpc.AnyWithProps, queries: readonly QueryReference[], options: Parameters<Rpc.Handler<string>["handler"]>[1]) {
|
|
25
21
|
return Stream.unwrap(
|
|
26
22
|
Effect.gen(function* () {
|
|
27
23
|
const { subscription, context } = yield* resolveChanges(rpc, queries);
|
|
28
24
|
const viewer = yield* CurrentViewer;
|
|
29
25
|
const authorized = authorizeSession(sessions, rpc._tag, viewer, subscription.authorized);
|
|
30
26
|
const stream = Option.isSome(changes)
|
|
31
|
-
? changes.value.subscribe({ ...subscription, authorized }, context, rerunQuery(group, options))
|
|
27
|
+
? Stream.unwrap(changes.value.subscribe({ ...subscription, authorized }, context, rerunQuery(group, options)))
|
|
32
28
|
: Stream.concat(Stream.succeed({ _tag: "Resync" } as const), Stream.never);
|
|
33
29
|
if (sessions === undefined || Option.isNone(viewer)) {
|
|
34
30
|
return stream;
|
|
35
31
|
}
|
|
36
|
-
|
|
32
|
+
const revalidation = Effect.never.pipe(
|
|
33
|
+
revalidateStream(sessions, rpc, viewer.value.id, recheckSession(sessions, Headers.empty, rpc._tag)),
|
|
34
|
+
);
|
|
35
|
+
return Stream.interruptWhen(stream, revalidation);
|
|
37
36
|
}),
|
|
38
37
|
);
|
|
39
38
|
}
|
|
40
39
|
for (const rpc of group.requests.values()) {
|
|
41
|
-
if (!rpc._tag.endsWith(".$changes")) {
|
|
40
|
+
if (!(Rpc.isRpc(rpc) && rpc._tag.endsWith(".$changes"))) {
|
|
42
41
|
continue;
|
|
43
42
|
}
|
|
44
43
|
const provided = services.mapUnsafe.get(rpc.key);
|
|
@@ -75,12 +74,3 @@ function authorizeSession<TSession extends SessionShape, E, R>(
|
|
|
75
74
|
),
|
|
76
75
|
);
|
|
77
76
|
}
|
|
78
|
-
|
|
79
|
-
function sessionRevoked<TSession extends SessionShape>(sessions: SessionPolicy<TSession>, tag: string, id: string) {
|
|
80
|
-
return Effect.gen(function* () {
|
|
81
|
-
const current = yield* recheckSession(sessions, Headers.empty, tag).pipe(Effect.catchTag("AuthUnavailable", () => Effect.succeed(undefined)));
|
|
82
|
-
if (current !== undefined && (Option.isNone(current) || current.value.user.id !== id)) {
|
|
83
|
-
return yield* new Unauthorized({ message: "Authentication required" });
|
|
84
|
-
}
|
|
85
|
-
}).pipe((check) => revalidateSession(check, sessions.streamRevalidationInterval ?? "30 seconds"));
|
|
86
|
-
}
|
package/src/hostRpcRoutes.ts
CHANGED
|
@@ -2,10 +2,11 @@ import { ByteSize, Effect, Layer } from "effect";
|
|
|
2
2
|
import { HttpEffect, HttpIncomingMessage, HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/http";
|
|
3
3
|
import * as HttpStatus from "effect/http/HttpStatus";
|
|
4
4
|
import { type Rpc, type RpcGroup, RpcSerialization } from "effect/rpc";
|
|
5
|
+
import { validateReadPolicies } from "@shivaedev/contract/validateReadPolicies.ts";
|
|
6
|
+
import type { schemaSensitiveKeys } from "@shivaedev/errors/schemaSensitiveKeys.ts";
|
|
5
7
|
import type { Authenticated, MaybeAuthenticated, RequestTracing } from "@shivaedev/identity/middleware.ts";
|
|
6
8
|
import { localRpcGuard } from "@shivaedev/identity/server/local.ts";
|
|
7
9
|
import { authenticatedLayer, maybeAuthenticatedLayer, type SessionShape } from "@shivaedev/identity/server/session.ts";
|
|
8
|
-
import type { AppChanges } from "#appHost.ts";
|
|
9
10
|
import { boundedRpcRequest } from "#boundedRpcRequest.ts";
|
|
10
11
|
import { hostChanges } from "#hostChanges.ts";
|
|
11
12
|
import type { HostPolicy } from "#hostOptions.ts";
|
|
@@ -13,7 +14,6 @@ import { hostRpc, rpcCacheControl } from "#hostRpc.ts";
|
|
|
13
14
|
import type { hostTransport } from "#hostTransport.ts";
|
|
14
15
|
import { redactingRpcSerialization } from "#redactingRpcSerialization.ts";
|
|
15
16
|
import { rpcRequestTracing } from "#rpcRequestTracing.ts";
|
|
16
|
-
import type { schemaSensitiveKeys } from "#schemaSensitiveKeys.ts";
|
|
17
17
|
import { socketRpc } from "#socketRpc.ts";
|
|
18
18
|
|
|
19
19
|
export function hostRpcRoutes<TRpcs extends Rpc.Any, THandlers, TError, TRequirements, TSession extends SessionShape>(
|
|
@@ -22,7 +22,6 @@ export function hostRpcRoutes<TRpcs extends Rpc.Any, THandlers, TError, TRequire
|
|
|
22
22
|
readonly handlers: Layer.Layer<THandlers, TError, TRequirements>;
|
|
23
23
|
readonly rpcPath?: HttpRouter.PathInput;
|
|
24
24
|
} & HostPolicy<TSession>,
|
|
25
|
-
changesService: typeof AppChanges,
|
|
26
25
|
{ bodySize, trustedProxy, nativeOrigins }: Effect.Success<ReturnType<typeof hostTransport>>,
|
|
27
26
|
sensitive: ReturnType<typeof schemaSensitiveKeys>,
|
|
28
27
|
) {
|
|
@@ -32,6 +31,7 @@ export function hostRpcRoutes<TRpcs extends Rpc.Any, THandlers, TError, TRequire
|
|
|
32
31
|
: Layer.mergeAll(authenticatedLayer(options.sessions), maybeAuthenticatedLayer(options.sessions), rpcRequestTracing(sensitive));
|
|
33
32
|
return HttpRouter.use((router) =>
|
|
34
33
|
Effect.gen(function* () {
|
|
34
|
+
yield* validateReadPolicies(options.group);
|
|
35
35
|
const wire = redactingRpcSerialization(yield* RpcSerialization.RpcSerialization, sensitive, sensitive.maskFailure, sensitive.forRpc);
|
|
36
36
|
const httpGroup = options.group.omit(...[...options.group.requests.keys()].filter((tag) => tag.endsWith(".$changes")));
|
|
37
37
|
const handler = yield* hostRpc(httpGroup).pipe(Effect.provideService(RpcSerialization.RpcSerialization, wire));
|
|
@@ -62,9 +62,7 @@ export function hostRpcRoutes<TRpcs extends Rpc.Any, THandlers, TError, TRequire
|
|
|
62
62
|
Layer.provide(
|
|
63
63
|
Layer.merge(
|
|
64
64
|
middleware,
|
|
65
|
-
hostChanges(options.group,
|
|
66
|
-
Layer.provideMerge(options.handlers),
|
|
67
|
-
),
|
|
65
|
+
hostChanges(options.group, options.policy === "loopback" ? undefined : options.sessions).pipe(Layer.provideMerge(options.handlers)),
|
|
68
66
|
),
|
|
69
67
|
),
|
|
70
68
|
Layer.provide(RpcSerialization.layerNdjson),
|
package/src/otlpHttpClient.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Effect, Layer } from "effect";
|
|
2
2
|
import { FetchHttpClient, HttpClient, HttpClientError, type HttpClientRequest } from "effect/http";
|
|
3
3
|
import * as HttpStatus from "effect/http/HttpStatus";
|
|
4
|
+
import { redactText } from "@shivaedev/errors/sensitive.ts";
|
|
4
5
|
import { RedactionPolicy } from "#RedactionPolicy.ts";
|
|
5
|
-
import { redactOtlpText } from "#safeOtlpSerialization.ts";
|
|
6
6
|
|
|
7
7
|
function timeoutError(request: HttpClientRequest.HttpClientRequest) {
|
|
8
8
|
return new HttpClientError.HttpClientError({
|
|
@@ -44,7 +44,7 @@ export function otlpHttpClient(timeout = 10_000) {
|
|
|
44
44
|
return warnOnce(
|
|
45
45
|
response.text.pipe(
|
|
46
46
|
Effect.orElseSucceed(() => "Unable to read collector response"),
|
|
47
|
-
Effect.map((body) => ({ "otlp.response":
|
|
47
|
+
Effect.map((body) => ({ "otlp.response": redactText(body, sensitive), "otlp.status": response.status })),
|
|
48
48
|
),
|
|
49
49
|
);
|
|
50
50
|
}),
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import { Context, Effect, ErrorReporter, Layer, Logger, References } from "effect";
|
|
2
|
-
import { redact } from "@shivaedev/errors/redact.ts";
|
|
2
|
+
import { redact, redactRecord } from "@shivaedev/errors/redact.ts";
|
|
3
3
|
import { redactDiagnosticCause, redactingErrorReporter } from "@shivaedev/errors/redactCause.ts";
|
|
4
|
-
import type { SensitiveKey } from "@shivaedev/errors/sensitive.ts";
|
|
5
4
|
import { type HostRedactionPolicy, redactionForRpc } from "#RedactionPolicy.ts";
|
|
6
5
|
|
|
7
|
-
export function redactRecord(values: Readonly<Record<string, unknown>>, sensitive: SensitiveKey): Record<string, unknown> {
|
|
8
|
-
return Object.fromEntries(Object.entries(values).map(([key, value]) => [key, sensitive(key) ? "<redacted>" : redact(value, sensitive)]));
|
|
9
|
-
}
|
|
10
6
|
function requestErrorReporter(reporter: ErrorReporter.ErrorReporter, policy: HostRedactionPolicy): ErrorReporter.ErrorReporter {
|
|
11
7
|
return {
|
|
12
8
|
[ErrorReporter.TypeId]: ErrorReporter.TypeId,
|
package/src/redactingTracer.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { Effect, Exit, Layer, Option, Tracer } from "effect";
|
|
2
|
-
import {
|
|
2
|
+
import { redactRecord } from "@shivaedev/errors/redact.ts";
|
|
3
3
|
import { redactDiagnosticCause } from "@shivaedev/errors/redactCause.ts";
|
|
4
|
-
import {
|
|
4
|
+
import type { SensitiveKey } from "@shivaedev/errors/sensitive.ts";
|
|
5
5
|
import { type HostRedactionPolicy, redactionForRpc } from "#RedactionPolicy.ts";
|
|
6
|
-
import { redactRecord } from "#redactingDiagnostics.ts";
|
|
7
6
|
|
|
8
7
|
function redactLinks(values: readonly Tracer.SpanLink[], sensitive: SensitiveKey) {
|
|
9
8
|
return values.map((link) => ({ ...link, attributes: redactRecord(link.attributes, sensitive) }));
|
|
@@ -11,6 +10,9 @@ function redactLinks(values: readonly Tracer.SpanLink[], sensitive: SensitiveKey
|
|
|
11
10
|
function redactSpan(span: Tracer.Span, policy: HostRedactionPolicy) {
|
|
12
11
|
const end = span.end.bind(span);
|
|
13
12
|
const attribute = span.attribute.bind(span);
|
|
13
|
+
function safeAttribute(key: string, value: unknown) {
|
|
14
|
+
return attribute(key, redactRecord({ [key]: value }, sensitive)[key]);
|
|
15
|
+
}
|
|
14
16
|
const event = span.event.bind(span);
|
|
15
17
|
const addLinks = span.addLinks.bind(span);
|
|
16
18
|
function sensitive(key: string) {
|
|
@@ -18,10 +20,10 @@ function redactSpan(span: Tracer.Span, policy: HostRedactionPolicy) {
|
|
|
18
20
|
}
|
|
19
21
|
return Object.assign(span, {
|
|
20
22
|
addLinks: (values: readonly Tracer.SpanLink[]) => addLinks(redactLinks(values, sensitive)),
|
|
21
|
-
attribute:
|
|
23
|
+
attribute: safeAttribute,
|
|
22
24
|
end: (time: bigint, exit: Exit.Exit<unknown, unknown>) => {
|
|
23
25
|
for (const [key, value] of span.attributes) {
|
|
24
|
-
|
|
26
|
+
safeAttribute(key, value);
|
|
25
27
|
}
|
|
26
28
|
end(time, Exit.isFailure(exit) ? Exit.failCause(redactDiagnosticCause(exit.cause, sensitive)) : exit);
|
|
27
29
|
},
|
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
import { Effect, Layer } from "effect";
|
|
2
2
|
import type { OtlpResource, OtlpTracer } from "effect/observability";
|
|
3
3
|
import { OtlpSerialization } from "effect/observability";
|
|
4
|
-
import {
|
|
4
|
+
import { REDACTED, redactText, type SensitiveKey } from "@shivaedev/errors/sensitive.ts";
|
|
5
5
|
import { type HostRedactionPolicy, RedactionPolicy, redactionForRpc } from "#RedactionPolicy.ts";
|
|
6
|
-
import { redactOtlpUrl } from "#redactOtlpUrl.ts";
|
|
7
6
|
|
|
8
|
-
function otlpSensitive(sensitive: SensitiveKey): SensitiveKey {
|
|
9
|
-
return (key) => sensitive(key) || ["db.user", "db.user.name", "db.connection_string"].includes(key.toLowerCase());
|
|
10
|
-
}
|
|
11
|
-
export function redactOtlpText(value: string, key = "", sensitive: SensitiveKey = isSensitiveKey) {
|
|
12
|
-
return redactText(redactOtlpUrl(value, key), otlpSensitive(sensitive));
|
|
13
|
-
}
|
|
14
7
|
function attributeValue(value: OtlpResource.AnyValue, key: string, sensitive: SensitiveKey): OtlpResource.AnyValue {
|
|
15
8
|
return {
|
|
16
9
|
...value,
|
|
17
|
-
...(typeof value.stringValue === "string" ? { stringValue:
|
|
10
|
+
...(typeof value.stringValue === "string" ? { stringValue: redactText(value.stringValue, sensitive, undefined, key) } : {}),
|
|
18
11
|
...(value.arrayValue ? { arrayValue: { values: value.arrayValue.values.map((item) => attributeValue(item, key, sensitive)) } } : {}),
|
|
19
12
|
...(value.kvlistValue ? { kvlistValue: { values: attributes(value.kvlistValue.values, sensitive) } } : {}),
|
|
20
13
|
};
|
|
@@ -38,7 +31,7 @@ function spanName(span: { readonly name: string; readonly kind: number; readonly
|
|
|
38
31
|
}
|
|
39
32
|
|
|
40
33
|
function redactTraces(data: OtlpTracer.TraceData, policy: HostRedactionPolicy): OtlpTracer.TraceData {
|
|
41
|
-
const resourceSensitive =
|
|
34
|
+
const resourceSensitive = policy;
|
|
42
35
|
return {
|
|
43
36
|
...data,
|
|
44
37
|
resourceSpans: data.resourceSpans.map((resource) => ({
|
|
@@ -48,9 +41,9 @@ function redactTraces(data: OtlpTracer.TraceData, policy: HostRedactionPolicy):
|
|
|
48
41
|
...scope,
|
|
49
42
|
spans: scope.spans.map((span) => {
|
|
50
43
|
const method = span.attributes.find(({ key }) => key === "rpc.method")?.value.stringValue;
|
|
51
|
-
const sensitive =
|
|
44
|
+
const sensitive = redactionForRpc(policy, method);
|
|
52
45
|
function text(value: string) {
|
|
53
|
-
return
|
|
46
|
+
return redactText(value, sensitive);
|
|
54
47
|
}
|
|
55
48
|
return {
|
|
56
49
|
...span,
|
package/src/setupDatabases.ts
CHANGED
|
@@ -8,5 +8,5 @@ export const setupDatabases = Effect.fn("Platform.setupDatabases")(function* ()
|
|
|
8
8
|
"DOCKER_HOST": Config.String("DOCKER_HOST").pipe(Config.withDefault("")),
|
|
9
9
|
"TEST_DATABASE_URL": Config.String("TEST_DATABASE_URL"),
|
|
10
10
|
});
|
|
11
|
-
yield*
|
|
11
|
+
yield* localPostgres(config).prepareDatabases([config.DATABASE_URL, config.TEST_DATABASE_URL]);
|
|
12
12
|
});
|
package/src/socketRpc.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { Effect, type Scope } from "effect";
|
|
2
2
|
import { Headers, HttpServerRequest, HttpServerResponse } from "effect/http";
|
|
3
3
|
import * as HttpStatus from "effect/http/HttpStatus";
|
|
4
|
+
import { isAuthUnavailable, isForbidden } from "@shivaedev/errors/taxonomy.ts";
|
|
4
5
|
import { localTransport } from "@shivaedev/identity/server/local.ts";
|
|
5
6
|
import { resolveSession, type SessionShape } from "@shivaedev/identity/server/session.ts";
|
|
6
7
|
import { makeSocketSession, SocketSession } from "@shivaedev/identity/server/socketSession.ts";
|
|
8
|
+
import { bearerSocketRpc } from "#bearerSocketRpc.ts";
|
|
7
9
|
import type { HostPolicy } from "#hostOptions.ts";
|
|
8
10
|
|
|
9
11
|
export function socketRpc<TSession extends SessionShape>(
|
|
@@ -18,17 +20,15 @@ export function socketRpc<TSession extends SessionShape>(
|
|
|
18
20
|
return HttpServerResponse.text("Local same-origin requests only", { status: HttpStatus.fromLiteral("Forbidden") });
|
|
19
21
|
}
|
|
20
22
|
if (policy.policy !== "loopback") {
|
|
23
|
+
if (new URL(request.url, "http://localhost").searchParams.get("auth") === "frame") {
|
|
24
|
+
return yield* bearerSocketRpc(handler, policy.sessions);
|
|
25
|
+
}
|
|
21
26
|
yield* resolveSession(policy.sessions, Headers.empty, "$connect");
|
|
22
27
|
const session = yield* makeSocketSession(policy.sessions.streamRevalidationInterval ?? "30 seconds");
|
|
23
28
|
return yield* Effect.provideService(handler, SocketSession, session);
|
|
24
29
|
}
|
|
25
30
|
return yield* handler;
|
|
26
|
-
}).pipe(
|
|
27
|
-
Effect.catchTags({
|
|
28
|
-
AuthUnavailable: authUnavailable,
|
|
29
|
-
Forbidden: forbidden,
|
|
30
|
-
}),
|
|
31
|
-
);
|
|
31
|
+
}).pipe(Effect.catchIf(isAuthUnavailable, authUnavailable), Effect.catchIf(isForbidden, forbidden));
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
function authUnavailable() {
|
package/dist/databaseLayer.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import * as PgClient from "@effect/sql-pg/PgClient";
|
|
2
|
-
import { Config, Layer, Redacted } from "effect";
|
|
3
|
-
import * as Reactivity from "effect/reactivity/Reactivity";
|
|
4
|
-
import { Migrator } from "effect/sql";
|
|
5
|
-
import { type PostgresChangesOptions } from "@shivaedev/sql/postgresChanges.ts";
|
|
6
|
-
import { type PostgresOptions } from "@shivaedev/sql/postgresLayer.ts";
|
|
7
|
-
import type { AppChanges, AppDatabaseOptions } from "#appHost.ts";
|
|
8
|
-
export interface DatabaseConfig extends PostgresOptions {
|
|
9
|
-
readonly changes?: Partial<Omit<PostgresChangesOptions, "client" | "listenerClient" | "connectionSetting" | "resync">>;
|
|
10
|
-
readonly databaseUrl?: Redacted.Redacted<string>;
|
|
11
|
-
readonly listenDatabaseUrl?: Redacted.Redacted<string>;
|
|
12
|
-
}
|
|
13
|
-
export declare function databaseLayer<R, E>(options: AppDatabaseOptions<R, E>, changesService: typeof AppChanges): Layer.Layer<AppChanges | PgClient.PgClient | import("@effect/sql-pg/PgPool").PgPool | Reactivity.Reactivity | import("effect/sql/SqlClient").SqlClient, E | Config.ConfigError | Migrator.MigrationError | import("@shivaedev/sql/PostgresListenerError.ts").PostgresListenerError | import("effect/Schema").SchemaError | import("effect/sql/SqlError").SqlError, Exclude<Exclude<R, import("effect/Scope").Scope>, PgClient.PgClient | import("@effect/sql-pg/PgPool").PgPool | import("effect/sql/SqlClient").SqlClient>>;
|
|
14
|
-
//# sourceMappingURL=databaseLayer.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"databaseLayer.d.ts","sourceRoot":"","sources":["../src/databaseLayer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,QAAQ,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAoC,KAAK,EAAU,QAAQ,EAAY,MAAM,QAAQ,CAAC;AACrG,OAAO,KAAK,UAAU,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAMtC,OAAO,EAAE,KAAK,sBAAsB,EAAmB,MAAM,mCAAmC,CAAC;AACjG,OAAO,EAAE,KAAK,eAAe,EAAiB,MAAM,iCAAiC,CAAC;AAEtF,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAElE,MAAM,WAAW,cAAe,SAAQ,eAAe;IACtD,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,sBAAsB,EAAE,QAAQ,GAAG,gBAAgB,GAAG,mBAAmB,GAAG,QAAQ,CAAC,CAAC,CAAC;IACvH,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjD,QAAQ,CAAC,iBAAiB,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;CACvD;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,UAAU,ggBAqCvG"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"databaseLayer.js","sourceRoot":"","sources":["../src/databaseLayer.ts"],"names":[],"mappings":"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"}
|
package/dist/redactOtlpUrl.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"redactOtlpUrl.d.ts","sourceRoot":"","sources":["../src/redactOtlpUrl.ts"],"names":[],"mappings":"AA6BA,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,SAAK,GAAG,MAAM,CAkB7D"}
|
package/dist/redactOtlpUrl.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { REDACTED } from "@shivaedev/errors/sensitive.ts";
|
|
2
|
-
function query(value) {
|
|
3
|
-
return value
|
|
4
|
-
.split("&")
|
|
5
|
-
.map((part) => (part === "" ? part : `${part.split("=", 1)[0]}=${REDACTED}`))
|
|
6
|
-
.join("&");
|
|
7
|
-
}
|
|
8
|
-
function url(value) {
|
|
9
|
-
const fragment = value.indexOf("#");
|
|
10
|
-
const target = fragment === -1 ? value : value.slice(0, fragment);
|
|
11
|
-
const separator = target.indexOf("?");
|
|
12
|
-
return ((separator === -1 ? target : `${target.slice(0, separator + 1)}${query(target.slice(separator + 1))}`).replaceAll(/(?<authority>(?:\b[a-z][a-z0-9+.-]*:)?\/\/)[^/?#]*@/giu, `$<authority>${REDACTED}@`) + (fragment === -1 ? "" : `#${REDACTED}`));
|
|
13
|
-
}
|
|
14
|
-
function structured(value) {
|
|
15
|
-
try {
|
|
16
|
-
return JSON.stringify(JSON.parse(value, (key, item) => (typeof item === "string" ? redactOtlpUrl(item, key) : item)));
|
|
17
|
-
}
|
|
18
|
-
catch {
|
|
19
|
-
return undefined;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
export function redactOtlpUrl(value, key = "") {
|
|
23
|
-
if (/(?:^|[._-])(?:fragment|userinfo|username|password)$/iu.test(key)) {
|
|
24
|
-
return REDACTED;
|
|
25
|
-
}
|
|
26
|
-
if (/(?:^|[._-])query$/iu.test(key)) {
|
|
27
|
-
return url(`?${value}`).slice(1);
|
|
28
|
-
}
|
|
29
|
-
if (/(?:^|[._-])(?:url|uri|target)(?:$|[._-])/iu.test(key) || /(?:^|[._-])(?:referer|referrer|location|origin)$/iu.test(key)) {
|
|
30
|
-
return url(value);
|
|
31
|
-
}
|
|
32
|
-
if (/^\s*[[{]/u.test(value)) {
|
|
33
|
-
const redacted = structured(value);
|
|
34
|
-
if (redacted !== undefined) {
|
|
35
|
-
return redacted;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
// Quotes and whitespace can belong to secret values, so free text has no safe closing delimiter.
|
|
39
|
-
return value.replaceAll(/(?:[a-z][a-z0-9+.-]*:\/\/|[/?#]).*/gisu, url);
|
|
40
|
-
}
|
|
41
|
-
//# sourceMappingURL=redactOtlpUrl.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"redactOtlpUrl.js","sourceRoot":"","sources":["../src/redactOtlpUrl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAE1D,SAAS,KAAK,CAAC,KAAa;IAC3B,OAAO,KAAK;SACV,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,QAAQ,EAAE,CAAC,CAAC;SAC5E,IAAI,CAAC,GAAG,CAAC,CAAC;AACb,CAAC;AAED,SAAS,GAAG,CAAC,KAAa;IACzB,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACpC,MAAM,MAAM,GAAG,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IAClE,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACtC,OAAO,CACN,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,UAAU,CAChH,wDAAwD,EACxD,eAAe,QAAQ,GAAG,CAC1B,GAAG,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,QAAQ,EAAE,CAAC,CAC3C,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAChC,IAAI,CAAC;QACJ,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,GAAW,EAAE,IAAa,EAAE,EAAE,CAAC,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACxI,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,SAAS,CAAC;IAClB,CAAC;AACF,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAa,EAAE,GAAG,GAAG,EAAE;IACpD,IAAI,uDAAuD,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACvE,OAAO,QAAQ,CAAC;IACjB,CAAC;IACD,IAAI,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,OAAO,GAAG,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC;IACD,IAAI,4CAA4C,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,oDAAoD,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9H,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC;IACnB,CAAC;IACD,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC5B,OAAO,QAAQ,CAAC;QACjB,CAAC;IACF,CAAC;IACD,iGAAiG;IACjG,OAAO,KAAK,CAAC,UAAU,CAAC,wCAAwC,EAAE,GAAG,CAAC,CAAC;AACxE,CAAC"}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { type SchemaAST } from "effect";
|
|
2
|
-
export declare function marked(ast: SchemaAST.AST, seen?: Set<SchemaAST.AST>): boolean;
|
|
3
|
-
export declare function collectEncodedKeys(ast: SchemaAST.AST, keys: Set<string>): void;
|
|
4
|
-
export declare function maskedValue(ast: SchemaAST.AST, keys: ReadonlySet<string>, seen?: Set<SchemaAST.AST>): boolean;
|
|
5
|
-
export declare function collect(ast: SchemaAST.AST, nodes: Set<SchemaAST.AST>, encodings?: boolean): void;
|
|
6
|
-
export declare function collectFieldKeys(properties: SchemaAST.Objects["propertySignatures"], keys: Set<string>, sensitive: (ast: SchemaAST.AST) => boolean): void;
|
|
7
|
-
//# sourceMappingURL=schemaSensitiveAst.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"schemaSensitiveAst.d.ts","sourceRoot":"","sources":["../src/schemaSensitiveAst.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,QAAQ,CAAC;AAsBnD,wBAAgB,MAAM,CAAC,GAAG,EAAE,SAAS,CAAC,GAAG,EAAE,IAAI,qBAA2B,GAAG,OAAO,CAenF;AAOD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,QAcvE;AAOD,wBAAgB,WAAW,CAAC,GAAG,EAAE,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,IAAI,qBAA2B,GAAG,OAAO,CAmBnH;AACD,wBAAgB,OAAO,CAAC,GAAG,EAAE,SAAS,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,SAAS,UAAO,QAQtF;AAMD,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,SAAS,CAAC,OAAO,CAAC,oBAAoB,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,GAAG,EAAE,SAAS,CAAC,GAAG,KAAK,OAAO,QAMlJ"}
|