@shivaedev/platform 0.5.2 → 0.6.1
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 +37 -0
- package/README.md +359 -106
- package/dist/errors/rejectedField.d.ts +7 -0
- package/dist/errors/rejectedField.d.ts.map +1 -0
- package/dist/errors/{rejected-field.js → rejectedField.js} +4 -2
- package/dist/errors/rejectedField.js.map +1 -0
- package/dist/errors/taxonomy.d.ts +10 -0
- package/dist/errors/taxonomy.d.ts.map +1 -1
- package/dist/errors/taxonomy.js +4 -0
- package/dist/errors/taxonomy.js.map +1 -1
- package/dist/rpc/identity.d.ts.map +1 -1
- package/dist/rpc/identity.js.map +1 -1
- package/dist/rpc/middleware.d.ts.map +1 -1
- package/dist/rpc/middleware.js.map +1 -1
- package/dist/server/appHost.d.ts +56 -0
- package/dist/server/appHost.d.ts.map +1 -0
- package/dist/server/appHost.js +55 -0
- package/dist/server/appHost.js.map +1 -0
- package/dist/server/better-auth/adapter.d.ts +10 -0
- package/dist/server/better-auth/adapter.d.ts.map +1 -0
- package/dist/{better-auth → server/better-auth}/adapter.js +26 -14
- package/dist/server/better-auth/adapter.js.map +1 -0
- package/dist/{better-auth → server/better-auth}/relation.d.ts +15 -15
- package/dist/server/better-auth/relation.d.ts.map +1 -0
- package/dist/{better-auth → server/better-auth}/relation.js +3 -3
- package/dist/server/better-auth/relation.js.map +1 -0
- package/dist/server/better-auth/rowAdapter.d.ts +8 -0
- package/dist/server/better-auth/rowAdapter.d.ts.map +1 -0
- package/dist/server/better-auth/rowAdapter.js +52 -0
- package/dist/server/better-auth/rowAdapter.js.map +1 -0
- package/dist/{better-auth → server/better-auth}/where.d.ts +2 -2
- package/dist/server/better-auth/where.d.ts.map +1 -0
- package/dist/{better-auth → server/better-auth}/where.js +23 -19
- package/dist/server/better-auth/where.js.map +1 -0
- package/dist/{node-http/subscription-signal.d.ts → server/node-http/subscriptionSignal.d.ts} +2 -2
- package/dist/server/node-http/subscriptionSignal.d.ts.map +1 -0
- package/dist/{node-http/subscription-signal.js → server/node-http/subscriptionSignal.js} +20 -13
- package/dist/server/node-http/subscriptionSignal.js.map +1 -0
- package/dist/server/rpc-server/adapters/betterAuthSessions.d.ts +4 -0
- package/dist/server/rpc-server/adapters/betterAuthSessions.d.ts.map +1 -0
- package/dist/server/rpc-server/adapters/betterAuthSessions.js +14 -0
- package/dist/server/rpc-server/adapters/betterAuthSessions.js.map +1 -0
- package/dist/server/rpc-server/boundary.d.ts +9 -0
- package/dist/server/rpc-server/boundary.d.ts.map +1 -0
- package/dist/server/rpc-server/boundary.js +18 -0
- package/dist/server/rpc-server/boundary.js.map +1 -0
- package/dist/server/rpc-server/local.d.ts +7 -0
- package/dist/server/rpc-server/local.d.ts.map +1 -0
- package/dist/server/rpc-server/local.js +66 -0
- package/dist/server/rpc-server/local.js.map +1 -0
- package/dist/{rpc-server → server/rpc-server}/origin.d.ts +2 -2
- package/dist/server/rpc-server/origin.d.ts.map +1 -0
- package/dist/server/rpc-server/origin.js +16 -0
- package/dist/server/rpc-server/origin.js.map +1 -0
- package/dist/server/rpc-server/redact.d.ts +3 -0
- package/dist/server/rpc-server/redact.d.ts.map +1 -0
- package/dist/{rpc-server → server/rpc-server}/redact.js +9 -7
- package/dist/server/rpc-server/redact.js.map +1 -0
- package/dist/server/rpc-server/redactCause.d.ts +6 -0
- package/dist/server/rpc-server/redactCause.d.ts.map +1 -0
- package/dist/server/rpc-server/redactCause.js +36 -0
- package/dist/server/rpc-server/redactCause.js.map +1 -0
- package/dist/server/rpc-server/sensitive.d.ts +5 -0
- package/dist/server/rpc-server/sensitive.d.ts.map +1 -0
- package/dist/{rpc-server → server/rpc-server}/sensitive.js +21 -17
- package/dist/server/rpc-server/sensitive.js.map +1 -0
- package/dist/server/rpc-server/session.d.ts +21 -0
- package/dist/server/rpc-server/session.d.ts.map +1 -0
- package/dist/server/rpc-server/session.js +37 -0
- package/dist/server/rpc-server/session.js.map +1 -0
- package/dist/{rpc-server → server/rpc-server}/tracing.d.ts +1 -1
- package/dist/server/rpc-server/tracing.d.ts.map +1 -0
- package/dist/server/rpc-server/tracing.js +42 -0
- package/dist/server/rpc-server/tracing.js.map +1 -0
- package/dist/server/rpc-server/transportHeaders.d.ts +4 -0
- package/dist/server/rpc-server/transportHeaders.d.ts.map +1 -0
- package/dist/server/rpc-server/transportHeaders.js +6 -0
- package/dist/server/rpc-server/transportHeaders.js.map +1 -0
- package/dist/server/runtime/makePlatform.d.ts +4 -0
- package/dist/server/runtime/makePlatform.d.ts.map +1 -0
- package/dist/{runtime/make.js → server/runtime/makePlatform.js} +10 -8
- package/dist/server/runtime/makePlatform.js.map +1 -0
- package/dist/server/runtime/types.d.ts +19 -0
- package/dist/server/runtime/types.d.ts.map +1 -0
- package/dist/{runtime → server/runtime}/types.js.map +1 -1
- package/dist/server/testing/makePlatformIt.d.ts +8 -0
- package/dist/server/testing/makePlatformIt.d.ts.map +1 -0
- package/dist/{testing/vitest.js → server/testing/makePlatformIt.js} +9 -5
- package/dist/server/testing/makePlatformIt.js.map +1 -0
- package/dist/server/testing/types.d.ts +28 -0
- package/dist/server/testing/types.d.ts.map +1 -0
- package/dist/{testing → server/testing}/types.js.map +1 -1
- package/docs/north-star.md +36 -0
- package/docs/roadmap.md +30 -0
- package/package.json +324 -54
- package/src/errors/{rejected-field.ts → rejectedField.ts} +3 -6
- package/src/errors/taxonomy.ts +2 -8
- package/src/rpc/identity.ts +0 -4
- package/src/rpc/middleware.ts +18 -9
- package/src/server/appHost.ts +134 -0
- package/src/{better-auth → server/better-auth}/adapter.ts +27 -31
- package/src/{better-auth → server/better-auth}/relation.ts +18 -20
- package/src/{better-auth/row-adapter.ts → server/better-auth/rowAdapter.ts} +19 -21
- package/src/{better-auth → server/better-auth}/where.ts +23 -33
- package/src/{node-http/subscription-signal.ts → server/node-http/subscriptionSignal.ts} +19 -22
- package/src/{rpc-server/adapters/better-auth-sessions.ts → server/rpc-server/adapters/betterAuthSessions.ts} +9 -10
- package/src/server/rpc-server/boundary.ts +26 -0
- package/src/server/rpc-server/local.ts +70 -0
- package/src/{rpc-server → server/rpc-server}/origin.ts +10 -13
- package/src/{rpc-server → server/rpc-server}/redact.ts +9 -11
- package/src/server/rpc-server/redactCause.ts +40 -0
- package/src/{rpc-server → server/rpc-server}/sensitive.ts +17 -22
- package/src/{rpc-server → server/rpc-server}/session.ts +22 -23
- package/src/{rpc-server → server/rpc-server}/tracing.ts +15 -18
- package/src/{rpc-server/transport.ts → server/rpc-server/transportHeaders.ts} +3 -3
- package/src/{runtime/make.ts → server/runtime/makePlatform.ts} +18 -26
- package/src/server/runtime/types.ts +24 -0
- package/src/server/testing/makePlatformIt.ts +76 -0
- package/src/server/testing/types.ts +38 -0
- package/dist/better-auth/adapter.d.ts +0 -10
- package/dist/better-auth/adapter.d.ts.map +0 -1
- package/dist/better-auth/adapter.js.map +0 -1
- package/dist/better-auth/relation.d.ts.map +0 -1
- package/dist/better-auth/relation.js.map +0 -1
- package/dist/better-auth/row-adapter.d.ts +0 -8
- package/dist/better-auth/row-adapter.d.ts.map +0 -1
- package/dist/better-auth/row-adapter.js +0 -46
- package/dist/better-auth/row-adapter.js.map +0 -1
- package/dist/better-auth/where.d.ts.map +0 -1
- package/dist/better-auth/where.js.map +0 -1
- package/dist/better-auth.d.ts +0 -2
- package/dist/better-auth.d.ts.map +0 -1
- package/dist/better-auth.js +0 -2
- package/dist/better-auth.js.map +0 -1
- package/dist/errors/rejected-field.d.ts +0 -7
- package/dist/errors/rejected-field.d.ts.map +0 -1
- package/dist/errors/rejected-field.js.map +0 -1
- package/dist/errors.d.ts +0 -3
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -3
- package/dist/errors.js.map +0 -1
- package/dist/node-http/subscription-signal.d.ts.map +0 -1
- package/dist/node-http/subscription-signal.js.map +0 -1
- package/dist/node-http.d.ts +0 -2
- package/dist/node-http.d.ts.map +0 -1
- package/dist/node-http.js +0 -2
- package/dist/node-http.js.map +0 -1
- package/dist/rpc-server/adapters/better-auth-sessions.d.ts +0 -4
- package/dist/rpc-server/adapters/better-auth-sessions.d.ts.map +0 -1
- package/dist/rpc-server/adapters/better-auth-sessions.js +0 -11
- package/dist/rpc-server/adapters/better-auth-sessions.js.map +0 -1
- package/dist/rpc-server/origin.d.ts.map +0 -1
- package/dist/rpc-server/origin.js +0 -12
- package/dist/rpc-server/origin.js.map +0 -1
- package/dist/rpc-server/redact-cause.d.ts +0 -6
- package/dist/rpc-server/redact-cause.d.ts.map +0 -1
- package/dist/rpc-server/redact-cause.js +0 -28
- package/dist/rpc-server/redact-cause.js.map +0 -1
- package/dist/rpc-server/redact.d.ts +0 -3
- package/dist/rpc-server/redact.d.ts.map +0 -1
- package/dist/rpc-server/redact.js.map +0 -1
- package/dist/rpc-server/sensitive.d.ts +0 -5
- package/dist/rpc-server/sensitive.d.ts.map +0 -1
- package/dist/rpc-server/sensitive.js.map +0 -1
- package/dist/rpc-server/session.d.ts +0 -21
- package/dist/rpc-server/session.d.ts.map +0 -1
- package/dist/rpc-server/session.js +0 -31
- package/dist/rpc-server/session.js.map +0 -1
- package/dist/rpc-server/tracing.d.ts.map +0 -1
- package/dist/rpc-server/tracing.js +0 -34
- package/dist/rpc-server/tracing.js.map +0 -1
- package/dist/rpc-server/transport.d.ts +0 -4
- package/dist/rpc-server/transport.d.ts.map +0 -1
- package/dist/rpc-server/transport.js +0 -4
- package/dist/rpc-server/transport.js.map +0 -1
- package/dist/rpc-server.d.ts +0 -9
- package/dist/rpc-server.d.ts.map +0 -1
- package/dist/rpc-server.js +0 -9
- package/dist/rpc-server.js.map +0 -1
- package/dist/rpc.d.ts +0 -3
- package/dist/rpc.d.ts.map +0 -1
- package/dist/rpc.js +0 -3
- package/dist/rpc.js.map +0 -1
- package/dist/runtime/make.d.ts +0 -4
- package/dist/runtime/make.d.ts.map +0 -1
- package/dist/runtime/make.js.map +0 -1
- package/dist/runtime/types.d.ts +0 -19
- package/dist/runtime/types.d.ts.map +0 -1
- package/dist/runtime.d.ts +0 -3
- package/dist/runtime.d.ts.map +0 -1
- package/dist/runtime.js +0 -2
- package/dist/runtime.js.map +0 -1
- package/dist/testing/types.d.ts +0 -26
- package/dist/testing/types.d.ts.map +0 -1
- package/dist/testing/vitest.d.ts +0 -8
- package/dist/testing/vitest.d.ts.map +0 -1
- package/dist/testing/vitest.js.map +0 -1
- package/dist/testing.d.ts +0 -3
- package/dist/testing.d.ts.map +0 -1
- package/dist/testing.js +0 -2
- package/dist/testing.js.map +0 -1
- package/src/better-auth.ts +0 -4
- package/src/errors.ts +0 -2
- package/src/node-http.ts +0 -5
- package/src/rpc-server/redact-cause.ts +0 -41
- package/src/rpc-server.ts +0 -15
- package/src/rpc.ts +0 -2
- package/src/runtime/types.ts +0 -24
- package/src/runtime.ts +0 -7
- package/src/testing/types.ts +0 -41
- package/src/testing/vitest.ts +0 -69
- package/src/testing.ts +0 -8
- /package/dist/{runtime → server/runtime}/types.js +0 -0
- /package/dist/{testing → server/testing}/types.js +0 -0
|
@@ -1,41 +1,40 @@
|
|
|
1
1
|
import type { BetterAuthOptions, DBAdapterInstance } from "better-auth";
|
|
2
2
|
import { createAdapterFactory } from "better-auth/adapters";
|
|
3
3
|
import { Effect } from "effect";
|
|
4
|
-
import type { AnyDatabase } from "@shivaedev/effect-prisma";
|
|
4
|
+
import type { AnyDatabase } from "@shivaedev/effect-prisma/databaseTypes.ts";
|
|
5
5
|
import type { PlatformRuntime } from "#runtime/types.ts";
|
|
6
6
|
import { type DynamicRelation, refineRelation } from "./relation.ts";
|
|
7
|
-
import { makeRowAdapter, type RelationQuery } from "./
|
|
8
|
-
|
|
7
|
+
import { makeRowAdapter, type RelationQuery } from "./rowAdapter.ts";
|
|
9
8
|
export interface EffectPrismaAdapterOptions {
|
|
10
9
|
readonly debugLogs?: boolean;
|
|
11
10
|
readonly modelName?: (model: string) => string;
|
|
12
11
|
readonly usePlural?: boolean;
|
|
13
12
|
}
|
|
14
|
-
|
|
15
13
|
interface DynamicDatabase {
|
|
16
14
|
readonly transaction: <A, E, R>(program: Effect.Effect<A, E, R>) => Effect.Effect<A, unknown, unknown>;
|
|
17
15
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
function isFunction(value: unknown, key: string): boolean {
|
|
17
|
+
return ((typeof value === "object" && value !== null) || typeof value === "function") && typeof Reflect.get(value, key) === "function";
|
|
18
|
+
}
|
|
19
|
+
function isDynamicDatabase(value: unknown): value is DynamicDatabase {
|
|
20
|
+
return isFunction(value, "transaction");
|
|
21
|
+
}
|
|
22
|
+
function isDynamicRelation(value: unknown): value is DynamicRelation<unknown> {
|
|
23
|
+
return isFunction(value, "where") && isFunction(value, "select");
|
|
24
|
+
}
|
|
25
|
+
function modelRelation(database: DynamicDatabase, model: string): DynamicRelation<unknown> {
|
|
27
26
|
const relation: unknown = Reflect.get(database, model);
|
|
28
27
|
if (!isDynamicRelation(relation)) {
|
|
29
28
|
throw new TypeError(`Unknown database model: ${model}`);
|
|
30
29
|
}
|
|
31
30
|
return relation;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
export function effectPrismaAdapter<
|
|
37
|
-
databaseTag:
|
|
38
|
-
runtime: PlatformRuntime<
|
|
31
|
+
}
|
|
32
|
+
function defaultModelName(model: string): string {
|
|
33
|
+
return model.length === 0 ? model : `${model[0]?.toUpperCase()}${model.slice(1)}`;
|
|
34
|
+
}
|
|
35
|
+
export function effectPrismaAdapter<TDatabase extends AnyDatabase, TServices, TBuildError>(
|
|
36
|
+
databaseTag: TDatabase,
|
|
37
|
+
runtime: PlatformRuntime<TServices, TBuildError>,
|
|
39
38
|
adapterOptions?: EffectPrismaAdapterOptions,
|
|
40
39
|
): DBAdapterInstance;
|
|
41
40
|
export function effectPrismaAdapter(
|
|
@@ -48,21 +47,19 @@ export function effectPrismaAdapter(
|
|
|
48
47
|
const databaseEffect = Effect.flatMap(databaseTag, (database) =>
|
|
49
48
|
isDynamicDatabase(database) ? Effect.succeed(database) : Effect.die(new TypeError("The database service has no transaction")),
|
|
50
49
|
);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
function run<TValue>(operation: (database: DynamicDatabase) => Effect.Effect<TValue, unknown, unknown>): Promise<TValue> {
|
|
51
|
+
return runtime.runPromise(Effect.flatMap(databaseEffect, operation));
|
|
52
|
+
}
|
|
55
53
|
const query: RelationQuery = (model, refinement, use) =>
|
|
56
54
|
run((database) => use(refineRelation(modelRelation(database, mapModelName(model)), refinement)));
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
Effect.flatMap(Effect.context<unknown>(), (services) =>
|
|
55
|
+
function inTransaction<TValue>(callback: () => Promise<TValue>): Effect.Effect<TValue, unknown, unknown> {
|
|
56
|
+
return Effect.flatMap(Effect.context<unknown>(), (services) =>
|
|
60
57
|
Effect.tryPromise({
|
|
61
58
|
catch: (error) => error,
|
|
62
59
|
try: () => runtime.runWithServices(services, callback),
|
|
63
60
|
}),
|
|
64
61
|
);
|
|
65
|
-
|
|
62
|
+
}
|
|
66
63
|
let factory: ReturnType<typeof createAdapterFactory>;
|
|
67
64
|
factory = createAdapterFactory({
|
|
68
65
|
adapter: makeRowAdapter(query, adapterOptions.usePlural ?? false),
|
|
@@ -73,13 +70,12 @@ export function effectPrismaAdapter(
|
|
|
73
70
|
supportsArrays: true,
|
|
74
71
|
supportsBooleans: true,
|
|
75
72
|
supportsDates: true,
|
|
76
|
-
supportsJSON: true,
|
|
73
|
+
"supportsJSON": true,
|
|
77
74
|
supportsNumericIds: true,
|
|
78
|
-
supportsUUIDs: true,
|
|
75
|
+
"supportsUUIDs": true,
|
|
79
76
|
transaction: (callback) => run((database) => database.transaction(inTransaction(() => callback(factory(authOptions))))),
|
|
80
77
|
},
|
|
81
78
|
});
|
|
82
|
-
|
|
83
79
|
return factory(authOptions);
|
|
84
80
|
};
|
|
85
81
|
}
|
|
@@ -1,24 +1,22 @@
|
|
|
1
|
+
import { all } from "@prisma-next/sql-orm-client";
|
|
1
2
|
import type { Effect, Option } from "effect";
|
|
2
|
-
import { all } from "@shivaedev/effect-prisma";
|
|
3
3
|
import { type CleanedWhere, type DynamicField, type Expression, whereExpression } from "./where.ts";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
where: (predicate: (fields: Record<string, DynamicField>) => Expression) => DynamicRelation<Requirements>;
|
|
4
|
+
export interface DynamicRelation<TRequirements> extends Effect.Effect<readonly Record<string, unknown>[], unknown, TRequirements> {
|
|
5
|
+
count: () => Effect.Effect<number, unknown, TRequirements>;
|
|
6
|
+
create: (data: Record<string, unknown>) => Effect.Effect<Record<string, unknown>, unknown, TRequirements>;
|
|
7
|
+
delete: () => Effect.Effect<Record<string, unknown> | null, unknown, TRequirements>;
|
|
8
|
+
deleteAll: () => Effect.Effect<readonly Record<string, unknown>[], unknown, TRequirements>;
|
|
9
|
+
first: () => Effect.Effect<Option.Option<Record<string, unknown>>, unknown, TRequirements>;
|
|
10
|
+
orderBy: (ordering: (fields: Record<string, DynamicField>) => unknown) => DynamicRelation<TRequirements>;
|
|
11
|
+
select: (...fields: readonly string[]) => DynamicRelation<TRequirements>;
|
|
12
|
+
skip: (count: number) => DynamicRelation<TRequirements>;
|
|
13
|
+
take: (count: number) => DynamicRelation<TRequirements>;
|
|
14
|
+
update: (data: Record<string, unknown>) => Effect.Effect<Record<string, unknown> | null, unknown, TRequirements>;
|
|
15
|
+
updateAll: (data: Record<string, unknown>) => Effect.Effect<readonly Record<string, unknown>[], unknown, TRequirements>;
|
|
16
|
+
where: (predicate: (fields: Record<string, DynamicField>) => Expression) => DynamicRelation<TRequirements>;
|
|
18
17
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
relation: DynamicRelation<Requirements>,
|
|
18
|
+
export function refineRelation<TRequirements>(
|
|
19
|
+
relation: DynamicRelation<TRequirements>,
|
|
22
20
|
options: {
|
|
23
21
|
readonly where?: readonly CleanedWhere[] | undefined;
|
|
24
22
|
readonly select?: readonly string[] | undefined;
|
|
@@ -31,7 +29,7 @@ export const refineRelation = <Requirements>(
|
|
|
31
29
|
}
|
|
32
30
|
| undefined;
|
|
33
31
|
},
|
|
34
|
-
): DynamicRelation<
|
|
32
|
+
): DynamicRelation<TRequirements> {
|
|
35
33
|
let refined = relation;
|
|
36
34
|
if (options.where !== undefined) {
|
|
37
35
|
refined = refined.where((fields) => {
|
|
@@ -58,4 +56,4 @@ export const refineRelation = <Requirements>(
|
|
|
58
56
|
refined = refined.select(...options.select);
|
|
59
57
|
}
|
|
60
58
|
return refined;
|
|
61
|
-
}
|
|
59
|
+
}
|
|
@@ -9,43 +9,40 @@ type FindManyInput = Parameters<CustomAdapter["findMany"]>[0];
|
|
|
9
9
|
type Row = Record<string, unknown>;
|
|
10
10
|
type RowMethod = "create" | "findMany" | "findOne" | "update";
|
|
11
11
|
type Refinement = Parameters<typeof refineRelation>[1];
|
|
12
|
-
|
|
13
12
|
interface RowMethods {
|
|
14
13
|
readonly create: (input: { readonly data: Row; readonly model: string; readonly select?: string[] | undefined }) => Promise<Row>;
|
|
15
14
|
readonly findMany: (input: FindManyInput) => Promise<readonly Row[]>;
|
|
16
15
|
readonly findOne: (input: FindOneInput) => Promise<Row | null>;
|
|
17
16
|
readonly update: (input: { readonly model: string; readonly update: Row; readonly where: CleanedWhere[] }) => Promise<Row | null>;
|
|
18
17
|
}
|
|
19
|
-
|
|
20
|
-
export type RelationQuery = <Value>(
|
|
18
|
+
export type RelationQuery = <TValue>(
|
|
21
19
|
model: string,
|
|
22
20
|
refinement: Refinement,
|
|
23
|
-
use: (relation: DynamicRelation<unknown>) => Effect.Effect<
|
|
24
|
-
) => Promise<
|
|
25
|
-
|
|
21
|
+
use: (relation: DynamicRelation<unknown>) => Effect.Effect<TValue, unknown, unknown>,
|
|
22
|
+
) => Promise<TValue>;
|
|
26
23
|
// Better Auth lets each call declare its row type; the rows are whatever its generated schema stored.
|
|
27
24
|
function rowTyped(adapter: Omit<CustomAdapter, RowMethod> & RowMethods): CustomAdapter;
|
|
28
25
|
function rowTyped(adapter: unknown): unknown {
|
|
29
26
|
return adapter;
|
|
30
27
|
}
|
|
31
|
-
|
|
32
|
-
const rejectJoin = (join: unknown): void => {
|
|
28
|
+
function rejectJoin(join: unknown): void {
|
|
33
29
|
if (join !== undefined) {
|
|
34
30
|
throw new TypeError("The Effect Prisma Better Auth adapter does not support experimental native joins");
|
|
35
31
|
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
export
|
|
41
|
-
(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
32
|
+
}
|
|
33
|
+
function namesId(condition: CleanedWhere): boolean {
|
|
34
|
+
return condition.connector !== "OR" && condition.field === "id" && condition.operator === "eq";
|
|
35
|
+
}
|
|
36
|
+
export function makeRowAdapter(query: RelationQuery, usePlural: boolean): AdapterFactoryCustomizeAdapterCreator {
|
|
37
|
+
return ({ debugLog, getFieldAttributes }) => {
|
|
38
|
+
function isUnique(model: string, condition: CleanedWhere): boolean {
|
|
39
|
+
return (
|
|
40
|
+
condition.connector !== "OR"
|
|
41
|
+
&& condition.operator === "eq"
|
|
42
|
+
&& condition.mode !== "insensitive"
|
|
43
|
+
&& (condition.field === "id" || getFieldAttributes({ field: condition.field, model }).unique === true)
|
|
44
|
+
);
|
|
45
|
+
}
|
|
49
46
|
return rowTyped({
|
|
50
47
|
count: ({ model, where }) => query(model, { where }, (relation) => relation.count()),
|
|
51
48
|
create: ({ data, model, select }) => {
|
|
@@ -78,3 +75,4 @@ export const makeRowAdapter =
|
|
|
78
75
|
updateMany: ({ model, update, where }) => query(model, { where }, (relation) => Effect.map(relation.updateAll(update), (rows) => rows.length)),
|
|
79
76
|
});
|
|
80
77
|
};
|
|
78
|
+
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
+
import { all, and, not, or } from "@prisma-next/sql-orm-client";
|
|
1
2
|
import type { Where } from "better-auth";
|
|
2
|
-
import { all, and, not, or } from "@shivaedev/effect-prisma";
|
|
3
|
-
|
|
4
3
|
export type CleanedWhere = Required<Where>;
|
|
5
4
|
export type Expression = Parameters<typeof and>[number];
|
|
6
|
-
|
|
7
5
|
export interface DynamicField {
|
|
8
6
|
asc: () => unknown;
|
|
9
7
|
desc: () => unknown;
|
|
@@ -20,53 +18,49 @@ export interface DynamicField {
|
|
|
20
18
|
neq: (value: unknown) => Expression;
|
|
21
19
|
notIn: (value: readonly unknown[]) => Expression;
|
|
22
20
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
function escapeLike(value: string): string {
|
|
22
|
+
return value.replaceAll("\\", "\\\\").replaceAll("%", "\\%").replaceAll("_", "\\_");
|
|
23
|
+
}
|
|
24
|
+
function nonNullValues(value: unknown): readonly unknown[] {
|
|
25
|
+
return (Array.isArray(value) ? value : [value]).filter((item) => item !== null);
|
|
26
|
+
}
|
|
27
|
+
function insensitiveStrings(field: DynamicField, where: CleanedWhere, values: readonly unknown[]) {
|
|
29
28
|
const ilike = field.ilike;
|
|
30
29
|
if (where.mode !== "insensitive" || ilike === undefined || !values.every((item) => typeof item === "string")) {
|
|
31
30
|
return undefined;
|
|
32
31
|
}
|
|
33
32
|
return values.map((item) => ilike(escapeLike(String(item))));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const membership = (field: DynamicField, where: CleanedWhere): Expression => {
|
|
33
|
+
}
|
|
34
|
+
function membership(field: DynamicField, where: CleanedWhere): Expression {
|
|
37
35
|
const values = nonNullValues(where.value);
|
|
38
36
|
if (values.length === 0) {
|
|
39
37
|
return and(field.isNull(), field.isNotNull());
|
|
40
38
|
}
|
|
41
39
|
const matches = insensitiveStrings(field, where, values);
|
|
42
40
|
return matches === undefined ? field.in(values) : or(...matches);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const exclusion = (field: DynamicField, where: CleanedWhere): Expression | undefined => {
|
|
41
|
+
}
|
|
42
|
+
function exclusion(field: DynamicField, where: CleanedWhere): Expression | undefined {
|
|
46
43
|
const values = nonNullValues(where.value);
|
|
47
44
|
if (values.length === 0) {
|
|
48
45
|
return undefined;
|
|
49
46
|
}
|
|
50
47
|
const matches = insensitiveStrings(field, where, values);
|
|
51
48
|
return matches === undefined ? field.notIn(values) : and(...matches.map((match) => not(match)));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const likePattern = (operator: CleanedWhere["operator"], escaped: string): string => {
|
|
49
|
+
}
|
|
50
|
+
function likePattern(operator: CleanedWhere["operator"], escaped: string): string {
|
|
55
51
|
if (operator === "contains") {
|
|
56
52
|
return `%${escaped}%`;
|
|
57
53
|
}
|
|
58
54
|
return operator === "starts_with" ? `${escaped}%` : `%${escaped}`;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const pattern = (field: DynamicField, where: CleanedWhere): Expression => {
|
|
55
|
+
}
|
|
56
|
+
function pattern(field: DynamicField, where: CleanedWhere): Expression {
|
|
62
57
|
if (typeof where.value !== "string") {
|
|
63
58
|
throw new TypeError(`${where.operator} requires a string value`);
|
|
64
59
|
}
|
|
65
60
|
const text = likePattern(where.operator, escapeLike(where.value));
|
|
66
61
|
return where.mode === "insensitive" && field.ilike !== undefined ? field.ilike(text) : field.like(text);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const insensitiveEquality = (field: DynamicField, where: CleanedWhere): Expression | undefined => {
|
|
62
|
+
}
|
|
63
|
+
function insensitiveEquality(field: DynamicField, where: CleanedWhere): Expression | undefined {
|
|
70
64
|
const value = where.value;
|
|
71
65
|
const operator = where.operator ?? "eq";
|
|
72
66
|
if (where.mode !== "insensitive" || typeof value !== "string" || field.ilike === undefined) {
|
|
@@ -79,9 +73,8 @@ const insensitiveEquality = (field: DynamicField, where: CleanedWhere): Expressi
|
|
|
79
73
|
return not(field.ilike(escapeLike(value)));
|
|
80
74
|
}
|
|
81
75
|
return undefined;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const comparison = (field: DynamicField, where: CleanedWhere): Expression | undefined => {
|
|
76
|
+
}
|
|
77
|
+
function comparison(field: DynamicField, where: CleanedWhere): Expression | undefined {
|
|
85
78
|
const value = where.value;
|
|
86
79
|
switch (where.operator ?? "eq") {
|
|
87
80
|
case "eq":
|
|
@@ -105,12 +98,10 @@ const comparison = (field: DynamicField, where: CleanedWhere): Expression | unde
|
|
|
105
98
|
case "ends_with":
|
|
106
99
|
return pattern(field, where);
|
|
107
100
|
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export const whereExpression = (fields: Record<string, DynamicField>, where: readonly CleanedWhere[]): Expression | undefined => {
|
|
101
|
+
}
|
|
102
|
+
export function whereExpression(fields: Record<string, DynamicField>, where: readonly CleanedWhere[]): Expression | undefined {
|
|
111
103
|
const conjunctions: Expression[] = [];
|
|
112
104
|
const disjunctions: Expression[] = [];
|
|
113
|
-
|
|
114
105
|
for (const condition of where) {
|
|
115
106
|
const field = fields[condition.field];
|
|
116
107
|
if (field === undefined) {
|
|
@@ -122,7 +113,6 @@ export const whereExpression = (fields: Record<string, DynamicField>, where: rea
|
|
|
122
113
|
}
|
|
123
114
|
(condition.connector === "OR" ? disjunctions : conjunctions).push(expression);
|
|
124
115
|
}
|
|
125
|
-
|
|
126
116
|
if (disjunctions.length > 0) {
|
|
127
117
|
conjunctions.push(or(...disjunctions));
|
|
128
118
|
}
|
|
@@ -130,4 +120,4 @@ export const whereExpression = (fields: Record<string, DynamicField>, where: rea
|
|
|
130
120
|
return all();
|
|
131
121
|
}
|
|
132
122
|
return conjunctions.length === 1 ? conjunctions[0] : and(...conjunctions);
|
|
133
|
-
}
|
|
123
|
+
}
|
|
@@ -1,28 +1,25 @@
|
|
|
1
1
|
import type { IncomingMessage } from "node:http";
|
|
2
|
-
|
|
3
2
|
export interface NodeSubscriptionSignalOptions {
|
|
4
3
|
readonly nodeRequest?: IncomingMessage | undefined;
|
|
5
4
|
readonly request?: Request | undefined;
|
|
6
5
|
readonly signals?: Iterable<AbortSignal | undefined>;
|
|
7
6
|
}
|
|
8
|
-
|
|
9
7
|
export interface NodeSubscriptionSignal {
|
|
10
8
|
readonly dispose: () => void;
|
|
11
9
|
readonly signal: AbortSignal;
|
|
12
10
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
typeof property(value, "socket") === "object" && typeof property(value, "once") === "function";
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
function property(value: unknown, key: string): unknown {
|
|
12
|
+
return typeof value === "object" && value !== null ? Reflect.get(value, key) : undefined;
|
|
13
|
+
}
|
|
14
|
+
function isIncomingMessage(value: unknown): value is IncomingMessage {
|
|
15
|
+
return typeof property(value, "socket") === "object" && typeof property(value, "once") === "function";
|
|
16
|
+
}
|
|
17
|
+
function nodeRequestFrom(request: Request | undefined): IncomingMessage | undefined {
|
|
20
18
|
const req = property(property(property(request, "runtime"), "node"), "req");
|
|
21
19
|
return isIncomingMessage(req) ? req : undefined;
|
|
22
|
-
}
|
|
23
|
-
|
|
20
|
+
}
|
|
24
21
|
// Under Bun's node:http compatibility layer a Web request signal may miss an abandoned socket, so node close events cover long-lived responses.
|
|
25
|
-
export
|
|
22
|
+
export function nodeSubscriptionSignal(options: NodeSubscriptionSignalOptions): NodeSubscriptionSignal {
|
|
26
23
|
const controller = new AbortController();
|
|
27
24
|
const signals: AbortSignal[] = [controller.signal];
|
|
28
25
|
if (options.request !== undefined) {
|
|
@@ -33,25 +30,25 @@ export const nodeSubscriptionSignal = (options: NodeSubscriptionSignalOptions):
|
|
|
33
30
|
signals.push(signal);
|
|
34
31
|
}
|
|
35
32
|
}
|
|
36
|
-
|
|
37
33
|
const nodeRequest = options.nodeRequest ?? nodeRequestFrom(options.request);
|
|
38
34
|
if (nodeRequest === undefined) {
|
|
39
|
-
return { dispose: () =>
|
|
35
|
+
return { dispose: (): void => undefined, signal: AbortSignal.any(signals) };
|
|
40
36
|
}
|
|
41
|
-
|
|
42
|
-
const socket =
|
|
43
|
-
|
|
44
|
-
if (!
|
|
37
|
+
const request = nodeRequest;
|
|
38
|
+
const socket = request.socket;
|
|
39
|
+
function onRequestClose() {
|
|
40
|
+
if (!request.complete) {
|
|
45
41
|
controller.abort();
|
|
46
42
|
}
|
|
47
|
-
}
|
|
48
|
-
|
|
43
|
+
}
|
|
44
|
+
function onSocketClose() {
|
|
45
|
+
return controller.abort();
|
|
46
|
+
}
|
|
49
47
|
nodeRequest.once("close", onRequestClose);
|
|
50
48
|
socket.once("close", onSocketClose);
|
|
51
49
|
if (nodeRequest.destroyed || socket.destroyed) {
|
|
52
50
|
controller.abort();
|
|
53
51
|
}
|
|
54
|
-
|
|
55
52
|
let disposed = false;
|
|
56
53
|
return {
|
|
57
54
|
dispose: () => {
|
|
@@ -64,4 +61,4 @@ export const nodeSubscriptionSignal = (options: NodeSubscriptionSignalOptions):
|
|
|
64
61
|
},
|
|
65
62
|
signal: AbortSignal.any(signals),
|
|
66
63
|
};
|
|
67
|
-
}
|
|
64
|
+
}
|
|
@@ -3,20 +3,19 @@ import type { Headers } from "effect/unstable/http";
|
|
|
3
3
|
import { AuthUnavailable } from "#errors/taxonomy.ts";
|
|
4
4
|
import { redact } from "#rpc-server/redact.ts";
|
|
5
5
|
import type { SessionProvider, SessionShape } from "#rpc-server/session.ts";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const unavailable = (cause: unknown) =>
|
|
10
|
-
Effect.andThen(
|
|
6
|
+
export type GetSession<TSession extends SessionShape> = (headers: globalThis.Headers) => Promise<TSession | null>;
|
|
7
|
+
function unavailable(cause: unknown) {
|
|
8
|
+
return Effect.andThen(
|
|
11
9
|
Effect.logError("Authentication provider failed", redact(cause)),
|
|
12
10
|
Effect.fail(new AuthUnavailable({ message: "Authentication is temporarily unavailable" })),
|
|
13
11
|
);
|
|
14
|
-
|
|
15
|
-
export
|
|
16
|
-
|
|
12
|
+
}
|
|
13
|
+
export function betterAuthSessions<TSession extends SessionShape>(getSession: GetSession<TSession>): SessionProvider<TSession> {
|
|
14
|
+
const betterAuthSession = Effect.fn("PlatformRpc.betterAuthSession")(function* (headers: Headers.Headers) {
|
|
17
15
|
const session = yield* Effect.tryPromise(() => getSession(new globalThis.Headers(headers))).pipe(
|
|
18
16
|
Effect.catch((error) => unavailable(error.cause)),
|
|
19
17
|
);
|
|
20
18
|
return Option.fromNullishOr(session);
|
|
21
|
-
})
|
|
22
|
-
}
|
|
19
|
+
});
|
|
20
|
+
return { get: betterAuthSession };
|
|
21
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Cause, Effect, Schema } from "effect";
|
|
2
|
+
import { isSensitiveKey, redactText } from "./sensitive.ts";
|
|
3
|
+
export interface RpcBoundaryOptions<E> {
|
|
4
|
+
readonly message?: string;
|
|
5
|
+
readonly unexpected: (message: string) => E;
|
|
6
|
+
}
|
|
7
|
+
export function rpcBoundary<
|
|
8
|
+
TSchema extends Schema.ConstraintDecoder<{
|
|
9
|
+
readonly message: string;
|
|
10
|
+
}>,
|
|
11
|
+
>(schema: TSchema, options: RpcBoundaryOptions<TSchema["Type"]>) {
|
|
12
|
+
const declared = Schema.is(schema);
|
|
13
|
+
return <A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, TSchema["Type"], R> =>
|
|
14
|
+
Effect.catchCause(self, (cause) => {
|
|
15
|
+
if (Cause.hasInterruptsOnly(cause)) {
|
|
16
|
+
return Effect.failCause(Cause.fromReasons(cause.reasons.filter(Cause.isInterruptReason)));
|
|
17
|
+
}
|
|
18
|
+
const reason = cause.reasons[0];
|
|
19
|
+
if (cause.reasons.length === 1 && reason && Cause.isFailReason(reason) && declared(reason.error)) {
|
|
20
|
+
return Effect.fail(reason.error);
|
|
21
|
+
}
|
|
22
|
+
const rejection = options.unexpected(redactText(options.message ?? "Internal server error", isSensitiveKey));
|
|
23
|
+
Object.defineProperty(rejection, "cause", { configurable: true, enumerable: false, value: cause });
|
|
24
|
+
return Effect.failCause(Cause.fromReasons([...Cause.fail(rejection).reasons, ...cause.reasons.filter(Cause.isInterruptReason)]));
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Effect, Option } from "effect";
|
|
2
|
+
import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
|
|
3
|
+
import { RpcSerialization } from "effect/unstable/rpc";
|
|
4
|
+
|
|
5
|
+
function loopbackAddress(address: string): boolean {
|
|
6
|
+
const unmapped = unmapAddress(address);
|
|
7
|
+
return unmapped === "::1" || (/^127(?:\.\d{1,3}){3}$/u.test(unmapped) && unmapped.split(".").every((part) => Number(part) <= 255));
|
|
8
|
+
}
|
|
9
|
+
function unmapAddress(address: string): string {
|
|
10
|
+
return address.replace(/^::ffff:/iu, "");
|
|
11
|
+
}
|
|
12
|
+
function loopbackHost(host: string | undefined): boolean {
|
|
13
|
+
if (host === undefined) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
const authority = /^(?<hostname>\[[^\]]+\]|[^:]+)(?::(?<port>\d{1,5}))?$/u.exec(host)?.groups;
|
|
17
|
+
if (authority === undefined || (authority.port !== undefined && Number(authority.port) > 65_535)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const hostname = authority.hostname?.toLowerCase();
|
|
21
|
+
return hostname !== undefined && (hostname === "localhost" || hostname === "[::1]" || loopbackAddress(hostname));
|
|
22
|
+
}
|
|
23
|
+
export function sameOrigin(request: HttpServerRequest.HttpServerRequest): boolean {
|
|
24
|
+
const origin = request.headers.origin;
|
|
25
|
+
if (origin === undefined) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const url = new URL(origin);
|
|
30
|
+
return url.protocol === "http:" && url.origin === origin && url.host === request.headers.host;
|
|
31
|
+
} catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function loopbackOnly<E, R>(
|
|
36
|
+
handler: (request: HttpServerRequest.HttpServerRequest) => Effect.Effect<HttpServerResponse.HttpServerResponse, E, R>,
|
|
37
|
+
) {
|
|
38
|
+
return (request: HttpServerRequest.HttpServerRequest) =>
|
|
39
|
+
loopbackHost(request.headers.host) && Option.match(request.remoteAddress, { onNone: () => true, onSome: loopbackAddress })
|
|
40
|
+
? handler(request)
|
|
41
|
+
: Effect.succeed(HttpServerResponse.text("Only loopback hosts are served", { status: 403 }));
|
|
42
|
+
}
|
|
43
|
+
function proxyOrigin(request: HttpServerRequest.HttpServerRequest, trustedProxy: string | undefined): boolean {
|
|
44
|
+
if (trustedProxy === undefined || !Option.exists(request.remoteAddress, (address) => unmapAddress(address) === unmapAddress(trustedProxy))) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
const host = request.headers["x-forwarded-host"];
|
|
48
|
+
const protocol = request.headers["x-forwarded-proto"];
|
|
49
|
+
if (host === undefined || !/^(?:\[[\da-f:]+\]|[\da-z.-]+)(?::\d{1,5})?$/iu.test(host) || (protocol !== "http" && protocol !== "https")) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const url = new URL(`${protocol}://${host}`);
|
|
54
|
+
return request.headers.origin === undefined || request.headers.origin === url.origin;
|
|
55
|
+
} catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export const localRpcGuard = Effect.fnUntraced(function* <E, R>(
|
|
60
|
+
effect: Effect.Effect<HttpServerResponse.HttpServerResponse, E, R>,
|
|
61
|
+
trustedProxy?: string,
|
|
62
|
+
) {
|
|
63
|
+
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
64
|
+
const serialization = yield* RpcSerialization.RpcSerialization;
|
|
65
|
+
const local = loopbackHost(request.headers.host) && Option.exists(request.remoteAddress, loopbackAddress);
|
|
66
|
+
const contentType = request.headers["content-type"]?.split(";")[0]?.trim().toLowerCase();
|
|
67
|
+
return ((local && sameOrigin(request)) || proxyOrigin(request, trustedProxy)) && contentType === serialization.contentType
|
|
68
|
+
? yield* effect
|
|
69
|
+
: HttpServerResponse.text("Local same-origin requests only", { status: 403 });
|
|
70
|
+
});
|
|
@@ -1,29 +1,26 @@
|
|
|
1
1
|
import { Option } from "effect";
|
|
2
2
|
import { Headers } from "effect/unstable/http";
|
|
3
|
-
|
|
4
3
|
export interface OriginRequest {
|
|
5
4
|
readonly headers: Headers.Headers;
|
|
6
5
|
readonly origin: Option.Option<string>;
|
|
7
6
|
readonly rpc: string;
|
|
8
7
|
}
|
|
9
|
-
|
|
10
8
|
export type OriginPolicy = (request: OriginRequest) => boolean;
|
|
11
|
-
|
|
12
9
|
export interface TrustedOriginsOptions {
|
|
13
10
|
readonly allow: readonly string[];
|
|
14
11
|
readonly missing: "allow" | "reject";
|
|
15
12
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
(options: TrustedOriginsOptions): OriginPolicy =>
|
|
19
|
-
({ origin }) =>
|
|
13
|
+
export function trustedOrigins(options: TrustedOriginsOptions): OriginPolicy {
|
|
14
|
+
return ({ origin }) =>
|
|
20
15
|
Option.match(origin, {
|
|
21
16
|
onNone: () => options.missing === "allow",
|
|
22
17
|
onSome: (value) => options.allow.includes(value),
|
|
23
18
|
});
|
|
24
|
-
|
|
25
|
-
export
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
}
|
|
20
|
+
export function originRequest(headers: Headers.Headers, rpc: string): OriginRequest {
|
|
21
|
+
return {
|
|
22
|
+
headers,
|
|
23
|
+
origin: Headers.get(headers, "origin"),
|
|
24
|
+
rpc,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -3,8 +3,7 @@ import { isSensitiveKey, REDACTED, redactText, type SensitiveKey } from "./sensi
|
|
|
3
3
|
|
|
4
4
|
const MAX_DEPTH = 8;
|
|
5
5
|
const MAX_ENTRIES = 50;
|
|
6
|
-
|
|
7
|
-
const binary = (value: object): string | undefined => {
|
|
6
|
+
function binary(value: object): string | undefined {
|
|
8
7
|
if (ArrayBuffer.isView(value)) {
|
|
9
8
|
return `<${value.constructor.name} ${value.byteLength} bytes>`;
|
|
10
9
|
}
|
|
@@ -12,9 +11,8 @@ const binary = (value: object): string | undefined => {
|
|
|
12
11
|
return `<ArrayBuffer ${value.byteLength} bytes>`;
|
|
13
12
|
}
|
|
14
13
|
return undefined;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const entriesOf = (value: object): [string, unknown][] => {
|
|
14
|
+
}
|
|
15
|
+
function entriesOf(value: object): [string, unknown][] {
|
|
18
16
|
const entries = Object.entries(value);
|
|
19
17
|
if (!Predicate.isError(value)) {
|
|
20
18
|
return entries;
|
|
@@ -25,9 +23,8 @@ const entriesOf = (value: object): [string, unknown][] => {
|
|
|
25
23
|
["stack", value.stack],
|
|
26
24
|
];
|
|
27
25
|
return value.cause === undefined ? [...shape, ...entries] : [...shape, ...entries, ["cause", value.cause]];
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const walk = (value: unknown, sensitive: SensitiveKey, depth: number): unknown => {
|
|
26
|
+
}
|
|
27
|
+
function walk(value: unknown, sensitive: SensitiveKey, depth: number): unknown {
|
|
31
28
|
if (Redacted.isRedacted(value)) {
|
|
32
29
|
return REDACTED;
|
|
33
30
|
}
|
|
@@ -53,6 +50,7 @@ const walk = (value: unknown, sensitive: SensitiveKey, depth: number): unknown =
|
|
|
53
50
|
.slice(0, MAX_ENTRIES)
|
|
54
51
|
.map(([key, nested]): [string, unknown] => [key, sensitive(key) ? REDACTED : walk(nested, sensitive, depth + 1)]);
|
|
55
52
|
return Object.fromEntries(entries.length > MAX_ENTRIES ? [...kept, ["<truncated>", `${entries.length - MAX_ENTRIES} more keys`]] : kept);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
}
|
|
54
|
+
export function redact(value: unknown, sensitive: SensitiveKey = isSensitiveKey): unknown {
|
|
55
|
+
return walk(value, sensitive, 0);
|
|
56
|
+
}
|