@confect/foldkit 10.0.0-next.19
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 +61 -0
- package/LICENSE +7 -0
- package/README.md +11 -0
- package/dist/Client.d.ts +69 -0
- package/dist/Client.d.ts.map +1 -0
- package/dist/Client.js +44 -0
- package/dist/Client.js.map +1 -0
- package/dist/Command.d.ts +158 -0
- package/dist/Command.d.ts.map +1 -0
- package/dist/Command.js +111 -0
- package/dist/Command.js.map +1 -0
- package/dist/PaginatedQuery.d.ts +227 -0
- package/dist/PaginatedQuery.d.ts.map +1 -0
- package/dist/PaginatedQuery.js +351 -0
- package/dist/PaginatedQuery.js.map +1 -0
- package/dist/Subscription.d.ts +132 -0
- package/dist/Subscription.d.ts.map +1 -0
- package/dist/Subscription.js +119 -0
- package/dist/Subscription.js.map +1 -0
- package/dist/WebSocketClient.d.ts +16 -0
- package/dist/WebSocketClient.d.ts.map +1 -0
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/package.json +71 -0
- package/src/Client.ts +53 -0
- package/src/Command.ts +452 -0
- package/src/PaginatedQuery.ts +1001 -0
- package/src/Subscription.ts +338 -0
- package/src/index.ts +4 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# @confect/foldkit
|
|
2
|
+
|
|
3
|
+
## 10.0.0-next.19
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- c1087eb: Add `@confect/foldkit` — client-side bindings for [Foldkit](https://foldkit.dev) apps. The package maps Confect's client surface onto Foldkit's integration seams: `Client.layer` provides the scoped WebSocket client and pagination-session allocator through an application's `resources`, `Subscription.reactiveQuery` builds a subscription entry that opens, re-opens, and closes a reactive query as `Option`-wrapped args derived from the Model change, and `Command.query` / `Command.mutation` / `Command.action` build Command definitions whose args are the ref's args and whose Messages are declared with the same `messages` field Foldkit's own `Command.define` takes. Every failure is folded into a Message via the required `onError` handler, so Command and Subscription error channels stay `never` as Foldkit requires.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import * as Confect from "@confect/foldkit";
|
|
11
|
+
import * as Schema from "effect/Schema";
|
|
12
|
+
import * as Subscription from "foldkit/subscription";
|
|
13
|
+
|
|
14
|
+
const SaveNote = Confect.Command.mutation(
|
|
15
|
+
"SaveNote",
|
|
16
|
+
refs.public.notes.insert,
|
|
17
|
+
{
|
|
18
|
+
messages: [SucceededSaveNote, FailedSaveNote],
|
|
19
|
+
onSuccess: (noteId) => SucceededSaveNote({ noteId }),
|
|
20
|
+
onError: (error) => FailedSaveNote({ message: String(error) }),
|
|
21
|
+
},
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const subscriptions = Subscription.make<
|
|
25
|
+
Model,
|
|
26
|
+
Message,
|
|
27
|
+
Confect.Client.Client
|
|
28
|
+
>()(() => ({
|
|
29
|
+
note: Confect.Subscription.reactiveQuery<Model>()(refs.public.notes.get, {
|
|
30
|
+
args: (model) => Option.map(model.noteId, (noteId) => ({ noteId })),
|
|
31
|
+
onSuccess: (note) => SucceededGetNote({ note }),
|
|
32
|
+
onError: (error) => FailedGetNote({ message: String(error) }),
|
|
33
|
+
}),
|
|
34
|
+
}));
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The Command factories accept Foldkit's `interrupt` option — `true` keys invocations by the Command name, `{ keyFields, toKey }` by a part derived from the ref's args — and the returned definition gains the `Interrupt` constructor for stopping in-flight invocations. Factory-built definitions instantiate Foldkit's own definition interfaces, so they are accepted wherever Foldkit accepts a Command definition — including Story/Scene `Command.resolve` and `expectExact` matchers. `Command.queryEffect`, `Command.mutationEffect`, and `Command.actionEffect` return execute bodies for hand-written `Command.define` calls (custom args schemas, multi-call Commands), and `Subscription.reactiveQueryStream` is the `dependenciesToStream` escape hatch for hand-written subscription entries.
|
|
38
|
+
|
|
39
|
+
`PaginatedQuery` navigates a paginated query one page at a time over Convex's cursor pagination. `PaginatedQuery.make(ref)` returns the Model and settlement schemas. Its states use Foldkit `AsyncData`'s vocabulary and semantics: `Idle`, `Loading`, `Refreshing`, `Success`, `Failure`, and `Stale`. Failed settlements preserve the complete error contract: declared function and middleware errors are wrapped as `FunctionError`, Convex's invalid-cursor pseudo-error is normalized to the new `@confect/core/PaginationError.InvalidCursor`, and `WebSocketClientError` and `SchemaError` are carried directly. `settle` accepts a request-bound `Result`, turns a failed refresh into `Stale`, automatically resets an invalid cursor, and ignores successes or failures from superseded requests. All machine operations (`init`, `reinitialize`, `reset`, `close`, `next`, `prev`, and `first`) remain pure.
|
|
40
|
+
|
|
41
|
+
`Subscription.paginatedQuery` keeps exactly one live, reactive page subscription in sync with the machine and emits one `onSettled` Message shape for either outcome. It allocates Convex's pagination `id` internally and returns it with the first settlement, so session allocation is not a separate Command or application Message. Installing that id is keep-alive-equivalent and does not restart the subscription. Query errors are emitted as values without closing the underlying Convex subscription, allowing a later successful result to recover naturally. Each request supports `maximumRowsRead` and `maximumBytesRead` and uses `initialNumItems` for the initial page and split heuristic. Page splits remain transparent, the last complete page stays visible during navigation and after refresh failure, and an empty terminal page automatically retreats to the preceding page.
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
const Notes = Confect.PaginatedQuery.make(refs.public.notes.paginate);
|
|
45
|
+
|
|
46
|
+
// Model: notes: Notes.schema
|
|
47
|
+
// Initial Model value: notes: Notes.idle
|
|
48
|
+
// Message: settlement: Notes.settlement
|
|
49
|
+
// update after matching Idle: Notes.init(idle, { channel }, { initialNumItems: 20 })
|
|
50
|
+
|
|
51
|
+
const subscriptions = Subscription.make<
|
|
52
|
+
Model,
|
|
53
|
+
Message,
|
|
54
|
+
Confect.Client.Client
|
|
55
|
+
>()(() => ({
|
|
56
|
+
notesPage: Confect.Subscription.paginatedQuery<Model>()(Notes, {
|
|
57
|
+
state: (model) => model.notes,
|
|
58
|
+
onSettled: (settlement) => SettledGetNotesPage({ settlement }),
|
|
59
|
+
}),
|
|
60
|
+
}));
|
|
61
|
+
```
|
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright 2024 RJ Dellecese
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Confect 🧁
|
|
2
|
+
|
|
3
|
+
Confect is a framework that deeply integrates Effect with Convex. It's more than just Effect bindings! Confect allows you to:
|
|
4
|
+
|
|
5
|
+
- Define your Convex database schema using Effect schemas.
|
|
6
|
+
- Write Convex function args and returns validators using Effect's schema library.
|
|
7
|
+
- Use Confect functions to automatically decode and encode your data according to your Effect schema definitions for end-to-end rich types, from client to function to database (and back).
|
|
8
|
+
- Use Effect's HTTP API modules to define your HTTP API(s). Includes interactive OpenAPI documentation powered by [Scalar](https://github.com/scalar/scalar).
|
|
9
|
+
- Access Convex platform capabilities via Effect services.
|
|
10
|
+
|
|
11
|
+
Want to learn more? Read the [docs](https://confect.dev)!
|
package/dist/Client.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as WebSocketClient from "@confect/js/WebSocketClient";
|
|
2
|
+
import type { ConvexClient } from "convex/browser";
|
|
3
|
+
import * as Context from "effect/Context";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as Layer from "effect/Layer";
|
|
6
|
+
import * as Option from "effect/Option";
|
|
7
|
+
/**
|
|
8
|
+
* Builds the Foldkit-facing Confect client around one WebSocket client. The
|
|
9
|
+
* pagination id allocator shares the WebSocket client's lifetime because its
|
|
10
|
+
* ids are cache-busters for that client's query cache.
|
|
11
|
+
*/
|
|
12
|
+
export declare const make: (webSocketClient: WebSocketClient.WebSocketClient) => Effect.Effect<{
|
|
13
|
+
url: string;
|
|
14
|
+
setAuth: (fetchToken: (args: {
|
|
15
|
+
forceRefreshToken: boolean;
|
|
16
|
+
}) => Effect.Effect<string | null | undefined>, onChange?: (isAuthenticated: boolean) => Effect.Effect<void>) => Effect.Effect<void, never, never>;
|
|
17
|
+
query: <Query extends import("@confect/core/Ref").AnyPublicQuery>(ref: Query, ...rest: import("@confect/core/Ref").OptionalArgs<Query>) => Effect.Effect<import("@confect/core/Ref").Returns<Query>, import("@confect/core/Ref").Error<Query> | WebSocketClient.WebSocketClientError | import("effect/Schema").SchemaError>;
|
|
18
|
+
mutation: <Mutation extends import("@confect/core/Ref").AnyPublicMutation>(ref: Mutation, ...rest: import("@confect/core/Ref").OptionalArgs<Mutation>) => Effect.Effect<import("@confect/core/Ref").Returns<Mutation>, import("@confect/core/Ref").Error<Mutation> | WebSocketClient.WebSocketClientError | import("effect/Schema").SchemaError>;
|
|
19
|
+
action: <Action extends import("@confect/core/Ref").AnyPublicAction>(ref: Action, ...rest: import("@confect/core/Ref").OptionalArgs<Action>) => Effect.Effect<import("@confect/core/Ref").Returns<Action>, import("@confect/core/Ref").Error<Action> | WebSocketClient.WebSocketClientError | import("effect/Schema").SchemaError>;
|
|
20
|
+
reactiveQuery: <Query extends import("@confect/core/Ref").AnyPublicQuery>(ref: Query, ...rest: import("@confect/core/Ref").OptionalArgs<Query>) => import("effect/Stream").Stream<import("@confect/core/Ref").Returns<Query>, import("effect/Schema").SchemaError | WebSocketClient.WebSocketClientError | import("@confect/core/Ref").Error<Query>>;
|
|
21
|
+
reactiveQueryResult: <Query extends import("@confect/core/Ref").AnyPublicQuery>(ref: Query, ...rest: import("@confect/core/Ref").OptionalArgs<Query>) => import("effect/Stream").Stream<import("effect/Result").Result<import("@confect/core/Ref").Returns<Query>, import("effect/Schema").SchemaError | WebSocketClient.WebSocketClientError | import("@confect/core/Ref").Error<Query>>>;
|
|
22
|
+
resolvePaginationId: (restored: Option.Option<number>) => Effect.Effect<number, never, never>;
|
|
23
|
+
}, never, never>;
|
|
24
|
+
/**
|
|
25
|
+
* The application-scoped Confect client used by Foldkit Commands and
|
|
26
|
+
* Subscriptions. It combines the WebSocket API with the pagination-session id
|
|
27
|
+
* allocator required by Convex's query cache.
|
|
28
|
+
*/
|
|
29
|
+
export declare const Client: Context.Service<{
|
|
30
|
+
url: string;
|
|
31
|
+
setAuth: (fetchToken: (args: {
|
|
32
|
+
forceRefreshToken: boolean;
|
|
33
|
+
}) => Effect.Effect<string | null | undefined>, onChange?: (isAuthenticated: boolean) => Effect.Effect<void>) => Effect.Effect<void, never, never>;
|
|
34
|
+
query: <Query extends import("@confect/core/Ref").AnyPublicQuery>(ref: Query, ...rest: import("@confect/core/Ref").OptionalArgs<Query>) => Effect.Effect<import("@confect/core/Ref").Returns<Query>, import("@confect/core/Ref").Error<Query> | WebSocketClient.WebSocketClientError | import("effect/Schema").SchemaError>;
|
|
35
|
+
mutation: <Mutation extends import("@confect/core/Ref").AnyPublicMutation>(ref: Mutation, ...rest: import("@confect/core/Ref").OptionalArgs<Mutation>) => Effect.Effect<import("@confect/core/Ref").Returns<Mutation>, import("@confect/core/Ref").Error<Mutation> | WebSocketClient.WebSocketClientError | import("effect/Schema").SchemaError>;
|
|
36
|
+
action: <Action extends import("@confect/core/Ref").AnyPublicAction>(ref: Action, ...rest: import("@confect/core/Ref").OptionalArgs<Action>) => Effect.Effect<import("@confect/core/Ref").Returns<Action>, import("@confect/core/Ref").Error<Action> | WebSocketClient.WebSocketClientError | import("effect/Schema").SchemaError>;
|
|
37
|
+
reactiveQuery: <Query extends import("@confect/core/Ref").AnyPublicQuery>(ref: Query, ...rest: import("@confect/core/Ref").OptionalArgs<Query>) => import("effect/Stream").Stream<import("@confect/core/Ref").Returns<Query>, import("effect/Schema").SchemaError | WebSocketClient.WebSocketClientError | import("@confect/core/Ref").Error<Query>>;
|
|
38
|
+
reactiveQueryResult: <Query extends import("@confect/core/Ref").AnyPublicQuery>(ref: Query, ...rest: import("@confect/core/Ref").OptionalArgs<Query>) => import("effect/Stream").Stream<import("effect/Result").Result<import("@confect/core/Ref").Returns<Query>, import("effect/Schema").SchemaError | WebSocketClient.WebSocketClientError | import("@confect/core/Ref").Error<Query>>>;
|
|
39
|
+
resolvePaginationId: (restored: Option.Option<number>) => Effect.Effect<number, never, never>;
|
|
40
|
+
}, {
|
|
41
|
+
url: string;
|
|
42
|
+
setAuth: (fetchToken: (args: {
|
|
43
|
+
forceRefreshToken: boolean;
|
|
44
|
+
}) => Effect.Effect<string | null | undefined>, onChange?: (isAuthenticated: boolean) => Effect.Effect<void>) => Effect.Effect<void, never, never>;
|
|
45
|
+
query: <Query extends import("@confect/core/Ref").AnyPublicQuery>(ref: Query, ...rest: import("@confect/core/Ref").OptionalArgs<Query>) => Effect.Effect<import("@confect/core/Ref").Returns<Query>, import("@confect/core/Ref").Error<Query> | WebSocketClient.WebSocketClientError | import("effect/Schema").SchemaError>;
|
|
46
|
+
mutation: <Mutation extends import("@confect/core/Ref").AnyPublicMutation>(ref: Mutation, ...rest: import("@confect/core/Ref").OptionalArgs<Mutation>) => Effect.Effect<import("@confect/core/Ref").Returns<Mutation>, import("@confect/core/Ref").Error<Mutation> | WebSocketClient.WebSocketClientError | import("effect/Schema").SchemaError>;
|
|
47
|
+
action: <Action extends import("@confect/core/Ref").AnyPublicAction>(ref: Action, ...rest: import("@confect/core/Ref").OptionalArgs<Action>) => Effect.Effect<import("@confect/core/Ref").Returns<Action>, import("@confect/core/Ref").Error<Action> | WebSocketClient.WebSocketClientError | import("effect/Schema").SchemaError>;
|
|
48
|
+
reactiveQuery: <Query extends import("@confect/core/Ref").AnyPublicQuery>(ref: Query, ...rest: import("@confect/core/Ref").OptionalArgs<Query>) => import("effect/Stream").Stream<import("@confect/core/Ref").Returns<Query>, import("effect/Schema").SchemaError | WebSocketClient.WebSocketClientError | import("@confect/core/Ref").Error<Query>>;
|
|
49
|
+
reactiveQueryResult: <Query extends import("@confect/core/Ref").AnyPublicQuery>(ref: Query, ...rest: import("@confect/core/Ref").OptionalArgs<Query>) => import("effect/Stream").Stream<import("effect/Result").Result<import("@confect/core/Ref").Returns<Query>, import("effect/Schema").SchemaError | WebSocketClient.WebSocketClientError | import("@confect/core/Ref").Error<Query>>>;
|
|
50
|
+
resolvePaginationId: (restored: Option.Option<number>) => Effect.Effect<number, never, never>;
|
|
51
|
+
}>;
|
|
52
|
+
export type Client = typeof Client.Identifier;
|
|
53
|
+
/**
|
|
54
|
+
* Constructs one scoped client for a Foldkit application's `resources`.
|
|
55
|
+
*/
|
|
56
|
+
export declare const layer: (address: string, options?: ConstructorParameters<typeof ConvexClient>[1]) => Layer.Layer<{
|
|
57
|
+
url: string;
|
|
58
|
+
setAuth: (fetchToken: (args: {
|
|
59
|
+
forceRefreshToken: boolean;
|
|
60
|
+
}) => Effect.Effect<string | null | undefined>, onChange?: (isAuthenticated: boolean) => Effect.Effect<void>) => Effect.Effect<void, never, never>;
|
|
61
|
+
query: <Query extends import("@confect/core/Ref").AnyPublicQuery>(ref: Query, ...rest: import("@confect/core/Ref").OptionalArgs<Query>) => Effect.Effect<import("@confect/core/Ref").Returns<Query>, import("@confect/core/Ref").Error<Query> | WebSocketClient.WebSocketClientError | import("effect/Schema").SchemaError>;
|
|
62
|
+
mutation: <Mutation extends import("@confect/core/Ref").AnyPublicMutation>(ref: Mutation, ...rest: import("@confect/core/Ref").OptionalArgs<Mutation>) => Effect.Effect<import("@confect/core/Ref").Returns<Mutation>, import("@confect/core/Ref").Error<Mutation> | WebSocketClient.WebSocketClientError | import("effect/Schema").SchemaError>;
|
|
63
|
+
action: <Action extends import("@confect/core/Ref").AnyPublicAction>(ref: Action, ...rest: import("@confect/core/Ref").OptionalArgs<Action>) => Effect.Effect<import("@confect/core/Ref").Returns<Action>, import("@confect/core/Ref").Error<Action> | WebSocketClient.WebSocketClientError | import("effect/Schema").SchemaError>;
|
|
64
|
+
reactiveQuery: <Query extends import("@confect/core/Ref").AnyPublicQuery>(ref: Query, ...rest: import("@confect/core/Ref").OptionalArgs<Query>) => import("effect/Stream").Stream<import("@confect/core/Ref").Returns<Query>, import("effect/Schema").SchemaError | WebSocketClient.WebSocketClientError | import("@confect/core/Ref").Error<Query>>;
|
|
65
|
+
reactiveQueryResult: <Query extends import("@confect/core/Ref").AnyPublicQuery>(ref: Query, ...rest: import("@confect/core/Ref").OptionalArgs<Query>) => import("effect/Stream").Stream<import("effect/Result").Result<import("@confect/core/Ref").Returns<Query>, import("effect/Schema").SchemaError | WebSocketClient.WebSocketClientError | import("@confect/core/Ref").Error<Query>>>;
|
|
66
|
+
resolvePaginationId: (restored: Option.Option<number>) => Effect.Effect<number, never, never>;
|
|
67
|
+
}, never, never>;
|
|
68
|
+
export { WebSocketClientError } from "@confect/js/WebSocketClient";
|
|
69
|
+
//# sourceMappingURL=Client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Client.d.ts","sourceRoot":"","sources":["../src/Client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,eAAe,MAAM,6BAA6B,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC;;;;GAIG;AACH,eAAO,MAAM,IAAI,oBAAqB,eAAe,CAAC,eAAe;;;;;;;;;;oCAM/B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;gBAQvD,CAAC;AAEL;;;;GAIG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;oCAfmB,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;;;;;;;;;;;;EAiB1D,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,UAAU,CAAC;AAE9C;;GAEG;AACH,eAAO,MAAM,KAAK,YACP,MAAM,YACL,qBAAqB,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC;;;;;;;;;;oCA1BnB,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;gBA+BK,CAAC;AAEjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC"}
|
package/dist/Client.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
import * as WebSocketClient from "@confect/js/WebSocketClient";
|
|
3
|
+
import { WebSocketClientError } from "@confect/js/WebSocketClient";
|
|
4
|
+
import * as Context from "effect/Context";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
import * as Layer from "effect/Layer";
|
|
7
|
+
import * as Option from "effect/Option";
|
|
8
|
+
import * as Ref from "effect/Ref";
|
|
9
|
+
//#region src/Client.ts
|
|
10
|
+
var Client_exports = /* @__PURE__ */ __exportAll({
|
|
11
|
+
Client: () => Client,
|
|
12
|
+
WebSocketClientError: () => WebSocketClientError,
|
|
13
|
+
layer: () => layer,
|
|
14
|
+
make: () => make
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* Builds the Foldkit-facing Confect client around one WebSocket client. The
|
|
18
|
+
* pagination id allocator shares the WebSocket client's lifetime because its
|
|
19
|
+
* ids are cache-busters for that client's query cache.
|
|
20
|
+
*/
|
|
21
|
+
const make = (webSocketClient) => Effect.gen(function* () {
|
|
22
|
+
const paginationId = yield* Ref.make(0);
|
|
23
|
+
return {
|
|
24
|
+
...webSocketClient,
|
|
25
|
+
resolvePaginationId: (restored) => Ref.modify(paginationId, (current) => Option.match(restored, {
|
|
26
|
+
onNone: () => [current + 1, current + 1],
|
|
27
|
+
onSome: (id) => [id, Math.max(current, id)]
|
|
28
|
+
}))
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
/**
|
|
32
|
+
* The application-scoped Confect client used by Foldkit Commands and
|
|
33
|
+
* Subscriptions. It combines the WebSocket API with the pagination-session id
|
|
34
|
+
* allocator required by Convex's query cache.
|
|
35
|
+
*/
|
|
36
|
+
const Client = Context.Service("@confect/foldkit/Client");
|
|
37
|
+
/**
|
|
38
|
+
* Constructs one scoped client for a Foldkit application's `resources`.
|
|
39
|
+
*/
|
|
40
|
+
const layer = (address, options) => Layer.effect(Client, Effect.flatMap(WebSocketClient.WebSocketClient, make)).pipe(Layer.provide(WebSocketClient.layer(address, options)));
|
|
41
|
+
//#endregion
|
|
42
|
+
export { Client, Client_exports, WebSocketClientError, layer, make };
|
|
43
|
+
|
|
44
|
+
//# sourceMappingURL=Client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Client.js","names":[],"sources":["../src/Client.ts"],"sourcesContent":["import * as WebSocketClient from \"@confect/js/WebSocketClient\";\nimport type { ConvexClient } from \"convex/browser\";\nimport * as Context from \"effect/Context\";\nimport * as Effect from \"effect/Effect\";\nimport * as Layer from \"effect/Layer\";\nimport * as Option from \"effect/Option\";\nimport * as Ref from \"effect/Ref\";\n\n/**\n * Builds the Foldkit-facing Confect client around one WebSocket client. The\n * pagination id allocator shares the WebSocket client's lifetime because its\n * ids are cache-busters for that client's query cache.\n */\nexport const make = (webSocketClient: WebSocketClient.WebSocketClient) =>\n Effect.gen(function* () {\n const paginationId = yield* Ref.make(0);\n\n return {\n ...webSocketClient,\n resolvePaginationId: (restored: Option.Option<number>) =>\n Ref.modify(paginationId, (current) =>\n Option.match(restored, {\n onNone: () => [current + 1, current + 1],\n onSome: (id) => [id, Math.max(current, id)],\n }),\n ),\n };\n });\n\n/**\n * The application-scoped Confect client used by Foldkit Commands and\n * Subscriptions. It combines the WebSocket API with the pagination-session id\n * allocator required by Convex's query cache.\n */\nexport const Client = Context.Service<Effect.Success<ReturnType<typeof make>>>(\n \"@confect/foldkit/Client\",\n);\n\nexport type Client = typeof Client.Identifier;\n\n/**\n * Constructs one scoped client for a Foldkit application's `resources`.\n */\nexport const layer = (\n address: string,\n options?: ConstructorParameters<typeof ConvexClient>[1],\n) =>\n Layer.effect(\n Client,\n Effect.flatMap(WebSocketClient.WebSocketClient, make),\n ).pipe(Layer.provide(WebSocketClient.layer(address, options)));\n\nexport { WebSocketClientError } from \"@confect/js/WebSocketClient\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAaA,MAAa,QAAQ,oBACnB,OAAO,IAAI,aAAa;CACtB,MAAM,eAAe,OAAO,IAAI,KAAK,CAAC;CAEtC,OAAO;EACL,GAAG;EACH,sBAAsB,aACpB,IAAI,OAAO,eAAe,YACxB,OAAO,MAAM,UAAU;GACrB,cAAc,CAAC,UAAU,GAAG,UAAU,CAAC;GACvC,SAAS,OAAO,CAAC,IAAI,KAAK,IAAI,SAAS,EAAE,CAAC;EAC5C,CAAC,CACH;CACJ;AACF,CAAC;;;;;;AAOH,MAAa,SAAS,QAAQ,QAC5B,yBACF;;;;AAOA,MAAa,SACX,SACA,YAEA,MAAM,OACJ,QACA,OAAO,QAAQ,gBAAgB,iBAAiB,IAAI,CACtD,CAAC,CAAC,KAAK,MAAM,QAAQ,gBAAgB,MAAM,SAAS,OAAO,CAAC,CAAC"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import type * as Ref from "@confect/core/Ref";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import type * as Schema from "effect/Schema";
|
|
4
|
+
import * as FoldkitCommand from "foldkit/command";
|
|
5
|
+
import * as Client from "./Client";
|
|
6
|
+
/**
|
|
7
|
+
* Everything a Command against `Ref_` can fail with before it is folded into
|
|
8
|
+
* an `onError` Message: the ref's typed error (if it declares an `error`
|
|
9
|
+
* schema), a transport-level `WebSocketClientError`, or a `SchemaError` from
|
|
10
|
+
* encoding args or decoding returns.
|
|
11
|
+
*/
|
|
12
|
+
export type Error<Ref_ extends Ref.AnyConfect> = Ref.Error<Ref_> | Client.WebSocketClientError | Schema.SchemaError;
|
|
13
|
+
/**
|
|
14
|
+
* Maps a Confect function's outcome into the app's Messages. Every failure —
|
|
15
|
+
* the ref's typed error, a transport error, or a codec error — arrives via
|
|
16
|
+
* `onError`, so the resulting Command's error channel is `never`, as Foldkit
|
|
17
|
+
* requires. Follow Foldkit's naming convention: past-tense facts like
|
|
18
|
+
* `SucceededSaveNote` / `FailedSaveNote`.
|
|
19
|
+
*/
|
|
20
|
+
export interface Handlers<Ref_ extends Ref.AnyConfect, SuccessMessage, ErrorMessage> {
|
|
21
|
+
readonly onSuccess: (returns: Ref.Returns<Ref_>) => SuccessMessage;
|
|
22
|
+
readonly onError: (error: Error<Ref_>) => ErrorMessage;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The Command instance a definition call constructs.
|
|
26
|
+
*/
|
|
27
|
+
type Instance<Name extends string, Ref_ extends Ref.AnyConfect, Message> = Readonly<{
|
|
28
|
+
name: Name;
|
|
29
|
+
args: Ref.Args<Ref_>;
|
|
30
|
+
effect: Effect.Effect<Message, never, Client.Client>;
|
|
31
|
+
}>;
|
|
32
|
+
type SchemaArgs<Ref_ extends Ref.AnyConfect> = Schema.Schema.Type<Schema.Struct<Ref.ArgsSchema<Ref_>["fields"]>>;
|
|
33
|
+
/**
|
|
34
|
+
* A Foldkit Command definition whose args are the ref's args: our
|
|
35
|
+
* ref-derived call signature (args optional when the ref declares none)
|
|
36
|
+
* intersected with Foldkit's own `CommandDefinitionWithArgs`, which supplies
|
|
37
|
+
* the `CommandDefinitionTypeId` brand and makes the definition assignable
|
|
38
|
+
* wherever Foldkit accepts one — including Story/Scene `Command.resolve`
|
|
39
|
+
* and `expectExact` matchers. Call it from `update` to construct a Command
|
|
40
|
+
* instance; nothing runs until the Foldkit runtime executes it.
|
|
41
|
+
*/
|
|
42
|
+
export type Definition<Name extends string, Ref_ extends Ref.AnyConfect, Message> = ((...args: Ref.OptionalArgs<Ref_>) => Instance<Name, Ref_, Message>) & FoldkitCommand.CommandDefinitionWithArgs<Name, Ref.ArgsSchema<Ref_>["fields"], Effect.Effect<Message, never, Client.Client>>;
|
|
43
|
+
/**
|
|
44
|
+
* Makes a factory-built Command interruptible — Foldkit's `InterruptOption`
|
|
45
|
+
* applied to the ref's args. `true` keys every invocation by the Command
|
|
46
|
+
* name — right when at most one invocation is meaningfully in flight; a
|
|
47
|
+
* `KeyedInterrupt` derives the key part from the ref's args, so concurrent
|
|
48
|
+
* invocations can be targeted independently.
|
|
49
|
+
*
|
|
50
|
+
* Interrupting stops the client-side Effect and guarantees the invocation's
|
|
51
|
+
* result Messages never dispatch — it does not cancel the Convex function on
|
|
52
|
+
* the server, which runs to completion once the call is on the wire.
|
|
53
|
+
*/
|
|
54
|
+
export type InterruptOption<Ref_ extends Ref.AnyConfect, KeyField extends keyof SchemaArgs<Ref_> & string = keyof SchemaArgs<Ref_> & string> = FoldkitCommand.InterruptOption<SchemaArgs<Ref_>, KeyField>;
|
|
55
|
+
/**
|
|
56
|
+
* The keyed arm of `InterruptOption`: `keyFields` declares the args the
|
|
57
|
+
* `Interrupt` constructor requires; `toKey` derives the key part from them.
|
|
58
|
+
*/
|
|
59
|
+
export type KeyedInterrupt<Ref_ extends Ref.AnyConfect, KeyField extends keyof SchemaArgs<Ref_> & string> = Exclude<InterruptOption<Ref_, KeyField>, true>;
|
|
60
|
+
/**
|
|
61
|
+
* An interruptible Command definition whose key is the Command name — the
|
|
62
|
+
* ref-derived call signature intersected with Foldkit's
|
|
63
|
+
* `Interruptible.DefinitionWithArgsNameKeyed`, which supplies the brand and
|
|
64
|
+
* the `Interrupt` constructor.
|
|
65
|
+
*/
|
|
66
|
+
export type InterruptibleDefinition<Name extends string, Ref_ extends Ref.AnyConfect, Message> = ((...args: Ref.OptionalArgs<Ref_>) => Instance<Name, Ref_, Message> & Readonly<{
|
|
67
|
+
key: string;
|
|
68
|
+
}>) & FoldkitCommand.Interruptible.DefinitionWithArgsNameKeyed<Name, Ref.ArgsSchema<Ref_>["fields"], Effect.Effect<Message, never, Client.Client>>;
|
|
69
|
+
/**
|
|
70
|
+
* An interruptible Command definition whose key is derived from the ref's
|
|
71
|
+
* args — the ref-derived call signature intersected with Foldkit's
|
|
72
|
+
* `Interruptible.DefinitionWithArgs`, which supplies the brand and the
|
|
73
|
+
* key-args-taking `Interrupt` constructor.
|
|
74
|
+
*/
|
|
75
|
+
export type KeyedInterruptibleDefinition<Name extends string, Ref_ extends Ref.AnyConfect, KeyField extends keyof SchemaArgs<Ref_> & string, Message> = ((...args: Ref.OptionalArgs<Ref_>) => Instance<Name, Ref_, Message> & Readonly<{
|
|
76
|
+
key: string;
|
|
77
|
+
}>) & FoldkitCommand.Interruptible.DefinitionWithArgs<Name, Ref.ArgsSchema<Ref_>["fields"], Pick<SchemaArgs<Ref_>, KeyField>, Effect.Effect<Message, never, Client.Client>>;
|
|
78
|
+
/**
|
|
79
|
+
* An execute body for a hand-written `Command.define` — every failure already
|
|
80
|
+
* folded into a Message. Reach for these when the factory below doesn't fit:
|
|
81
|
+
* a custom args schema, `interrupt`, or a Command that makes several calls.
|
|
82
|
+
*/
|
|
83
|
+
export declare const queryEffect: <Query extends Ref.AnyConfectPublicQuery, SuccessMessage, ErrorMessage>(ref: Query, handlers: Handlers<Query, SuccessMessage, ErrorMessage>) => (...args: Ref.OptionalArgs<Query>) => Effect.Effect<SuccessMessage | ErrorMessage, never, Client.Client>;
|
|
84
|
+
/**
|
|
85
|
+
* An execute body for a hand-written `Command.define` — every failure already
|
|
86
|
+
* folded into a Message. See `queryEffect`.
|
|
87
|
+
*/
|
|
88
|
+
export declare const mutationEffect: <Mutation extends Ref.AnyConfectPublicMutation, SuccessMessage, ErrorMessage>(ref: Mutation, handlers: Handlers<Mutation, SuccessMessage, ErrorMessage>) => (...args: Ref.OptionalArgs<Mutation>) => Effect.Effect<SuccessMessage | ErrorMessage, never, Client.Client>;
|
|
89
|
+
/**
|
|
90
|
+
* An execute body for a hand-written `Command.define` — every failure already
|
|
91
|
+
* folded into a Message. See `queryEffect`.
|
|
92
|
+
*/
|
|
93
|
+
export declare const actionEffect: <Action extends Ref.AnyConfectPublicAction, SuccessMessage, ErrorMessage>(ref: Action, handlers: Handlers<Action, SuccessMessage, ErrorMessage>) => (...args: Ref.OptionalArgs<Action>) => Effect.Effect<SuccessMessage | ErrorMessage, never, Client.Client>;
|
|
94
|
+
type RunWithArgs<Ref_ extends Ref.AnyConfect, Message> = (...args: Ref.OptionalArgs<Ref_>) => Effect.Effect<Message, never, Client.Client>;
|
|
95
|
+
type FactoryConfig<Ref_ extends Ref.AnyConfect, Messages extends ReadonlyArray<Schema.Top>, SuccessMessage, ErrorMessage> = Handlers<Ref_, SuccessMessage, ErrorMessage> & {
|
|
96
|
+
readonly messages: Messages;
|
|
97
|
+
};
|
|
98
|
+
type KeyedFactoryConfig<Ref_ extends Ref.AnyConfect, Messages extends ReadonlyArray<Schema.Top>, SuccessMessage, ErrorMessage, KeyField extends keyof SchemaArgs<Ref_> & string> = FactoryConfig<Ref_, Messages, SuccessMessage, ErrorMessage> & {
|
|
99
|
+
readonly interrupt: KeyedInterrupt<Ref_, KeyField>;
|
|
100
|
+
};
|
|
101
|
+
type NameKeyedFactoryConfig<Ref_ extends Ref.AnyConfect, Messages extends ReadonlyArray<Schema.Top>, SuccessMessage, ErrorMessage> = FactoryConfig<Ref_, Messages, SuccessMessage, ErrorMessage> & {
|
|
102
|
+
readonly interrupt: true;
|
|
103
|
+
};
|
|
104
|
+
type StandardFactoryConfig<Ref_ extends Ref.AnyConfect, Messages extends ReadonlyArray<Schema.Top>, SuccessMessage, ErrorMessage> = FactoryConfig<Ref_, Messages, SuccessMessage, ErrorMessage> & {
|
|
105
|
+
readonly interrupt?: never;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Builds the overload set for one ref kind. Each arm preserves whether
|
|
109
|
+
* `interrupt` is absent, `true`, or args-keyed all the way to
|
|
110
|
+
* `makeDefinition`.
|
|
111
|
+
*/
|
|
112
|
+
declare const makeFactory: <BoundRef extends Ref.AnyConfect>(effectHelper: <Ref_ extends BoundRef, SuccessMessage, ErrorMessage>(ref: Ref_, handlers: Handlers<Ref_, SuccessMessage, ErrorMessage>) => RunWithArgs<Ref_, SuccessMessage | ErrorMessage>) => {
|
|
113
|
+
<const Name extends string, Ref_ extends BoundRef, const Messages extends ReadonlyArray<Schema.Top>, SuccessMessage extends Schema.Schema.Type<Messages[number]>, ErrorMessage extends Schema.Schema.Type<Messages[number]>, KeyField extends keyof SchemaArgs<Ref_> & string>(name: Name, ref: Ref_, config: KeyedFactoryConfig<Ref_, Messages, SuccessMessage, ErrorMessage, KeyField>): KeyedInterruptibleDefinition<Name, Ref_, KeyField, SuccessMessage | ErrorMessage>;
|
|
114
|
+
<const Name extends string, Ref_ extends BoundRef, const Messages extends ReadonlyArray<Schema.Top>, SuccessMessage extends Schema.Schema.Type<Messages[number]>, ErrorMessage extends Schema.Schema.Type<Messages[number]>>(name: Name, ref: Ref_, config: NameKeyedFactoryConfig<Ref_, Messages, SuccessMessage, ErrorMessage>): InterruptibleDefinition<Name, Ref_, SuccessMessage | ErrorMessage>;
|
|
115
|
+
<const Name extends string, Ref_ extends BoundRef, const Messages extends ReadonlyArray<Schema.Top>, SuccessMessage extends Schema.Schema.Type<Messages[number]>, ErrorMessage extends Schema.Schema.Type<Messages[number]>>(name: Name, ref: Ref_, config: StandardFactoryConfig<Ref_, Messages, SuccessMessage, ErrorMessage>): Definition<Name, Ref_, SuccessMessage | ErrorMessage>;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* A Foldkit Command definition for a Confect query whose Command args are the
|
|
119
|
+
* ref's args:
|
|
120
|
+
*
|
|
121
|
+
* ```ts
|
|
122
|
+
* const FetchNote = Command.query("FetchNote", refs.public.notes.get, {
|
|
123
|
+
* messages: [SucceededGetNote, FailedGetNote],
|
|
124
|
+
* onSuccess: (note) => SucceededGetNote({ note }),
|
|
125
|
+
* onError: (error) => FailedGetNote({ message: String(error) }),
|
|
126
|
+
* })
|
|
127
|
+
* // In update:
|
|
128
|
+
* // [model, [FetchNote({ noteId })]]
|
|
129
|
+
* ```
|
|
130
|
+
*
|
|
131
|
+
* Pass `interrupt` to make invocations interruptible via the returned
|
|
132
|
+
* definition's `Interrupt` constructor — `true` keys them by the Command
|
|
133
|
+
* name, `{ keyFields, toKey }` by a part derived from the ref's args:
|
|
134
|
+
*
|
|
135
|
+
* ```ts
|
|
136
|
+
* const SaveDraft = Command.mutation("SaveDraft", refs.public.notes.insert, {
|
|
137
|
+
* messages: [SucceededSaveDraft, FailedSaveDraft],
|
|
138
|
+
* onSuccess: (noteId) => SucceededSaveDraft({ noteId }),
|
|
139
|
+
* onError: (error) => FailedSaveDraft({ message: String(error) }),
|
|
140
|
+
* interrupt: true,
|
|
141
|
+
* })
|
|
142
|
+
* // In update:
|
|
143
|
+
* // [model, [SaveDraft.Interrupt((outcome) => CompletedCancelSaveDraft({ outcome }))]]
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
export declare const query: ReturnType<typeof makeFactory<Ref.AnyConfectPublicQuery>>;
|
|
147
|
+
/**
|
|
148
|
+
* A Foldkit Command definition for a Confect mutation whose Command args are
|
|
149
|
+
* the ref's args. See `query`.
|
|
150
|
+
*/
|
|
151
|
+
export declare const mutation: ReturnType<typeof makeFactory<Ref.AnyConfectPublicMutation>>;
|
|
152
|
+
/**
|
|
153
|
+
* A Foldkit Command definition for a Confect action whose Command args are
|
|
154
|
+
* the ref's args. See `query`.
|
|
155
|
+
*/
|
|
156
|
+
export declare const action: ReturnType<typeof makeFactory<Ref.AnyConfectPublicAction>>;
|
|
157
|
+
export {};
|
|
158
|
+
//# sourceMappingURL=Command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Command.d.ts","sourceRoot":"","sources":["../src/Command.ts"],"names":[],"mappings":"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"}
|
package/dist/Command.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { Client } from "./Client.js";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Match from "effect/Match";
|
|
5
|
+
import * as FoldkitCommand from "foldkit/command";
|
|
6
|
+
//#region src/Command.ts
|
|
7
|
+
var Command_exports = /* @__PURE__ */ __exportAll({
|
|
8
|
+
action: () => action,
|
|
9
|
+
actionEffect: () => actionEffect,
|
|
10
|
+
mutation: () => mutation,
|
|
11
|
+
mutationEffect: () => mutationEffect,
|
|
12
|
+
query: () => query,
|
|
13
|
+
queryEffect: () => queryEffect
|
|
14
|
+
});
|
|
15
|
+
const run = (handlers, invoke) => Effect.flatMap(Client, invoke).pipe(Effect.match({
|
|
16
|
+
onSuccess: handlers.onSuccess,
|
|
17
|
+
onFailure: handlers.onError
|
|
18
|
+
}));
|
|
19
|
+
/**
|
|
20
|
+
* An execute body for a hand-written `Command.define` — every failure already
|
|
21
|
+
* folded into a Message. Reach for these when the factory below doesn't fit:
|
|
22
|
+
* a custom args schema, `interrupt`, or a Command that makes several calls.
|
|
23
|
+
*/
|
|
24
|
+
const queryEffect = (ref, handlers) => (...args) => run(handlers, (client) => client.query(ref, ...args));
|
|
25
|
+
/**
|
|
26
|
+
* An execute body for a hand-written `Command.define` — every failure already
|
|
27
|
+
* folded into a Message. See `queryEffect`.
|
|
28
|
+
*/
|
|
29
|
+
const mutationEffect = (ref, handlers) => (...args) => run(handlers, (client) => client.mutation(ref, ...args));
|
|
30
|
+
/**
|
|
31
|
+
* An execute body for a hand-written `Command.define` — every failure already
|
|
32
|
+
* folded into a Message. See `queryEffect`.
|
|
33
|
+
*/
|
|
34
|
+
const actionEffect = (ref, handlers) => (...args) => run(handlers, (client) => client.action(ref, ...args));
|
|
35
|
+
function makeDefinition(name, ref, messages, runWithArgs, interrupt) {
|
|
36
|
+
const fields = ref.args.fields;
|
|
37
|
+
const execute = (args) => runWithArgs(...args === void 0 ? [] : [args]);
|
|
38
|
+
return Match.value(interrupt).pipe(Match.withReturnType(), Match.when(void 0, () => FoldkitCommand.define(name, {
|
|
39
|
+
args: fields,
|
|
40
|
+
messages,
|
|
41
|
+
execute
|
|
42
|
+
})), Match.when(true, (nameKeyedInterrupt) => FoldkitCommand.define(name, {
|
|
43
|
+
args: fields,
|
|
44
|
+
messages,
|
|
45
|
+
interrupt: nameKeyedInterrupt,
|
|
46
|
+
execute
|
|
47
|
+
})), Match.when(Match.defined, (keyedInterrupt) => {
|
|
48
|
+
return FoldkitCommand.define(name, {
|
|
49
|
+
args: fields,
|
|
50
|
+
messages,
|
|
51
|
+
interrupt: keyedInterrupt,
|
|
52
|
+
execute
|
|
53
|
+
});
|
|
54
|
+
}), Match.exhaustive);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Builds the overload set for one ref kind. Each arm preserves whether
|
|
58
|
+
* `interrupt` is absent, `true`, or args-keyed all the way to
|
|
59
|
+
* `makeDefinition`.
|
|
60
|
+
*/
|
|
61
|
+
const makeFactory = (effectHelper) => {
|
|
62
|
+
function factory(name, ref, config) {
|
|
63
|
+
const runWithArgs = effectHelper(ref, config);
|
|
64
|
+
return Match.value(config.interrupt).pipe(Match.withReturnType(), Match.when(void 0, () => makeDefinition(name, ref, config.messages, runWithArgs)), Match.when(true, (interrupt) => makeDefinition(name, ref, config.messages, runWithArgs, interrupt)), Match.when(Match.defined, (interrupt) => makeDefinition(name, ref, config.messages, runWithArgs, interrupt)), Match.exhaustive);
|
|
65
|
+
}
|
|
66
|
+
return factory;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* A Foldkit Command definition for a Confect query whose Command args are the
|
|
70
|
+
* ref's args:
|
|
71
|
+
*
|
|
72
|
+
* ```ts
|
|
73
|
+
* const FetchNote = Command.query("FetchNote", refs.public.notes.get, {
|
|
74
|
+
* messages: [SucceededGetNote, FailedGetNote],
|
|
75
|
+
* onSuccess: (note) => SucceededGetNote({ note }),
|
|
76
|
+
* onError: (error) => FailedGetNote({ message: String(error) }),
|
|
77
|
+
* })
|
|
78
|
+
* // In update:
|
|
79
|
+
* // [model, [FetchNote({ noteId })]]
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* Pass `interrupt` to make invocations interruptible via the returned
|
|
83
|
+
* definition's `Interrupt` constructor — `true` keys them by the Command
|
|
84
|
+
* name, `{ keyFields, toKey }` by a part derived from the ref's args:
|
|
85
|
+
*
|
|
86
|
+
* ```ts
|
|
87
|
+
* const SaveDraft = Command.mutation("SaveDraft", refs.public.notes.insert, {
|
|
88
|
+
* messages: [SucceededSaveDraft, FailedSaveDraft],
|
|
89
|
+
* onSuccess: (noteId) => SucceededSaveDraft({ noteId }),
|
|
90
|
+
* onError: (error) => FailedSaveDraft({ message: String(error) }),
|
|
91
|
+
* interrupt: true,
|
|
92
|
+
* })
|
|
93
|
+
* // In update:
|
|
94
|
+
* // [model, [SaveDraft.Interrupt((outcome) => CompletedCancelSaveDraft({ outcome }))]]
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
const query = makeFactory(queryEffect);
|
|
98
|
+
/**
|
|
99
|
+
* A Foldkit Command definition for a Confect mutation whose Command args are
|
|
100
|
+
* the ref's args. See `query`.
|
|
101
|
+
*/
|
|
102
|
+
const mutation = makeFactory(mutationEffect);
|
|
103
|
+
/**
|
|
104
|
+
* A Foldkit Command definition for a Confect action whose Command args are
|
|
105
|
+
* the ref's args. See `query`.
|
|
106
|
+
*/
|
|
107
|
+
const action = makeFactory(actionEffect);
|
|
108
|
+
//#endregion
|
|
109
|
+
export { Command_exports, action, actionEffect, mutation, mutationEffect, query, queryEffect };
|
|
110
|
+
|
|
111
|
+
//# sourceMappingURL=Command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Command.js","names":["Client.Client"],"sources":["../src/Command.ts"],"sourcesContent":["import type * as Ref from \"@confect/core/Ref\";\nimport * as Effect from \"effect/Effect\";\nimport * as Match from \"effect/Match\";\nimport type * as Schema from \"effect/Schema\";\nimport * as FoldkitCommand from \"foldkit/command\";\nimport * as Client from \"./Client\";\n\n/**\n * Everything a Command against `Ref_` can fail with before it is folded into\n * an `onError` Message: the ref's typed error (if it declares an `error`\n * schema), a transport-level `WebSocketClientError`, or a `SchemaError` from\n * encoding args or decoding returns.\n */\nexport type Error<Ref_ extends Ref.AnyConfect> =\n | Ref.Error<Ref_>\n | Client.WebSocketClientError\n | Schema.SchemaError;\n\n/**\n * Maps a Confect function's outcome into the app's Messages. Every failure —\n * the ref's typed error, a transport error, or a codec error — arrives via\n * `onError`, so the resulting Command's error channel is `never`, as Foldkit\n * requires. Follow Foldkit's naming convention: past-tense facts like\n * `SucceededSaveNote` / `FailedSaveNote`.\n */\nexport interface Handlers<\n Ref_ extends Ref.AnyConfect,\n SuccessMessage,\n ErrorMessage,\n> {\n readonly onSuccess: (returns: Ref.Returns<Ref_>) => SuccessMessage;\n readonly onError: (error: Error<Ref_>) => ErrorMessage;\n}\n\n/**\n * The Command instance a definition call constructs.\n */\ntype Instance<\n Name extends string,\n Ref_ extends Ref.AnyConfect,\n Message,\n> = Readonly<{\n name: Name;\n args: Ref.Args<Ref_>;\n effect: Effect.Effect<Message, never, Client.Client>;\n}>;\n\ntype SchemaArgs<Ref_ extends Ref.AnyConfect> = Schema.Schema.Type<\n Schema.Struct<Ref.ArgsSchema<Ref_>[\"fields\"]>\n>;\n\n/**\n * A Foldkit Command definition whose args are the ref's args: our\n * ref-derived call signature (args optional when the ref declares none)\n * intersected with Foldkit's own `CommandDefinitionWithArgs`, which supplies\n * the `CommandDefinitionTypeId` brand and makes the definition assignable\n * wherever Foldkit accepts one — including Story/Scene `Command.resolve`\n * and `expectExact` matchers. Call it from `update` to construct a Command\n * instance; nothing runs until the Foldkit runtime executes it.\n */\nexport type Definition<\n Name extends string,\n Ref_ extends Ref.AnyConfect,\n Message,\n> = ((...args: Ref.OptionalArgs<Ref_>) => Instance<Name, Ref_, Message>) &\n FoldkitCommand.CommandDefinitionWithArgs<\n Name,\n Ref.ArgsSchema<Ref_>[\"fields\"],\n Effect.Effect<Message, never, Client.Client>\n >;\n\n/**\n * Makes a factory-built Command interruptible — Foldkit's `InterruptOption`\n * applied to the ref's args. `true` keys every invocation by the Command\n * name — right when at most one invocation is meaningfully in flight; a\n * `KeyedInterrupt` derives the key part from the ref's args, so concurrent\n * invocations can be targeted independently.\n *\n * Interrupting stops the client-side Effect and guarantees the invocation's\n * result Messages never dispatch — it does not cancel the Convex function on\n * the server, which runs to completion once the call is on the wire.\n */\nexport type InterruptOption<\n Ref_ extends Ref.AnyConfect,\n KeyField extends keyof SchemaArgs<Ref_> & string = keyof SchemaArgs<Ref_> &\n string,\n> = FoldkitCommand.InterruptOption<SchemaArgs<Ref_>, KeyField>;\n\n/**\n * The keyed arm of `InterruptOption`: `keyFields` declares the args the\n * `Interrupt` constructor requires; `toKey` derives the key part from them.\n */\nexport type KeyedInterrupt<\n Ref_ extends Ref.AnyConfect,\n KeyField extends keyof SchemaArgs<Ref_> & string,\n> = Exclude<InterruptOption<Ref_, KeyField>, true>;\n\n/**\n * An interruptible Command definition whose key is the Command name — the\n * ref-derived call signature intersected with Foldkit's\n * `Interruptible.DefinitionWithArgsNameKeyed`, which supplies the brand and\n * the `Interrupt` constructor.\n */\nexport type InterruptibleDefinition<\n Name extends string,\n Ref_ extends Ref.AnyConfect,\n Message,\n> = ((\n ...args: Ref.OptionalArgs<Ref_>\n) => Instance<Name, Ref_, Message> & Readonly<{ key: string }>) &\n FoldkitCommand.Interruptible.DefinitionWithArgsNameKeyed<\n Name,\n Ref.ArgsSchema<Ref_>[\"fields\"],\n Effect.Effect<Message, never, Client.Client>\n >;\n\n/**\n * An interruptible Command definition whose key is derived from the ref's\n * args — the ref-derived call signature intersected with Foldkit's\n * `Interruptible.DefinitionWithArgs`, which supplies the brand and the\n * key-args-taking `Interrupt` constructor.\n */\nexport type KeyedInterruptibleDefinition<\n Name extends string,\n Ref_ extends Ref.AnyConfect,\n KeyField extends keyof SchemaArgs<Ref_> & string,\n Message,\n> = ((\n ...args: Ref.OptionalArgs<Ref_>\n) => Instance<Name, Ref_, Message> & Readonly<{ key: string }>) &\n FoldkitCommand.Interruptible.DefinitionWithArgs<\n Name,\n Ref.ArgsSchema<Ref_>[\"fields\"],\n Pick<SchemaArgs<Ref_>, KeyField>,\n Effect.Effect<Message, never, Client.Client>\n >;\n\nconst run = <Ref_ extends Ref.AnyConfect, SuccessMessage, ErrorMessage>(\n handlers: Handlers<Ref_, SuccessMessage, ErrorMessage>,\n invoke: (\n client: Client.Client,\n ) => Effect.Effect<Ref.Returns<Ref_>, Error<Ref_>>,\n): Effect.Effect<SuccessMessage | ErrorMessage, never, Client.Client> =>\n Effect.flatMap(Client.Client, invoke).pipe(\n Effect.match({\n onSuccess: handlers.onSuccess,\n onFailure: handlers.onError,\n }),\n );\n\n/**\n * An execute body for a hand-written `Command.define` — every failure already\n * folded into a Message. Reach for these when the factory below doesn't fit:\n * a custom args schema, `interrupt`, or a Command that makes several calls.\n */\nexport const queryEffect =\n <Query extends Ref.AnyConfectPublicQuery, SuccessMessage, ErrorMessage>(\n ref: Query,\n handlers: Handlers<Query, SuccessMessage, ErrorMessage>,\n ) =>\n (\n ...args: Ref.OptionalArgs<Query>\n ): Effect.Effect<SuccessMessage | ErrorMessage, never, Client.Client> =>\n run(handlers, (client) => client.query(ref, ...args));\n\n/**\n * An execute body for a hand-written `Command.define` — every failure already\n * folded into a Message. See `queryEffect`.\n */\nexport const mutationEffect =\n <Mutation extends Ref.AnyConfectPublicMutation, SuccessMessage, ErrorMessage>(\n ref: Mutation,\n handlers: Handlers<Mutation, SuccessMessage, ErrorMessage>,\n ) =>\n (\n ...args: Ref.OptionalArgs<Mutation>\n ): Effect.Effect<SuccessMessage | ErrorMessage, never, Client.Client> =>\n run(handlers, (client) => client.mutation(ref, ...args));\n\n/**\n * An execute body for a hand-written `Command.define` — every failure already\n * folded into a Message. See `queryEffect`.\n */\nexport const actionEffect =\n <Action extends Ref.AnyConfectPublicAction, SuccessMessage, ErrorMessage>(\n ref: Action,\n handlers: Handlers<Action, SuccessMessage, ErrorMessage>,\n ) =>\n (\n ...args: Ref.OptionalArgs<Action>\n ): Effect.Effect<SuccessMessage | ErrorMessage, never, Client.Client> =>\n run(handlers, (client) => client.action(ref, ...args));\n\ntype RunWithArgs<Ref_ extends Ref.AnyConfect, Message> = (\n ...args: Ref.OptionalArgs<Ref_>\n) => Effect.Effect<Message, never, Client.Client>;\n\nfunction makeDefinition<\n Name extends string,\n Ref_ extends Ref.AnyConfect,\n SuccessMessage,\n ErrorMessage,\n KeyField extends keyof SchemaArgs<Ref_> & string = keyof SchemaArgs<Ref_> &\n string,\n>(\n name: Name,\n ref: Ref_,\n messages: ReadonlyArray<Schema.Top>,\n runWithArgs: RunWithArgs<Ref_, SuccessMessage | ErrorMessage>,\n interrupt?: InterruptOption<Ref_, KeyField>,\n): Definition<Name, Ref_, SuccessMessage | ErrorMessage> {\n type Fields = Ref.ArgsSchema<Ref_>[\"fields\"];\n type FoldkitArgs = SchemaArgs<Ref_>;\n\n const fields: Fields = ref.args.fields;\n const execute = (args: FoldkitArgs | undefined) =>\n runWithArgs(\n ...((args === undefined ? [] : [args]) as Ref.OptionalArgs<Ref_>),\n );\n\n return Match.value(interrupt).pipe(\n Match.withReturnType<\n Definition<Name, Ref_, SuccessMessage | ErrorMessage>\n >(),\n Match.when(\n undefined,\n () =>\n FoldkitCommand.define(name, {\n args: fields,\n messages,\n execute,\n }) as Definition<Name, Ref_, SuccessMessage | ErrorMessage>,\n ),\n Match.when(\n true,\n (nameKeyedInterrupt) =>\n FoldkitCommand.define(name, {\n args: fields,\n messages,\n interrupt: nameKeyedInterrupt,\n execute,\n }) as InterruptibleDefinition<\n Name,\n Ref_,\n SuccessMessage | ErrorMessage\n >,\n ),\n Match.when(Match.defined, (keyedInterrupt) => {\n const definition: FoldkitCommand.CommandDefinitionWithArgs<\n Name,\n Fields,\n ReturnType<typeof execute>\n > = FoldkitCommand.define(name, {\n args: fields,\n messages,\n interrupt: keyedInterrupt,\n execute,\n });\n return definition as Definition<\n Name,\n Ref_,\n SuccessMessage | ErrorMessage\n >;\n }),\n Match.exhaustive,\n );\n}\n\ntype FactoryConfig<\n Ref_ extends Ref.AnyConfect,\n Messages extends ReadonlyArray<Schema.Top>,\n SuccessMessage,\n ErrorMessage,\n> = Handlers<Ref_, SuccessMessage, ErrorMessage> & {\n readonly messages: Messages;\n};\n\ntype KeyedFactoryConfig<\n Ref_ extends Ref.AnyConfect,\n Messages extends ReadonlyArray<Schema.Top>,\n SuccessMessage,\n ErrorMessage,\n KeyField extends keyof SchemaArgs<Ref_> & string,\n> = FactoryConfig<Ref_, Messages, SuccessMessage, ErrorMessage> & {\n readonly interrupt: KeyedInterrupt<Ref_, KeyField>;\n};\n\ntype NameKeyedFactoryConfig<\n Ref_ extends Ref.AnyConfect,\n Messages extends ReadonlyArray<Schema.Top>,\n SuccessMessage,\n ErrorMessage,\n> = FactoryConfig<Ref_, Messages, SuccessMessage, ErrorMessage> & {\n readonly interrupt: true;\n};\n\ntype StandardFactoryConfig<\n Ref_ extends Ref.AnyConfect,\n Messages extends ReadonlyArray<Schema.Top>,\n SuccessMessage,\n ErrorMessage,\n> = FactoryConfig<Ref_, Messages, SuccessMessage, ErrorMessage> & {\n readonly interrupt?: never;\n};\n\n/**\n * Builds the overload set for one ref kind. Each arm preserves whether\n * `interrupt` is absent, `true`, or args-keyed all the way to\n * `makeDefinition`.\n */\nconst makeFactory = <BoundRef extends Ref.AnyConfect>(\n effectHelper: <Ref_ extends BoundRef, SuccessMessage, ErrorMessage>(\n ref: Ref_,\n handlers: Handlers<Ref_, SuccessMessage, ErrorMessage>,\n ) => RunWithArgs<Ref_, SuccessMessage | ErrorMessage>,\n) => {\n function factory<\n const Name extends string,\n Ref_ extends BoundRef,\n const Messages extends ReadonlyArray<Schema.Top>,\n SuccessMessage extends Schema.Schema.Type<Messages[number]>,\n ErrorMessage extends Schema.Schema.Type<Messages[number]>,\n KeyField extends keyof SchemaArgs<Ref_> & string,\n >(\n name: Name,\n ref: Ref_,\n config: KeyedFactoryConfig<\n Ref_,\n Messages,\n SuccessMessage,\n ErrorMessage,\n KeyField\n >,\n ): KeyedInterruptibleDefinition<\n Name,\n Ref_,\n KeyField,\n SuccessMessage | ErrorMessage\n >;\n function factory<\n const Name extends string,\n Ref_ extends BoundRef,\n const Messages extends ReadonlyArray<Schema.Top>,\n SuccessMessage extends Schema.Schema.Type<Messages[number]>,\n ErrorMessage extends Schema.Schema.Type<Messages[number]>,\n >(\n name: Name,\n ref: Ref_,\n config: NameKeyedFactoryConfig<\n Ref_,\n Messages,\n SuccessMessage,\n ErrorMessage\n >,\n ): InterruptibleDefinition<Name, Ref_, SuccessMessage | ErrorMessage>;\n function factory<\n const Name extends string,\n Ref_ extends BoundRef,\n const Messages extends ReadonlyArray<Schema.Top>,\n SuccessMessage extends Schema.Schema.Type<Messages[number]>,\n ErrorMessage extends Schema.Schema.Type<Messages[number]>,\n >(\n name: Name,\n ref: Ref_,\n config: StandardFactoryConfig<Ref_, Messages, SuccessMessage, ErrorMessage>,\n ): Definition<Name, Ref_, SuccessMessage | ErrorMessage>;\n function factory<\n const Name extends string,\n Ref_ extends BoundRef,\n SuccessMessage,\n ErrorMessage,\n KeyField extends keyof SchemaArgs<Ref_> & string,\n >(\n name: Name,\n ref: Ref_,\n config: FactoryConfig<\n Ref_,\n ReadonlyArray<Schema.Top>,\n SuccessMessage,\n ErrorMessage\n > & {\n readonly interrupt?: InterruptOption<Ref_, KeyField>;\n },\n ): Definition<Name, Ref_, SuccessMessage | ErrorMessage> {\n const runWithArgs = effectHelper(ref, config);\n return Match.value(config.interrupt).pipe(\n Match.withReturnType<\n Definition<Name, Ref_, SuccessMessage | ErrorMessage>\n >(),\n Match.when(undefined, () =>\n makeDefinition(name, ref, config.messages, runWithArgs),\n ),\n Match.when(true, (interrupt) =>\n makeDefinition(name, ref, config.messages, runWithArgs, interrupt),\n ),\n Match.when(Match.defined, (interrupt) =>\n makeDefinition(name, ref, config.messages, runWithArgs, interrupt),\n ),\n Match.exhaustive,\n );\n }\n\n return factory;\n};\n\n/**\n * A Foldkit Command definition for a Confect query whose Command args are the\n * ref's args:\n *\n * ```ts\n * const FetchNote = Command.query(\"FetchNote\", refs.public.notes.get, {\n * messages: [SucceededGetNote, FailedGetNote],\n * onSuccess: (note) => SucceededGetNote({ note }),\n * onError: (error) => FailedGetNote({ message: String(error) }),\n * })\n * // In update:\n * // [model, [FetchNote({ noteId })]]\n * ```\n *\n * Pass `interrupt` to make invocations interruptible via the returned\n * definition's `Interrupt` constructor — `true` keys them by the Command\n * name, `{ keyFields, toKey }` by a part derived from the ref's args:\n *\n * ```ts\n * const SaveDraft = Command.mutation(\"SaveDraft\", refs.public.notes.insert, {\n * messages: [SucceededSaveDraft, FailedSaveDraft],\n * onSuccess: (noteId) => SucceededSaveDraft({ noteId }),\n * onError: (error) => FailedSaveDraft({ message: String(error) }),\n * interrupt: true,\n * })\n * // In update:\n * // [model, [SaveDraft.Interrupt((outcome) => CompletedCancelSaveDraft({ outcome }))]]\n * ```\n */\nexport const query: ReturnType<typeof makeFactory<Ref.AnyConfectPublicQuery>> =\n makeFactory(queryEffect);\n\n/**\n * A Foldkit Command definition for a Confect mutation whose Command args are\n * the ref's args. See `query`.\n */\nexport const mutation: ReturnType<\n typeof makeFactory<Ref.AnyConfectPublicMutation>\n> = makeFactory(mutationEffect);\n\n/**\n * A Foldkit Command definition for a Confect action whose Command args are\n * the ref's args. See `query`.\n */\nexport const action: ReturnType<\n typeof makeFactory<Ref.AnyConfectPublicAction>\n> = makeFactory(actionEffect);\n"],"mappings":";;;;;;;;;;;;;;AAyIA,MAAM,OACJ,UACA,WAIA,OAAO,QAAQA,QAAe,MAAM,CAAC,CAAC,KACpC,OAAO,MAAM;CACX,WAAW,SAAS;CACpB,WAAW,SAAS;AACtB,CAAC,CACH;;;;;;AAOF,MAAa,eAET,KACA,cAGA,GAAG,SAEH,IAAI,WAAW,WAAW,OAAO,MAAM,KAAK,GAAG,IAAI,CAAC;;;;;AAMxD,MAAa,kBAET,KACA,cAGA,GAAG,SAEH,IAAI,WAAW,WAAW,OAAO,SAAS,KAAK,GAAG,IAAI,CAAC;;;;;AAM3D,MAAa,gBAET,KACA,cAGA,GAAG,SAEH,IAAI,WAAW,WAAW,OAAO,OAAO,KAAK,GAAG,IAAI,CAAC;AAMzD,SAAS,eAQP,MACA,KACA,UACA,aACA,WACuD;CAIvD,MAAM,SAAiB,IAAI,KAAK;CAChC,MAAM,WAAW,SACf,YACE,GAAK,SAAS,KAAA,IAAY,CAAC,IAAI,CAAC,IAAI,CACtC;CAEF,OAAO,MAAM,MAAM,SAAS,CAAC,CAAC,KAC5B,MAAM,eAEJ,GACF,MAAM,KACJ,KAAA,SAEE,eAAe,OAAO,MAAM;EAC1B,MAAM;EACN;EACA;CACF,CAAC,CACL,GACA,MAAM,KACJ,OACC,uBACC,eAAe,OAAO,MAAM;EAC1B,MAAM;EACN;EACA,WAAW;EACX;CACF,CAAC,CAKL,GACA,MAAM,KAAK,MAAM,UAAU,mBAAmB;EAW5C,OANI,eAAe,OAAO,MAAM;GAC9B,MAAM;GACN;GACA,WAAW;GACX;EACF,CACgB;CAKlB,CAAC,GACD,MAAM,UACR;AACF;;;;;;AA4CA,MAAM,eACJ,iBAIG;CAmDH,SAAS,QAOP,MACA,KACA,QAQuD;EACvD,MAAM,cAAc,aAAa,KAAK,MAAM;EAC5C,OAAO,MAAM,MAAM,OAAO,SAAS,CAAC,CAAC,KACnC,MAAM,eAEJ,GACF,MAAM,KAAK,KAAA,SACT,eAAe,MAAM,KAAK,OAAO,UAAU,WAAW,CACxD,GACA,MAAM,KAAK,OAAO,cAChB,eAAe,MAAM,KAAK,OAAO,UAAU,aAAa,SAAS,CACnE,GACA,MAAM,KAAK,MAAM,UAAU,cACzB,eAAe,MAAM,KAAK,OAAO,UAAU,aAAa,SAAS,CACnE,GACA,MAAM,UACR;CACF;CAEA,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,MAAa,QACX,YAAY,WAAW;;;;;AAMzB,MAAa,WAET,YAAY,cAAc;;;;;AAM9B,MAAa,SAET,YAAY,YAAY"}
|