@shivaedev/effect-react 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +454 -55
  3. package/dist/client/create.d.ts +19 -0
  4. package/dist/client/create.d.ts.map +1 -0
  5. package/dist/{create.js → client/create.js} +12 -10
  6. package/dist/client/create.js.map +1 -0
  7. package/dist/client/editor.d.ts +22 -0
  8. package/dist/client/editor.d.ts.map +1 -0
  9. package/dist/{editor.js → client/editor.js} +8 -5
  10. package/dist/client/editor.js.map +1 -0
  11. package/dist/client/resultState.d.ts +23 -0
  12. package/dist/client/resultState.d.ts.map +1 -0
  13. package/dist/client/resultState.js +48 -0
  14. package/dist/client/resultState.js.map +1 -0
  15. package/dist/{save-state.d.ts → client/saveState.d.ts} +2 -2
  16. package/dist/client/saveState.d.ts.map +1 -0
  17. package/dist/{save-state.js → client/saveState.js} +5 -5
  18. package/dist/client/saveState.js.map +1 -0
  19. package/dist/client/sessionBoundary.d.ts +12 -0
  20. package/dist/client/sessionBoundary.d.ts.map +1 -0
  21. package/dist/{session-boundary.js → client/sessionBoundary.js} +22 -12
  22. package/dist/client/sessionBoundary.js.map +1 -0
  23. package/dist/client/testing.d.ts +12 -0
  24. package/dist/client/testing.d.ts.map +1 -0
  25. package/dist/client/testing.js +66 -0
  26. package/dist/client/testing.js.map +1 -0
  27. package/dist/client/useDraftOwner.d.ts +3 -0
  28. package/dist/client/useDraftOwner.d.ts.map +1 -0
  29. package/dist/client/useDraftOwner.js +36 -0
  30. package/dist/client/useDraftOwner.js.map +1 -0
  31. package/dist/field-rejection.d.ts.map +1 -1
  32. package/dist/field-rejection.js +3 -1
  33. package/dist/field-rejection.js.map +1 -1
  34. package/dist/loadQuery.d.ts +6 -0
  35. package/dist/loadQuery.d.ts.map +1 -0
  36. package/dist/loadQuery.js +21 -0
  37. package/dist/loadQuery.js.map +1 -0
  38. package/dist/submission.d.ts +2 -0
  39. package/dist/submission.d.ts.map +1 -1
  40. package/dist/submission.js +1 -0
  41. package/dist/submission.js.map +1 -1
  42. package/docs/north-star.md +45 -0
  43. package/docs/roadmap.md +28 -0
  44. package/package.json +123 -21
  45. package/src/client/create.ts +44 -0
  46. package/src/client/editor.ts +60 -0
  47. package/src/client/resultState.ts +73 -0
  48. package/src/{save-state.ts → client/saveState.ts} +11 -10
  49. package/src/client/sessionBoundary.ts +88 -0
  50. package/src/client/testing.ts +68 -0
  51. package/src/client/useDraftOwner.ts +42 -0
  52. package/src/field-rejection.ts +3 -1
  53. package/src/loadQuery.ts +27 -0
  54. package/src/submission.ts +3 -0
  55. package/dist/create.d.ts +0 -17
  56. package/dist/create.d.ts.map +0 -1
  57. package/dist/create.js.map +0 -1
  58. package/dist/editor.d.ts +0 -20
  59. package/dist/editor.d.ts.map +0 -1
  60. package/dist/editor.js.map +0 -1
  61. package/dist/result-state.d.ts +0 -20
  62. package/dist/result-state.d.ts.map +0 -1
  63. package/dist/result-state.js +0 -36
  64. package/dist/result-state.js.map +0 -1
  65. package/dist/save-state.d.ts.map +0 -1
  66. package/dist/save-state.js.map +0 -1
  67. package/dist/session-boundary.d.ts +0 -12
  68. package/dist/session-boundary.d.ts.map +0 -1
  69. package/dist/session-boundary.js.map +0 -1
  70. package/src/create.ts +0 -43
  71. package/src/editor.ts +0 -58
  72. package/src/result-state.ts +0 -58
  73. package/src/session-boundary.ts +0 -84
