effect-zero 0.3.0 → 0.3.2
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/README.md +7 -9
- package/dist/client-transaction.d.ts +1 -0
- package/dist/client-transaction.d.ts.map +1 -1
- package/dist/client-transaction.js +4 -0
- package/dist/client-transaction.js.map +1 -1
- package/dist/internal/query-result.d.ts +20 -0
- package/dist/internal/query-result.d.ts.map +1 -0
- package/dist/internal/query-result.js +5 -0
- package/dist/internal/query-result.js.map +1 -0
- package/dist/internal/server.d.ts +1 -0
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +12 -7
- package/dist/internal/server.js.map +1 -1
- package/dist/internal/server.test.d.ts +2 -0
- package/dist/internal/server.test.d.ts.map +1 -0
- package/dist/internal/server.test.js +21 -0
- package/dist/internal/server.test.js.map +1 -0
- package/dist/query.d.ts +5 -13
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +9 -13
- package/dist/query.js.map +1 -1
- package/dist/result.d.ts +71 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +43 -0
- package/dist/result.js.map +1 -0
- package/dist/server.d.ts +2 -2
- package/package.json +12 -3
- package/src/client-transaction.ts +6 -1
- package/src/internal/query-result.ts +33 -0
- package/src/internal/server.test.ts +37 -0
- package/src/internal/server.ts +19 -9
- package/src/query.ts +9 -14
- package/src/result.ts +212 -0
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ import * as Server from "effect-zero/server";
|
|
|
45
45
|
import * as Schema from "effect/Schema";
|
|
46
46
|
import * as Effect from "effect/Effect";
|
|
47
47
|
import postgres from "postgres";
|
|
48
|
-
import { clientTransaction } from "./client"; // see below
|
|
48
|
+
import { clientTransaction, clientMutators } from "./client"; // see below
|
|
49
49
|
import { mutatorSchema } from "./mutators";
|
|
50
50
|
|
|
51
51
|
// zero schema (define or use something like drizzle-zero)
|
|
@@ -89,7 +89,8 @@ export const serverMutators = Mutators.make(mutatorSchema, {
|
|
|
89
89
|
yield* Effect.log("after the transaction");
|
|
90
90
|
}),
|
|
91
91
|
toggle: Effect.fn(function* ({ id, done }) {
|
|
92
|
-
|
|
92
|
+
// You can also reuse client mutators on the server
|
|
93
|
+
yield* clientMutators.todo.toggle({ id, done }).pipe(serverTransaction.execute);
|
|
93
94
|
}),
|
|
94
95
|
},
|
|
95
96
|
});
|
|
@@ -121,18 +122,16 @@ import * as Effect from "effect/Effect";
|
|
|
121
122
|
import { schema } from "./schema"; // your schema
|
|
122
123
|
import { mutatorSchema } from "./mutators"; // see below
|
|
123
124
|
|
|
124
|
-
const clientTransaction = ClientTransaction.make("ClientTransaction", schema);
|
|
125
|
-
|
|
126
125
|
// The "client-side" transaction
|
|
127
|
-
|
|
126
|
+
const clientTransaction = ClientTransaction.make("ClientTransaction", schema);
|
|
128
127
|
|
|
129
128
|
export const clientMutators = Mutators.make(mutatorSchema, {
|
|
130
129
|
todo: {
|
|
131
130
|
create: Effect.fn(function* ({ id, title }) {
|
|
132
|
-
yield*
|
|
131
|
+
yield* clientTransaction.use((tx) => tx.mutate.TodoTable.insert({ id, title, createdAt: Date.now() }));
|
|
133
132
|
}),
|
|
134
133
|
toggle: Effect.fn(function* ({ id, done }) {
|
|
135
|
-
yield*
|
|
134
|
+
yield* clientTransaction.use((tx) => tx.mutate.TodoTable.update({ id, done }));
|
|
136
135
|
}),
|
|
137
136
|
},
|
|
138
137
|
});
|
|
@@ -259,8 +258,7 @@ const todoAtom = Atom.fn(Effect.fn(function* (id: string, get: Atom.FnContext) {
|
|
|
259
258
|
});
|
|
260
259
|
```
|
|
261
260
|
|
|
262
|
-
> [
|
|
263
|
-
> Queries created via `Query.make` implement the [`Equal` trait](https://effect.website/docs/trait/equal/), so `Atom.query` would properly cache the results when using queries as arguments.
|
|
261
|
+
> **Note:** Queries created via `Query.make` implement the [`Equal` trait](https://effect.website/docs/trait/equal/), so `Atom.family` would properly cache the results when using queries as arguments.
|
|
264
262
|
|
|
265
263
|
## Differences from the original implementation
|
|
266
264
|
|
|
@@ -17,6 +17,7 @@ declare const ClientTransactionError_base: new <A extends Record<string, any> =
|
|
|
17
17
|
declare class ClientTransactionError extends ClientTransactionError_base<{
|
|
18
18
|
readonly cause: Cause.Cause<unknown>;
|
|
19
19
|
}> {
|
|
20
|
+
get message(): string;
|
|
20
21
|
}
|
|
21
22
|
export {};
|
|
22
23
|
//# sourceMappingURL=client-transaction.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-transaction.d.ts","sourceRoot":"","sources":["../src/client-transaction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC3F,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAE1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,OAAO,MAAM,CAAC;CAC9B;AAED,eAAO,MAAM,IAAI,GAAI,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,OAAO,SAAS,UAAU,EAAE,IAAI,EAAE,EAAE,QAAQ,OAAO;UAGlF,CAAC,MACR,CAAC,WAAW,EAAE,eAAe,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;KAAE,KAAK,WAAW,CAAC,CAAC,CAAC;;CAU3G,CAAC;;;;AAEF,cAAM,sBAAuB,SAAQ,4BAA2C;IAC9E,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;CACtC,CAAC;
|
|
1
|
+
{"version":3,"file":"client-transaction.d.ts","sourceRoot":"","sources":["../src/client-transaction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC3F,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAE1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,OAAO,MAAM,CAAC;CAC9B;AAED,eAAO,MAAM,IAAI,GAAI,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,OAAO,SAAS,UAAU,EAAE,IAAI,EAAE,EAAE,QAAQ,OAAO;UAGlF,CAAC,MACR,CAAC,WAAW,EAAE,eAAe,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;KAAE,KAAK,WAAW,CAAC,CAAC,CAAC;;CAU3G,CAAC;;;;AAEF,cAAM,sBAAuB,SAAQ,4BAA2C;IAC9E,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;CACtC,CAAC;IACA,IAAa,OAAO,WAGnB;CACF"}
|
|
@@ -11,5 +11,9 @@ export const make = (id, schema) => {
|
|
|
11
11
|
return Object.assign(ClientTransaction, { use, schema });
|
|
12
12
|
};
|
|
13
13
|
class ClientTransactionError extends Data.TaggedError("ClientTransactionError") {
|
|
14
|
+
get message() {
|
|
15
|
+
const err = Cause.squash(this.cause);
|
|
16
|
+
return err instanceof Error ? err.message : "exception was not of type `Error`";
|
|
17
|
+
}
|
|
14
18
|
}
|
|
15
19
|
//# sourceMappingURL=client-transaction.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-transaction.js","sourceRoot":"","sources":["../src/client-transaction.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AACpC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAOxC,MAAM,CAAC,MAAM,IAAI,GAAG,CAAsD,EAAM,EAAE,MAAe,EAAE,EAAE;IACnG,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,EAA+C,CAAC;IAEzF,MAAM,GAAG,GAAG,CACV,EAAwG,EACxG,EAAE,CACF,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,WAAW,EAAE,EAAE,CAChD,MAAM,CAAC,UAAU,CAAC;QAChB,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,CAAC;QAC5C,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,sBAAsB,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;KAC3E,CAAC,CACH,CAAC;IAEJ,OAAO,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;AAC3D,CAAC,CAAC;AAEF,MAAM,sBAAuB,SAAQ,IAAI,CAAC,WAAW,CAAC,wBAAwB,CAE5E;
|
|
1
|
+
{"version":3,"file":"client-transaction.js","sourceRoot":"","sources":["../src/client-transaction.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AACpC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAOxC,MAAM,CAAC,MAAM,IAAI,GAAG,CAAsD,EAAM,EAAE,MAAe,EAAE,EAAE;IACnG,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,EAA+C,CAAC;IAEzF,MAAM,GAAG,GAAG,CACV,EAAwG,EACxG,EAAE,CACF,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,WAAW,EAAE,EAAE,CAChD,MAAM,CAAC,UAAU,CAAC;QAChB,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,CAAC;QAC5C,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,sBAAsB,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;KAC3E,CAAC,CACH,CAAC;IAEJ,OAAO,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;AAC3D,CAAC,CAAC;AAEF,MAAM,sBAAuB,SAAQ,IAAI,CAAC,WAAW,CAAC,wBAAwB,CAE5E;IACA,IAAa,OAAO;QAClB,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrC,OAAO,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,mCAAmC,CAAC;IAClF,CAAC;CACF"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { HumanReadable, QueryResult as ZeroQueryResult, QueryResultDetails as ZeroQueryResultDetails } from "@rocicorp/zero/react";
|
|
2
|
+
export type QueryResult<A> = QueryResult.Partial<A> | QueryResult.Complete<A> | QueryResult.Error;
|
|
3
|
+
export declare namespace QueryResult {
|
|
4
|
+
interface Complete<A> {
|
|
5
|
+
readonly _tag: "Complete";
|
|
6
|
+
readonly value: HumanReadable<A>;
|
|
7
|
+
}
|
|
8
|
+
interface Partial<A> {
|
|
9
|
+
readonly _tag: "Partial";
|
|
10
|
+
readonly value: HumanReadable<A>;
|
|
11
|
+
}
|
|
12
|
+
interface Error {
|
|
13
|
+
readonly _tag: "Error";
|
|
14
|
+
readonly details: Omit<Extract<ZeroQueryResultDetails, {
|
|
15
|
+
readonly type: "error";
|
|
16
|
+
}>, "type">;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export declare const make: <A>([value, details]: ZeroQueryResult<A>) => QueryResult<A>;
|
|
20
|
+
//# sourceMappingURL=query-result.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-result.d.ts","sourceRoot":"","sources":["../../src/internal/query-result.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,WAAW,IAAI,eAAe,EAC9B,kBAAkB,IAAI,sBAAsB,EAC7C,MAAM,sBAAsB,CAAC;AAK9B,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC;AAClG,yBAAiB,WAAW,CAAC;IAC3B,UAAiB,QAAQ,CAAC,CAAC;QACzB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;QAC1B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;KAClC;IACD,UAAiB,OAAO,CAAC,CAAC;QACxB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;QACzB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;KAClC;IACD,UAAiB,KAAK;QACpB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;QACvB,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,sBAAsB,EAAE;YAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;SAAE,CAAC,EAAE,MAAM,CAAC,CAAC;KAC7F;CACF;AAED,eAAO,MAAM,IAAI,GAAI,CAAC,EAAE,kBAAkB,eAAe,CAAC,CAAC,CAAC,KAAG,WAAW,CAAC,CAAC,CAO3E,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import * as Match from "effect/Match";
|
|
2
|
+
export const make = ([value, details]) => {
|
|
3
|
+
return Match.value(details).pipe(Match.when({ type: "complete" }, () => ({ _tag: "Complete", value })), Match.when({ type: "unknown" }, () => ({ _tag: "Partial", value })), Match.when({ type: "error" }, ({ type: _type, ...details }) => ({ _tag: "Error", details })), Match.exhaustive);
|
|
4
|
+
};
|
|
5
|
+
//# sourceMappingURL=query-result.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-result.js","sourceRoot":"","sources":["../../src/internal/query-result.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAoBtC,MAAM,CAAC,MAAM,IAAI,GAAG,CAAI,CAAC,KAAK,EAAE,OAAO,CAAqB,EAAkB,EAAE;IAC9E,OAAO,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAC9B,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,CAAU,CAAC,EAC9E,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAU,CAAC,EAC5E,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAU,CAAC,EACrG,KAAK,CAAC,UAAU,CACjB,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -18,6 +18,7 @@ declare const NoTransactionError_base: new <A extends Record<string, any> = {}>(
|
|
|
18
18
|
readonly _tag: "NoTransactionError";
|
|
19
19
|
} & Readonly<A>;
|
|
20
20
|
export declare class NoTransactionError extends NoTransactionError_base {
|
|
21
|
+
message: string;
|
|
21
22
|
}
|
|
22
23
|
declare const MultipleTransactionsError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
|
|
23
24
|
readonly _tag: "MultipleTransactionsError";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/internal/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAE1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAC;;AAG1D,qBAAa,sBAAuB,SAAQ,2BAGzC;CAAG;;;;;;AAEN,qBAAa,4BAA6B,SAAQ,iCASjD;IAEC,MAAM,CAAC,KAAK,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,uFAYnD;IAEJ,MAAM,CAAC,QAAQ,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/internal/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAE1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAC;;AAG1D,qBAAa,sBAAuB,SAAQ,2BAGzC;CAAG;;;;;;AAEN,qBAAa,4BAA6B,SAAQ,iCASjD;IAEC,MAAM,CAAC,KAAK,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,uFAYnD;IAEJ,MAAM,CAAC,QAAQ,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,uFAyBrD;CACN;;;;AAED,qBAAa,kBAAmB,SAAQ,uBAAsC;IAC5E,OAAO,SAAuE;CAC/E;;;;AACD,qBAAa,yBAA0B,SAAQ,8BAA6C;CAAG;AAE/F,QAAA,MAAM,6BAA6B,eAAkD,CAAC;;;;AACtF,qBAAa,uBAAwB,SAAQ,6BAA4C;IACvF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1C,CAAC;IACA,QAAQ,CAAC,CAAC,6BAA6B,CAAC,SAAiC;IACzE,IAAa,OAAO,WAEnB;IACD,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,uBAAuB;CAGpD;AAED,QAAA,MAAM,mCAAmC,eAAwD,CAAC;;;;AAClG,qBAAa,6BAA8B,SAAQ,mCAAkD;IACnG,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;CAClC,CAAC;IACA,QAAQ,CAAC,CAAC,mCAAmC,CAAC,SAAuC;IACrF,IAAa,OAAO,WAEnB;IACD,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,6BAA6B;CAG1D"}
|
package/dist/internal/server.js
CHANGED
|
@@ -22,22 +22,27 @@ export class ServerSynchronizationContext extends Effect.Service()(prefixId("Ser
|
|
|
22
22
|
return [result, true];
|
|
23
23
|
})));
|
|
24
24
|
static finalize = (effect) => Effect.gen(this, function* () {
|
|
25
|
-
const wasTransactionExecuted = yield* this.pipe(Effect.
|
|
25
|
+
const wasTransactionExecuted = yield* this.pipe(Effect.map((ctx) => ctx.wasTransactionExecuted));
|
|
26
26
|
return yield* effect.pipe(
|
|
27
27
|
// Case #2 "One transaction then fail"
|
|
28
28
|
// If the transaction was executed successfully, swallow the error and just log it, otherwise re-throw it
|
|
29
|
-
Effect.catchAllCause((e)
|
|
30
|
-
if (wasTransactionExecuted) {
|
|
31
|
-
return Effect.logError("Error occurred after transaction execution completed", e);
|
|
29
|
+
Effect.catchAllCause(Effect.fn(function* (e) {
|
|
30
|
+
if (yield* wasTransactionExecuted) {
|
|
31
|
+
return yield* Effect.logError("Error occurred after transaction execution completed", e);
|
|
32
32
|
}
|
|
33
|
-
return Effect.failCause(e);
|
|
34
|
-
}),
|
|
33
|
+
return yield* Effect.failCause(e);
|
|
34
|
+
})),
|
|
35
35
|
// Case #4 "Zero transactions then succeed"
|
|
36
36
|
// Check that the transaction was executed during the mutation
|
|
37
|
-
Effect.tap(
|
|
37
|
+
Effect.tap(Effect.gen(function* () {
|
|
38
|
+
if (!(yield* wasTransactionExecuted)) {
|
|
39
|
+
return yield* new NoTransactionError();
|
|
40
|
+
}
|
|
41
|
+
})));
|
|
38
42
|
});
|
|
39
43
|
}
|
|
40
44
|
export class NoTransactionError extends Data.TaggedError("NoTransactionError") {
|
|
45
|
+
message = "No transaction detected in a mutation, a transaction is required.";
|
|
41
46
|
}
|
|
42
47
|
export class MultipleTransactionsError extends Data.TaggedError("MultipleTransactionsError") {
|
|
43
48
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/internal/server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AACpC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,SAAS,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,MAAM,OAAO,sBAAuB,SAAQ,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,wBAAwB,CAAC,CAAC,EAGxF;CAAG;AAEN,MAAM,OAAO,4BAA6B,SAAQ,MAAM,CAAC,OAAO,EAAgC,CAC9F,QAAQ,CAAC,8BAA8B,CAAC,EACxC;IACE,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC1B,OAAO;YACL,sBAAsB,EAAE,KAAK,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC;SAC3D,CAAC;IACJ,CAAC,CAAC;CACH,CACF;IACC,uHAAuH;IACvH,MAAM,CAAC,KAAK,GAAG,CAAU,MAA8B,EAAE,EAAE,CACzD,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAC3B,eAAe,CAAC,YAAY,CAC1B,GAAG,CAAC,sBAAsB,EAC1B,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,sBAAsB;QACzC,IAAI,sBAAsB,EAAE,CAAC;YAC3B,OAAO,KAAK,CAAC,CAAC,IAAI,yBAAyB,EAAE,CAAC;QAChD,CAAC;QACD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC;QAC7B,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACxB,CAAC,CAAC,CACH,CACF,CAAC;IAEJ,MAAM,CAAC,QAAQ,GAAG,CAAU,MAA8B,EAAE,EAAE,CAC5D,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC;QACxB,MAAM,sBAAsB,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/internal/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.test.d.ts","sourceRoot":"","sources":["../../src/internal/server.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { describe, it } from "@effect/vitest";
|
|
2
|
+
import * as Cause from "effect/Cause";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Exit from "effect/Exit";
|
|
5
|
+
import { NoTransactionError, ServerSynchronizationContext } from "./server.js";
|
|
6
|
+
describe("ServerSynchronizationContext", () => {
|
|
7
|
+
it.effect("finalize should return NoTransactionError when called without guard", Effect.fn(function* ({ expect }) {
|
|
8
|
+
const result = yield* Effect.void.pipe(ServerSynchronizationContext.finalize, Effect.provide(ServerSynchronizationContext.Default), Effect.exit);
|
|
9
|
+
Exit.match(result, {
|
|
10
|
+
onSuccess: () => expect.fail("Expected failure"),
|
|
11
|
+
onFailure: (cause) => expect(cause).toStrictEqual(Cause.fail(new NoTransactionError())),
|
|
12
|
+
});
|
|
13
|
+
}));
|
|
14
|
+
it.effect("finalize shouldn't return errors when called with guard", Effect.fn(function* ({ expect }) {
|
|
15
|
+
const result = yield* Effect.void.pipe(ServerSynchronizationContext.guard, ServerSynchronizationContext.finalize, Effect.provide(ServerSynchronizationContext.Default), Effect.exit);
|
|
16
|
+
if (Exit.isFailure(result)) {
|
|
17
|
+
expect.fail("Expected success", result.cause);
|
|
18
|
+
}
|
|
19
|
+
}));
|
|
20
|
+
});
|
|
21
|
+
//# sourceMappingURL=server.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.test.js","sourceRoot":"","sources":["../../src/internal/server.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,kBAAkB,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAC;AAE/E,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;IAC5C,EAAE,CAAC,MAAM,CACP,qEAAqE,EACrE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE;QAC7B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CACpC,4BAA4B,CAAC,QAAQ,EACrC,MAAM,CAAC,OAAO,CAAC,4BAA4B,CAAC,OAAO,CAAC,EACpD,MAAM,CAAC,IAAI,CACZ,CAAC;QACF,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;YACjB,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC;YAChD,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,kBAAkB,EAAE,CAAC,CAAC;SACxF,CAAC,CAAC;IACL,CAAC,CAAC,CACH,CAAC;IAEF,EAAE,CAAC,MAAM,CACP,yDAAyD,EACzD,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE;QAC7B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CACpC,4BAA4B,CAAC,KAAK,EAClC,4BAA4B,CAAC,QAAQ,EACrC,MAAM,CAAC,OAAO,CAAC,4BAA4B,CAAC,OAAO,CAAC,EACpD,MAAM,CAAC,IAAI,CACZ,CAAC;QACF,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QAChD,CAAC;IACH,CAAC,CAAC,CACH,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
package/dist/query.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ReadonlyJSONValue, Zero, Query as ZeroQuery, Schema as ZeroSchema } from "@rocicorp/zero";
|
|
2
2
|
import * as Effect from "effect/Effect";
|
|
3
3
|
import * as Equal from "effect/Equal";
|
|
4
4
|
import * as Schema from "effect/Schema";
|
|
5
5
|
import * as Stream from "effect/Stream";
|
|
6
6
|
import * as Subscribable from "effect/Subscribable";
|
|
7
|
+
import * as QueryResult from "./internal/query-result.js";
|
|
7
8
|
export type Query<S extends ZeroSchema, T extends keyof S["tables"] & string, R> = ZeroQuery<S, T, R> & Equal.Equal;
|
|
8
9
|
type QueryArgs<A extends ReadonlyJSONValue[], B extends unknown[]> = {
|
|
9
10
|
_tag: "Encoded";
|
|
@@ -23,17 +24,8 @@ export declare const make: <N extends string, A extends ReadonlyJSONValue[], B e
|
|
|
23
24
|
[RunQuerySymbol]: (args: QueryArgs<A, B>) => Effect.Effect<Query<S, T, R>, import("effect/ParseResult").ParseError | E, R1 | R2>;
|
|
24
25
|
};
|
|
25
26
|
export type MakeQueryResult<E = any, R1 = any, R2 = any> = ReturnType<typeof make<string, any, any, ZeroSchema, string, Record<string, any> | undefined, E, R1, R2>>;
|
|
26
|
-
export declare const stream: <S extends ZeroSchema, T extends keyof S["tables"] & string, R>(zero: Zero<S>, query: ZeroQuery<S, T, R> | Query<S, T, R>) => Stream.Stream<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}, never, never>;
|
|
30
|
-
export declare const initialValue: <S extends ZeroSchema, T extends keyof S["tables"] & string, R>(query: ZeroQuery<S, T, R> | Query<S, T, R>) => {
|
|
31
|
-
data: HumanReadable<R>;
|
|
32
|
-
status: "error" | "unknown" | "complete";
|
|
33
|
-
};
|
|
34
|
-
export declare const subscribable: <S extends ZeroSchema, T extends keyof S["tables"] & string, R>(zero: Zero<S>, query: ZeroQuery<S, T, R> | Query<S, T, R>) => Subscribable.Subscribable<{
|
|
35
|
-
data: HumanReadable<R>;
|
|
36
|
-
status: "error" | "unknown" | "complete";
|
|
37
|
-
}, never, never>;
|
|
27
|
+
export declare const stream: <S extends ZeroSchema, T extends keyof S["tables"] & string, R>(zero: Zero<S>, query: ZeroQuery<S, T, R> | Query<S, T, R>) => Stream.Stream<QueryResult.QueryResult<R>, never, never>;
|
|
28
|
+
export declare const initialValue: <S extends ZeroSchema, T extends keyof S["tables"] & string, R>(query: ZeroQuery<S, T, R> | Query<S, T, R>) => QueryResult.QueryResult<R>;
|
|
29
|
+
export declare const subscribable: <S extends ZeroSchema, T extends keyof S["tables"] & string, R>(zero: Zero<S>, query: ZeroQuery<S, T, R> | Query<S, T, R>) => Subscribable.Subscribable<QueryResult.QueryResult<R>, never, never>;
|
|
38
30
|
export {};
|
|
39
31
|
//# sourceMappingURL=query.d.ts.map
|
package/dist/query.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
|
package/dist/query.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect";
|
|
2
2
|
import * as Equal from "effect/Equal";
|
|
3
|
-
import * as Fn from "effect/Function";
|
|
4
3
|
import * as Hash from "effect/Hash";
|
|
5
4
|
import * as Match from "effect/Match";
|
|
6
5
|
import * as Schema from "effect/Schema";
|
|
7
6
|
import * as Stream from "effect/Stream";
|
|
8
7
|
import * as Subscribable from "effect/Subscribable";
|
|
8
|
+
import * as QueryResult from "./internal/query-result.js";
|
|
9
9
|
import { prefixId } from "./internal/utils.js";
|
|
10
10
|
import { deepClone, getDefaultSnapshot, getSnapshot } from "./snapshot.js";
|
|
11
11
|
export const RunQuerySymbol = Symbol.for(prefixId("RunQuery"));
|
|
@@ -49,17 +49,13 @@ query) => Effect.gen(function* () {
|
|
|
49
49
|
// TODO: Look into why cast needs to be applied to whole ternary here, unlike source.
|
|
50
50
|
const cloned = (data === undefined ? data : deepClone(data));
|
|
51
51
|
return getSnapshot(query.format.singular, cloned, resultType);
|
|
52
|
-
})), Stream.map(
|
|
53
|
-
data,
|
|
54
|
-
status,
|
|
55
|
-
})));
|
|
52
|
+
})), Stream.map(QueryResult.make));
|
|
56
53
|
}).pipe(Stream.unwrapScoped);
|
|
57
|
-
export const initialValue = (query) =>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}))
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
});
|
|
54
|
+
export const initialValue = (query) => QueryResult.make(getDefaultSnapshot(query.format.singular));
|
|
55
|
+
export const subscribable = (zero, query) => {
|
|
56
|
+
return Subscribable.make({
|
|
57
|
+
get: Effect.promise(() => zero.run(query, { type: "unknown" })).pipe(Effect.map((value) => ({ _tag: "Partial", value }))),
|
|
58
|
+
changes: stream(zero, query),
|
|
59
|
+
});
|
|
60
|
+
};
|
|
65
61
|
//# sourceMappingURL=query.js.map
|
package/dist/query.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
|
package/dist/result.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Result as AtomResult } from "@effect-atom/atom";
|
|
2
|
+
import type { HumanReadable } from "@rocicorp/zero/react";
|
|
3
|
+
import * as Cause from "effect/Cause";
|
|
4
|
+
import type { QueryResult } from "./internal/query-result.js";
|
|
5
|
+
export type Result<A, E = never> = AtomResult.Result<QueryResult.Partial<A> | QueryResult.Complete<A>, E | QueryError>;
|
|
6
|
+
export declare namespace Result {
|
|
7
|
+
type Initial<A, E = never> = AtomResult.Initial<QueryResult.Partial<A> | QueryResult.Complete<A>, E | QueryError>;
|
|
8
|
+
type Failure<A, E = never> = AtomResult.Failure<QueryResult.Partial<A> | QueryResult.Complete<A>, E | QueryError>;
|
|
9
|
+
type Success<A, E = never> = AtomResult.Success<QueryResult.Partial<A> | QueryResult.Complete<A>, E | QueryError>;
|
|
10
|
+
type Partial<A, E = never> = AtomResult.Success<QueryResult.Partial<A>, E | QueryError>;
|
|
11
|
+
type Complete<A, E = never> = AtomResult.Success<QueryResult.Complete<A>, E | QueryError>;
|
|
12
|
+
}
|
|
13
|
+
declare const QueryError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => Cause.YieldableError & {
|
|
14
|
+
readonly _tag: "QueryError";
|
|
15
|
+
} & Readonly<A>;
|
|
16
|
+
export declare class QueryError extends QueryError_base<{
|
|
17
|
+
readonly details: QueryResult.Error["details"];
|
|
18
|
+
}> {
|
|
19
|
+
}
|
|
20
|
+
export declare const make: <A, E>(value: AtomResult.Result<QueryResult<A>, E>) => Result<A, E>;
|
|
21
|
+
export declare const match: {
|
|
22
|
+
<A, E, W, X, Y, Z>(options: {
|
|
23
|
+
readonly onInitial: (_: Result.Initial<A, E>) => W;
|
|
24
|
+
readonly onFailure: (_: Result.Failure<A, E>) => X;
|
|
25
|
+
readonly onPartial: (_: QueryResult.Partial<A>) => Y;
|
|
26
|
+
readonly onComplete: (_: QueryResult.Complete<A>) => Z;
|
|
27
|
+
}): (self: Result<A, E>) => W | X | Y | Z;
|
|
28
|
+
<A, E, W, X, Y, Z>(self: Result<A, E>, options: {
|
|
29
|
+
readonly onInitial: (_: Result.Initial<A, E>) => W;
|
|
30
|
+
readonly onFailure: (_: Result.Failure<A, E>) => X;
|
|
31
|
+
readonly onPartial: (_: QueryResult.Partial<A>) => Y;
|
|
32
|
+
readonly onComplete: (_: QueryResult.Complete<A>) => Z;
|
|
33
|
+
}): W | X | Y | Z;
|
|
34
|
+
};
|
|
35
|
+
export declare const mapSuccess: {
|
|
36
|
+
<A, B>(f: (value: A) => B): <E = never>(self: AtomResult.Success<A, E>) => AtomResult.Success<B, E>;
|
|
37
|
+
<A, B, E = never>(self: AtomResult.Success<A, E>, f: (value: A) => B): AtomResult.Success<B, E>;
|
|
38
|
+
};
|
|
39
|
+
export declare const acceptPartialWith: {
|
|
40
|
+
<A, E = never>(self: Result<A, E>, predicate: (value: HumanReadable<A>) => boolean): AtomResult.Initial<HumanReadable<A>, E> | AtomResult.Success<HumanReadable<A>, QueryError | E> | AtomResult.Failure<HumanReadable<A>, QueryError | E>;
|
|
41
|
+
<A, E = never>(self: Result<A, E>): (predicate: (value: HumanReadable<A>) => boolean) => AtomResult.Initial<HumanReadable<A>, E> | AtomResult.Success<HumanReadable<A>, QueryError | E> | AtomResult.Failure<HumanReadable<A>, QueryError | E>;
|
|
42
|
+
};
|
|
43
|
+
export type AcceptPartialMode = "waitForServer" | "acceptCached";
|
|
44
|
+
export declare const acceptPartialMode: {
|
|
45
|
+
<A, E>(self: Result<A, E>,
|
|
46
|
+
/**
|
|
47
|
+
* - `acceptCached` -> Will resolve to the partial result if it is available
|
|
48
|
+
* - For singular queries, it will resolve if the cached value exists.
|
|
49
|
+
* - For list queries, it will resolve to the cached value regardless of the length of the list.
|
|
50
|
+
* - It will update the returned value as additional rows come in, and then optionally settle on a final value.
|
|
51
|
+
*
|
|
52
|
+
* - `waitForServer` -> Will wait for the complete result (from the server) before resolving
|
|
53
|
+
* - When you want to wait for the server before doing something.
|
|
54
|
+
* - Only used for queries in which you need an fully accurate result before doing something.
|
|
55
|
+
**/
|
|
56
|
+
mode: AcceptPartialMode): AtomResult.Initial<HumanReadable<A>, E> | AtomResult.Success<HumanReadable<A>, QueryError | E> | AtomResult.Failure<HumanReadable<A>, QueryError | E>;
|
|
57
|
+
<A, E>(
|
|
58
|
+
/**
|
|
59
|
+
* - `acceptCached` -> Will resolve to the partial result if it is available
|
|
60
|
+
* - For singular queries, it will resolve if the cached value exists.
|
|
61
|
+
* - For list queries, it will resolve to the cached value regardless of the length of the list.
|
|
62
|
+
* - It will update the returned value as additional rows come in, and then optionally settle on a final value.
|
|
63
|
+
*
|
|
64
|
+
* - `waitForServer` -> Will wait for the complete result (from the server) before resolving
|
|
65
|
+
* - When you want to wait for the server before doing something.
|
|
66
|
+
* - Only used for queries in which you need an fully accurate result before doing something.
|
|
67
|
+
**/
|
|
68
|
+
mode: AcceptPartialMode): (self: Result<A, E>) => AtomResult.Initial<HumanReadable<A>, E> | AtomResult.Success<HumanReadable<A>, QueryError | E> | AtomResult.Failure<HumanReadable<A>, QueryError | E>;
|
|
69
|
+
};
|
|
70
|
+
export {};
|
|
71
|
+
//# sourceMappingURL=result.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"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"}
|
package/dist/result.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Result as AtomResult } from "@effect-atom/atom";
|
|
2
|
+
import * as Cause from "effect/Cause";
|
|
3
|
+
import * as Data from "effect/Data";
|
|
4
|
+
import * as Fn from "effect/Function";
|
|
5
|
+
import * as Match from "effect/Match";
|
|
6
|
+
import * as Option from "effect/Option";
|
|
7
|
+
import * as Predicate from "effect/Predicate";
|
|
8
|
+
import * as Struct from "effect/Struct";
|
|
9
|
+
export class QueryError extends Data.TaggedError("QueryError") {
|
|
10
|
+
}
|
|
11
|
+
export const make = (value) => Match.valueTags(value, {
|
|
12
|
+
Initial: () => AtomResult.initial(),
|
|
13
|
+
Success: ({ value }) => Match.valueTags(value, {
|
|
14
|
+
Partial: (value) => AtomResult.success(value),
|
|
15
|
+
Complete: (value) => AtomResult.success(value),
|
|
16
|
+
Error: (e) => AtomResult.failure(Cause.fail(new QueryError(e))),
|
|
17
|
+
}),
|
|
18
|
+
Failure: (e) => AtomResult.failure(e.cause),
|
|
19
|
+
});
|
|
20
|
+
// based on: https://github.com/tim-smart/effect-atom/blob/04c15cacda42dd230782f52c0e978400793a502c/packages/atom/src/Result.ts#L424
|
|
21
|
+
export const match = Fn.dual(2, (self, options) => Match.valueTags(self, {
|
|
22
|
+
Initial: (initial) => options.onInitial(initial),
|
|
23
|
+
Failure: (failure) => options.onFailure(failure),
|
|
24
|
+
Success: (success) => Match.valueTags(success.value, {
|
|
25
|
+
Partial: (partial) => options.onPartial(partial),
|
|
26
|
+
Complete: (complete) => options.onComplete(complete),
|
|
27
|
+
}),
|
|
28
|
+
}));
|
|
29
|
+
export const mapSuccess = Fn.dual(2, (self, f) => AtomResult.map(self, f));
|
|
30
|
+
export const acceptPartialWith = Fn.dual(2, (self, predicate) => AtomResult.match(self, {
|
|
31
|
+
onSuccess: (success) => Predicate.isTagged(success.value, "Partial") && !predicate(success.value.value)
|
|
32
|
+
? AtomResult.initial(success.waiting)
|
|
33
|
+
: mapSuccess(success, Struct.get("value")),
|
|
34
|
+
onFailure: (failure) => AtomResult.failure(failure.cause, {
|
|
35
|
+
waiting: failure.waiting,
|
|
36
|
+
previousSuccess: failure.previousSuccess.pipe(Option.flatMap((success) => Predicate.isTagged(success.value, "Partial") && !predicate(success.value.value)
|
|
37
|
+
? Option.none()
|
|
38
|
+
: Option.some(mapSuccess(success, Struct.get("value"))))),
|
|
39
|
+
}),
|
|
40
|
+
onInitial: (initial) => AtomResult.initial(initial.waiting),
|
|
41
|
+
}));
|
|
42
|
+
export const acceptPartialMode = Fn.dual(2, (self, mode) => Match.value(mode).pipe(Match.when("waitForServer", () => acceptPartialWith(self, () => false)), Match.when("acceptCached", () => acceptPartialWith(self, (v) => v !== undefined)), Match.exhaustive));
|
|
43
|
+
//# sourceMappingURL=result.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.js","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
CHANGED
|
@@ -48,15 +48,15 @@ export declare const processPush: <TSchema extends ZeroSchema, TTransaction, TMu
|
|
|
48
48
|
readonly tableName: string;
|
|
49
49
|
})[];
|
|
50
50
|
}];
|
|
51
|
-
readonly clientID: string;
|
|
52
51
|
readonly timestamp: number;
|
|
52
|
+
readonly clientID: string;
|
|
53
53
|
} | {
|
|
54
54
|
readonly name: string;
|
|
55
55
|
readonly type: "custom";
|
|
56
56
|
readonly id: number;
|
|
57
57
|
readonly args: readonly unknown[];
|
|
58
|
-
readonly clientID: string;
|
|
59
58
|
readonly timestamp: number;
|
|
59
|
+
readonly clientID: string;
|
|
60
60
|
})[];
|
|
61
61
|
readonly pushVersion: number;
|
|
62
62
|
readonly schemaVersion?: number | undefined;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "effect-zero",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.2",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"url": "https://github.com/realms-labs/effect-zero"
|
|
@@ -11,12 +11,14 @@
|
|
|
11
11
|
"lint": "biome check --error-on-warnings",
|
|
12
12
|
"lint:fix": "biome check --write --unsafe",
|
|
13
13
|
"build": "bun scripts/build.ts",
|
|
14
|
-
"test": "
|
|
14
|
+
"test": "bun vitest run src"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"@arethetypeswrong/cli": "^0.18.2",
|
|
18
18
|
"@biomejs/biome": "^2.2.6",
|
|
19
|
+
"@effect-atom/atom": "^0.4.3",
|
|
19
20
|
"@effect/language-service": "^0.55.2",
|
|
21
|
+
"@effect/vitest": "^0.27.0",
|
|
20
22
|
"@rocicorp/zero": "^0.24.3000000000",
|
|
21
23
|
"@types/bun": "latest",
|
|
22
24
|
"@types/fs-extra": "^11.0.4",
|
|
@@ -28,7 +30,8 @@
|
|
|
28
30
|
},
|
|
29
31
|
"peerDependencies": {
|
|
30
32
|
"@rocicorp/zero": "*",
|
|
31
|
-
"effect": "3"
|
|
33
|
+
"effect": "3",
|
|
34
|
+
"@effect-atom/atom": "*"
|
|
32
35
|
},
|
|
33
36
|
"exports": {
|
|
34
37
|
"./server": {
|
|
@@ -37,6 +40,12 @@
|
|
|
37
40
|
"default": "./dist/server.js"
|
|
38
41
|
}
|
|
39
42
|
},
|
|
43
|
+
"./result": {
|
|
44
|
+
"import": {
|
|
45
|
+
"types": "./dist/result.d.ts",
|
|
46
|
+
"default": "./dist/result.js"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
40
49
|
"./server-transaction": {
|
|
41
50
|
"import": {
|
|
42
51
|
"types": "./dist/server-transaction.d.ts",
|
|
@@ -27,4 +27,9 @@ export const make = <const Id extends string, TSchema extends ZeroSchema>(id: Id
|
|
|
27
27
|
|
|
28
28
|
class ClientTransactionError extends Data.TaggedError("ClientTransactionError")<{
|
|
29
29
|
readonly cause: Cause.Cause<unknown>;
|
|
30
|
-
}> {
|
|
30
|
+
}> {
|
|
31
|
+
override get message() {
|
|
32
|
+
const err = Cause.squash(this.cause);
|
|
33
|
+
return err instanceof Error ? err.message : "exception was not of type `Error`";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
HumanReadable,
|
|
3
|
+
QueryResult as ZeroQueryResult,
|
|
4
|
+
QueryResultDetails as ZeroQueryResultDetails,
|
|
5
|
+
} from "@rocicorp/zero/react";
|
|
6
|
+
import * as Match from "effect/Match";
|
|
7
|
+
|
|
8
|
+
// Our internal representation of Zero's QueryResult type.
|
|
9
|
+
|
|
10
|
+
export type QueryResult<A> = QueryResult.Partial<A> | QueryResult.Complete<A> | QueryResult.Error;
|
|
11
|
+
export namespace QueryResult {
|
|
12
|
+
export interface Complete<A> {
|
|
13
|
+
readonly _tag: "Complete";
|
|
14
|
+
readonly value: HumanReadable<A>;
|
|
15
|
+
}
|
|
16
|
+
export interface Partial<A> {
|
|
17
|
+
readonly _tag: "Partial";
|
|
18
|
+
readonly value: HumanReadable<A>;
|
|
19
|
+
}
|
|
20
|
+
export interface Error {
|
|
21
|
+
readonly _tag: "Error";
|
|
22
|
+
readonly details: Omit<Extract<ZeroQueryResultDetails, { readonly type: "error" }>, "type">;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const make = <A>([value, details]: ZeroQueryResult<A>): QueryResult<A> => {
|
|
27
|
+
return Match.value(details).pipe(
|
|
28
|
+
Match.when({ type: "complete" }, () => ({ _tag: "Complete", value }) as const),
|
|
29
|
+
Match.when({ type: "unknown" }, () => ({ _tag: "Partial", value }) as const),
|
|
30
|
+
Match.when({ type: "error" }, ({ type: _type, ...details }) => ({ _tag: "Error", details }) as const),
|
|
31
|
+
Match.exhaustive,
|
|
32
|
+
);
|
|
33
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, it } from "@effect/vitest";
|
|
2
|
+
import * as Cause from "effect/Cause";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Exit from "effect/Exit";
|
|
5
|
+
import { NoTransactionError, ServerSynchronizationContext } from "./server.js";
|
|
6
|
+
|
|
7
|
+
describe("ServerSynchronizationContext", () => {
|
|
8
|
+
it.effect(
|
|
9
|
+
"finalize should return NoTransactionError when called without guard",
|
|
10
|
+
Effect.fn(function* ({ expect }) {
|
|
11
|
+
const result = yield* Effect.void.pipe(
|
|
12
|
+
ServerSynchronizationContext.finalize,
|
|
13
|
+
Effect.provide(ServerSynchronizationContext.Default),
|
|
14
|
+
Effect.exit,
|
|
15
|
+
);
|
|
16
|
+
Exit.match(result, {
|
|
17
|
+
onSuccess: () => expect.fail("Expected failure"),
|
|
18
|
+
onFailure: (cause) => expect(cause).toStrictEqual(Cause.fail(new NoTransactionError())),
|
|
19
|
+
});
|
|
20
|
+
}),
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
it.effect(
|
|
24
|
+
"finalize shouldn't return errors when called with guard",
|
|
25
|
+
Effect.fn(function* ({ expect }) {
|
|
26
|
+
const result = yield* Effect.void.pipe(
|
|
27
|
+
ServerSynchronizationContext.guard,
|
|
28
|
+
ServerSynchronizationContext.finalize,
|
|
29
|
+
Effect.provide(ServerSynchronizationContext.Default),
|
|
30
|
+
Effect.exit,
|
|
31
|
+
);
|
|
32
|
+
if (Exit.isFailure(result)) {
|
|
33
|
+
expect.fail("Expected success", result.cause);
|
|
34
|
+
}
|
|
35
|
+
}),
|
|
36
|
+
);
|
|
37
|
+
});
|
package/src/internal/server.ts
CHANGED
|
@@ -38,25 +38,35 @@ export class ServerSynchronizationContext extends Effect.Service<ServerSynchroni
|
|
|
38
38
|
|
|
39
39
|
static finalize = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
|
|
40
40
|
Effect.gen(this, function* () {
|
|
41
|
-
const wasTransactionExecuted = yield* this.pipe(Effect.
|
|
41
|
+
const wasTransactionExecuted = yield* this.pipe(Effect.map((ctx) => ctx.wasTransactionExecuted));
|
|
42
42
|
|
|
43
43
|
return yield* effect.pipe(
|
|
44
44
|
// Case #2 "One transaction then fail"
|
|
45
45
|
// If the transaction was executed successfully, swallow the error and just log it, otherwise re-throw it
|
|
46
|
-
Effect.catchAllCause(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
Effect.catchAllCause(
|
|
47
|
+
Effect.fn(function* (e) {
|
|
48
|
+
if (yield* wasTransactionExecuted) {
|
|
49
|
+
return yield* Effect.logError("Error occurred after transaction execution completed", e);
|
|
50
|
+
}
|
|
51
|
+
return yield* Effect.failCause(e);
|
|
52
|
+
}),
|
|
53
|
+
),
|
|
52
54
|
// Case #4 "Zero transactions then succeed"
|
|
53
55
|
// Check that the transaction was executed during the mutation
|
|
54
|
-
Effect.tap(
|
|
56
|
+
Effect.tap(
|
|
57
|
+
Effect.gen(function* () {
|
|
58
|
+
if (!(yield* wasTransactionExecuted)) {
|
|
59
|
+
return yield* new NoTransactionError();
|
|
60
|
+
}
|
|
61
|
+
}),
|
|
62
|
+
),
|
|
55
63
|
);
|
|
56
64
|
});
|
|
57
65
|
}
|
|
58
66
|
|
|
59
|
-
export class NoTransactionError extends Data.TaggedError("NoTransactionError") {
|
|
67
|
+
export class NoTransactionError extends Data.TaggedError("NoTransactionError") {
|
|
68
|
+
message = "No transaction detected in a mutation, a transaction is required.";
|
|
69
|
+
}
|
|
60
70
|
export class MultipleTransactionsError extends Data.TaggedError("MultipleTransactionsError") {}
|
|
61
71
|
|
|
62
72
|
const OutOfOrderMutationErrorTypeId = Symbol.for(prefixId("OutOfOrderMutationError"));
|
package/src/query.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import type { HumanReadable, ReadonlyJSONValue, Zero, Query as ZeroQuery, Schema as ZeroSchema } from "@rocicorp/zero";
|
|
2
|
-
import type { QueryResult as ZeroQueryResult } from "@rocicorp/zero/react";
|
|
3
2
|
import * as Effect from "effect/Effect";
|
|
4
3
|
import * as Equal from "effect/Equal";
|
|
5
|
-
import * as Fn from "effect/Function";
|
|
6
4
|
import * as Hash from "effect/Hash";
|
|
7
5
|
import * as Match from "effect/Match";
|
|
8
6
|
import * as Schema from "effect/Schema";
|
|
9
7
|
import * as Stream from "effect/Stream";
|
|
10
8
|
import * as Subscribable from "effect/Subscribable";
|
|
9
|
+
import * as QueryResult from "./internal/query-result.js";
|
|
11
10
|
import { prefixId } from "./internal/utils.js";
|
|
12
11
|
import { deepClone, getDefaultSnapshot, getSnapshot } from "./snapshot.js";
|
|
13
12
|
|
|
@@ -107,26 +106,22 @@ export const stream = <S extends ZeroSchema, T extends keyof S["tables"] & strin
|
|
|
107
106
|
return getSnapshot<R>(query.format.singular, cloned, resultType);
|
|
108
107
|
}),
|
|
109
108
|
),
|
|
110
|
-
Stream.map(
|
|
111
|
-
data,
|
|
112
|
-
status,
|
|
113
|
-
})),
|
|
109
|
+
Stream.map(QueryResult.make),
|
|
114
110
|
);
|
|
115
111
|
}).pipe(Stream.unwrapScoped);
|
|
116
112
|
|
|
117
113
|
export const initialValue = <S extends ZeroSchema, T extends keyof S["tables"] & string, R>(
|
|
118
114
|
query: ZeroQuery<S, T, R> | Query<S, T, R>,
|
|
119
|
-
) =>
|
|
120
|
-
Fn.pipe(getDefaultSnapshot(query.format.singular) as ZeroQueryResult<R>, ([data, { type: status }]) => ({
|
|
121
|
-
data,
|
|
122
|
-
status,
|
|
123
|
-
}));
|
|
115
|
+
) => QueryResult.make<R>(getDefaultSnapshot(query.format.singular));
|
|
124
116
|
|
|
125
117
|
export const subscribable = <S extends ZeroSchema, T extends keyof S["tables"] & string, R>(
|
|
126
118
|
zero: Zero<S>,
|
|
127
119
|
query: ZeroQuery<S, T, R> | Query<S, T, R>,
|
|
128
|
-
) =>
|
|
129
|
-
Subscribable.make({
|
|
130
|
-
get: Effect.
|
|
120
|
+
) => {
|
|
121
|
+
return Subscribable.make({
|
|
122
|
+
get: Effect.promise(() => zero.run(query as ZeroQuery<S, T, R>, { type: "unknown" })).pipe(
|
|
123
|
+
Effect.map((value) => ({ _tag: "Partial", value }) satisfies QueryResult.QueryResult.Partial<R>),
|
|
124
|
+
),
|
|
131
125
|
changes: stream(zero, query),
|
|
132
126
|
});
|
|
127
|
+
};
|
package/src/result.ts
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { Result as AtomResult } from "@effect-atom/atom";
|
|
2
|
+
import type { HumanReadable } from "@rocicorp/zero/react";
|
|
3
|
+
import * as Cause from "effect/Cause";
|
|
4
|
+
import * as Data from "effect/Data";
|
|
5
|
+
import * as Fn from "effect/Function";
|
|
6
|
+
import * as Match from "effect/Match";
|
|
7
|
+
import * as Option from "effect/Option";
|
|
8
|
+
import * as Predicate from "effect/Predicate";
|
|
9
|
+
import * as Struct from "effect/Struct";
|
|
10
|
+
import type { QueryResult } from "./internal/query-result.js";
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
### Introduction
|
|
14
|
+
|
|
15
|
+
When using effect-atom with effect-zero, you will often have atoms which contain a type in the form of
|
|
16
|
+
|
|
17
|
+
`@effect-atom/atom`.Result<`@rocicorp/zero/react`.QueryResult<A>, E>
|
|
18
|
+
|
|
19
|
+
However, such a type is not easy to work with.
|
|
20
|
+
The purpose of `effect-zero`.Result is to provide a more convenient type for this use case.
|
|
21
|
+
|
|
22
|
+
### Background
|
|
23
|
+
|
|
24
|
+
To start, let's analyze the possible states of the original type:
|
|
25
|
+
- The outer `@effect-atom/atom`.Result may be `Initial<_, _>`, `Failure<_, E>`, or `Success<QueryResult, _>`.
|
|
26
|
+
- The inner `@rocicorp/zero/react`.QueryResult may be `unknown<A>`, `complete<A>`, or `error`.
|
|
27
|
+
|
|
28
|
+
This means that there are 5 total possible states:
|
|
29
|
+
- Initial, representing the state before the query has been executed on either the client or the server.
|
|
30
|
+
- Success(unknown(A)), representing the state when the query has only been executed on the client.
|
|
31
|
+
- Success(complete(A)), representing the state when the query has been executed on the server.
|
|
32
|
+
- Success(error), representing the state when an error occurs within the query.
|
|
33
|
+
- Failure(E), representing the state when an error occurs outside the query (e.g. within your Atom code).
|
|
34
|
+
|
|
35
|
+
### Solution
|
|
36
|
+
|
|
37
|
+
On the other hand, `effect-zero`.Result has the following states:
|
|
38
|
+
- Initial -> Representing the same state as `Initial` in the original type.
|
|
39
|
+
- Success(Partial(A)) -> Representing the same state as `Success(unknown)` in the original type.
|
|
40
|
+
- Success(Complete(A)) -> Representing the same state as `Success(complete)` in the original type.
|
|
41
|
+
- Failure(QueryError) -> Representing the same state as `Success(error)` in the original type.
|
|
42
|
+
- Failure(E) -> Representing the same state as `Failure(E)` in the original type.
|
|
43
|
+
|
|
44
|
+
This has a few key benefits:
|
|
45
|
+
- It allows you to deal with errors in a more streamlined way, as now all error states are represented as a `Failure(E | QueryError)`,
|
|
46
|
+
thus you can use the builtin facilities of `effect-atom`.Result to work with the possible error states.
|
|
47
|
+
- We provide a method `acceptPartialMode` which allows for simplifying the type further by specifying the situations in which you want to accept
|
|
48
|
+
partial results, resulting a further simplified type of `Initial | Success(A) | Failure(E | QueryError)`.
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
export type Result<A, E = never> = AtomResult.Result<QueryResult.Partial<A> | QueryResult.Complete<A>, E | QueryError>;
|
|
52
|
+
export namespace Result {
|
|
53
|
+
export type Initial<A, E = never> = AtomResult.Initial<
|
|
54
|
+
QueryResult.Partial<A> | QueryResult.Complete<A>,
|
|
55
|
+
E | QueryError
|
|
56
|
+
>;
|
|
57
|
+
export type Failure<A, E = never> = AtomResult.Failure<
|
|
58
|
+
QueryResult.Partial<A> | QueryResult.Complete<A>,
|
|
59
|
+
E | QueryError
|
|
60
|
+
>;
|
|
61
|
+
export type Success<A, E = never> = AtomResult.Success<
|
|
62
|
+
QueryResult.Partial<A> | QueryResult.Complete<A>,
|
|
63
|
+
E | QueryError
|
|
64
|
+
>;
|
|
65
|
+
|
|
66
|
+
// More granular Partial and Complete types
|
|
67
|
+
export type Partial<A, E = never> = AtomResult.Success<QueryResult.Partial<A>, E | QueryError>;
|
|
68
|
+
export type Complete<A, E = never> = AtomResult.Success<QueryResult.Complete<A>, E | QueryError>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export class QueryError extends Data.TaggedError("QueryError")<{
|
|
72
|
+
readonly details: QueryResult.Error["details"];
|
|
73
|
+
}> {}
|
|
74
|
+
|
|
75
|
+
export const make = <A, E>(value: AtomResult.Result<QueryResult<A>, E>): Result<A, E> =>
|
|
76
|
+
Match.valueTags(value, {
|
|
77
|
+
Initial: () => AtomResult.initial(),
|
|
78
|
+
Success: ({ value }) =>
|
|
79
|
+
Match.valueTags(value, {
|
|
80
|
+
Partial: (value) => AtomResult.success(value),
|
|
81
|
+
Complete: (value) => AtomResult.success(value),
|
|
82
|
+
Error: (e) => AtomResult.failure(Cause.fail(new QueryError(e))),
|
|
83
|
+
}),
|
|
84
|
+
Failure: (e) => AtomResult.failure(e.cause),
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// based on: https://github.com/tim-smart/effect-atom/blob/04c15cacda42dd230782f52c0e978400793a502c/packages/atom/src/Result.ts#L424
|
|
88
|
+
export const match: {
|
|
89
|
+
<A, E, W, X, Y, Z>(options: {
|
|
90
|
+
readonly onInitial: (_: Result.Initial<A, E>) => W;
|
|
91
|
+
readonly onFailure: (_: Result.Failure<A, E>) => X;
|
|
92
|
+
readonly onPartial: (_: QueryResult.Partial<A>) => Y;
|
|
93
|
+
readonly onComplete: (_: QueryResult.Complete<A>) => Z;
|
|
94
|
+
}): (self: Result<A, E>) => W | X | Y | Z;
|
|
95
|
+
<A, E, W, X, Y, Z>(
|
|
96
|
+
self: Result<A, E>,
|
|
97
|
+
options: {
|
|
98
|
+
readonly onInitial: (_: Result.Initial<A, E>) => W;
|
|
99
|
+
readonly onFailure: (_: Result.Failure<A, E>) => X;
|
|
100
|
+
readonly onPartial: (_: QueryResult.Partial<A>) => Y;
|
|
101
|
+
readonly onComplete: (_: QueryResult.Complete<A>) => Z;
|
|
102
|
+
},
|
|
103
|
+
): W | X | Y | Z;
|
|
104
|
+
} = Fn.dual(
|
|
105
|
+
2,
|
|
106
|
+
<A, E, W, X, Y, Z>(
|
|
107
|
+
self: Result<A, E>,
|
|
108
|
+
options: {
|
|
109
|
+
readonly onInitial: (_: Result.Initial<A, E>) => W;
|
|
110
|
+
readonly onFailure: (_: Result.Failure<A, E>) => X;
|
|
111
|
+
readonly onPartial: (_: QueryResult.Partial<A>) => Y;
|
|
112
|
+
readonly onComplete: (_: QueryResult.Complete<A>) => Z;
|
|
113
|
+
},
|
|
114
|
+
) =>
|
|
115
|
+
Match.valueTags(self, {
|
|
116
|
+
Initial: (initial) => options.onInitial(initial),
|
|
117
|
+
Failure: (failure) => options.onFailure(failure),
|
|
118
|
+
Success: (success) =>
|
|
119
|
+
Match.valueTags(success.value, {
|
|
120
|
+
Partial: (partial) => options.onPartial(partial),
|
|
121
|
+
Complete: (complete) => options.onComplete(complete),
|
|
122
|
+
}),
|
|
123
|
+
}),
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
export const mapSuccess: {
|
|
127
|
+
<A, B>(f: (value: A) => B): <E = never>(self: AtomResult.Success<A, E>) => AtomResult.Success<B, E>;
|
|
128
|
+
<A, B, E = never>(self: AtomResult.Success<A, E>, f: (value: A) => B): AtomResult.Success<B, E>;
|
|
129
|
+
} = Fn.dual(2, <A, B, E = never>(self: AtomResult.Success<A, E>, f: (value: A) => B) => AtomResult.map(self, f));
|
|
130
|
+
|
|
131
|
+
export const acceptPartialWith: {
|
|
132
|
+
<A, E = never>(
|
|
133
|
+
self: Result<A, E>,
|
|
134
|
+
predicate: (value: HumanReadable<A>) => boolean,
|
|
135
|
+
):
|
|
136
|
+
| AtomResult.Initial<HumanReadable<A>, E>
|
|
137
|
+
| AtomResult.Success<HumanReadable<A>, QueryError | E>
|
|
138
|
+
| AtomResult.Failure<HumanReadable<A>, QueryError | E>;
|
|
139
|
+
<A, E = never>(
|
|
140
|
+
self: Result<A, E>,
|
|
141
|
+
): (
|
|
142
|
+
predicate: (value: HumanReadable<A>) => boolean,
|
|
143
|
+
) =>
|
|
144
|
+
| AtomResult.Initial<HumanReadable<A>, E>
|
|
145
|
+
| AtomResult.Success<HumanReadable<A>, QueryError | E>
|
|
146
|
+
| AtomResult.Failure<HumanReadable<A>, QueryError | E>;
|
|
147
|
+
} = Fn.dual(2, <A, E = never>(self: Result<A, E>, predicate: (value: HumanReadable<A>) => boolean) =>
|
|
148
|
+
AtomResult.match(self, {
|
|
149
|
+
onSuccess: (success) =>
|
|
150
|
+
Predicate.isTagged(success.value, "Partial") && !predicate(success.value.value)
|
|
151
|
+
? AtomResult.initial<HumanReadable<A>, E>(success.waiting)
|
|
152
|
+
: mapSuccess(success, Struct.get("value")),
|
|
153
|
+
onFailure: (failure) =>
|
|
154
|
+
AtomResult.failure<E | QueryError, HumanReadable<A>>(failure.cause, {
|
|
155
|
+
waiting: failure.waiting,
|
|
156
|
+
previousSuccess: failure.previousSuccess.pipe(
|
|
157
|
+
Option.flatMap((success) =>
|
|
158
|
+
Predicate.isTagged(success.value, "Partial") && !predicate(success.value.value)
|
|
159
|
+
? Option.none()
|
|
160
|
+
: Option.some(mapSuccess(success, Struct.get("value"))),
|
|
161
|
+
),
|
|
162
|
+
),
|
|
163
|
+
}),
|
|
164
|
+
onInitial: (initial) => AtomResult.initial<HumanReadable<A>, E>(initial.waiting),
|
|
165
|
+
}),
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
export type AcceptPartialMode = "waitForServer" | "acceptCached";
|
|
169
|
+
|
|
170
|
+
export const acceptPartialMode: {
|
|
171
|
+
<A, E>(
|
|
172
|
+
self: Result<A, E>,
|
|
173
|
+
/**
|
|
174
|
+
* - `acceptCached` -> Will resolve to the partial result if it is available
|
|
175
|
+
* - For singular queries, it will resolve if the cached value exists.
|
|
176
|
+
* - For list queries, it will resolve to the cached value regardless of the length of the list.
|
|
177
|
+
* - It will update the returned value as additional rows come in, and then optionally settle on a final value.
|
|
178
|
+
*
|
|
179
|
+
* - `waitForServer` -> Will wait for the complete result (from the server) before resolving
|
|
180
|
+
* - When you want to wait for the server before doing something.
|
|
181
|
+
* - Only used for queries in which you need an fully accurate result before doing something.
|
|
182
|
+
**/
|
|
183
|
+
mode: AcceptPartialMode,
|
|
184
|
+
):
|
|
185
|
+
| AtomResult.Initial<HumanReadable<A>, E>
|
|
186
|
+
| AtomResult.Success<HumanReadable<A>, QueryError | E>
|
|
187
|
+
| AtomResult.Failure<HumanReadable<A>, QueryError | E>;
|
|
188
|
+
<A, E>(
|
|
189
|
+
/**
|
|
190
|
+
* - `acceptCached` -> Will resolve to the partial result if it is available
|
|
191
|
+
* - For singular queries, it will resolve if the cached value exists.
|
|
192
|
+
* - For list queries, it will resolve to the cached value regardless of the length of the list.
|
|
193
|
+
* - It will update the returned value as additional rows come in, and then optionally settle on a final value.
|
|
194
|
+
*
|
|
195
|
+
* - `waitForServer` -> Will wait for the complete result (from the server) before resolving
|
|
196
|
+
* - When you want to wait for the server before doing something.
|
|
197
|
+
* - Only used for queries in which you need an fully accurate result before doing something.
|
|
198
|
+
**/
|
|
199
|
+
mode: AcceptPartialMode,
|
|
200
|
+
): (
|
|
201
|
+
self: Result<A, E>,
|
|
202
|
+
) =>
|
|
203
|
+
| AtomResult.Initial<HumanReadable<A>, E>
|
|
204
|
+
| AtomResult.Success<HumanReadable<A>, QueryError | E>
|
|
205
|
+
| AtomResult.Failure<HumanReadable<A>, QueryError | E>;
|
|
206
|
+
} = Fn.dual(2, <A, E>(self: Result<A, E>, mode: AcceptPartialMode) =>
|
|
207
|
+
Match.value(mode).pipe(
|
|
208
|
+
Match.when("waitForServer", () => acceptPartialWith(self, () => false)),
|
|
209
|
+
Match.when("acceptCached", () => acceptPartialWith(self, (v) => v !== undefined)),
|
|
210
|
+
Match.exhaustive,
|
|
211
|
+
),
|
|
212
|
+
);
|