@shivaedev/effect-changes-prisma 0.3.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +250 -87
- package/dist/changes.d.ts +9 -9
- package/dist/changes.d.ts.map +1 -1
- package/dist/changes.js +7 -5
- package/dist/changes.js.map +1 -1
- package/dist/coverage.d.ts +1 -1
- package/dist/coverage.d.ts.map +1 -1
- package/dist/coverage.js +6 -2
- package/dist/coverage.js.map +1 -1
- package/dist/expiry.d.ts +1 -1
- package/dist/expiry.d.ts.map +1 -1
- package/dist/expiry.js.map +1 -1
- package/dist/model.d.ts +20 -20
- package/dist/model.d.ts.map +1 -1
- package/dist/recording.d.ts +1 -1
- package/dist/recording.d.ts.map +1 -1
- package/dist/recording.js +17 -13
- package/dist/recording.js.map +1 -1
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js.map +1 -1
- package/dist/transaction.d.ts +6 -6
- package/dist/transaction.d.ts.map +1 -1
- package/dist/transaction.js +12 -8
- package/dist/transaction.js.map +1 -1
- package/dist/write.d.ts +1 -1
- package/dist/write.d.ts.map +1 -1
- package/dist/write.js +16 -10
- package/dist/write.js.map +1 -1
- package/docs/north-star.md +43 -0
- package/docs/roadmap.md +22 -0
- package/package.json +22 -17
- package/src/changes.ts +20 -17
- package/src/coverage.ts +7 -5
- package/src/expiry.ts +3 -3
- package/src/model.ts +13 -13
- package/src/recording.ts +9 -6
- package/src/transaction.ts +25 -22
- package/src/write.ts +10 -6
- package/dist/index.d.ts +0 -7
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -5
- package/dist/index.js.map +0 -1
- package/src/index.ts +0 -15
package/src/model.ts
CHANGED
|
@@ -4,22 +4,22 @@ export interface TransactionOptions {
|
|
|
4
4
|
readonly timeout?: number;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
export interface Transactional<
|
|
8
|
-
$transaction: <
|
|
7
|
+
export interface Transactional<TTx> {
|
|
8
|
+
$transaction: <TX>(run: (tx: TTx) => Promise<TX>, options?: TransactionOptions) => PromiseLike<TX>;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
type PayloadOf<
|
|
11
|
+
type PayloadOf<TDelegate> = TDelegate extends { readonly [key: symbol]: { readonly types: { readonly payload: infer TPayload } } } ? TPayload : never;
|
|
12
12
|
|
|
13
|
-
type Delegates<
|
|
13
|
+
type Delegates<TClient> = Exclude<keyof TClient & string, `$${string}`>;
|
|
14
14
|
|
|
15
|
-
export type ModelName<
|
|
16
|
-
[
|
|
17
|
-
}[Delegates<
|
|
15
|
+
export type ModelName<TClient> = {
|
|
16
|
+
[TKey in Delegates<TClient>]: PayloadOf<TClient[TKey]> extends { readonly name: infer TName extends string } ? TName : never;
|
|
17
|
+
}[Delegates<TClient>];
|
|
18
18
|
|
|
19
|
-
export type ModelRow<
|
|
19
|
+
export type ModelRow<TClient, TModel extends string> = Extract<
|
|
20
20
|
{
|
|
21
|
-
[
|
|
22
|
-
}[Delegates<
|
|
21
|
+
[TKey in Delegates<TClient>]: PayloadOf<TClient[TKey]> extends { readonly name: TModel; readonly scalars: infer TRow } ? TRow : never;
|
|
22
|
+
}[Delegates<TClient>],
|
|
23
23
|
object
|
|
24
24
|
>;
|
|
25
25
|
|
|
@@ -27,10 +27,10 @@ export type RowOperation = "create" | "update" | "upsert" | "delete" | "createMa
|
|
|
27
27
|
|
|
28
28
|
export type CountOperation = "createMany" | "updateMany" | "deleteMany";
|
|
29
29
|
|
|
30
|
-
export type ModelChanges<
|
|
30
|
+
export type ModelChanges<TRow, A> = (row: TRow, operation: RowOperation) => Iterable<A>;
|
|
31
31
|
|
|
32
|
-
export type ChangeMap<
|
|
33
|
-
readonly [
|
|
32
|
+
export type ChangeMap<TClient, A> = {
|
|
33
|
+
readonly [TModel in ModelName<TClient>]: ModelChanges<ModelRow<TClient, TModel>, A> | null;
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
export const rowOperations: ReadonlySet<string> = new Set<RowOperation>([
|
package/src/recording.ts
CHANGED
|
@@ -10,10 +10,12 @@ export const delegatesOf = <A>(models: LooseMap<A>): ReadonlyMap<string, string>
|
|
|
10
10
|
),
|
|
11
11
|
);
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
function bound(value: unknown, target: object): unknown {
|
|
14
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
15
|
+
}
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
new Proxy(delegate, {
|
|
17
|
+
function recordingDelegate(delegate: object, model: string, collect: (write: Write) => void): object {
|
|
18
|
+
return new Proxy(delegate, {
|
|
17
19
|
get: (target, key) => {
|
|
18
20
|
const value: unknown = Reflect.get(target, key);
|
|
19
21
|
if (typeof key !== "string" || typeof value !== "function" || !isWriteOperation(key)) {
|
|
@@ -26,12 +28,13 @@ const recordingDelegate = (delegate: object, model: string, collect: (write: Wri
|
|
|
26
28
|
});
|
|
27
29
|
},
|
|
28
30
|
});
|
|
31
|
+
}
|
|
29
32
|
|
|
30
|
-
export const recordingClient = <
|
|
31
|
-
client:
|
|
33
|
+
export const recordingClient = <TClient extends object>(
|
|
34
|
+
client: TClient,
|
|
32
35
|
delegates: ReadonlyMap<string, string>,
|
|
33
36
|
collect: (write: Write) => void,
|
|
34
|
-
):
|
|
37
|
+
): TClient =>
|
|
35
38
|
new Proxy(client, {
|
|
36
39
|
get: (target, key) => {
|
|
37
40
|
const value: unknown = Reflect.get(target, key);
|
package/src/transaction.ts
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
import { type Context, Effect, Exit } from "effect";
|
|
2
|
-
import type { Frame } from "@shivaedev/effect-changes";
|
|
2
|
+
import type { Frame } from "@shivaedev/effect-changes/frame.ts";
|
|
3
3
|
import { PrismaError, TransactionExpired } from "./error.ts";
|
|
4
4
|
import { type Expire, expiring, isTransactionClosed } from "./expiry.ts";
|
|
5
5
|
import type { Transactional, TransactionOptions } from "./model.ts";
|
|
6
6
|
|
|
7
|
-
interface Settled<
|
|
7
|
+
interface Settled<TX, E> {
|
|
8
8
|
readonly closed?: TransactionExpired;
|
|
9
9
|
readonly committed: boolean;
|
|
10
|
-
readonly exit: Exit.Exit<
|
|
10
|
+
readonly exit: Exit.Exit<TX, E | TransactionExpired | PrismaError>;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
class BodyFailed {}
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
client:
|
|
15
|
+
function commit<TTx extends Transactional<TTx>, TX, E, R>(
|
|
16
|
+
client: TTx,
|
|
17
17
|
options: TransactionOptions | undefined,
|
|
18
18
|
savepoint: boolean,
|
|
19
|
-
body: (tx:
|
|
19
|
+
body: (tx: TTx) => Effect.Effect<TX, E, R>,
|
|
20
20
|
context: Context.Context<R>,
|
|
21
21
|
signal: AbortSignal,
|
|
22
|
-
): Promise<Settled<
|
|
22
|
+
): Promise<Settled<TX, E>> {
|
|
23
23
|
let started = false;
|
|
24
|
-
let failed: Exit.Exit<
|
|
25
|
-
|
|
24
|
+
let failed: Exit.Exit<TX, E> | undefined;
|
|
25
|
+
async function run(tx: TTx) {
|
|
26
26
|
started = true;
|
|
27
27
|
const exit = await Effect.runPromiseExitWith(context)(body(tx), { signal });
|
|
28
28
|
if (Exit.isSuccess(exit)) {
|
|
@@ -30,8 +30,8 @@ const commit = <Tx extends Transactional<Tx>, X, E, R>(
|
|
|
30
30
|
}
|
|
31
31
|
failed = exit;
|
|
32
32
|
throw new BodyFailed();
|
|
33
|
-
}
|
|
34
|
-
|
|
33
|
+
}
|
|
34
|
+
function rejected(cause: unknown): Settled<TX, E> {
|
|
35
35
|
if (failed !== undefined) {
|
|
36
36
|
return { committed: false, exit: failed };
|
|
37
37
|
}
|
|
@@ -40,36 +40,39 @@ const commit = <Tx extends Transactional<Tx>, X, E, R>(
|
|
|
40
40
|
}
|
|
41
41
|
const closed = new TransactionExpired({ cause, message: "The transaction was closed before it could finish" });
|
|
42
42
|
return { closed, committed: false, exit: Exit.fail(closed) };
|
|
43
|
-
}
|
|
43
|
+
}
|
|
44
44
|
return Promise.resolve(client.$transaction(run, options)).then(
|
|
45
|
-
(value): Settled<
|
|
45
|
+
(value): Settled<TX, E> => ({ committed: true, exit: Exit.succeed(value) }),
|
|
46
46
|
rejected,
|
|
47
47
|
);
|
|
48
|
-
}
|
|
48
|
+
}
|
|
49
49
|
|
|
50
|
-
export const runTransaction = <
|
|
51
|
-
readonly client:
|
|
52
|
-
readonly open: Effect.Effect<Frame, never,
|
|
53
|
-
readonly body: (tx:
|
|
50
|
+
export const runTransaction = <TTx extends Transactional<TTx>, TX, E, R, TOpenRequirements>(options: {
|
|
51
|
+
readonly client: TTx;
|
|
52
|
+
readonly open: Effect.Effect<Frame, never, TOpenRequirements>;
|
|
53
|
+
readonly body: (tx: TTx) => Effect.Effect<TX, E, R>;
|
|
54
54
|
readonly transaction: TransactionOptions | undefined;
|
|
55
55
|
readonly savepoint: boolean;
|
|
56
56
|
readonly deadline: number | undefined;
|
|
57
57
|
readonly Expiry: Context.Reference<Expire>;
|
|
58
|
-
}): Effect.Effect<
|
|
58
|
+
}): Effect.Effect<TX, E | TransactionExpired | PrismaError, R | TOpenRequirements> =>
|
|
59
59
|
Effect.uninterruptibleMask((restore) =>
|
|
60
60
|
Effect.gen(function* () {
|
|
61
61
|
const { client, savepoint, body, deadline, Expiry } = options;
|
|
62
62
|
const frame = yield* options.open;
|
|
63
63
|
const context = yield* Effect.context<R>();
|
|
64
64
|
const abort = new AbortController();
|
|
65
|
-
|
|
65
|
+
function guarded(tx: TTx) {
|
|
66
|
+
return expiring(frame.provide(body(tx)), deadline, Expiry);
|
|
67
|
+
}
|
|
66
68
|
const outcome = commit(client, options.transaction, savepoint, guarded, context, abort.signal);
|
|
67
69
|
const expire = yield* Expiry;
|
|
68
|
-
|
|
69
|
-
Effect.andThen(
|
|
70
|
+
function settle(settled: Settled<TX, E | TransactionExpired>) {
|
|
71
|
+
return Effect.andThen(
|
|
70
72
|
settled.closed === undefined ? Effect.void : expire(settled.closed.cause),
|
|
71
73
|
Effect.as(frame.settle(settled.committed ? "committed" : "rolledBack"), settled.exit),
|
|
72
74
|
);
|
|
75
|
+
}
|
|
73
76
|
const settled = yield* restore(Effect.promise(() => outcome)).pipe(
|
|
74
77
|
Effect.onInterrupt(() =>
|
|
75
78
|
Effect.andThen(
|
package/src/write.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Bivariant } from "@shivaedev/types";
|
|
1
|
+
import type { Bivariant } from "@shivaedev/types/bivariant.ts";
|
|
2
2
|
import { countOperations, type RowOperation, rowOperations } from "./model.ts";
|
|
3
3
|
|
|
4
4
|
export interface Write {
|
|
@@ -28,8 +28,8 @@ class MissingField {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
new Proxy(row, {
|
|
31
|
+
function guarded(row: object): object {
|
|
32
|
+
return new Proxy(row, {
|
|
33
33
|
get: (target, key, receiver) => {
|
|
34
34
|
if (typeof key === "string" && !(key in target)) {
|
|
35
35
|
throw new MissingField(key);
|
|
@@ -37,11 +37,15 @@ const guarded = (row: object): object =>
|
|
|
37
37
|
return Reflect.get(target, key, receiver);
|
|
38
38
|
},
|
|
39
39
|
});
|
|
40
|
+
}
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
function isRowOperation(operation: string): operation is RowOperation {
|
|
43
|
+
return rowOperations.has(operation);
|
|
44
|
+
}
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
(Array.isArray(result) ? result : [result]).filter((row): row is object => typeof row === "object" && row !== null);
|
|
46
|
+
function rowsOf(result: unknown): readonly object[] {
|
|
47
|
+
return (Array.isArray(result) ? result : [result]).filter((row): row is object => typeof row === "object" && row !== null);
|
|
48
|
+
}
|
|
45
49
|
|
|
46
50
|
const nothing: Interpreted<never> = { changes: [], unnamed: undefined };
|
|
47
51
|
|
package/dist/index.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export { makePrismaChanges, type PrismaChanges, type PrismaChangesOptions, type UnnamedObserver } from "./changes.ts";
|
|
2
|
-
export { type Coverage, type CoverageViolation, checkCoverage } from "./coverage.ts";
|
|
3
|
-
export { PrismaError, TransactionExpired } from "./error.ts";
|
|
4
|
-
export type { ChangeMap, CountOperation, ModelChanges, ModelName, ModelRow, RowOperation, Transactional, TransactionOptions, } from "./model.ts";
|
|
5
|
-
export { type RawQueryClient, type TableWrites, tablesOf, tableWrites, writtenTables } from "./tables.ts";
|
|
6
|
-
export type { UnnamedWrite, Write } from "./write.ts";
|
|
7
|
-
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,KAAK,aAAa,EAAE,KAAK,oBAAoB,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACtH,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,iBAAiB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACrF,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC7D,YAAY,EACX,SAAS,EACT,cAAc,EACd,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,YAAY,EACZ,aAAa,EACb,kBAAkB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC1G,YAAY,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
DELETED
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAuE,MAAM,cAAc,CAAC;AACtH,OAAO,EAAyC,aAAa,EAAE,MAAM,eAAe,CAAC;AACrF,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAW7D,OAAO,EAAyC,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
|
package/src/index.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export { makePrismaChanges, type PrismaChanges, type PrismaChangesOptions, type UnnamedObserver } from "./changes.ts";
|
|
2
|
-
export { type Coverage, type CoverageViolation, checkCoverage } from "./coverage.ts";
|
|
3
|
-
export { PrismaError, TransactionExpired } from "./error.ts";
|
|
4
|
-
export type {
|
|
5
|
-
ChangeMap,
|
|
6
|
-
CountOperation,
|
|
7
|
-
ModelChanges,
|
|
8
|
-
ModelName,
|
|
9
|
-
ModelRow,
|
|
10
|
-
RowOperation,
|
|
11
|
-
Transactional,
|
|
12
|
-
TransactionOptions,
|
|
13
|
-
} from "./model.ts";
|
|
14
|
-
export { type RawQueryClient, type TableWrites, tablesOf, tableWrites, writtenTables } from "./tables.ts";
|
|
15
|
-
export type { UnnamedWrite, Write } from "./write.ts";
|