@shivaedev/platform 0.1.0 → 0.2.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 +18 -0
- package/README.md +40 -3
- package/dist/better-auth/adapter.d.ts +11 -0
- package/dist/better-auth/adapter.d.ts.map +1 -0
- package/dist/better-auth/adapter.js +101 -0
- package/dist/better-auth/adapter.js.map +1 -0
- package/dist/better-auth/relation.d.ts +47 -0
- package/dist/better-auth/relation.d.ts.map +1 -0
- package/dist/better-auth/relation.js +111 -0
- package/dist/better-auth/relation.js.map +1 -0
- package/dist/better-auth.d.ts +2 -0
- package/dist/better-auth.d.ts.map +1 -0
- package/dist/better-auth.js +2 -0
- package/dist/better-auth.js.map +1 -0
- package/dist/runtime/make.d.ts +4 -0
- package/dist/runtime/make.d.ts.map +1 -0
- package/dist/runtime/make.js +59 -0
- package/dist/runtime/make.js.map +1 -0
- package/dist/runtime/types.d.ts +20 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime/types.js +2 -0
- package/dist/runtime/types.js.map +1 -0
- package/dist/runtime.d.ts +3 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +2 -0
- package/dist/runtime.js.map +1 -0
- package/dist/testing/types.d.ts +3 -1
- package/dist/testing/types.d.ts.map +1 -1
- package/dist/testing/vitest.d.ts.map +1 -1
- package/dist/testing/vitest.js +3 -1
- package/dist/testing/vitest.js.map +1 -1
- package/package.json +26 -6
- package/src/better-auth/adapter.ts +226 -0
- package/src/better-auth/relation.ts +199 -0
- package/src/better-auth.ts +4 -0
- package/src/runtime/make.ts +84 -0
- package/src/runtime/types.ts +34 -0
- package/src/runtime.ts +7 -0
- package/src/testing/types.ts +6 -1
- package/src/testing/vitest.ts +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.2.1 - 2026-08-03
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Require Effect Prisma 0.4.1 so PostgreSQL timestamp fields use their runtime
|
|
10
|
+
`Date` types throughout the shared application setup.
|
|
11
|
+
|
|
12
|
+
## 0.2.0 - 2026-08-03
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Add a shared application runtime that carries Effect services across tRPC,
|
|
17
|
+
Better Auth, and other promise boundaries.
|
|
18
|
+
- Add a Better Auth adapter backed by Effect Prisma, including transaction
|
|
19
|
+
propagation and PostgreSQL filter support.
|
|
20
|
+
- Add a `promise` test-harness helper for driving promise-based boundaries
|
|
21
|
+
inside the test rollback transaction.
|
|
22
|
+
|
|
5
23
|
## 0.1.0 - 2026-08-03
|
|
6
24
|
|
|
7
25
|
### Added
|
package/README.md
CHANGED
|
@@ -4,10 +4,44 @@ Opinionated shared setup for ShivaeDev Effect applications. This package favors
|
|
|
4
4
|
one consistent application shape over supporting every possible combination of
|
|
5
5
|
database, transport, and test framework.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
Prisma and tRPC integrations remain available separately from
|
|
7
|
+
Generic Prisma and tRPC integrations remain available separately from
|
|
9
8
|
`@shivaedev/effect-prisma` and `@shivaedev/effect-trpc`.
|
|
10
9
|
|
|
10
|
+
## Runtime
|
|
11
|
+
|
|
12
|
+
Build one managed runtime for the application and share it with integrations:
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { makePlatformRuntime } from "@shivaedev/platform/runtime"
|
|
16
|
+
|
|
17
|
+
export const runtime = makePlatformRuntime(ApplicationLive, {
|
|
18
|
+
developmentCacheKey: "application",
|
|
19
|
+
})
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The optional cache key keeps one runtime across development module reloads.
|
|
23
|
+
The runtime also carries transaction- and request-scoped service overrides
|
|
24
|
+
across promise boundaries.
|
|
25
|
+
|
|
26
|
+
## Better Auth
|
|
27
|
+
|
|
28
|
+
`effectPrismaAdapter` stores Better Auth data through the application's Effect
|
|
29
|
+
Prisma database and runtime, including Better Auth transactions:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { effectPrismaAdapter } from "@shivaedev/platform/better-auth"
|
|
33
|
+
import { betterAuth } from "better-auth"
|
|
34
|
+
|
|
35
|
+
const auth = betterAuth({
|
|
36
|
+
database: effectPrismaAdapter(Database, runtime),
|
|
37
|
+
})
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The adapter owns database translation only. Providers, plugins, cookies,
|
|
41
|
+
session policy, and authorization remain application configuration. Native
|
|
42
|
+
experimental Better Auth joins are not supported; the standard adapter join
|
|
43
|
+
fallback remains available.
|
|
44
|
+
|
|
11
45
|
## Testing
|
|
12
46
|
|
|
13
47
|
Configure the application database, tRPC caller, and test Layer once:
|
|
@@ -33,7 +67,7 @@ database calls, factories, and fixtures share that transaction, which is rolled
|
|
|
33
67
|
back after both successful and failed tests:
|
|
34
68
|
|
|
35
69
|
```ts
|
|
36
|
-
it.effectApp("creates a movie", function* ({ trpc, db, factories }) {
|
|
70
|
+
it.effectApp("creates a movie", function* ({ trpc, db, factories, promise }) {
|
|
37
71
|
const input = factories.movie()
|
|
38
72
|
const movie = yield* trpc.movie.create(input)
|
|
39
73
|
const stored = yield* db.Movie.where({ id: movie.id }).first()
|
|
@@ -42,6 +76,9 @@ it.effectApp("creates a movie", function* ({ trpc, db, factories }) {
|
|
|
42
76
|
})
|
|
43
77
|
```
|
|
44
78
|
|
|
79
|
+
`promise(() => ...)` runs a promise-based application boundary, such as Better
|
|
80
|
+
Auth or an HTTP handler, with the same transaction-scoped Effect services.
|
|
81
|
+
|
|
45
82
|
Calling `trpc(options)` creates a caller for another application actor without
|
|
46
83
|
rebuilding the worker-scoped test Layer. `effectApp` also supports `skip`,
|
|
47
84
|
`skipIf`, `runIf`, `only`, `each`, and `fails`.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AnyDatabase } from "@shivaedev/effect-prisma";
|
|
2
|
+
import type { DBAdapterInstance } from "better-auth";
|
|
3
|
+
import type { PlatformRuntime } from "../runtime/types.js";
|
|
4
|
+
export interface EffectPrismaAdapterOptions {
|
|
5
|
+
readonly debugLogs?: boolean;
|
|
6
|
+
/** Map Better Auth model names to Prisma contract model names. */
|
|
7
|
+
readonly modelName?: (model: string) => string;
|
|
8
|
+
readonly usePlural?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare const effectPrismaAdapter: <Database extends AnyDatabase, Services, BuildError>(databaseTag: Database, runtime: PlatformRuntime<Services, BuildError>, adapterOptions?: EffectPrismaAdapterOptions) => DBAdapterInstance;
|
|
11
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/better-auth/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAqB,MAAM,0BAA0B,CAAC;AAC/E,OAAO,KAAK,EAAqB,iBAAiB,EAAS,MAAM,aAAa,CAAC;AAG/E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAY3D,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/C,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC7B;AA2BD,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,SAAS,WAAW,EAAE,QAAQ,EAAE,UAAU,EAClD,aAAa,QAAQ,EACrB,SAAS,eAAe,CAAC,QAAQ,EAAE,UAAU,CAAC,EAC9C,iBAAgB,0BAA+B,KAC7C,iBA4KF,CAAC"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { createAdapterFactory } from "better-auth/adapters";
|
|
2
|
+
import { Effect, Option } from "effect";
|
|
3
|
+
import { refineRelation } from "./relation.js";
|
|
4
|
+
const modelRelation = (database, model, mapModelName) => Reflect.get(database, mapModelName(model));
|
|
5
|
+
const defaultModelName = (model) => model.length === 0 ? model : `${model[0]?.toUpperCase()}${model.slice(1)}`;
|
|
6
|
+
const rejectJoin = (join) => {
|
|
7
|
+
if (join !== undefined) {
|
|
8
|
+
throw new TypeError("The Effect Prisma Better Auth adapter does not support experimental native joins");
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
export const effectPrismaAdapter = (databaseTag, runtime, adapterOptions = {}) => (authOptions) => {
|
|
12
|
+
const mapModelName = adapterOptions.modelName ?? defaultModelName;
|
|
13
|
+
const databaseEffect = databaseTag;
|
|
14
|
+
const run = (operation) => runtime.runPromise(Effect.flatMap(databaseEffect, (database) => operation(database)));
|
|
15
|
+
let factory;
|
|
16
|
+
factory = createAdapterFactory({
|
|
17
|
+
config: {
|
|
18
|
+
adapterId: "effect-prisma",
|
|
19
|
+
adapterName: "Effect Prisma",
|
|
20
|
+
debugLogs: adapterOptions.debugLogs ?? false,
|
|
21
|
+
supportsArrays: true,
|
|
22
|
+
supportsBooleans: true,
|
|
23
|
+
supportsDates: true,
|
|
24
|
+
supportsJSON: true,
|
|
25
|
+
supportsNumericIds: true,
|
|
26
|
+
supportsUUIDs: true,
|
|
27
|
+
transaction: (callback) => run((database) => database.transaction(Effect.flatMap(Effect.context(), (services) => Effect.tryPromise({
|
|
28
|
+
try: () => runtime.runWithServices(services, () => callback(factory(authOptions))),
|
|
29
|
+
catch: (error) => error,
|
|
30
|
+
})))),
|
|
31
|
+
},
|
|
32
|
+
adapter: ({ debugLog, getFieldAttributes }) => ({
|
|
33
|
+
create: async ({ data, model, select }) => {
|
|
34
|
+
debugLog("create", { model });
|
|
35
|
+
return (await run((database) => refineRelation(modelRelation(database, model, mapModelName), {
|
|
36
|
+
select,
|
|
37
|
+
}).create(data)));
|
|
38
|
+
},
|
|
39
|
+
findOne: async ({ join, model, select, where, }) => {
|
|
40
|
+
rejectJoin(join);
|
|
41
|
+
return (await run((database) => Effect.map(refineRelation(modelRelation(database, model, mapModelName), {
|
|
42
|
+
select,
|
|
43
|
+
where,
|
|
44
|
+
}).first(), Option.getOrNull)));
|
|
45
|
+
},
|
|
46
|
+
findMany: async ({ join, limit, model, offset, select, sortBy, where, }) => {
|
|
47
|
+
rejectJoin(join);
|
|
48
|
+
return (await run((database) => refineRelation(modelRelation(database, model, mapModelName), {
|
|
49
|
+
limit,
|
|
50
|
+
offset,
|
|
51
|
+
select,
|
|
52
|
+
sortBy,
|
|
53
|
+
where,
|
|
54
|
+
})));
|
|
55
|
+
},
|
|
56
|
+
update: async ({ model, update, where }) => {
|
|
57
|
+
if (where.length === 0)
|
|
58
|
+
return null;
|
|
59
|
+
const hasUniqueCondition = where.some((condition) => condition.connector !== "OR" &&
|
|
60
|
+
condition.operator === "eq" &&
|
|
61
|
+
condition.mode !== "insensitive" &&
|
|
62
|
+
(condition.field === "id" ||
|
|
63
|
+
getFieldAttributes({
|
|
64
|
+
field: condition.field,
|
|
65
|
+
model,
|
|
66
|
+
}).unique === true));
|
|
67
|
+
return (await run((database) => {
|
|
68
|
+
const relation = refineRelation(modelRelation(database, model, mapModelName), { where });
|
|
69
|
+
return hasUniqueCondition
|
|
70
|
+
? relation.update(update)
|
|
71
|
+
: Effect.map(relation.updateAll(update), (rows) => rows[0] ?? null);
|
|
72
|
+
}));
|
|
73
|
+
},
|
|
74
|
+
updateMany: ({ model, update, where }) => run((database) => Effect.map(refineRelation(modelRelation(database, model, mapModelName), {
|
|
75
|
+
where,
|
|
76
|
+
}).updateAll(update), (rows) => rows.length)),
|
|
77
|
+
delete: ({ model, where }) => {
|
|
78
|
+
const hasId = where.some((condition) => condition.connector !== "OR" &&
|
|
79
|
+
condition.field === "id" &&
|
|
80
|
+
condition.operator === "eq");
|
|
81
|
+
return run((database) => {
|
|
82
|
+
const relation = refineRelation(modelRelation(database, model, mapModelName), { where });
|
|
83
|
+
return hasId
|
|
84
|
+
? Effect.asVoid(relation.delete())
|
|
85
|
+
: Effect.asVoid(relation.deleteAll());
|
|
86
|
+
});
|
|
87
|
+
},
|
|
88
|
+
deleteMany: ({ model, where }) => run((database) => Effect.map(refineRelation(modelRelation(database, model, mapModelName), {
|
|
89
|
+
where,
|
|
90
|
+
}).deleteAll(), (rows) => rows.length)),
|
|
91
|
+
count: ({ model, where }) => run((database) => refineRelation(modelRelation(database, model, mapModelName), {
|
|
92
|
+
where: where,
|
|
93
|
+
}).count()),
|
|
94
|
+
options: {
|
|
95
|
+
usePlural: adapterOptions.usePlural ?? false,
|
|
96
|
+
},
|
|
97
|
+
}),
|
|
98
|
+
});
|
|
99
|
+
return factory(authOptions);
|
|
100
|
+
};
|
|
101
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../src/better-auth/adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { and } from "@shivaedev/effect-prisma";
|
|
2
|
+
import type { Where } from "better-auth";
|
|
3
|
+
import type { Effect, Option } from "effect";
|
|
4
|
+
type CleanedWhere = Required<Where>;
|
|
5
|
+
type Expression = Parameters<typeof and>[number];
|
|
6
|
+
interface DynamicField {
|
|
7
|
+
eq(value: unknown): Expression;
|
|
8
|
+
neq(value: unknown): Expression;
|
|
9
|
+
gt(value: unknown): Expression;
|
|
10
|
+
gte(value: unknown): Expression;
|
|
11
|
+
lt(value: unknown): Expression;
|
|
12
|
+
lte(value: unknown): Expression;
|
|
13
|
+
in(value: ReadonlyArray<unknown>): Expression;
|
|
14
|
+
notIn(value: ReadonlyArray<unknown>): Expression;
|
|
15
|
+
like(value: string): Expression;
|
|
16
|
+
ilike?(value: string): Expression;
|
|
17
|
+
isNull(): Expression;
|
|
18
|
+
isNotNull(): Expression;
|
|
19
|
+
asc(): unknown;
|
|
20
|
+
desc(): unknown;
|
|
21
|
+
}
|
|
22
|
+
export interface DynamicRelation<Requirements> {
|
|
23
|
+
where(predicate: (fields: Record<string, DynamicField>) => Expression): DynamicRelation<Requirements>;
|
|
24
|
+
orderBy(ordering: (fields: Record<string, DynamicField>) => unknown): DynamicRelation<Requirements>;
|
|
25
|
+
take(count: number): DynamicRelation<Requirements>;
|
|
26
|
+
skip(count: number): DynamicRelation<Requirements>;
|
|
27
|
+
select(...fields: ReadonlyArray<string>): DynamicRelation<Requirements>;
|
|
28
|
+
first(): Effect.Effect<Option.Option<Record<string, unknown>>, unknown, Requirements>;
|
|
29
|
+
count(): Effect.Effect<number, unknown, Requirements>;
|
|
30
|
+
create(data: Record<string, unknown>): Effect.Effect<Record<string, unknown>, unknown, Requirements>;
|
|
31
|
+
update(data: Record<string, unknown>): Effect.Effect<Record<string, unknown> | null, unknown, Requirements>;
|
|
32
|
+
updateAll(data: Record<string, unknown>): Effect.Effect<ReadonlyArray<Record<string, unknown>>, unknown, Requirements>;
|
|
33
|
+
delete(): Effect.Effect<Record<string, unknown> | null, unknown, Requirements>;
|
|
34
|
+
deleteAll(): Effect.Effect<ReadonlyArray<Record<string, unknown>>, unknown, Requirements>;
|
|
35
|
+
}
|
|
36
|
+
export declare const refineRelation: <Requirements>(relation: DynamicRelation<Requirements>, options: {
|
|
37
|
+
readonly where?: ReadonlyArray<CleanedWhere>;
|
|
38
|
+
readonly select?: ReadonlyArray<string>;
|
|
39
|
+
readonly limit?: number;
|
|
40
|
+
readonly offset?: number;
|
|
41
|
+
readonly sortBy?: {
|
|
42
|
+
readonly direction: "asc" | "desc";
|
|
43
|
+
readonly field: string;
|
|
44
|
+
};
|
|
45
|
+
}) => DynamicRelation<Requirements>;
|
|
46
|
+
export {};
|
|
47
|
+
//# sourceMappingURL=relation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relation.d.ts","sourceRoot":"","sources":["../../src/better-auth/relation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { all, and, not, or } from "@shivaedev/effect-prisma";
|
|
2
|
+
const escapeLike = (value) => value.replaceAll("\\", "\\\\").replaceAll("%", "\\%").replaceAll("_", "\\_");
|
|
3
|
+
const comparison = (field, where) => {
|
|
4
|
+
const value = where.value;
|
|
5
|
+
if (where.mode === "insensitive" &&
|
|
6
|
+
typeof value === "string" &&
|
|
7
|
+
field.ilike !== undefined) {
|
|
8
|
+
if (where.operator === "eq")
|
|
9
|
+
return field.ilike(escapeLike(value));
|
|
10
|
+
if (where.operator === "ne")
|
|
11
|
+
return not(field.ilike(escapeLike(value)));
|
|
12
|
+
}
|
|
13
|
+
switch (where.operator) {
|
|
14
|
+
case "eq":
|
|
15
|
+
return value === null ? field.isNull() : field.eq(value);
|
|
16
|
+
case "ne":
|
|
17
|
+
return value === null ? field.isNotNull() : field.neq(value);
|
|
18
|
+
case "lt":
|
|
19
|
+
return field.lt(value);
|
|
20
|
+
case "lte":
|
|
21
|
+
return field.lte(value);
|
|
22
|
+
case "gt":
|
|
23
|
+
return field.gt(value);
|
|
24
|
+
case "gte":
|
|
25
|
+
return field.gte(value);
|
|
26
|
+
case "in": {
|
|
27
|
+
const values = (Array.isArray(value) ? value : [value]).filter((item) => item !== null);
|
|
28
|
+
const ilike = field.ilike;
|
|
29
|
+
return values.length === 0
|
|
30
|
+
? and(field.isNull(), field.isNotNull())
|
|
31
|
+
: where.mode === "insensitive" &&
|
|
32
|
+
ilike !== undefined &&
|
|
33
|
+
values.every((item) => typeof item === "string")
|
|
34
|
+
? or(...values.map((item) => ilike(escapeLike(String(item)))))
|
|
35
|
+
: field.in(values);
|
|
36
|
+
}
|
|
37
|
+
case "not_in": {
|
|
38
|
+
const values = (Array.isArray(value) ? value : [value]).filter((item) => item !== null);
|
|
39
|
+
if (values.length === 0)
|
|
40
|
+
return undefined;
|
|
41
|
+
const ilike = field.ilike;
|
|
42
|
+
return where.mode === "insensitive" &&
|
|
43
|
+
ilike !== undefined &&
|
|
44
|
+
values.every((item) => typeof item === "string")
|
|
45
|
+
? and(...values.map((item) => not(ilike(escapeLike(String(item))))))
|
|
46
|
+
: field.notIn(values);
|
|
47
|
+
}
|
|
48
|
+
case "contains":
|
|
49
|
+
case "starts_with":
|
|
50
|
+
case "ends_with": {
|
|
51
|
+
if (typeof value !== "string") {
|
|
52
|
+
throw new TypeError(`${where.operator} requires a string value`);
|
|
53
|
+
}
|
|
54
|
+
const escaped = escapeLike(value);
|
|
55
|
+
const pattern = where.operator === "contains"
|
|
56
|
+
? `%${escaped}%`
|
|
57
|
+
: where.operator === "starts_with"
|
|
58
|
+
? `${escaped}%`
|
|
59
|
+
: `%${escaped}`;
|
|
60
|
+
return where.mode === "insensitive" && field.ilike !== undefined
|
|
61
|
+
? field.ilike(pattern)
|
|
62
|
+
: field.like(pattern);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const whereExpression = (fields, where) => {
|
|
67
|
+
const conjunctions = [];
|
|
68
|
+
const disjunctions = [];
|
|
69
|
+
for (const condition of where) {
|
|
70
|
+
const field = fields[condition.field];
|
|
71
|
+
if (field === undefined) {
|
|
72
|
+
throw new TypeError(`Unknown database field: ${condition.field}`);
|
|
73
|
+
}
|
|
74
|
+
const expression = comparison(field, condition);
|
|
75
|
+
if (expression === undefined)
|
|
76
|
+
continue;
|
|
77
|
+
(condition.connector === "OR" ? disjunctions : conjunctions).push(expression);
|
|
78
|
+
}
|
|
79
|
+
if (disjunctions.length > 0)
|
|
80
|
+
conjunctions.push(or(...disjunctions));
|
|
81
|
+
if (conjunctions.length === 0)
|
|
82
|
+
return all();
|
|
83
|
+
return conjunctions.length === 1 ? conjunctions[0] : and(...conjunctions);
|
|
84
|
+
};
|
|
85
|
+
export const refineRelation = (relation, options) => {
|
|
86
|
+
let refined = relation;
|
|
87
|
+
if (options.where !== undefined) {
|
|
88
|
+
refined = refined.where((fields) => {
|
|
89
|
+
const expression = whereExpression(fields, options.where ?? []);
|
|
90
|
+
return expression ?? all();
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
if (options.sortBy !== undefined) {
|
|
94
|
+
refined = refined.orderBy((fields) => {
|
|
95
|
+
const field = fields[options.sortBy?.field ?? ""];
|
|
96
|
+
if (field === undefined) {
|
|
97
|
+
throw new TypeError(`Unknown database field: ${options.sortBy?.field}`);
|
|
98
|
+
}
|
|
99
|
+
return options.sortBy?.direction === "desc" ? field.desc() : field.asc();
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
if (options.offset !== undefined)
|
|
103
|
+
refined = refined.skip(options.offset);
|
|
104
|
+
if (options.limit !== undefined)
|
|
105
|
+
refined = refined.take(options.limit);
|
|
106
|
+
if (options.select !== undefined && options.select.length > 0) {
|
|
107
|
+
refined = refined.select(...options.select);
|
|
108
|
+
}
|
|
109
|
+
return refined;
|
|
110
|
+
};
|
|
111
|
+
//# sourceMappingURL=relation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relation.js","sourceRoot":"","sources":["../../src/better-auth/relation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAiE7D,MAAM,UAAU,GAAG,CAAC,KAAa,EAAU,EAAE,CAC5C,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;AAE9E,MAAM,UAAU,GAAG,CAClB,KAAmB,EACnB,KAAmB,EACM,EAAE;IAC3B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IAC1B,IACC,KAAK,CAAC,IAAI,KAAK,aAAa;QAC5B,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,CAAC,KAAK,KAAK,SAAS,EACxB,CAAC;QACF,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;QACnE,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI;YAAE,OAAO,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACzE,CAAC;IACD,QAAQ,KAAK,CAAC,QAAQ,EAAE,CAAC;QACxB,KAAK,IAAI;YACR,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;QAC1D,KAAK,IAAI;YACR,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9D,KAAK,IAAI;YACR,OAAO,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;QACxB,KAAK,KAAK;YACT,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACzB,KAAK,IAAI;YACR,OAAO,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;QACxB,KAAK,KAAK;YACT,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACzB,KAAK,IAAI,CAAC,CAAC,CAAC;YACX,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAC7D,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,IAAI,CACvB,CAAC;YACF,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;YAC1B,OAAO,MAAM,CAAC,MAAM,KAAK,CAAC;gBACzB,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC;gBACxC,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,aAAa;oBAC5B,KAAK,KAAK,SAAS;oBACnB,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC;oBACjD,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;oBAC9D,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;QACtB,CAAC;QACD,KAAK,QAAQ,CAAC,CAAC,CAAC;YACf,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAC7D,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,IAAI,CACvB,CAAC;YACF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,SAAS,CAAC;YAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;YAC1B,OAAO,KAAK,CAAC,IAAI,KAAK,aAAa;gBAClC,KAAK,KAAK,SAAS;gBACnB,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC;gBAChD,CAAC,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBACpE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACxB,CAAC;QACD,KAAK,UAAU,CAAC;QAChB,KAAK,aAAa,CAAC;QACnB,KAAK,WAAW,CAAC,CAAC,CAAC;YAClB,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC/B,MAAM,IAAI,SAAS,CAAC,GAAG,KAAK,CAAC,QAAQ,0BAA0B,CAAC,CAAC;YAClE,CAAC;YACD,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;YAClC,MAAM,OAAO,GACZ,KAAK,CAAC,QAAQ,KAAK,UAAU;gBAC5B,CAAC,CAAC,IAAI,OAAO,GAAG;gBAChB,CAAC,CAAC,KAAK,CAAC,QAAQ,KAAK,aAAa;oBACjC,CAAC,CAAC,GAAG,OAAO,GAAG;oBACf,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC;YACnB,OAAO,KAAK,CAAC,IAAI,KAAK,aAAa,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;gBAC/D,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC;gBACtB,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;IACF,CAAC;AACF,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,CACvB,MAAoC,EACpC,KAAkC,EACT,EAAE;IAC3B,MAAM,YAAY,GAAiB,EAAE,CAAC;IACtC,MAAM,YAAY,GAAiB,EAAE,CAAC;IAEtC,KAAK,MAAM,SAAS,IAAI,KAAK,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,IAAI,SAAS,CAAC,2BAA2B,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,UAAU,GAAG,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QAChD,IAAI,UAAU,KAAK,SAAS;YAAE,SAAS;QACvC,CAAC,SAAS,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,IAAI,CAChE,UAAU,CACV,CAAC;IACH,CAAC;IAED,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC;QAAE,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC;IACpE,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,EAAE,CAAC;IAC5C,OAAO,YAAY,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,CAAC;AAC3E,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,CAC7B,QAAuC,EACvC,OASC,EAC+B,EAAE;IAClC,IAAI,OAAO,GAAG,QAAQ,CAAC;IACvB,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QACjC,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE;YAClC,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;YAChE,OAAO,UAAU,IAAI,GAAG,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAClC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;YACpC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;YAClD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACzB,MAAM,IAAI,SAAS,CAAC,2BAA2B,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;YACzE,CAAC;YACD,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QAC1E,CAAC,CAAC,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACzE,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACvE,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/D,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,OAAO,CAAC;AAChB,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"better-auth.d.ts","sourceRoot":"","sources":["../src/better-auth.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,0BAA0B,EAC/B,mBAAmB,GACnB,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"better-auth.js","sourceRoot":"","sources":["../src/better-auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,mBAAmB,GACnB,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Layer } from "effect";
|
|
2
|
+
import type { PlatformRuntime, PlatformRuntimeOptions } from "./types.js";
|
|
3
|
+
export declare const makePlatformRuntime: <Services, BuildError>(layer: Layer.Layer<Services, BuildError>, options?: PlatformRuntimeOptions) => PlatformRuntime<Services, BuildError>;
|
|
4
|
+
//# sourceMappingURL=make.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"make.d.ts","sourceRoot":"","sources":["../../src/runtime/make.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAEpC,OAAO,KAAK,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAuD1E,eAAO,MAAM,mBAAmB,GAAI,QAAQ,EAAE,UAAU,EACvD,OAAO,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC,EACxC,UAAS,sBAA2B,KAClC,eAAe,CAAC,QAAQ,EAAE,UAAU,CAsBtC,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import { Cause, Context, Effect, Exit, ManagedRuntime } from "effect";
|
|
3
|
+
const RuntimeCacheKey = Symbol.for("@shivaedev/platform/runtime-cache");
|
|
4
|
+
const globalRuntimeCache = globalThis;
|
|
5
|
+
const runtimeCache = globalRuntimeCache[RuntimeCacheKey] ??
|
|
6
|
+
new Map();
|
|
7
|
+
globalRuntimeCache[RuntimeCacheKey] = runtimeCache;
|
|
8
|
+
const make = (layer) => {
|
|
9
|
+
const managed = ManagedRuntime.make(layer);
|
|
10
|
+
const services = new AsyncLocalStorage();
|
|
11
|
+
const currentServices = () => services.getStore();
|
|
12
|
+
const withAmbient = (effect) => {
|
|
13
|
+
const ambient = currentServices();
|
|
14
|
+
return ambient === undefined
|
|
15
|
+
? effect
|
|
16
|
+
: Effect.flatMap(managed.contextEffect, (base) => Effect.provideContext(effect, Context.merge(base, ambient)));
|
|
17
|
+
};
|
|
18
|
+
const runPromiseExit = (effect, options) => managed.runPromiseExit(withAmbient(effect), options);
|
|
19
|
+
const runtime = {
|
|
20
|
+
contextEffect: Effect.map(managed.contextEffect, (base) => {
|
|
21
|
+
const ambient = currentServices();
|
|
22
|
+
return ambient === undefined ? base : Context.merge(base, ambient);
|
|
23
|
+
}),
|
|
24
|
+
currentServices,
|
|
25
|
+
dispose: () => managed.dispose(),
|
|
26
|
+
runPromise: async (effect, options) => {
|
|
27
|
+
const exit = await runPromiseExit(effect, options);
|
|
28
|
+
if (Exit.isSuccess(exit))
|
|
29
|
+
return exit.value;
|
|
30
|
+
throw Cause.squash(exit.cause);
|
|
31
|
+
},
|
|
32
|
+
runPromiseExit,
|
|
33
|
+
runWithServices: (context, evaluate) => services.run(context, evaluate),
|
|
34
|
+
};
|
|
35
|
+
return runtime;
|
|
36
|
+
};
|
|
37
|
+
export const makePlatformRuntime = (layer, options = {}) => {
|
|
38
|
+
const key = process.env.NODE_ENV === "production"
|
|
39
|
+
? undefined
|
|
40
|
+
: options.developmentCacheKey;
|
|
41
|
+
if (key === undefined)
|
|
42
|
+
return make(layer);
|
|
43
|
+
const cached = runtimeCache.get(key);
|
|
44
|
+
if (cached !== undefined) {
|
|
45
|
+
return cached;
|
|
46
|
+
}
|
|
47
|
+
const runtime = make(layer);
|
|
48
|
+
const cachedRuntime = {
|
|
49
|
+
...runtime,
|
|
50
|
+
dispose: async () => {
|
|
51
|
+
if (runtimeCache.get(key) === cachedRuntime)
|
|
52
|
+
runtimeCache.delete(key);
|
|
53
|
+
await runtime.dispose();
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
runtimeCache.set(key, cachedRuntime);
|
|
57
|
+
return cachedRuntime;
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=make.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"make.js","sourceRoot":"","sources":["../../src/runtime/make.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Context, Effect, Exit } from "effect";
|
|
2
|
+
export interface PlatformRuntimeOptions {
|
|
3
|
+
/** Reuse one runtime across development module reloads. */
|
|
4
|
+
readonly developmentCacheKey?: string | symbol;
|
|
5
|
+
}
|
|
6
|
+
export interface PlatformRuntime<Services, BuildError> {
|
|
7
|
+
readonly contextEffect: Effect.Effect<Context.Context<Services>, BuildError>;
|
|
8
|
+
readonly currentServices: () => Context.Context<never> | undefined;
|
|
9
|
+
readonly dispose: () => Promise<void>;
|
|
10
|
+
readonly runPromise: <A, E, Requirements extends Services>(effect: Effect.Effect<A, E, Requirements>, options?: {
|
|
11
|
+
readonly signal?: AbortSignal;
|
|
12
|
+
}) => Promise<A>;
|
|
13
|
+
readonly runPromiseExit: <A, E, Requirements extends Services>(effect: Effect.Effect<A, E, Requirements>, options?: {
|
|
14
|
+
readonly signal?: AbortSignal;
|
|
15
|
+
}) => Promise<Exit.Exit<A, E | BuildError>>;
|
|
16
|
+
readonly runWithServices: <Provided, Value>(services: Context.Context<Provided>, evaluate: () => Value) => Value;
|
|
17
|
+
}
|
|
18
|
+
export type PlatformRuntimeServices<Runtime> = Runtime extends PlatformRuntime<infer Services, infer _BuildError> ? Services : never;
|
|
19
|
+
export type PlatformRuntimeBuildError<Runtime> = Runtime extends PlatformRuntime<infer _Services, infer BuildError> ? BuildError : never;
|
|
20
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/runtime/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEpD,MAAM,WAAW,sBAAsB;IACtC,2DAA2D;IAC3D,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC/C;AAED,MAAM,WAAW,eAAe,CAAC,QAAQ,EAAE,UAAU;IACpD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC,CAAC;IAC7E,QAAQ,CAAC,eAAe,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC;IACnE,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,YAAY,SAAS,QAAQ,EACxD,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC,EACzC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,KACvC,OAAO,CAAC,CAAC,CAAC,CAAC;IAChB,QAAQ,CAAC,cAAc,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,YAAY,SAAS,QAAQ,EAC5D,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC,EACzC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,KACvC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;IAC3C,QAAQ,CAAC,eAAe,EAAE,CAAC,QAAQ,EAAE,KAAK,EACzC,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,EACnC,QAAQ,EAAE,MAAM,KAAK,KACjB,KAAK,CAAC;CACX;AAED,MAAM,MAAM,uBAAuB,CAAC,OAAO,IAC1C,OAAO,SAAS,eAAe,CAAC,MAAM,QAAQ,EAAE,MAAM,WAAW,CAAC,GAC/D,QAAQ,GACR,KAAK,CAAC;AAEV,MAAM,MAAM,yBAAyB,CAAC,OAAO,IAC5C,OAAO,SAAS,eAAe,CAAC,MAAM,SAAS,EAAE,MAAM,UAAU,CAAC,GAC/D,UAAU,GACV,KAAK,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/runtime/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,YAAY,EACX,eAAe,EACf,yBAAyB,EACzB,sBAAsB,EACtB,uBAAuB,GACvB,MAAM,oBAAoB,CAAC"}
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/testing/types.d.ts
CHANGED
|
@@ -6,7 +6,9 @@ import type { Effect, Layer } from "effect";
|
|
|
6
6
|
export type PlatformHarness<Database extends AnyDatabase, Options, Caller, Extension> = Readonly<{
|
|
7
7
|
db: DatabaseService<Database>;
|
|
8
8
|
trpc: EffectCallerFactory<Options, Caller>;
|
|
9
|
-
}> &
|
|
9
|
+
}> & Readonly<{
|
|
10
|
+
promise: <Value>(evaluate: () => Promise<Value>) => Effect.Effect<Value, unknown>;
|
|
11
|
+
}> & Omit<Extension, "db" | "promise" | "trpc">;
|
|
10
12
|
export type PlatformTest<Harness, Provided> = TrpcHarnessTest<Harness, Provided>;
|
|
11
13
|
export type PlatformTester<Harness, Provided> = TrpcHarnessTester<Harness, Provided>;
|
|
12
14
|
export type PlatformIt<Harness, Provided> = Vitest.Methods & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/testing/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,KAAK,EACX,WAAW,EACX,mBAAmB,EACnB,eAAe,EACf,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,KAAK,EACX,aAAa,EACb,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAE5C,MAAM,MAAM,eAAe,CAC1B,QAAQ,SAAS,WAAW,EAC5B,OAAO,EACP,MAAM,EACN,SAAS,IACN,QAAQ,CAAC;IACZ,EAAE,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC9B,IAAI,EAAE,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;CAC3C,CAAC,GACD,IAAI,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/testing/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,KAAK,EACX,WAAW,EACX,mBAAmB,EACnB,eAAe,EACf,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,KAAK,EACX,aAAa,EACb,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAE5C,MAAM,MAAM,eAAe,CAC1B,QAAQ,SAAS,WAAW,EAC5B,OAAO,EACP,MAAM,EACN,SAAS,IACN,QAAQ,CAAC;IACZ,EAAE,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC9B,IAAI,EAAE,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;CAC3C,CAAC,GACD,QAAQ,CAAC;IACR,OAAO,EAAE,CAAC,KAAK,EACd,QAAQ,EAAE,MAAM,OAAO,CAAC,KAAK,CAAC,KAC1B,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;CACnC,CAAC,GACF,IAAI,CAAC,SAAS,EAAE,IAAI,GAAG,SAAS,GAAG,MAAM,CAAC,CAAC;AAE5C,MAAM,MAAM,YAAY,CAAC,OAAO,EAAE,QAAQ,IAAI,eAAe,CAC5D,OAAO,EACP,QAAQ,CACR,CAAC;AAEF,MAAM,MAAM,cAAc,CAAC,OAAO,EAAE,QAAQ,IAAI,iBAAiB,CAChE,OAAO,EACP,QAAQ,CACR,CAAC;AAEF,MAAM,MAAM,UAAU,CAAC,OAAO,EAAE,QAAQ,IAAI,MAAM,CAAC,OAAO,GAAG;IAC5D,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;CACtD,CAAC;AAEF,MAAM,WAAW,qBAAqB,CACrC,QAAQ,SAAS,WAAW,EAC5B,YAAY,SAAS,CAAC,GAAG,UAAU,EAAE,KAAK,EAAE,KAAK,MAAM,EACvD,QAAQ,EACR,UAAU,EACV,SAAS;IAET,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE,iBAAiB,CAAC,CAAC;IACpE,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,CACjB,IAAI,EAAE,QAAQ,CAAC;QACd,EAAE,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC;QAC9B,IAAI,EAAE,mBAAmB,CACxB,aAAa,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,EACpC,YAAY,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CACnC,CAAC;KACF,CAAC,EACF,OAAO,EAAE,WAAW,KAChB,MAAM,CAAC,MAAM,CACjB,SAAS,EACT,OAAO,EACP,QAAQ,GAAG,mBAAmB,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CACpE,CAAC;IACF,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;CAClD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vitest.d.ts","sourceRoot":"","sources":["../../src/testing/vitest.ts"],"names":[],"mappings":"AACA,OAAO,EACN,KAAK,WAAW,EAChB,KAAK,mBAAmB,EAGxB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,YAAY,EAGjB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,KAAK,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,KAAK,EACX,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,cAAc,GACzB,KAAK,CAAC,QAAQ,SAAS,WAAW,EAAE,UAAU,QAAQ,MAEtD,YAAY,SAAS,CAAC,GAAG,UAAU,EAAE,KAAK,EAAE,KAAK,MAAM,EACvD,QAAQ,EACR,UAAU,EACV,SAAS,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAE/C,SAAS,qBAAqB,CAC7B,QAAQ,EACR,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,SAAS,CACT,GAAG;IACH,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAC1B,QAAQ,GAAG,mBAAmB,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,EACpE,UAAU,CACV,CAAC;CACF,KACC,UAAU,CACZ,eAAe,CACd,QAAQ,EACR,aAAa,CAAC,YAAY,CAAC,EAC3B,YAAY,CAAC,YAAY,CAAC,EAC1B,SAAS,CACT,EACD,QAAQ,GAAG,mBAAmB,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"vitest.d.ts","sourceRoot":"","sources":["../../src/testing/vitest.ts"],"names":[],"mappings":"AACA,OAAO,EACN,KAAK,WAAW,EAChB,KAAK,mBAAmB,EAGxB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,YAAY,EAGjB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,KAAK,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,KAAK,EACX,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,cAAc,GACzB,KAAK,CAAC,QAAQ,SAAS,WAAW,EAAE,UAAU,QAAQ,MAEtD,YAAY,SAAS,CAAC,GAAG,UAAU,EAAE,KAAK,EAAE,KAAK,MAAM,EACvD,QAAQ,EACR,UAAU,EACV,SAAS,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAE/C,SAAS,qBAAqB,CAC7B,QAAQ,EACR,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,SAAS,CACT,GAAG;IACH,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAC1B,QAAQ,GAAG,mBAAmB,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,EACpE,UAAU,CACV,CAAC;CACF,KACC,UAAU,CACZ,eAAe,CACd,QAAQ,EACR,aAAa,CAAC,YAAY,CAAC,EAC3B,YAAY,CAAC,YAAY,CAAC,EAC1B,SAAS,CACT,EACD,QAAQ,GAAG,mBAAmB,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAoDpE,CAAC"}
|
package/dist/testing/vitest.js
CHANGED
|
@@ -8,12 +8,14 @@ export const makePlatformIt = (database) => (options) => {
|
|
|
8
8
|
layer: options.layer,
|
|
9
9
|
around: (effect) => withTestTransaction(database, effect),
|
|
10
10
|
makeHarness: (trpc, context) => Effect.gen(function* () {
|
|
11
|
+
const services = yield* Effect.context();
|
|
11
12
|
const databaseEffect = database;
|
|
12
13
|
const db = yield* databaseEffect;
|
|
13
14
|
const extension = options.extend
|
|
14
15
|
? yield* options.extend({ db, trpc }, context)
|
|
15
16
|
: {};
|
|
16
|
-
|
|
17
|
+
const promise = (evaluate) => Effect.tryPromise(() => options.adapter.runWithServices(services, evaluate));
|
|
18
|
+
return { ...extension, db, promise, trpc };
|
|
17
19
|
}),
|
|
18
20
|
});
|
|
19
21
|
return new Proxy(trpcIt, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vitest.js","sourceRoot":"","sources":["../../src/testing/vitest.ts"],"names":[],"mappings":"AACA,OAAO,EAIN,mBAAmB,GACnB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAIN,iBAAiB,GACjB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,MAAM,EAAc,MAAM,QAAQ,CAAC;AAO5C,MAAM,CAAC,MAAM,cAAc,GAC1B,CAAqC,QAAkB,EAAE,EAAE,CAC3D,CAMC,OAWC,EASA,EAAE;IASH,MAAM,MAAM,GAAG,iBAAiB,CAI9B;QACD,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC;QACzD,WAAW,EAAE,CACZ,IAA0C,EAC1C,OAAO,EACqC,EAAE,CAC9C,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACnB,MAAM,cAAc,GAAG,QAItB,CAAC;YACF,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC;YACjC,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM;gBAC/B,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,OAAO,CAAC;gBAC9C,CAAC,CAAE,EAAgB,CAAC;YACrB,OAAO,EAAE,GAAG,SAAS,EAAE,EAAE,EAAE,IAAI,EAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"vitest.js","sourceRoot":"","sources":["../../src/testing/vitest.ts"],"names":[],"mappings":"AACA,OAAO,EAIN,mBAAmB,GACnB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAIN,iBAAiB,GACjB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,MAAM,EAAc,MAAM,QAAQ,CAAC;AAO5C,MAAM,CAAC,MAAM,cAAc,GAC1B,CAAqC,QAAkB,EAAE,EAAE,CAC3D,CAMC,OAWC,EASA,EAAE;IASH,MAAM,MAAM,GAAG,iBAAiB,CAI9B;QACD,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC;QACzD,WAAW,EAAE,CACZ,IAA0C,EAC1C,OAAO,EACqC,EAAE,CAC9C,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACnB,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,EAAY,CAAC;YACnD,MAAM,cAAc,GAAG,QAItB,CAAC;YACF,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC;YACjC,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM;gBAC/B,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,OAAO,CAAC;gBAC9C,CAAC,CAAE,EAAgB,CAAC;YACrB,MAAM,OAAO,GAAG,CAAQ,QAA8B,EAAE,EAAE,CACzD,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CACtB,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,QAAQ,EAAE,QAAQ,CAAC,CACnD,CAAC;YACH,OAAO,EAAE,GAAG,SAAS,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAa,CAAC;QACvD,CAAC,CAA8C;KAChD,CAAC,CAAC;IAEH,OAAO,IAAI,KAAK,CAAC,MAAM,EAAE;QACxB,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ;YAC7B,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;gBAC9B,OAAO,MAAM,CAAC,UAAU,CAAC;YAC1B,CAAC;YACD,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAChD,CAAC;KACD,CAEiC,CAAC;AACpC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shivaedev/platform",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "Opinionated Effect application
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Opinionated Effect application infrastructure for ShivaeDev services",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -24,6 +24,18 @@
|
|
|
24
24
|
"README.md"
|
|
25
25
|
],
|
|
26
26
|
"exports": {
|
|
27
|
+
"./better-auth": {
|
|
28
|
+
"types": "./dist/better-auth.d.ts",
|
|
29
|
+
"source": "./src/better-auth.ts",
|
|
30
|
+
"import": "./dist/better-auth.js",
|
|
31
|
+
"default": "./dist/better-auth.js"
|
|
32
|
+
},
|
|
33
|
+
"./runtime": {
|
|
34
|
+
"types": "./dist/runtime.d.ts",
|
|
35
|
+
"source": "./src/runtime.ts",
|
|
36
|
+
"import": "./dist/runtime.js",
|
|
37
|
+
"default": "./dist/runtime.js"
|
|
38
|
+
},
|
|
27
39
|
"./testing": {
|
|
28
40
|
"types": "./dist/testing.d.ts",
|
|
29
41
|
"source": "./src/testing.ts",
|
|
@@ -38,22 +50,30 @@
|
|
|
38
50
|
},
|
|
39
51
|
"peerDependencies": {
|
|
40
52
|
"@effect/vitest": "4.0.0-beta.102",
|
|
41
|
-
"@shivaedev/effect-prisma": "0.
|
|
42
|
-
"@shivaedev/effect-trpc": "0.
|
|
53
|
+
"@shivaedev/effect-prisma": "0.4.1",
|
|
54
|
+
"@shivaedev/effect-trpc": "0.3.0",
|
|
43
55
|
"@trpc/server": "11.18.0",
|
|
56
|
+
"better-auth": "1.6.25",
|
|
44
57
|
"effect": "4.0.0-beta.102"
|
|
45
58
|
},
|
|
59
|
+
"peerDependenciesMeta": {
|
|
60
|
+
"better-auth": {
|
|
61
|
+
"optional": true
|
|
62
|
+
}
|
|
63
|
+
},
|
|
46
64
|
"devDependencies": {
|
|
47
65
|
"@effect/vitest": "4.0.0-beta.102",
|
|
48
66
|
"@prisma-next/adapter-postgres": "0.15.0",
|
|
49
67
|
"@prisma-next/contract": "0.15.0",
|
|
68
|
+
"@prisma-next/postgres": "0.15.0",
|
|
50
69
|
"@prisma-next/sql-contract": "0.15.0",
|
|
51
70
|
"@prisma-next/target-postgres": "0.15.0",
|
|
52
|
-
"@shivaedev/effect-prisma": "0.
|
|
53
|
-
"@shivaedev/effect-trpc": "0.
|
|
71
|
+
"@shivaedev/effect-prisma": "0.4.1",
|
|
72
|
+
"@shivaedev/effect-trpc": "0.3.0",
|
|
54
73
|
"@trpc/server": "11.18.0",
|
|
55
74
|
"@types/node": "24.10.1",
|
|
56
75
|
"@typescript/native": "npm:typescript@7.0.2",
|
|
76
|
+
"better-auth": "1.6.25",
|
|
57
77
|
"effect": "4.0.0-beta.102",
|
|
58
78
|
"typescript": "npm:@typescript/typescript6@6.0.2",
|
|
59
79
|
"vitest": "4.1.9"
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import type { AnyDatabase, DatabaseServiceOf } from "@shivaedev/effect-prisma";
|
|
2
|
+
import type { BetterAuthOptions, DBAdapterInstance, Where } from "better-auth";
|
|
3
|
+
import { type CustomAdapter, createAdapterFactory } from "better-auth/adapters";
|
|
4
|
+
import { Effect, Option } from "effect";
|
|
5
|
+
import type { PlatformRuntime } from "../runtime/types.js";
|
|
6
|
+
import { type DynamicRelation, refineRelation } from "./relation.js";
|
|
7
|
+
|
|
8
|
+
type CleanedWhere = Required<Where>;
|
|
9
|
+
type FindOneInput = Parameters<CustomAdapter["findOne"]>[0];
|
|
10
|
+
type FindManyInput = Parameters<CustomAdapter["findMany"]>[0];
|
|
11
|
+
type UpdateInput<Value> = {
|
|
12
|
+
readonly model: string;
|
|
13
|
+
readonly update: Value;
|
|
14
|
+
readonly where: CleanedWhere[];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export interface EffectPrismaAdapterOptions {
|
|
18
|
+
readonly debugLogs?: boolean;
|
|
19
|
+
/** Map Better Auth model names to Prisma contract model names. */
|
|
20
|
+
readonly modelName?: (model: string) => string;
|
|
21
|
+
readonly usePlural?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface DynamicDatabase<Requirements> {
|
|
25
|
+
readonly transaction: <A, E, R>(
|
|
26
|
+
program: Effect.Effect<A, E, R>,
|
|
27
|
+
) => Effect.Effect<A, E | unknown, R | Requirements>;
|
|
28
|
+
readonly [model: string]: DynamicRelation<Requirements> | unknown;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const modelRelation = <Requirements>(
|
|
32
|
+
database: DynamicDatabase<Requirements>,
|
|
33
|
+
model: string,
|
|
34
|
+
mapModelName: (model: string) => string,
|
|
35
|
+
): DynamicRelation<Requirements> =>
|
|
36
|
+
Reflect.get(database, mapModelName(model)) as DynamicRelation<Requirements>;
|
|
37
|
+
|
|
38
|
+
const defaultModelName = (model: string): string =>
|
|
39
|
+
model.length === 0 ? model : `${model[0]?.toUpperCase()}${model.slice(1)}`;
|
|
40
|
+
|
|
41
|
+
const rejectJoin = (join: unknown): void => {
|
|
42
|
+
if (join !== undefined) {
|
|
43
|
+
throw new TypeError(
|
|
44
|
+
"The Effect Prisma Better Auth adapter does not support experimental native joins",
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const effectPrismaAdapter =
|
|
50
|
+
<Database extends AnyDatabase, Services, BuildError>(
|
|
51
|
+
databaseTag: Database,
|
|
52
|
+
runtime: PlatformRuntime<Services, BuildError>,
|
|
53
|
+
adapterOptions: EffectPrismaAdapterOptions = {},
|
|
54
|
+
): DBAdapterInstance =>
|
|
55
|
+
(authOptions: BetterAuthOptions) => {
|
|
56
|
+
const mapModelName = adapterOptions.modelName ?? defaultModelName;
|
|
57
|
+
const databaseEffect = databaseTag as Effect.Effect<
|
|
58
|
+
DatabaseServiceOf<Database>,
|
|
59
|
+
never,
|
|
60
|
+
Services
|
|
61
|
+
>;
|
|
62
|
+
|
|
63
|
+
const run = <Value>(
|
|
64
|
+
operation: (
|
|
65
|
+
database: DynamicDatabase<Services>,
|
|
66
|
+
) => Effect.Effect<Value, unknown, Services>,
|
|
67
|
+
): Promise<Value> =>
|
|
68
|
+
runtime.runPromise(
|
|
69
|
+
Effect.flatMap(databaseEffect, (database) =>
|
|
70
|
+
operation(database as DynamicDatabase<Services>),
|
|
71
|
+
),
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
let factory: ReturnType<typeof createAdapterFactory>;
|
|
75
|
+
factory = createAdapterFactory({
|
|
76
|
+
config: {
|
|
77
|
+
adapterId: "effect-prisma",
|
|
78
|
+
adapterName: "Effect Prisma",
|
|
79
|
+
debugLogs: adapterOptions.debugLogs ?? false,
|
|
80
|
+
supportsArrays: true,
|
|
81
|
+
supportsBooleans: true,
|
|
82
|
+
supportsDates: true,
|
|
83
|
+
supportsJSON: true,
|
|
84
|
+
supportsNumericIds: true,
|
|
85
|
+
supportsUUIDs: true,
|
|
86
|
+
transaction: (callback) =>
|
|
87
|
+
run((database) =>
|
|
88
|
+
database.transaction(
|
|
89
|
+
Effect.flatMap(Effect.context<Services>(), (services) =>
|
|
90
|
+
Effect.tryPromise({
|
|
91
|
+
try: () =>
|
|
92
|
+
runtime.runWithServices(services, () =>
|
|
93
|
+
callback(factory(authOptions)),
|
|
94
|
+
),
|
|
95
|
+
catch: (error) => error,
|
|
96
|
+
}),
|
|
97
|
+
),
|
|
98
|
+
),
|
|
99
|
+
),
|
|
100
|
+
},
|
|
101
|
+
adapter: ({ debugLog, getFieldAttributes }) => ({
|
|
102
|
+
create: async ({ data, model, select }) => {
|
|
103
|
+
debugLog("create", { model });
|
|
104
|
+
return (await run((database) =>
|
|
105
|
+
refineRelation(modelRelation(database, model, mapModelName), {
|
|
106
|
+
select,
|
|
107
|
+
}).create(data),
|
|
108
|
+
)) as typeof data;
|
|
109
|
+
},
|
|
110
|
+
findOne: async <Value>({
|
|
111
|
+
join,
|
|
112
|
+
model,
|
|
113
|
+
select,
|
|
114
|
+
where,
|
|
115
|
+
}: FindOneInput) => {
|
|
116
|
+
rejectJoin(join);
|
|
117
|
+
return (await run((database) =>
|
|
118
|
+
Effect.map(
|
|
119
|
+
refineRelation(modelRelation(database, model, mapModelName), {
|
|
120
|
+
select,
|
|
121
|
+
where,
|
|
122
|
+
}).first(),
|
|
123
|
+
Option.getOrNull,
|
|
124
|
+
),
|
|
125
|
+
)) as Value | null;
|
|
126
|
+
},
|
|
127
|
+
findMany: async <Value>({
|
|
128
|
+
join,
|
|
129
|
+
limit,
|
|
130
|
+
model,
|
|
131
|
+
offset,
|
|
132
|
+
select,
|
|
133
|
+
sortBy,
|
|
134
|
+
where,
|
|
135
|
+
}: FindManyInput) => {
|
|
136
|
+
rejectJoin(join);
|
|
137
|
+
return (await run(
|
|
138
|
+
(database) =>
|
|
139
|
+
refineRelation(modelRelation(database, model, mapModelName), {
|
|
140
|
+
limit,
|
|
141
|
+
offset,
|
|
142
|
+
select,
|
|
143
|
+
sortBy,
|
|
144
|
+
where,
|
|
145
|
+
}) as unknown as Effect.Effect<
|
|
146
|
+
ReadonlyArray<Record<string, unknown>>,
|
|
147
|
+
unknown,
|
|
148
|
+
Services
|
|
149
|
+
>,
|
|
150
|
+
)) as Value[];
|
|
151
|
+
},
|
|
152
|
+
update: async <Value>({ model, update, where }: UpdateInput<Value>) => {
|
|
153
|
+
if (where.length === 0) return null;
|
|
154
|
+
const hasUniqueCondition = where.some(
|
|
155
|
+
(condition) =>
|
|
156
|
+
condition.connector !== "OR" &&
|
|
157
|
+
condition.operator === "eq" &&
|
|
158
|
+
condition.mode !== "insensitive" &&
|
|
159
|
+
(condition.field === "id" ||
|
|
160
|
+
getFieldAttributes({
|
|
161
|
+
field: condition.field,
|
|
162
|
+
model,
|
|
163
|
+
}).unique === true),
|
|
164
|
+
);
|
|
165
|
+
return (await run((database) => {
|
|
166
|
+
const relation = refineRelation(
|
|
167
|
+
modelRelation(database, model, mapModelName),
|
|
168
|
+
{ where },
|
|
169
|
+
);
|
|
170
|
+
return hasUniqueCondition
|
|
171
|
+
? relation.update(update as Record<string, unknown>)
|
|
172
|
+
: Effect.map(
|
|
173
|
+
relation.updateAll(update as Record<string, unknown>),
|
|
174
|
+
(rows) => rows[0] ?? null,
|
|
175
|
+
);
|
|
176
|
+
})) as Value | null;
|
|
177
|
+
},
|
|
178
|
+
updateMany: ({ model, update, where }) =>
|
|
179
|
+
run((database) =>
|
|
180
|
+
Effect.map(
|
|
181
|
+
refineRelation(modelRelation(database, model, mapModelName), {
|
|
182
|
+
where,
|
|
183
|
+
}).updateAll(update),
|
|
184
|
+
(rows) => rows.length,
|
|
185
|
+
),
|
|
186
|
+
),
|
|
187
|
+
delete: ({ model, where }) => {
|
|
188
|
+
const hasId = where.some(
|
|
189
|
+
(condition) =>
|
|
190
|
+
condition.connector !== "OR" &&
|
|
191
|
+
condition.field === "id" &&
|
|
192
|
+
condition.operator === "eq",
|
|
193
|
+
);
|
|
194
|
+
return run((database) => {
|
|
195
|
+
const relation = refineRelation(
|
|
196
|
+
modelRelation(database, model, mapModelName),
|
|
197
|
+
{ where },
|
|
198
|
+
);
|
|
199
|
+
return hasId
|
|
200
|
+
? Effect.asVoid(relation.delete())
|
|
201
|
+
: Effect.asVoid(relation.deleteAll());
|
|
202
|
+
});
|
|
203
|
+
},
|
|
204
|
+
deleteMany: ({ model, where }) =>
|
|
205
|
+
run((database) =>
|
|
206
|
+
Effect.map(
|
|
207
|
+
refineRelation(modelRelation(database, model, mapModelName), {
|
|
208
|
+
where,
|
|
209
|
+
}).deleteAll(),
|
|
210
|
+
(rows) => rows.length,
|
|
211
|
+
),
|
|
212
|
+
),
|
|
213
|
+
count: ({ model, where }) =>
|
|
214
|
+
run((database) =>
|
|
215
|
+
refineRelation(modelRelation(database, model, mapModelName), {
|
|
216
|
+
where: where as CleanedWhere[] | undefined,
|
|
217
|
+
}).count(),
|
|
218
|
+
),
|
|
219
|
+
options: {
|
|
220
|
+
usePlural: adapterOptions.usePlural ?? false,
|
|
221
|
+
},
|
|
222
|
+
}),
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
return factory(authOptions);
|
|
226
|
+
};
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { all, and, not, or } from "@shivaedev/effect-prisma";
|
|
2
|
+
import type { Where } from "better-auth";
|
|
3
|
+
import type { Effect, Option } from "effect";
|
|
4
|
+
|
|
5
|
+
type CleanedWhere = Required<Where>;
|
|
6
|
+
type Expression = Parameters<typeof and>[number];
|
|
7
|
+
|
|
8
|
+
interface DynamicField {
|
|
9
|
+
eq(value: unknown): Expression;
|
|
10
|
+
neq(value: unknown): Expression;
|
|
11
|
+
gt(value: unknown): Expression;
|
|
12
|
+
gte(value: unknown): Expression;
|
|
13
|
+
lt(value: unknown): Expression;
|
|
14
|
+
lte(value: unknown): Expression;
|
|
15
|
+
in(value: ReadonlyArray<unknown>): Expression;
|
|
16
|
+
notIn(value: ReadonlyArray<unknown>): Expression;
|
|
17
|
+
like(value: string): Expression;
|
|
18
|
+
ilike?(value: string): Expression;
|
|
19
|
+
isNull(): Expression;
|
|
20
|
+
isNotNull(): Expression;
|
|
21
|
+
asc(): unknown;
|
|
22
|
+
desc(): unknown;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface DynamicRelation<Requirements> {
|
|
26
|
+
where(
|
|
27
|
+
predicate: (fields: Record<string, DynamicField>) => Expression,
|
|
28
|
+
): DynamicRelation<Requirements>;
|
|
29
|
+
orderBy(
|
|
30
|
+
ordering: (fields: Record<string, DynamicField>) => unknown,
|
|
31
|
+
): DynamicRelation<Requirements>;
|
|
32
|
+
take(count: number): DynamicRelation<Requirements>;
|
|
33
|
+
skip(count: number): DynamicRelation<Requirements>;
|
|
34
|
+
select(...fields: ReadonlyArray<string>): DynamicRelation<Requirements>;
|
|
35
|
+
first(): Effect.Effect<
|
|
36
|
+
Option.Option<Record<string, unknown>>,
|
|
37
|
+
unknown,
|
|
38
|
+
Requirements
|
|
39
|
+
>;
|
|
40
|
+
count(): Effect.Effect<number, unknown, Requirements>;
|
|
41
|
+
create(
|
|
42
|
+
data: Record<string, unknown>,
|
|
43
|
+
): Effect.Effect<Record<string, unknown>, unknown, Requirements>;
|
|
44
|
+
update(
|
|
45
|
+
data: Record<string, unknown>,
|
|
46
|
+
): Effect.Effect<Record<string, unknown> | null, unknown, Requirements>;
|
|
47
|
+
updateAll(
|
|
48
|
+
data: Record<string, unknown>,
|
|
49
|
+
): Effect.Effect<
|
|
50
|
+
ReadonlyArray<Record<string, unknown>>,
|
|
51
|
+
unknown,
|
|
52
|
+
Requirements
|
|
53
|
+
>;
|
|
54
|
+
delete(): Effect.Effect<
|
|
55
|
+
Record<string, unknown> | null,
|
|
56
|
+
unknown,
|
|
57
|
+
Requirements
|
|
58
|
+
>;
|
|
59
|
+
deleteAll(): Effect.Effect<
|
|
60
|
+
ReadonlyArray<Record<string, unknown>>,
|
|
61
|
+
unknown,
|
|
62
|
+
Requirements
|
|
63
|
+
>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const escapeLike = (value: string): string =>
|
|
67
|
+
value.replaceAll("\\", "\\\\").replaceAll("%", "\\%").replaceAll("_", "\\_");
|
|
68
|
+
|
|
69
|
+
const comparison = (
|
|
70
|
+
field: DynamicField,
|
|
71
|
+
where: CleanedWhere,
|
|
72
|
+
): Expression | undefined => {
|
|
73
|
+
const value = where.value;
|
|
74
|
+
if (
|
|
75
|
+
where.mode === "insensitive" &&
|
|
76
|
+
typeof value === "string" &&
|
|
77
|
+
field.ilike !== undefined
|
|
78
|
+
) {
|
|
79
|
+
if (where.operator === "eq") return field.ilike(escapeLike(value));
|
|
80
|
+
if (where.operator === "ne") return not(field.ilike(escapeLike(value)));
|
|
81
|
+
}
|
|
82
|
+
switch (where.operator) {
|
|
83
|
+
case "eq":
|
|
84
|
+
return value === null ? field.isNull() : field.eq(value);
|
|
85
|
+
case "ne":
|
|
86
|
+
return value === null ? field.isNotNull() : field.neq(value);
|
|
87
|
+
case "lt":
|
|
88
|
+
return field.lt(value);
|
|
89
|
+
case "lte":
|
|
90
|
+
return field.lte(value);
|
|
91
|
+
case "gt":
|
|
92
|
+
return field.gt(value);
|
|
93
|
+
case "gte":
|
|
94
|
+
return field.gte(value);
|
|
95
|
+
case "in": {
|
|
96
|
+
const values = (Array.isArray(value) ? value : [value]).filter(
|
|
97
|
+
(item) => item !== null,
|
|
98
|
+
);
|
|
99
|
+
const ilike = field.ilike;
|
|
100
|
+
return values.length === 0
|
|
101
|
+
? and(field.isNull(), field.isNotNull())
|
|
102
|
+
: where.mode === "insensitive" &&
|
|
103
|
+
ilike !== undefined &&
|
|
104
|
+
values.every((item) => typeof item === "string")
|
|
105
|
+
? or(...values.map((item) => ilike(escapeLike(String(item)))))
|
|
106
|
+
: field.in(values);
|
|
107
|
+
}
|
|
108
|
+
case "not_in": {
|
|
109
|
+
const values = (Array.isArray(value) ? value : [value]).filter(
|
|
110
|
+
(item) => item !== null,
|
|
111
|
+
);
|
|
112
|
+
if (values.length === 0) return undefined;
|
|
113
|
+
const ilike = field.ilike;
|
|
114
|
+
return where.mode === "insensitive" &&
|
|
115
|
+
ilike !== undefined &&
|
|
116
|
+
values.every((item) => typeof item === "string")
|
|
117
|
+
? and(...values.map((item) => not(ilike(escapeLike(String(item))))))
|
|
118
|
+
: field.notIn(values);
|
|
119
|
+
}
|
|
120
|
+
case "contains":
|
|
121
|
+
case "starts_with":
|
|
122
|
+
case "ends_with": {
|
|
123
|
+
if (typeof value !== "string") {
|
|
124
|
+
throw new TypeError(`${where.operator} requires a string value`);
|
|
125
|
+
}
|
|
126
|
+
const escaped = escapeLike(value);
|
|
127
|
+
const pattern =
|
|
128
|
+
where.operator === "contains"
|
|
129
|
+
? `%${escaped}%`
|
|
130
|
+
: where.operator === "starts_with"
|
|
131
|
+
? `${escaped}%`
|
|
132
|
+
: `%${escaped}`;
|
|
133
|
+
return where.mode === "insensitive" && field.ilike !== undefined
|
|
134
|
+
? field.ilike(pattern)
|
|
135
|
+
: field.like(pattern);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const whereExpression = (
|
|
141
|
+
fields: Record<string, DynamicField>,
|
|
142
|
+
where: ReadonlyArray<CleanedWhere>,
|
|
143
|
+
): Expression | undefined => {
|
|
144
|
+
const conjunctions: Expression[] = [];
|
|
145
|
+
const disjunctions: Expression[] = [];
|
|
146
|
+
|
|
147
|
+
for (const condition of where) {
|
|
148
|
+
const field = fields[condition.field];
|
|
149
|
+
if (field === undefined) {
|
|
150
|
+
throw new TypeError(`Unknown database field: ${condition.field}`);
|
|
151
|
+
}
|
|
152
|
+
const expression = comparison(field, condition);
|
|
153
|
+
if (expression === undefined) continue;
|
|
154
|
+
(condition.connector === "OR" ? disjunctions : conjunctions).push(
|
|
155
|
+
expression,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (disjunctions.length > 0) conjunctions.push(or(...disjunctions));
|
|
160
|
+
if (conjunctions.length === 0) return all();
|
|
161
|
+
return conjunctions.length === 1 ? conjunctions[0] : and(...conjunctions);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export const refineRelation = <Requirements>(
|
|
165
|
+
relation: DynamicRelation<Requirements>,
|
|
166
|
+
options: {
|
|
167
|
+
readonly where?: ReadonlyArray<CleanedWhere>;
|
|
168
|
+
readonly select?: ReadonlyArray<string>;
|
|
169
|
+
readonly limit?: number;
|
|
170
|
+
readonly offset?: number;
|
|
171
|
+
readonly sortBy?: {
|
|
172
|
+
readonly direction: "asc" | "desc";
|
|
173
|
+
readonly field: string;
|
|
174
|
+
};
|
|
175
|
+
},
|
|
176
|
+
): DynamicRelation<Requirements> => {
|
|
177
|
+
let refined = relation;
|
|
178
|
+
if (options.where !== undefined) {
|
|
179
|
+
refined = refined.where((fields) => {
|
|
180
|
+
const expression = whereExpression(fields, options.where ?? []);
|
|
181
|
+
return expression ?? all();
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
if (options.sortBy !== undefined) {
|
|
185
|
+
refined = refined.orderBy((fields) => {
|
|
186
|
+
const field = fields[options.sortBy?.field ?? ""];
|
|
187
|
+
if (field === undefined) {
|
|
188
|
+
throw new TypeError(`Unknown database field: ${options.sortBy?.field}`);
|
|
189
|
+
}
|
|
190
|
+
return options.sortBy?.direction === "desc" ? field.desc() : field.asc();
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
if (options.offset !== undefined) refined = refined.skip(options.offset);
|
|
194
|
+
if (options.limit !== undefined) refined = refined.take(options.limit);
|
|
195
|
+
if (options.select !== undefined && options.select.length > 0) {
|
|
196
|
+
refined = refined.select(...options.select);
|
|
197
|
+
}
|
|
198
|
+
return refined;
|
|
199
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import type { Layer } from "effect";
|
|
3
|
+
import { Cause, Context, Effect, Exit, ManagedRuntime } from "effect";
|
|
4
|
+
import type { PlatformRuntime, PlatformRuntimeOptions } from "./types.js";
|
|
5
|
+
|
|
6
|
+
const RuntimeCacheKey = Symbol.for("@shivaedev/platform/runtime-cache");
|
|
7
|
+
const globalRuntimeCache = globalThis as typeof globalThis & {
|
|
8
|
+
[RuntimeCacheKey]?: Map<string | symbol, PlatformRuntime<unknown, unknown>>;
|
|
9
|
+
};
|
|
10
|
+
const runtimeCache =
|
|
11
|
+
globalRuntimeCache[RuntimeCacheKey] ??
|
|
12
|
+
new Map<string | symbol, PlatformRuntime<unknown, unknown>>();
|
|
13
|
+
globalRuntimeCache[RuntimeCacheKey] = runtimeCache;
|
|
14
|
+
|
|
15
|
+
const make = <Services, BuildError>(
|
|
16
|
+
layer: Layer.Layer<Services, BuildError>,
|
|
17
|
+
): PlatformRuntime<Services, BuildError> => {
|
|
18
|
+
const managed = ManagedRuntime.make(layer);
|
|
19
|
+
const services = new AsyncLocalStorage<Context.Context<never>>();
|
|
20
|
+
|
|
21
|
+
const currentServices = () => services.getStore();
|
|
22
|
+
const withAmbient = <A, E, Requirements extends Services>(
|
|
23
|
+
effect: Effect.Effect<A, E, Requirements>,
|
|
24
|
+
): Effect.Effect<A, E | BuildError, Requirements> => {
|
|
25
|
+
const ambient = currentServices();
|
|
26
|
+
return ambient === undefined
|
|
27
|
+
? effect
|
|
28
|
+
: Effect.flatMap(managed.contextEffect, (base) =>
|
|
29
|
+
Effect.provideContext(effect, Context.merge(base, ambient)),
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const runPromiseExit: PlatformRuntime<
|
|
34
|
+
Services,
|
|
35
|
+
BuildError
|
|
36
|
+
>["runPromiseExit"] = (effect, options) =>
|
|
37
|
+
managed.runPromiseExit(withAmbient(effect), options);
|
|
38
|
+
|
|
39
|
+
const runtime: PlatformRuntime<Services, BuildError> = {
|
|
40
|
+
contextEffect: Effect.map(managed.contextEffect, (base) => {
|
|
41
|
+
const ambient = currentServices();
|
|
42
|
+
return ambient === undefined ? base : Context.merge(base, ambient);
|
|
43
|
+
}),
|
|
44
|
+
currentServices,
|
|
45
|
+
dispose: () => managed.dispose(),
|
|
46
|
+
runPromise: async (effect, options) => {
|
|
47
|
+
const exit = await runPromiseExit(effect, options);
|
|
48
|
+
if (Exit.isSuccess(exit)) return exit.value;
|
|
49
|
+
throw Cause.squash(exit.cause);
|
|
50
|
+
},
|
|
51
|
+
runPromiseExit,
|
|
52
|
+
runWithServices: (context, evaluate) =>
|
|
53
|
+
services.run(context as Context.Context<never>, evaluate),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return runtime;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const makePlatformRuntime = <Services, BuildError>(
|
|
60
|
+
layer: Layer.Layer<Services, BuildError>,
|
|
61
|
+
options: PlatformRuntimeOptions = {},
|
|
62
|
+
): PlatformRuntime<Services, BuildError> => {
|
|
63
|
+
const key =
|
|
64
|
+
process.env.NODE_ENV === "production"
|
|
65
|
+
? undefined
|
|
66
|
+
: options.developmentCacheKey;
|
|
67
|
+
if (key === undefined) return make(layer);
|
|
68
|
+
|
|
69
|
+
const cached = runtimeCache.get(key);
|
|
70
|
+
if (cached !== undefined) {
|
|
71
|
+
return cached as PlatformRuntime<Services, BuildError>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const runtime = make(layer);
|
|
75
|
+
const cachedRuntime: PlatformRuntime<Services, BuildError> = {
|
|
76
|
+
...runtime,
|
|
77
|
+
dispose: async () => {
|
|
78
|
+
if (runtimeCache.get(key) === cachedRuntime) runtimeCache.delete(key);
|
|
79
|
+
await runtime.dispose();
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
runtimeCache.set(key, cachedRuntime as PlatformRuntime<unknown, unknown>);
|
|
83
|
+
return cachedRuntime;
|
|
84
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Context, Effect, Exit } from "effect";
|
|
2
|
+
|
|
3
|
+
export interface PlatformRuntimeOptions {
|
|
4
|
+
/** Reuse one runtime across development module reloads. */
|
|
5
|
+
readonly developmentCacheKey?: string | symbol;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface PlatformRuntime<Services, BuildError> {
|
|
9
|
+
readonly contextEffect: Effect.Effect<Context.Context<Services>, BuildError>;
|
|
10
|
+
readonly currentServices: () => Context.Context<never> | undefined;
|
|
11
|
+
readonly dispose: () => Promise<void>;
|
|
12
|
+
readonly runPromise: <A, E, Requirements extends Services>(
|
|
13
|
+
effect: Effect.Effect<A, E, Requirements>,
|
|
14
|
+
options?: { readonly signal?: AbortSignal },
|
|
15
|
+
) => Promise<A>;
|
|
16
|
+
readonly runPromiseExit: <A, E, Requirements extends Services>(
|
|
17
|
+
effect: Effect.Effect<A, E, Requirements>,
|
|
18
|
+
options?: { readonly signal?: AbortSignal },
|
|
19
|
+
) => Promise<Exit.Exit<A, E | BuildError>>;
|
|
20
|
+
readonly runWithServices: <Provided, Value>(
|
|
21
|
+
services: Context.Context<Provided>,
|
|
22
|
+
evaluate: () => Value,
|
|
23
|
+
) => Value;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type PlatformRuntimeServices<Runtime> =
|
|
27
|
+
Runtime extends PlatformRuntime<infer Services, infer _BuildError>
|
|
28
|
+
? Services
|
|
29
|
+
: never;
|
|
30
|
+
|
|
31
|
+
export type PlatformRuntimeBuildError<Runtime> =
|
|
32
|
+
Runtime extends PlatformRuntime<infer _Services, infer BuildError>
|
|
33
|
+
? BuildError
|
|
34
|
+
: never;
|
package/src/runtime.ts
ADDED
package/src/testing/types.ts
CHANGED
|
@@ -23,7 +23,12 @@ export type PlatformHarness<
|
|
|
23
23
|
db: DatabaseService<Database>;
|
|
24
24
|
trpc: EffectCallerFactory<Options, Caller>;
|
|
25
25
|
}> &
|
|
26
|
-
|
|
26
|
+
Readonly<{
|
|
27
|
+
promise: <Value>(
|
|
28
|
+
evaluate: () => Promise<Value>,
|
|
29
|
+
) => Effect.Effect<Value, unknown>;
|
|
30
|
+
}> &
|
|
31
|
+
Omit<Extension, "db" | "promise" | "trpc">;
|
|
27
32
|
|
|
28
33
|
export type PlatformTest<Harness, Provided> = TrpcHarnessTest<
|
|
29
34
|
Harness,
|
package/src/testing/vitest.ts
CHANGED
|
@@ -69,6 +69,7 @@ export const makePlatformIt =
|
|
|
69
69
|
context,
|
|
70
70
|
): Effect.Effect<Harness, unknown, Services> =>
|
|
71
71
|
Effect.gen(function* () {
|
|
72
|
+
const services = yield* Effect.context<Services>();
|
|
72
73
|
const databaseEffect = database as unknown as Effect.Effect<
|
|
73
74
|
DatabaseService<Database>,
|
|
74
75
|
never,
|
|
@@ -78,7 +79,11 @@ export const makePlatformIt =
|
|
|
78
79
|
const extension = options.extend
|
|
79
80
|
? yield* options.extend({ db, trpc }, context)
|
|
80
81
|
: ({} as Extension);
|
|
81
|
-
|
|
82
|
+
const promise = <Value>(evaluate: () => Promise<Value>) =>
|
|
83
|
+
Effect.tryPromise(() =>
|
|
84
|
+
options.adapter.runWithServices(services, evaluate),
|
|
85
|
+
);
|
|
86
|
+
return { ...extension, db, promise, trpc } as Harness;
|
|
82
87
|
}) as Effect.Effect<Harness, unknown, Services>,
|
|
83
88
|
});
|
|
84
89
|
|