@@ -0,0 +1,42 @@
1
+ import { useInsertionEffect, useRef, useState } from "react";
2
+ import { type DraftSession, draftSession } from "@shivaedev/effect-form/draftSession.ts";
3
+
4
+ interface DraftOwner {
5
+ readonly activate: () => void;
6
+ readonly session: DraftSession;
7
+ }
8
+
9
+ function owner(id: string, active: boolean): DraftOwner {
10
+ const session = draftSession(id);
11
+ let committed = active;
12
+ return {
13
+ activate: () => {
14
+ committed = true;
15
+ },
16
+ session: {
17
+ ...session,
18
+ get active() {
19
+ return committed && session.active;
20
+ },
21
+ },
22
+ };
23
+ }
24
+
25
+ export function useDraftOwner(id: string | undefined): DraftSession | undefined {
26
+ const [held, hold] = useState(() => (id === undefined ? undefined : owner(id, true)));
27
+ const committedRef = useRef(held);
28
+ let current = held;
29
+ if (current?.session.id !== id) {
30
+ current = id === undefined ? undefined : owner(id, committedRef.current === undefined);
31
+ hold(current);
32
+ }
33
+ // Insertion effects also run for Activity commits while layout and passive effects are disconnected.
34
+ useInsertionEffect(() => {
35
+ if (committedRef.current !== current) {
36
+ committedRef.current?.session.drop();
37
+ current?.activate();
38
+ committedRef.current = current;
39
+ }
40
+ }, [current]);
41
+ return current?.session;
42
+ }
@@ -25,7 +25,9 @@ export type FieldRejectionMapping<F extends Fields, E> = { readonly rejectField?
25
25
  export const fieldRejectionOf =
26
26
  <F extends Fields>(schema: Schema.Struct<F>) =>
27
27
  (error: unknown): FieldRejection<Name<F>> | undefined => {
28
- const isName = (name: unknown): name is Name<F> => typeof name === "string" && Object.hasOwn(schema.fields, name);
28
+ function isName(name: unknown): name is Name<F> {
29
+ return typeof name === "string" && Object.hasOwn(schema.fields, name);
30
+ }
29
31
  if (!(Predicate.hasProperty(error, "_tag") && Predicate.hasProperty(error, "field") && Predicate.hasProperty(error, "message"))) {
30
32
  return undefined;
31
33
  }
@@ -0,0 +1,27 @@
1
+ import type { LoaderFnContext } from "@tanstack/react-router";
2
+ import { Effect } from "effect";
3
+ import type * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
4
+ import type * as Atom from "effect/unstable/reactivity/Atom";
5
+ import * as AtomRegistry from "effect/unstable/reactivity/AtomRegistry";
6
+
7
+ export async function loadQuery<A, E>(
8
+ registry: AtomRegistry.AtomRegistry,
9
+ atom: Atom.Atom<AsyncResult.AsyncResult<A, E>>,
10
+ { abortController }: Pick<LoaderFnContext, "abortController">,
11
+ ): Promise<A> {
12
+ const { signal } = abortController;
13
+ try {
14
+ const result = await Effect.runPromise(Effect.result(AtomRegistry.getResult(registry, atom, { suspendOnWaiting: true })), { signal });
15
+ if (result._tag === "Failure") {
16
+ throw result.failure;
17
+ }
18
+ return result.success;
19
+ } catch (error) {
20
+ if (signal.aborted) {
21
+ const canceled = new Error("Query loading canceled", { cause: error });
22
+ canceled.name = "AbortError";
23
+ throw canceled;
24
+ }
25
+ throw error;
26
+ }
27
+ }
package/src/submission.ts CHANGED
@@ -1,12 +1,14 @@
1
1
  import type { Effect, Schema } from "effect";
2
2
  import type * as Atom from "effect/unstable/reactivity/Atom";
3
3
  import { make } from "@shivaedev/effect-form/form.ts";
4
+ import type { PersistedDraftConfig } from "@shivaedev/effect-form/persistence.ts";
4
5
  import type { Decoded, Encoded, FieldFailure, Fields, Form, Services } from "@shivaedev/effect-form/shape.ts";
5
6
  import { type AtomServices, type RejectField, rejecting } from "./field-rejection.ts";
6
7
 
7
8
  export interface SubmissionConfig<F extends Fields, A, E, R, ER> {
8
9
  readonly fields: Schema.Struct<F>;
9
10
  readonly initialValues: Encoded<F>;
11
+ readonly persistedDraft?: PersistedDraftConfig | undefined;
10
12
  readonly rejectField: RejectField<F, E> | undefined;
11
13
  readonly runtime: Atom.AtomRuntime<Services<F, R>, ER>;
12
14
  readonly save: (values: Decoded<F>) => Effect.Effect<A, E, AtomServices<R>>;
@@ -27,6 +29,7 @@ export const submission = <F extends Fields, A, E, R, ER>(config: SubmissionConf
27
29
  submitted = snapshot;
28
30
  return save(values, submitter);
29
31
  },
32
+ persistedDraft: config.persistedDraft,
30
33
  runtime,
31
34
  });
32
35
  return { form, submitted: () => submitted };
package/dist/create.d.ts DELETED
@@ -1,17 +0,0 @@
1
- import { type Effect, Option, type Schema } from "effect";
2
- import type * as Atom from "effect/unstable/reactivity/Atom";
3
- import type { Decoded, Encoded, FieldFailure, Fields, Form, Services } from "@shivaedev/effect-form/shape.ts";
4
- import type { AtomServices, FieldRejectionMapping } from "./field-rejection.ts";
5
- import { type SaveState } from "./save-state.ts";
6
- export type CreateConfig<F extends Fields, Created, SE, R, ER> = {
7
- readonly fields: Schema.Struct<F>;
8
- readonly initialValues: Encoded<F>;
9
- readonly create: (values: Decoded<F>) => Effect.Effect<Created, SE, AtomServices<R>>;
10
- readonly runtime: Atom.AtomRuntime<Services<F, R>, ER>;
11
- } & FieldRejectionMapping<F, SE>;
12
- export interface Create<F extends Fields, Created, SE, ER> extends SaveState<SE | ER> {
13
- readonly created: Option.Option<Created>;
14
- readonly form: Form<F, Created, SE | FieldFailure, ER>;
15
- }
16
- export declare const useCreate: <F extends Fields, Created, SE, R, ER>(config: CreateConfig<F, Created, SE, R, ER>) => Create<F, Created, SE, ER>;
17
- //# sourceMappingURL=create.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAS,MAAM,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AACjE,OAAO,KAAK,KAAK,IAAI,MAAM,iCAAiC,CAAC;AAE7D,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAC9G,OAAO,KAAK,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,EAAE,KAAK,SAAS,EAAgB,MAAM,iBAAiB,CAAC;AAG/D,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,IAAI;IAChE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IACrF,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;CACvD,GAAG,qBAAqB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAEjC,MAAM,WAAW,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,CAAE,SAAQ,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC;IACpF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACzC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,GAAG,YAAY,EAAE,EAAE,CAAC,CAAC;CACvD;AAOD,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,KAAG,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,CAiBtI,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"create.js","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,EAAE,MAAM,EAAe,MAAM,QAAQ,CAAC;AAEjE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAG9C,OAAO,EAAkB,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAc7C,MAAM,WAAW,GAAG,CAAmB,OAAU,EAAE,SAAwB,EAAqC,EAAE;IACjH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,CAAC,CAAC;IAC7D,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AACtH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,CAAuC,MAA2C,EAA8B,EAAE;IAC1I,MAAM,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE;QAChD,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;QACvE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC;IAClH,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC3C,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAyB,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1E,MAAM,OAAO,GAAG,WAAW,CAC1B,CAAC,KAAc,EAAE,EAAE;QAClB,MAAM,IAAI,GAAG,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,aAAa,EAAE,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3G,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3B,OAAO,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,EACD,CAAC,OAAO,EAAE,KAAK,EAAE,aAAa,CAAC,CAC/B,CAAC;IACF,OAAO,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;AAChF,CAAC,CAAC"}
package/dist/editor.d.ts DELETED
@@ -1,20 +0,0 @@
1
- import { type Effect, type Schema } from "effect";
2
- import type * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
3
- import type * as Atom from "effect/unstable/reactivity/Atom";
4
- import type { Decoded, Encoded, FieldFailure, Fields, Form, Services } from "@shivaedev/effect-form/shape.ts";
5
- import type { AtomServices, FieldRejectionMapping } from "./field-rejection.ts";
6
- import { type QueryState } from "./result-state.ts";
7
- import { type SaveState } from "./save-state.ts";
8
- export type EditorConfig<F extends Fields, Row, QE, SE, R, ER> = {
9
- readonly query: Atom.Atom<AsyncResult.AsyncResult<Row, QE>>;
10
- readonly fields: Schema.Struct<F>;
11
- readonly values: (row: Row) => Encoded<F>;
12
- readonly save: (values: Decoded<F>) => Effect.Effect<Row, SE, AtomServices<R>>;
13
- readonly runtime: Atom.AtomRuntime<Services<F, R>, ER>;
14
- } & FieldRejectionMapping<F, SE>;
15
- export interface Editor<F extends Fields, Row, QE, SE, ER> extends SaveState<SE | ER> {
16
- readonly form: Form<F, Row, SE | FieldFailure, ER> | undefined;
17
- readonly query: QueryState<Row, QE>;
18
- }
19
- export declare const useEditor: <F extends Fields, Row, QE, SE, R, ER>(config: EditorConfig<F, Row, QE, SE, R, ER>) => Editor<F, Row, QE, SE, ER>;
20
- //# sourceMappingURL=editor.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAU,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,KAAK,KAAK,WAAW,MAAM,wCAAwC,CAAC;AAC3E,OAAO,KAAK,KAAK,IAAI,MAAM,iCAAiC,CAAC;AAE7D,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAC9G,OAAO,KAAK,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,EAAE,KAAK,UAAU,EAAY,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,KAAK,SAAS,EAAgB,MAAM,iBAAiB,CAAC;AAG/D,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,IAAI;IAChE,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;IAC5D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAC1C,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/E,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;CACvD,GAAG,qBAAqB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAEjC,MAAM,WAAW,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAE,SAAQ,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC;IACpF,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,YAAY,EAAE,EAAE,CAAC,GAAG,SAAS,CAAC;IAC/D,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;CACpC;AAQD,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,YAAY,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,KAAG,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CA4BtI,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"editor.js","sourceRoot":"","sources":["../src/editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,MAAM,EAAe,MAAM,QAAQ,CAAC;AAG1D,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAGzD,OAAO,EAAmB,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAkB,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAqB7C,MAAM,CAAC,MAAM,SAAS,GAAG,CAAuC,MAA2C,EAA8B,EAAE;IAC1I,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,GAAG,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC9C,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,QAAQ,CAAmC,SAAS,CAAC,CAAC;IAC3E,IAAI,OAAO,GAAG,IAAI,EAAE,KAAK,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9D,IAAI,OAAO,KAAK,SAAS,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QAChD,OAAO,GAAG;YACT,IAAI,EAAE,UAAU,CAAC;gBAChB,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC;gBACjC,WAAW,EAAE,MAAM,CAAC,WAAW;gBAC/B,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,IAAI,EAAE,MAAM,CAAC,IAAI;aACjB,CAAC,CAAC,IAAI;YACP,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,MAAM,EAAE,MAAM,CAAC,MAAM;SACrB,CAAC;QACF,IAAI,CAAC,OAAO,CAAC,CAAC;IACf,CAAC;IACD,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,CAAC;IAC3B,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,CAAC;IAC/B,SAAS,CAAC,GAAG,EAAE;QACd,IAAI,IAAI,IAAI,MAAM,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACzC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3B,CAAC;IACF,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;IACxB,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,KAAU,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IACxH,OAAO,EAAE,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACxD,CAAC,CAAC"}
@@ -1,20 +0,0 @@
1
- import { Cause, Option } from "effect";
2
- import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
3
- import type * as Atom from "effect/unstable/reactivity/Atom";
4
- export interface ResultState<A, E> {
5
- readonly cause: Option.Option<Cause.Cause<E>>;
6
- readonly data: Option.Option<A>;
7
- readonly pending: boolean;
8
- readonly refreshing: boolean;
9
- readonly result: AsyncResult.AsyncResult<A, E>;
10
- }
11
- export declare const isUnauthorized: <E>(cause: Cause.Cause<E>) => boolean;
12
- export interface QueryState<A, E> extends ResultState<A, E> {
13
- readonly refresh: () => void;
14
- }
15
- export declare const useQuery: <A, E>(atom: Atom.Atom<AsyncResult.AsyncResult<A, E>>) => QueryState<A, E>;
16
- export interface ActionState<Input, A, E> extends ResultState<A, E> {
17
- readonly dispatch: (input: Input) => void;
18
- }
19
- export declare const useAction: <Input, A, E>(atom: Atom.AtomResultFn<Input, A, E>) => ActionState<Input, A, E>;
20
- //# sourceMappingURL=result-state.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"result-state.d.ts","sourceRoot":"","sources":["../src/result-state.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAa,MAAM,QAAQ,CAAC;AAClD,OAAO,KAAK,WAAW,MAAM,wCAAwC,CAAC;AACtE,OAAO,KAAK,KAAK,IAAI,MAAM,iCAAiC,CAAC;AAI7D,MAAM,WAAW,WAAW,CAAC,CAAC,EAAE,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CAC/C;AAaD,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAG,OACwE,CAAC;AAWnI,MAAM,WAAW,UAAU,CAAC,CAAC,EAAE,CAAC,CAAE,SAAQ,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC;IAC1D,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED,eAAO,MAAM,QAAQ,GAAI,CAAC,EAAE,CAAC,EAAE,MAAM,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAG,UAAU,CAAC,CAAC,EAAE,CAAC,CAI9F,CAAC;AAEF,MAAM,WAAW,WAAW,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAE,SAAQ,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC;IAClE,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAC1C;AAGD,eAAO,MAAM,SAAS,GAAI,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,KAAG,WAAW,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAIpG,CAAC"}
@@ -1,36 +0,0 @@
1
- import { useAtomRefresh, useAtomSet, useAtomValue } from "@effect/atom-react";
2
- import { Cause, Option, Predicate } from "effect";
3
- import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
4
- import { useEffect } from "react";
5
- import { useSessionRecheck } from "./session-boundary.js";
6
- const state = (result) => {
7
- const data = AsyncResult.value(result);
8
- return {
9
- cause: AsyncResult.cause(result),
10
- data,
11
- pending: result.waiting,
12
- refreshing: result.waiting && Option.isSome(data),
13
- result,
14
- };
15
- };
16
- export const isUnauthorized = (cause) => Option.match(Cause.findErrorOption(cause), { onNone: () => false, onSome: (error) => Predicate.isTagged(error, "Unauthorized") });
17
- const useRecheckOnUnauthorized = (result) => {
18
- const recheck = useSessionRecheck();
19
- useEffect(() => {
20
- if (AsyncResult.isFailure(result) && isUnauthorized(result.cause)) {
21
- recheck();
22
- }
23
- }, [recheck, result]);
24
- };
25
- export const useQuery = (atom) => {
26
- const result = useAtomValue(atom);
27
- useRecheckOnUnauthorized(result);
28
- return { ...state(result), refresh: useAtomRefresh(atom) };
29
- };
30
- // The atom's state is shared by every dispatch, so it never claims to be one invocation's completion result.
31
- export const useAction = (atom) => {
32
- const result = useAtomValue(atom);
33
- useRecheckOnUnauthorized(result);
34
- return { ...state(result), dispatch: useAtomSet(atom) };
35
- };
36
- //# sourceMappingURL=result-state.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"result-state.js","sourceRoot":"","sources":["../src/result-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,KAAK,WAAW,MAAM,wCAAwC,CAAC;AAEtE,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAClC,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAU1D,MAAM,KAAK,GAAG,CAAO,MAAqC,EAAqB,EAAE;IAChF,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACvC,OAAO;QACN,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC;QAChC,IAAI;QACJ,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,UAAU,EAAE,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;QACjD,MAAM;KACN,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAI,KAAqB,EAAW,EAAE,CACnE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC,EAAE,CAAC,CAAC;AAEnI,MAAM,wBAAwB,GAAG,CAAO,MAAqC,EAAQ,EAAE;IACtF,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;IACpC,SAAS,CAAC,GAAG,EAAE;QACd,IAAI,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACnE,OAAO,EAAE,CAAC;QACX,CAAC;IACF,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;AACvB,CAAC,CAAC;AAMF,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAO,IAA8C,EAAoB,EAAE;IAClG,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAClC,wBAAwB,CAAC,MAAM,CAAC,CAAC;IACjC,OAAO,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;AAC5D,CAAC,CAAC;AAMF,6GAA6G;AAC7G,MAAM,CAAC,MAAM,SAAS,GAAG,CAAc,IAAoC,EAA4B,EAAE;IACxG,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAClC,wBAAwB,CAAC,MAAM,CAAC,CAAC;IACjC,OAAO,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;AACzD,CAAC,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"save-state.d.ts","sourceRoot":"","sources":["../src/save-state.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIhC,OAAO,EAAE,YAAY,EAAE,KAAK,MAAM,EAAE,KAAK,IAAI,EAAW,MAAM,iCAAiC,CAAC;AAOhG,MAAM,WAAW,SAAS,CAAC,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CACzB;AAED,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EACtD,MAAM,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,YAAY,EAAE,EAAE,CAAC,GAAG,SAAS,EAClD,SAAS,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI,KAC1B,SAAS,CAAC,CAAC,GAAG,EAAE,CA6BlB,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"save-state.js","sourceRoot":"","sources":["../src/save-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,KAAK,WAAW,MAAM,wCAAwC,CAAC;AACtE,OAAO,KAAK,IAAI,MAAM,iCAAiC,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC3D,OAAO,EAAE,YAAY,EAA0B,OAAO,EAAE,MAAM,iCAAiC,CAAC;AAChG,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC;AAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAU/B,MAAM,CAAC,MAAM,YAAY,GAAG,CAC3B,IAAkD,EAClD,OAA4B,EACR,EAAE;IACtB,MAAM,QAAQ,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;IACpC,MAAM,MAAM,GAAgE,YAAY,CAAC,IAAI,EAAE,MAAM,IAAI,IAAI,CAAC,CAAC;IAC/G,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,EAAE,KAAK,IAAI,KAAK,CAAC,CAAC;IACjD,SAAS,CAAC,GAAG,EAAE;QACd,IAAI,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACnE,OAAO,EAAE,CAAC;QACX,CAAC;IACF,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;IACtB,SAAS,CACR,GAAG,EAAE,CACJ,IAAI;WACD,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YAC3C,IAAI,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;gBAClD,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACrB,CAAC;QACF,CAAC,CAAC,EACH,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CACzB,CAAC;IACF,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE;QAC7B,IAAI,IAAI,KAAK,SAAS,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC;YAC7D,OAAO;QACR,CAAC;QACD,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACtC,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IACrB,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IACzD,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAmB,EAAE,CAAC,CAAC,CAAC,KAAK,YAAY,YAAY,IAAI,KAAK,YAAY,OAAO,CAAC,CAAC,CAAC;IACnJ,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;AACjE,CAAC,CAAC"}
@@ -1,12 +0,0 @@
1
- import { type ReactNode } from "react";
2
- export interface SessionBoundaryProps<S, Client> {
3
- readonly children: (client: Client) => ReactNode;
4
- readonly connect: (session: S) => Client;
5
- readonly identify: (session: S) => string;
6
- readonly recheck: () => void;
7
- readonly session: S | undefined;
8
- readonly signedOut?: ReactNode;
9
- }
10
- export declare const useSessionRecheck: () => (() => void);
11
- export declare const SessionBoundary: <S, Client>({ session, identify, connect, recheck, signedOut, children, }: SessionBoundaryProps<S, Client>) => ReactNode;
12
- //# sourceMappingURL=session-boundary.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"session-boundary.d.ts","sourceRoot":"","sources":["../src/session-boundary.ts"],"names":[],"mappings":"AAEA,OAAO,EAA0C,KAAK,SAAS,EAAmC,MAAM,OAAO,CAAC;AAEhH,MAAM,WAAW,oBAAoB,CAAC,CAAC,EAAE,MAAM;IAC9C,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,SAAS,CAAC;IACjD,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,MAAM,CAAC;IACzC,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,MAAM,CAAC;IAC1C,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,SAAS,CAAC;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;CAC/B;AAID,eAAO,MAAM,iBAAiB,QAAO,CAAC,MAAM,IAAI,CAAwB,CAAC;AA0DzE,eAAO,MAAM,eAAe,GAAI,CAAC,EAAE,MAAM,EAAE,+DAOxC,oBAAoB,CAAC,CAAC,EAAE,MAAM,CAAC,KAAG,SAGoE,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"session-boundary.js","sourceRoot":"","sources":["../src/session-boundary.ts"],"names":[],"mappings":"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"}
package/src/create.ts DELETED
@@ -1,43 +0,0 @@
1
- import { type Effect, Equal, Option, type Schema } from "effect";
2
- import type * as Atom from "effect/unstable/reactivity/Atom";
3
- import { useCallback, useState } from "react";
4
- import type { Decoded, Encoded, FieldFailure, Fields, Form, Services } from "@shivaedev/effect-form/shape.ts";
5
- import type { AtomServices, FieldRejectionMapping } from "./field-rejection.ts";
6
- import { type SaveState, useSaveState } from "./save-state.ts";
7
- import { submission } from "./submission.ts";
8
-
9
- export type CreateConfig<F extends Fields, Created, SE, R, ER> = {
10
- readonly fields: Schema.Struct<F>;
11
- readonly initialValues: Encoded<F>;
12
- readonly create: (values: Decoded<F>) => Effect.Effect<Created, SE, AtomServices<R>>;
13
- readonly runtime: Atom.AtomRuntime<Services<F, R>, ER>;
14
- } & FieldRejectionMapping<F, SE>;
15
-
16
- export interface Create<F extends Fields, Created, SE, ER> extends SaveState<SE | ER> {
17
- readonly created: Option.Option<Created>;
18
- readonly form: Form<F, Created, SE | FieldFailure, ER>;
19
- }
20
-
21
- const editedSince = <V extends object>(current: V, submitted: V | undefined): Readonly<Record<string, unknown>> => {
22
- const before = new Map(Object.entries(submitted ?? current));
23
- return Object.fromEntries(Object.entries(current).filter(([name, value]) => !Equal.equals(value, before.get(name))));
24
- };
25
-
26
- export const useCreate = <F extends Fields, Created, SE, R, ER>(config: CreateConfig<F, Created, SE, R, ER>): Create<F, Created, SE, ER> => {
27
- const [{ initialValues, fresh }] = useState(() => {
28
- const { fields, initialValues, runtime, create, rejectField } = config;
29
- return { fresh: () => submission({ fields, initialValues, rejectField, runtime, save: create }), initialValues };
30
- });
31
- const [current, replace] = useState(fresh);
32
- const [created, record] = useState<Option.Option<Created>>(Option.none());
33
- const settled = useCallback(
34
- (value: Created) => {
35
- const next = fresh();
36
- next.form.values.set({ ...initialValues, ...editedSince(current.form.values.value, current.submitted()) });
37
- record(Option.some(value));
38
- replace(next);
39
- },
40
- [current, fresh, initialValues],
41
- );
42
- return { ...useSaveState(current.form, settled), created, form: current.form };
43
- };
package/src/editor.ts DELETED
@@ -1,58 +0,0 @@
1
- import { type Effect, Option, type Schema } from "effect";
2
- import type * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
3
- import type * as Atom from "effect/unstable/reactivity/Atom";
4
- import { useCallback, useEffect, useState } from "react";
5
- import type { Decoded, Encoded, FieldFailure, Fields, Form, Services } from "@shivaedev/effect-form/shape.ts";
6
- import type { AtomServices, FieldRejectionMapping } from "./field-rejection.ts";
7
- import { type QueryState, useQuery } from "./result-state.ts";
8
- import { type SaveState, useSaveState } from "./save-state.ts";
9
- import { submission } from "./submission.ts";
10
-
11
- export type EditorConfig<F extends Fields, Row, QE, SE, R, ER> = {
12
- readonly query: Atom.Atom<AsyncResult.AsyncResult<Row, QE>>;
13
- readonly fields: Schema.Struct<F>;
14
- readonly values: (row: Row) => Encoded<F>;
15
- readonly save: (values: Decoded<F>) => Effect.Effect<Row, SE, AtomServices<R>>;
16
- readonly runtime: Atom.AtomRuntime<Services<F, R>, ER>;
17
- } & FieldRejectionMapping<F, SE>;
18
-
19
- export interface Editor<F extends Fields, Row, QE, SE, ER> extends SaveState<SE | ER> {
20
- readonly form: Form<F, Row, SE | FieldFailure, ER> | undefined;
21
- readonly query: QueryState<Row, QE>;
22
- }
23
-
24
- interface Held<F extends Fields, Row, SE, ER> {
25
- readonly form: Form<F, Row, SE | FieldFailure, ER>;
26
- readonly query: Atom.Atom<unknown>;
27
- readonly values: (row: Row) => Encoded<F>;
28
- }
29
-
30
- export const useEditor = <F extends Fields, Row, QE, SE, R, ER>(config: EditorConfig<F, Row, QE, SE, R, ER>): Editor<F, Row, QE, SE, ER> => {
31
- const query = useQuery(config.query);
32
- const row = Option.getOrUndefined(query.data);
33
- const [held, hold] = useState<Held<F, Row, SE, ER> | undefined>(undefined);
34
- let current = held?.query === config.query ? held : undefined;
35
- if (current === undefined && row !== undefined) {
36
- current = {
37
- form: submission({
38
- fields: config.fields,
39
- initialValues: config.values(row),
40
- rejectField: config.rejectField,
41
- runtime: config.runtime,
42
- save: config.save,
43
- }).form,
44
- query: config.query,
45
- values: config.values,
46
- };
47
- hold(current);
48
- }
49
- const form = current?.form;
50
- const values = current?.values;
51
- useEffect(() => {
52
- if (form && values && row !== undefined) {
53
- form.receive(values(row));
54
- }
55
- }, [form, values, row]);
56
- const settled = useCallback((saved: Row) => (form && values ? form.receive(values(saved)) : undefined), [form, values]);
57
- return { ...useSaveState(form, settled), form, query };
58
- };
@@ -1,58 +0,0 @@
1
- import { useAtomRefresh, useAtomSet, useAtomValue } from "@effect/atom-react";
2
- import { Cause, Option, Predicate } from "effect";
3
- import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
4
- import type * as Atom from "effect/unstable/reactivity/Atom";
5
- import { useEffect } from "react";
6
- import { useSessionRecheck } from "./session-boundary.ts";
7
-
8
- export interface ResultState<A, E> {
9
- readonly cause: Option.Option<Cause.Cause<E>>;
10
- readonly data: Option.Option<A>;
11
- readonly pending: boolean;
12
- readonly refreshing: boolean;
13
- readonly result: AsyncResult.AsyncResult<A, E>;
14
- }
15
-
16
- const state = <A, E>(result: AsyncResult.AsyncResult<A, E>): ResultState<A, E> => {
17
- const data = AsyncResult.value(result);
18
- return {
19
- cause: AsyncResult.cause(result),
20
- data,
21
- pending: result.waiting,
22
- refreshing: result.waiting && Option.isSome(data),
23
- result,
24
- };
25
- };
26
-
27
- export const isUnauthorized = <E>(cause: Cause.Cause<E>): boolean =>
28
- Option.match(Cause.findErrorOption(cause), { onNone: () => false, onSome: (error) => Predicate.isTagged(error, "Unauthorized") });
29
-
30
- const useRecheckOnUnauthorized = <A, E>(result: AsyncResult.AsyncResult<A, E>): void => {
31
- const recheck = useSessionRecheck();
32
- useEffect(() => {
33
- if (AsyncResult.isFailure(result) && isUnauthorized(result.cause)) {
34
- recheck();
35
- }
36
- }, [recheck, result]);
37
- };
38
-
39
- export interface QueryState<A, E> extends ResultState<A, E> {
40
- readonly refresh: () => void;
41
- }
42
-
43
- export const useQuery = <A, E>(atom: Atom.Atom<AsyncResult.AsyncResult<A, E>>): QueryState<A, E> => {
44
- const result = useAtomValue(atom);
45
- useRecheckOnUnauthorized(result);
46
- return { ...state(result), refresh: useAtomRefresh(atom) };
47
- };
48
-
49
- export interface ActionState<Input, A, E> extends ResultState<A, E> {
50
- readonly dispatch: (input: Input) => void;
51
- }
52
-
53
- // The atom's state is shared by every dispatch, so it never claims to be one invocation's completion result.
54
- export const useAction = <Input, A, E>(atom: Atom.AtomResultFn<Input, A, E>): ActionState<Input, A, E> => {
55
- const result = useAtomValue(atom);
56
- useRecheckOnUnauthorized(result);
57
- return { ...state(result), dispatch: useAtomSet(atom) };
58
- };
@@ -1,84 +0,0 @@
1
- import { RegistryContext } from "@effect/atom-react";
2
- import * as AtomRegistry from "effect/unstable/reactivity/AtomRegistry";
3
- import { createContext, createElement, Fragment, type ReactNode, useContext, useEffect, useState } from "react";
4
-
5
- export interface SessionBoundaryProps<S, Client> {
6
- readonly children: (client: Client) => ReactNode;
7
- readonly connect: (session: S) => Client;
8
- readonly identify: (session: S) => string;
9
- readonly recheck: () => void;
10
- readonly session: S | undefined;
11
- readonly signedOut?: ReactNode;
12
- }
13
-
14
- const Recheck = createContext<() => void>(() => {});
15
-
16
- export const useSessionRecheck = (): (() => void) => useContext(Recheck);
17
-
18
- interface Owned<Client> {
19
- readonly client: Client;
20
- mounted: number;
21
- readonly registry: AtomRegistry.AtomRegistry;
22
- }
23
-
24
- const owning = <Client>(client: Client): Owned<Client> => ({ client, mounted: 0, registry: AtomRegistry.make() });
25
-
26
- // No cleanup runs when a hidden <Activity> subtree is unmounted, so nodes a hidden render creates must not outlive it.
27
- const hidden = <Client>(client: Client): Owned<Client> => {
28
- const owned = owning(client);
29
- let reaping = false;
30
- owned.registry.onNodeAdded = () => {
31
- if (reaping || owned.mounted > 0) {
32
- return;
33
- }
34
- reaping = true;
35
- setTimeout(() => {
36
- reaping = false;
37
- if (owned.mounted === 0) {
38
- owned.registry.reset();
39
- }
40
- }, 0);
41
- };
42
- return owned;
43
- };
44
-
45
- interface GenerationProps<S, Client> {
46
- readonly children: (client: Client) => ReactNode;
47
- readonly connect: (session: S) => Client;
48
- readonly recheck: () => void;
49
- readonly session: S;
50
- }
51
-
52
- const Generation = <S, Client>({ session, connect, recheck, children }: GenerationProps<S, Client>): ReactNode => {
53
- const [owned, own] = useState(() => owning(connect(session)));
54
- useEffect(() => {
55
- owned.mounted += 1;
56
- return () => {
57
- owned.mounted -= 1;
58
- queueMicrotask(() => {
59
- if (owned.mounted > 0) {
60
- return;
61
- }
62
- owned.registry.dispose();
63
- own((current) => (current === owned ? hidden(owned.client) : current));
64
- });
65
- };
66
- }, [owned]);
67
- return createElement(
68
- Recheck.Provider,
69
- { value: recheck },
70
- createElement(RegistryContext.Provider, { value: owned.registry }, children(owned.client)),
71
- );
72
- };
73
-
74
- export const SessionBoundary = <S, Client>({
75
- session,
76
- identify,
77
- connect,
78
- recheck,
79
- signedOut,
80
- children,
81
- }: SessionBoundaryProps<S, Client>): ReactNode =>
82
- session === undefined
83
- ? createElement(Fragment, null, signedOut)
84
- : createElement(Generation<S, Client>, { children, connect, key: identify(session), recheck, session });