@shivaedev/effect-react 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +452 -55
  3. package/dist/client/create.d.ts +17 -0
  4. package/dist/client/create.d.ts.map +1 -0
  5. package/dist/{create.js → client/create.js} +9 -9
  6. package/dist/client/create.js.map +1 -0
  7. package/dist/client/editor.d.ts +20 -0
  8. package/dist/client/editor.d.ts.map +1 -0
  9. package/dist/{editor.js → client/editor.js} +5 -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} +18 -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/field-rejection.d.ts.map +1 -1
  28. package/dist/field-rejection.js +3 -1
  29. package/dist/field-rejection.js.map +1 -1
  30. package/dist/loadQuery.d.ts +6 -0
  31. package/dist/loadQuery.d.ts.map +1 -0
  32. package/dist/loadQuery.js +21 -0
  33. package/dist/loadQuery.js.map +1 -0
  34. package/dist/submission.d.ts.map +1 -1
  35. package/docs/north-star.md +45 -0
  36. package/docs/roadmap.md +28 -0
  37. package/package.json +122 -16
  38. package/src/client/create.ts +41 -0
  39. package/src/client/editor.ts +56 -0
  40. package/src/client/resultState.ts +73 -0
  41. package/src/{save-state.ts → client/saveState.ts} +11 -10
  42. package/src/{session-boundary.ts → client/sessionBoundary.ts} +28 -32
  43. package/src/client/testing.ts +68 -0
  44. package/src/field-rejection.ts +3 -1
  45. package/src/loadQuery.ts +27 -0
  46. package/dist/create.d.ts +0 -17
  47. package/dist/create.d.ts.map +0 -1
  48. package/dist/create.js.map +0 -1
  49. package/dist/editor.d.ts +0 -20
  50. package/dist/editor.d.ts.map +0 -1
  51. package/dist/editor.js.map +0 -1
  52. package/dist/result-state.d.ts +0 -20
  53. package/dist/result-state.d.ts.map +0 -1
  54. package/dist/result-state.js +0 -36
  55. package/dist/result-state.js.map +0 -1
  56. package/dist/save-state.d.ts.map +0 -1
  57. package/dist/save-state.js.map +0 -1
  58. package/dist/session-boundary.d.ts +0 -12
  59. package/dist/session-boundary.d.ts.map +0 -1
  60. package/dist/session-boundary.js.map +0 -1
  61. package/src/create.ts +0 -43
  62. package/src/editor.ts +0 -58
  63. package/src/result-state.ts +0 -58
@@ -1,13 +1,13 @@
1
1
  import { Equal, Option } from "effect";
2
2
  import { useCallback, useState } from "react";
3
- import { useSaveState } from "./save-state.js";
4
- import { submission } from "./submission.js";
5
- const editedSince = (current, submitted) => {
3
+ import { submission } from "#submission.ts";
4
+ import { useSaveState } from "./saveState.js";
5
+ function editedSince(current, submitted) {
6
6
  const before = new Map(Object.entries(submitted ?? current));
7
7
  return Object.fromEntries(Object.entries(current).filter(([name, value]) => !Equal.equals(value, before.get(name))));
8
- };
9
- export const useCreate = (config) => {
10
- const [{ initialValues, fresh }] = useState(() => {
8
+ }
9
+ export function useCreate(config) {
10
+ const [{ initialValues: baseline, fresh }] = useState(() => {
11
11
  const { fields, initialValues, runtime, create, rejectField } = config;
12
12
  return { fresh: () => submission({ fields, initialValues, rejectField, runtime, save: create }), initialValues };
13
13
  });
@@ -15,10 +15,10 @@ export const useCreate = (config) => {
15
15
  const [created, record] = useState(Option.none());
16
16
  const settled = useCallback((value) => {
17
17
  const next = fresh();
18
- next.form.values.set({ ...initialValues, ...editedSince(current.form.values.value, current.submitted()) });
18
+ next.form.values.set({ ...baseline, ...editedSince(current.form.values.value, current.submitted()) });
19
19
  record(Option.some(value));
20
20
  replace(next);
21
- }, [current, fresh, initialValues]);
21
+ }, [current, fresh, baseline]);
22
22
  return { ...useSaveState(current.form, settled), created, form: current.form };
23
- };
23
+ }
24
24
  //# sourceMappingURL=create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/client/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,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAkB,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAW9D,SAAS,WAAW,CAAoB,OAAW,EAAE,SAAyB;IAC7E,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;AACD,MAAM,UAAU,SAAS,CACxB,MAAqE;IAErE,MAAM,CAAC,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE;QAC1D,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,CAA0B,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3E,MAAM,OAAO,GAAG,WAAW,CAC1B,CAAC,KAAe,EAAE,EAAE;QACnB,MAAM,IAAI,GAAG,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;QACtG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3B,OAAO,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,EACD,CAAC,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,CAC1B,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"}
@@ -0,0 +1,20 @@
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 "./resultState.ts";
7
+ import { type SaveState } from "./saveState.ts";
8
+ export type EditorConfig<TFields extends Fields, TRow, TQueryError, TSaveError, R, TRuntimeError> = {
9
+ readonly query: Atom.Atom<AsyncResult.AsyncResult<TRow, TQueryError>>;
10
+ readonly fields: Schema.Struct<TFields>;
11
+ readonly values: (row: TRow) => Encoded<TFields>;
12
+ readonly save: (values: Decoded<TFields>) => Effect.Effect<TRow, TSaveError, AtomServices<R>>;
13
+ readonly runtime: Atom.AtomRuntime<Services<TFields, R>, TRuntimeError>;
14
+ } & FieldRejectionMapping<TFields, TSaveError>;
15
+ export interface Editor<TFields extends Fields, TRow, TQueryError, TSaveError, TRuntimeError> extends SaveState<TSaveError | TRuntimeError> {
16
+ readonly form: Form<TFields, TRow, TSaveError | FieldFailure, TRuntimeError> | undefined;
17
+ readonly query: QueryState<TRow, TQueryError>;
18
+ }
19
+ export declare function useEditor<TFields extends Fields, TRow, TQueryError, TSaveError, R, TRuntimeError>(config: EditorConfig<TFields, TRow, TQueryError, TSaveError, R, TRuntimeError>): Editor<TFields, TRow, TQueryError, TSaveError, TRuntimeError>;
20
+ //# sourceMappingURL=editor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../src/client/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,qBAAqB,CAAC;AAE/E,OAAO,EAAE,KAAK,UAAU,EAAY,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,KAAK,SAAS,EAAgB,MAAM,gBAAgB,CAAC;AAC9D,MAAM,MAAM,YAAY,CAAC,OAAO,SAAS,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,EAAE,aAAa,IAAI;IACnG,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;IACtE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACjD,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9F,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;CACxE,GAAG,qBAAqB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC/C,MAAM,WAAW,MAAM,CAAC,OAAO,SAAS,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,CAAE,SAAQ,SAAS,CAAC,UAAU,GAAG,aAAa,CAAC;IAC1I,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,UAAU,GAAG,YAAY,EAAE,aAAa,CAAC,GAAG,SAAS,CAAC;IACzF,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;CAC9C;AAMD,wBAAgB,SAAS,CAAC,OAAO,SAAS,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,EAAE,aAAa,EAChG,MAAM,EAAE,YAAY,CAAC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,EAAE,aAAa,CAAC,GAC5E,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,CAAC,CA4B/D"}
@@ -1,9 +1,9 @@
1
1
  import { Option } from "effect";
2
2
  import { useCallback, useEffect, useState } from "react";
3
- import { useQuery } from "./result-state.js";
4
- import { useSaveState } from "./save-state.js";
5
- import { submission } from "./submission.js";
6
- export const useEditor = (config) => {
3
+ import { submission } from "#submission.ts";
4
+ import { useQuery } from "./resultState.js";
5
+ import { useSaveState } from "./saveState.js";
6
+ export function useEditor(config) {
7
7
  const query = useQuery(config.query);
8
8
  const row = Option.getOrUndefined(query.data);
9
9
  const [held, hold] = useState(undefined);
@@ -31,5 +31,5 @@ export const useEditor = (config) => {
31
31
  }, [form, values, row]);
32
32
  const settled = useCallback((saved) => (form && values ? form.receive(values(saved)) : undefined), [form, values]);
33
33
  return { ...useSaveState(form, settled), form, query };
34
- };
34
+ }
35
35
  //# sourceMappingURL=editor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editor.js","sourceRoot":"","sources":["../../src/client/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,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAmB,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAkB,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAiB9D,MAAM,UAAU,SAAS,CACxB,MAA8E;IAE9E,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,CAA6D,SAAS,CAAC,CAAC;IACrG,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,KAAW,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;IACzH,OAAO,EAAE,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACxD,CAAC"}
@@ -0,0 +1,23 @@
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 function isUnauthorized<E>(cause: Cause.Cause<E>): boolean;
12
+ export interface QueryState<A, E> extends ResultState<A, E> {
13
+ readonly refresh: () => void;
14
+ }
15
+ export interface QueryOptions {
16
+ readonly keepPreviousData?: boolean;
17
+ }
18
+ export declare function useQuery<A, E>(atom: Atom.Atom<AsyncResult.AsyncResult<A, E>>, options?: QueryOptions): QueryState<A, E>;
19
+ export interface ActionState<TInput, A, E> extends ResultState<A, E> {
20
+ readonly dispatch: (input: TInput) => void;
21
+ }
22
+ export declare function useAction<TInput, A, E>(atom: Atom.AtomResultFn<TInput, A, E>): ActionState<TInput, A, E>;
23
+ //# sourceMappingURL=resultState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resultState.d.ts","sourceRoot":"","sources":["../../src/client/resultState.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;AAUD,wBAAgB,cAAc,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,CAEhE;AASD,MAAM,WAAW,UAAU,CAAC,CAAC,EAAE,CAAC,CAAE,SAAQ,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC;IAC1D,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;CAC7B;AACD,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CACpC;AASD,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,GAAE,YAAiB,GAAG,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAe3H;AACD,MAAM,WAAW,WAAW,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAE,SAAQ,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC;IACnE,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3C;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAIxG"}
@@ -0,0 +1,48 @@
1
+ import { RegistryContext, 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 { useContext, useEffect, useState } from "react";
5
+ import { useSessionRecheck } from "./sessionBoundary.js";
6
+ function state(result, data = AsyncResult.value(result)) {
7
+ return {
8
+ cause: AsyncResult.cause(result),
9
+ data,
10
+ pending: result.waiting,
11
+ refreshing: result.waiting && Option.isSome(data),
12
+ result,
13
+ };
14
+ }
15
+ export function isUnauthorized(cause) {
16
+ return Option.match(Cause.findErrorOption(cause), { onNone: () => false, onSome: (error) => Predicate.isTagged(error, "Unauthorized") });
17
+ }
18
+ function useRecheckOnUnauthorized(result) {
19
+ const recheck = useSessionRecheck();
20
+ useEffect(() => {
21
+ if (AsyncResult.isFailure(result) && isUnauthorized(result.cause)) {
22
+ recheck();
23
+ }
24
+ }, [recheck, result]);
25
+ }
26
+ export function useQuery(atom, options = {}) {
27
+ const result = useAtomValue(atom);
28
+ const registry = useContext(RegistryContext);
29
+ const keepPreviousData = options.keepPreviousData === true;
30
+ const nativeData = AsyncResult.value(result);
31
+ const [held, hold] = useState(() => ({ atom, data: nativeData, dataAtom: atom, keepPreviousData, registry, result }));
32
+ const previous = held.registry === registry && (held.dataAtom === atom || keepPreviousData) ? held.data : Option.none();
33
+ const data = Option.orElse(nativeData, () => previous);
34
+ const dataAtom = Option.isSome(nativeData) || Option.isNone(previous) ? atom : held.dataAtom;
35
+ // Render state keeps abandoned React renders from changing the committed screen's retained value.
36
+ if (held.atom !== atom || held.registry !== registry || held.result !== result || held.keepPreviousData !== keepPreviousData) {
37
+ hold({ atom, data, dataAtom, keepPreviousData, registry, result });
38
+ }
39
+ useRecheckOnUnauthorized(result);
40
+ return { ...state(result, data), refresh: useAtomRefresh(atom) };
41
+ }
42
+ // The atom's state is shared by every dispatch, so it never claims to be one invocation's completion result.
43
+ export function useAction(atom) {
44
+ const result = useAtomValue(atom);
45
+ useRecheckOnUnauthorized(result);
46
+ return { ...state(result), dispatch: useAtomSet(atom) };
47
+ }
48
+ //# sourceMappingURL=resultState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resultState.js","sourceRoot":"","sources":["../../src/client/resultState.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC/F,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,KAAK,WAAW,MAAM,wCAAwC,CAAC;AAGtE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAQzD,SAAS,KAAK,CAAO,MAAqC,EAAE,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3F,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;AACD,MAAM,UAAU,cAAc,CAAI,KAAqB;IACtD,OAAO,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;AAC1I,CAAC;AACD,SAAS,wBAAwB,CAAO,MAAqC;IAC5E,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;AAeD,MAAM,UAAU,QAAQ,CAAO,IAA8C,EAAE,OAAO,GAAiB,EAAE;IACxG,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC;IAC7C,MAAM,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,KAAK,IAAI,CAAC;IAC3D,MAAM,UAAU,GAAG,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7C,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,QAAQ,CAAkB,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IACvI,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,QAAQ,KAAK,IAAI,IAAI,gBAAgB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAK,CAAC;IAC3H,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC;IAC7F,kGAAkG;IAClG,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,IAAI,IAAI,CAAC,gBAAgB,KAAK,gBAAgB,EAAE,CAAC;QAC9H,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IACpE,CAAC;IACD,wBAAwB,CAAC,MAAM,CAAC,CAAC;IACjC,OAAO,EAAE,GAAG,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;AAClE,CAAC;AAID,6GAA6G;AAC7G,MAAM,UAAU,SAAS,CAAe,IAAqC;IAC5E,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"}
@@ -7,5 +7,5 @@ export interface SaveState<E> {
7
7
  readonly save: () => void;
8
8
  readonly saving: boolean;
9
9
  }
10
- export declare const useSaveState: <F extends Fields, A, E, ER>(form: Form<F, A, E | FieldFailure, ER> | undefined, settled: (result: A) => void) => SaveState<E | ER>;
11
- //# sourceMappingURL=save-state.d.ts.map
10
+ export declare function useSaveState<TFields extends Fields, A, E, TRuntimeError>(form: Form<TFields, A, E | FieldFailure, TRuntimeError> | undefined, settled: (result: A) => void): SaveState<E | TRuntimeError>;
11
+ //# sourceMappingURL=saveState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saveState.d.ts","sourceRoot":"","sources":["../../src/client/saveState.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;AAMhG,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;AACD,wBAAgB,YAAY,CAAC,OAAO,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,aAAa,EACvE,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,GAAG,YAAY,EAAE,aAAa,CAAC,GAAG,SAAS,EACnE,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI,GAC1B,SAAS,CAAC,CAAC,GAAG,aAAa,CAAC,CAgC9B"}
@@ -4,11 +4,11 @@ import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
4
4
  import * as Atom from "effect/unstable/reactivity/Atom";
5
5
  import { useCallback, useContext, useEffect } from "react";
6
6
  import { FieldFailure, Invalid } from "@shivaedev/effect-form/shape.ts";
7
- import { isUnauthorized } from "./result-state.js";
8
- import { useSessionRecheck } from "./session-boundary.js";
7
+ import { isUnauthorized } from "./resultState.js";
8
+ import { useSessionRecheck } from "./sessionBoundary.js";
9
9
  const idle = Atom.make(AsyncResult.initial());
10
10
  const clean = Atom.make(false);
11
- export const useSaveState = (form, settled) => {
11
+ export function useSaveState(form, settled) {
12
12
  const registry = useContext(RegistryContext);
13
13
  const recheck = useSessionRecheck();
14
14
  const result = useAtomValue(form?.submit ?? idle);
@@ -33,5 +33,5 @@ export const useSaveState = (form, settled) => {
33
33
  const revert = useCallback(() => form?.revert(), [form]);
34
34
  const failure = Option.filter(AsyncResult.error(result), (error) => !(error instanceof FieldFailure || error instanceof Invalid));
35
35
  return { dirty, failure, revert, save, saving: result.waiting };
36
- };
37
- //# sourceMappingURL=save-state.js.map
36
+ }
37
+ //# sourceMappingURL=saveState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saveState.js","sourceRoot":"","sources":["../../src/client/saveState.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,kBAAkB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC;AAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAQ/B,MAAM,UAAU,YAAY,CAC3B,IAAmE,EACnE,OAA4B;IAE5B,MAAM,QAAQ,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;IACpC,MAAM,MAAM,GAA2E,YAAY,CAAC,IAAI,EAAE,MAAM,IAAI,IAAI,CAAC,CAAC;IAC1H,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,CAC5B,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,EACzB,CAAC,KAAK,EAA8B,EAAE,CAAC,CAAC,CAAC,KAAK,YAAY,YAAY,IAAI,KAAK,YAAY,OAAO,CAAC,CACnG,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;AACjE,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { type ReactNode } from "react";
2
+ export interface SessionBoundaryProps<TSession, TClient> {
3
+ readonly children: (client: TClient) => ReactNode;
4
+ readonly connect: (session: TSession) => TClient;
5
+ readonly identify: (session: TSession) => string;
6
+ readonly recheck: () => void;
7
+ readonly session: TSession | undefined;
8
+ readonly signedOut?: ReactNode;
9
+ }
10
+ export declare function useSessionRecheck(): () => void;
11
+ export declare function SessionBoundary<TSession, TClient>({ session, identify, connect, recheck, signedOut, children, }: SessionBoundaryProps<TSession, TClient>): ReactNode;
12
+ //# sourceMappingURL=sessionBoundary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionBoundary.d.ts","sourceRoot":"","sources":["../../src/client/sessionBoundary.ts"],"names":[],"mappings":"AAEA,OAAO,EAA0C,KAAK,SAAS,EAAmC,MAAM,OAAO,CAAC;AAChH,MAAM,WAAW,oBAAoB,CAAC,QAAQ,EAAE,OAAO;IACtD,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,SAAS,CAAC;IAClD,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,OAAO,CAAC;IACjD,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,MAAM,CAAC;IACjD,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;CAC/B;AAED,wBAAgB,iBAAiB,IAAI,MAAM,IAAI,CAE9C;AAsDD,wBAAgB,eAAe,CAAC,QAAQ,EAAE,OAAO,EAAE,EAClD,OAAO,EACP,QAAQ,EACR,OAAO,EACP,OAAO,EACP,SAAS,EACT,QAAQ,GACR,EAAE,oBAAoB,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,SAAS,CAIrD"}
@@ -1,11 +1,15 @@
1
1
  import { RegistryContext } from "@effect/atom-react";
2
2
  import * as AtomRegistry from "effect/unstable/reactivity/AtomRegistry";
3
3
  import { createContext, createElement, Fragment, useContext, useEffect, useState } from "react";
4
- const Recheck = createContext(() => { });
5
- export const useSessionRecheck = () => useContext(Recheck);
6
- const owning = (client) => ({ client, mounted: 0, registry: AtomRegistry.make() });
4
+ const RecheckContext = createContext(() => undefined);
5
+ export function useSessionRecheck() {
6
+ return useContext(RecheckContext);
7
+ }
8
+ function owning(client) {
9
+ return { client, mounted: 0, registry: AtomRegistry.make() };
10
+ }
7
11
  // No cleanup runs when a hidden <Activity> subtree is unmounted, so nodes a hidden render creates must not outlive it.
8
- const hidden = (client) => {
12
+ function hidden(client) {
9
13
  const owned = owning(client);
10
14
  let reaping = false;
11
15
  owned.registry.onNodeAdded = () => {
@@ -21,8 +25,8 @@ const hidden = (client) => {
21
25
  }, 0);
22
26
  };
23
27
  return owned;
24
- };
25
- const Generation = ({ session, connect, recheck, children }) => {
28
+ }
29
+ function Generation({ session, connect, recheck, children }) {
26
30
  const [owned, own] = useState(() => owning(connect(session)));
27
31
  useEffect(() => {
28
32
  owned.mounted += 1;
@@ -37,9 +41,11 @@ const Generation = ({ session, connect, recheck, children }) => {
37
41
  });
38
42
  };
39
43
  }, [owned]);
40
- return createElement(Recheck.Provider, { value: recheck }, createElement(RegistryContext.Provider, { value: owned.registry }, children(owned.client)));
41
- };
42
- export const SessionBoundary = ({ session, identify, connect, recheck, signedOut, children, }) => session === undefined
43
- ? createElement(Fragment, null, signedOut)
44
- : createElement((Generation), { children, connect, key: identify(session), recheck, session });
45
- //# sourceMappingURL=session-boundary.js.map
44
+ return createElement(RecheckContext.Provider, { value: recheck }, createElement(RegistryContext.Provider, { value: owned.registry }, children(owned.client)));
45
+ }
46
+ export function SessionBoundary({ session, identify, connect, recheck, signedOut, children, }) {
47
+ return session === undefined
48
+ ? createElement(Fragment, null, signedOut)
49
+ : createElement(Generation, { children, connect, key: identify(session), recheck, session });
50
+ }
51
+ //# sourceMappingURL=sessionBoundary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionBoundary.js","sourceRoot":"","sources":["../../src/client/sessionBoundary.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import * as AtomRegistry from "effect/unstable/reactivity/AtomRegistry";
2
+ import { type ReactNode } from "react";
3
+ export declare function expectNoReactWarnings(): void;
4
+ export declare function eventually(assert: () => void): Promise<void>;
5
+ export interface Rendered {
6
+ readonly container: HTMLDivElement;
7
+ readonly registry: AtomRegistry.AtomRegistry;
8
+ readonly render: (node: ReactNode) => Promise<void>;
9
+ readonly unmount: () => Promise<void>;
10
+ }
11
+ export declare function render(node: ReactNode): Promise<Rendered>;
12
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../src/client/testing.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,YAAY,MAAM,yCAAyC,CAAC;AACxE,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAG3D,wBAAgB,qBAAqB,SASpC;AACD,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,IAAI,iBAgBlD;AACD,MAAM,WAAW,QAAQ;IACxB,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC,YAAY,CAAC;IAC7C,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AACD,wBAAsB,MAAM,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,CA4B/D"}
@@ -0,0 +1,66 @@
1
+ import { RegistryContext } from "@effect/atom-react";
2
+ import { Clock, Effect } from "effect";
3
+ import * as AtomRegistry from "effect/unstable/reactivity/AtomRegistry";
4
+ import { act, createElement } from "react";
5
+ import { createRoot } from "react-dom/client";
6
+ import { beforeEach, expect, vi } from "vitest";
7
+ export function expectNoReactWarnings() {
8
+ beforeEach(({ onTestFinished }) => {
9
+ const errors = vi.spyOn(console, "error");
10
+ onTestFinished(() => {
11
+ const calls = errors.mock.calls;
12
+ errors.mockRestore();
13
+ expect(calls).toEqual([]);
14
+ });
15
+ });
16
+ }
17
+ export async function eventually(assert) {
18
+ const deadline = Effect.runSync(Clock.currentTimeMillis) + 3000;
19
+ while (true) {
20
+ // Network responses must settle inside act, including the time between assertions.
21
+ await act(async () => {
22
+ await new Promise((resolve) => setTimeout(resolve, 10));
23
+ });
24
+ try {
25
+ assert();
26
+ return;
27
+ }
28
+ catch (error) {
29
+ if (Effect.runSync(Clock.currentTimeMillis) >= deadline) {
30
+ throw error;
31
+ }
32
+ }
33
+ }
34
+ }
35
+ export async function render(node) {
36
+ const registry = AtomRegistry.make();
37
+ const container = document.createElement("div");
38
+ document.body.append(container);
39
+ const root = createRoot(container);
40
+ let mounted = true;
41
+ async function unmount() {
42
+ if (!mounted) {
43
+ return;
44
+ }
45
+ mounted = false;
46
+ try {
47
+ await act(async () => root.unmount());
48
+ }
49
+ finally {
50
+ registry.dispose();
51
+ container.remove();
52
+ }
53
+ }
54
+ async function update(next) {
55
+ await act(async () => root.render(createElement(RegistryContext.Provider, { value: registry }, next)));
56
+ }
57
+ try {
58
+ await update(node);
59
+ }
60
+ catch (error) {
61
+ await unmount();
62
+ throw error;
63
+ }
64
+ return { container, registry, render: update, unmount };
65
+ }
66
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../../src/client/testing.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"field-rejection.d.ts","sourceRoot":"","sources":["../src/field-rejection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAa,KAAK,MAAM,EAAE,KAAK,KAAK,EAAE,MAAM,QAAQ,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uCAAuC,CAAC;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAE7F,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,MAAM;IAC/C,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,GAAG,YAAY,GAAG,UAAU,CAAC;AAE1E,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;AAEjG,KAAK,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,IAAI,CAAC,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAChG,OAAO,SAAS,MAAM,CAAC,GACtB,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,GACxC,KAAK,GACN,KAAK,CAAC;AAET,MAAM,MAAM,qBAAqB,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,IAAI;IAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;CAAE,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GACvI,OAAO,GACP;IAAE,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;CAAE,CAAC,CAAC;AAEhD,eAAO,MAAM,gBAAgB,GAC3B,CAAC,SAAS,MAAM,EAAE,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAC1C,OAAO,OAAO,KAAG,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,SAO3C,CAAC;AAEH,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EACrD,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EACxB,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC3C,QAAQ,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,MAG7B,QAAQ,CAAC,EAAE,WAAW,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,YAAY,EAAE,CAAC,CAKvF,CAAC"}
1
+ {"version":3,"file":"field-rejection.d.ts","sourceRoot":"","sources":["../src/field-rejection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAa,KAAK,MAAM,EAAE,KAAK,KAAK,EAAE,MAAM,QAAQ,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uCAAuC,CAAC;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAE7F,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,MAAM;IAC/C,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,GAAG,YAAY,GAAG,UAAU,CAAC;AAE1E,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;AAEjG,KAAK,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,IAAI,CAAC,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAChG,OAAO,SAAS,MAAM,CAAC,GACtB,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,GACxC,KAAK,GACN,KAAK,CAAC;AAET,MAAM,MAAM,qBAAqB,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,IAAI;IAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;CAAE,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GACvI,OAAO,GACP;IAAE,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;CAAE,CAAC,CAAC;AAEhD,eAAO,MAAM,gBAAgB,GAC3B,CAAC,SAAS,MAAM,UAAU,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,aACnC,OAAO,KAAG,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,SAS3C,CAAC;AAEH,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,UAC7C,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,QAClB,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,UACnC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,cAGrB,CAAC,aAAa,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,YAAY,EAAE,CAAC,CAKvF,CAAC"}
@@ -1,6 +1,8 @@
1
1
  import { Effect, Predicate } from "effect";
2
2
  export const fieldRejectionOf = (schema) => (error) => {
3
- const isName = (name) => typeof name === "string" && Object.hasOwn(schema.fields, name);
3
+ function isName(name) {
4
+ return typeof name === "string" && Object.hasOwn(schema.fields, name);
5
+ }
4
6
  if (!(Predicate.hasProperty(error, "_tag") && Predicate.hasProperty(error, "field") && Predicate.hasProperty(error, "message"))) {
5
7
  return undefined;
6
8
  }
@@ -1 +1 @@
1
- {"version":3,"file":"field-rejection.js","sourceRoot":"","sources":["../src/field-rejection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,SAAS,EAA2B,MAAM,QAAQ,CAAC;AAwBpE,MAAM,CAAC,MAAM,gBAAgB,GAC5B,CAAmB,MAAwB,EAAE,EAAE,CAC/C,CAAC,KAAc,EAAuC,EAAE;IACvD,MAAM,MAAM,GAAG,CAAC,IAAa,EAAmB,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAClH,IAAI,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC;QACjI,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IACvC,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAClH,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,SAAS,GAAG,CACxB,MAAwB,EACxB,IAA2C,EAC3C,MAAqC,EACpC,EAAE;IACH,MAAM,SAAS,GAAG,MAAM,IAAI,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACrD,OAAO,CAAC,MAAS,EAAE,SAA6B,EAAyC,EAAE,CAC1F,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAA0C,EAAE;QAC5E,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;QAC/B,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9F,CAAC,CAAC,CAAC;AACL,CAAC,CAAC"}
1
+ {"version":3,"file":"field-rejection.js","sourceRoot":"","sources":["../src/field-rejection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,SAAS,EAA2B,MAAM,QAAQ,CAAC;AAwBpE,MAAM,CAAC,MAAM,gBAAgB,GAC5B,CAAmB,MAAwB,EAAE,EAAE,CAC/C,CAAC,KAAc,EAAuC,EAAE;IACvD,SAAS,MAAM,CAAC,IAAa;QAC5B,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC;QACjI,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IACvC,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAClH,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,SAAS,GAAG,CACxB,MAAwB,EACxB,IAA2C,EAC3C,MAAqC,EACpC,EAAE;IACH,MAAM,SAAS,GAAG,MAAM,IAAI,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACrD,OAAO,CAAC,MAAS,EAAE,SAA6B,EAAyC,EAAE,CAC1F,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAA0C,EAAE;QAC5E,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;QAC/B,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9F,CAAC,CAAC,CAAC;AACL,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { LoaderFnContext } from "@tanstack/react-router";
2
+ import type * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
3
+ import type * as Atom from "effect/unstable/reactivity/Atom";
4
+ import * as AtomRegistry from "effect/unstable/reactivity/AtomRegistry";
5
+ export declare function loadQuery<A, E>(registry: AtomRegistry.AtomRegistry, atom: Atom.Atom<AsyncResult.AsyncResult<A, E>>, { abortController }: Pick<LoaderFnContext, "abortController">): Promise<A>;
6
+ //# sourceMappingURL=loadQuery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadQuery.d.ts","sourceRoot":"","sources":["../src/loadQuery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,OAAO,KAAK,KAAK,WAAW,MAAM,wCAAwC,CAAC;AAC3E,OAAO,KAAK,KAAK,IAAI,MAAM,iCAAiC,CAAC;AAC7D,OAAO,KAAK,YAAY,MAAM,yCAAyC,CAAC;AAExE,wBAAsB,SAAS,CAAC,CAAC,EAAE,CAAC,EACnC,QAAQ,EAAE,YAAY,CAAC,YAAY,EACnC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAC9C,EAAE,eAAe,EAAE,EAAE,IAAI,CAAC,eAAe,EAAE,iBAAiB,CAAC,GAC3D,OAAO,CAAC,CAAC,CAAC,CAgBZ"}
@@ -0,0 +1,21 @@
1
+ import { Effect } from "effect";
2
+ import * as AtomRegistry from "effect/unstable/reactivity/AtomRegistry";
3
+ export async function loadQuery(registry, atom, { abortController }) {
4
+ const { signal } = abortController;
5
+ try {
6
+ const result = await Effect.runPromise(Effect.result(AtomRegistry.getResult(registry, atom, { suspendOnWaiting: true })), { signal });
7
+ if (result._tag === "Failure") {
8
+ throw result.failure;
9
+ }
10
+ return result.success;
11
+ }
12
+ catch (error) {
13
+ if (signal.aborted) {
14
+ const canceled = new Error("Query loading canceled", { cause: error });
15
+ canceled.name = "AbortError";
16
+ throw canceled;
17
+ }
18
+ throw error;
19
+ }
20
+ }
21
+ //# sourceMappingURL=loadQuery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadQuery.js","sourceRoot":"","sources":["../src/loadQuery.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhC,OAAO,KAAK,YAAY,MAAM,yCAAyC,CAAC;AAExE,MAAM,CAAC,KAAK,UAAU,SAAS,CAC9B,QAAmC,EACnC,IAA8C,EAC9C,EAAE,eAAe,EAA4C;IAE7D,MAAM,EAAE,MAAM,EAAE,GAAG,eAAe,CAAC;IACnC,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QACtI,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC/B,MAAM,MAAM,CAAC,OAAO,CAAC;QACtB,CAAC;QACD,OAAO,MAAM,CAAC,OAAO,CAAC;IACvB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,QAAQ,GAAG,IAAI,KAAK,CAAC,wBAAwB,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YACvE,QAAQ,CAAC,IAAI,GAAG,YAAY,CAAC;YAC7B,MAAM,QAAQ,CAAC;QAChB,CAAC;QACD,MAAM,KAAK,CAAC;IACb,CAAC;AACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"submission.d.ts","sourceRoot":"","sources":["../src/submission.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC7C,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,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAa,MAAM,sBAAsB,CAAC;AAEtF,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE;IAC9D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC;IACpD,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACvD,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;CAC5E;AAED,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE;IACrD,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,YAAY,EAAE,EAAE,CAAC,CAAC;IAChD,QAAQ,CAAC,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;CACjD;AAED,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,gBAAgB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAG,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAa1H,CAAC"}
1
+ {"version":3,"file":"submission.d.ts","sourceRoot":"","sources":["../src/submission.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC7C,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,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAa,MAAM,sBAAsB,CAAC;AAEtF,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE;IAC9D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC;IACpD,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACvD,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;CAC5E;AAED,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE;IACrD,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,YAAY,EAAE,EAAE,CAAC,CAAC;IAChD,QAAQ,CAAC,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;CACjD;AAED,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,UAAU,gBAAgB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAG,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAa1H,CAAC"}
@@ -0,0 +1,45 @@
1
+ # North star
2
+
3
+ ## The problem
4
+
5
+ Native Effect atoms already know how to run, refresh and share an asynchronous result. A React application still needs to decide what to display while they work, what to keep when a request fails and which lifetime owns their registry. Repeating those decisions in every screen makes them drift. One screen blanks during a refresh, another loses a draft when a response arrives, and a third keeps the previous session's state after the user changes.
6
+
7
+ Editing combines two kinds of state. A server row can change independently, while the user types encoded input values that a schema must decode before saving. The latest server response is not necessarily the user's latest intent. Whole-form replacement makes a simple screen overwrite newer edits; leaving the form alone makes untouched fields stale. A create form adds another race: clearing the successful entry must not clear what the user has already begun typing for the next one.
8
+
9
+ ## The ideal
10
+
11
+ React components read state in the words they render: data, failure, pending and refreshing. An editor says which query supplies its row, which fields describe its inputs and which Effect saves the decoded values. A session root says which identity owns those screens. Each definition stays small because the package supplies repeated coordination, while native Effect and effect-form remain the owners of the underlying work.
12
+
13
+ Good client state has these properties:
14
+
15
+ - Useful data can stay on screen during refresh and after a refresh failure. Showing retained data does not hide the failed result.
16
+ - Query identity changes reset displayed data by default. Applications can opt into `keepPreviousData` for payload transitions; displayed data still clears when the registry owner changes.
17
+ - A draft belongs to one query identity and one session generation. Changing either must not transfer edits into a different record or person's screen.
18
+ - A refresh, a normalized save result and a create reset respect edits made after the operation began. Untouched fields can adopt new server values without replacing newer input.
19
+ - Failure types reach the component. A field rejection names its field; another failure remains available for application-owned wording.
20
+ - An action reports the native atom's current state honestly. An imperative caller that needs one invocation's outcome runs an Effect with that lifetime instead of guessing from a shared latest-result atom.
21
+ - A registry has an owner. Session replacement and unmount release the old generation, including work created around React's StrictMode and Activity lifecycles.
22
+
23
+ ## Boundaries
24
+
25
+ The root repository's one path for client state is this package. It should add React coordination where applications repeat it, without taking a neighbouring package's job:
26
+
27
+ - Native atoms own execution, identity, cache retention, invalidation and action scheduling.
28
+ - effect-contract owns query and command declarations, declared rejections and native client bindings. Its shared resume source also works without React.
29
+ - effect-form owns encoded fields, schema decoding, validation, submission and draft merge rules. The editor and create hooks arrange those pieces around rows and command results.
30
+ - The platform request modules and the application's auth integration verify identity. The session boundary owns a client-side generation; its `recheck` callback asks the auth owner for a verdict.
31
+ - The application owns transport, loading/error wording, credential rotation policy and the point where a runtime runs a particular Effect.
32
+
33
+ ## Trade-offs
34
+
35
+ 1. **Native ownership before convenience.** A second query cache or parallel form model would make easy call sites look simpler while giving the application two places to understand identity and cleanup. Thin bindings keep the native state visible.
36
+ 2. **Identity isolation before draft continuity.** A draft is valuable only within the record and session that created it. Switching identities starts fresh even if retaining a previous draft would save typing.
37
+ 3. **Newer intent before replacing the whole form.** A response updates what it can without erasing fields the user edited meanwhile. A user who wants the server's latest values can explicitly revert.
38
+ 4. **Truthful shared state before per-call ergonomics.** One atom's result cannot describe several concurrent invocations independently. The action hook preserves its native semantics rather than implying a Promise belongs to one dispatch.
39
+ 5. **Observable evidence before brevity.** A helper earns its place by removing repeated application code and having tests for the races and lifetimes it claims to handle. A DOM fixture proves that fixture, not another runtime or deployment.
40
+
41
+ ## What it leaves out
42
+
43
+ The package does not define another RPC contract language, replace native atom scheduling or decide an application's auth policy. It does not supply a general form language, an optimistic reconciliation strategy or an offline write queue. Server rendering and mobile host integration need their own architecture and evidence; a resume callback alone cannot establish them.
44
+
45
+ Use [README.md](../README.md) for examples and [roadmap.md](./roadmap.md) for what is implemented and which choices remain with the maintainer.
@@ -0,0 +1,28 @@
1
+ # Roadmap
2
+
3
+ This roadmap owns the package's hooks, form coordination and session-registry implementation. The [framework roadmap](https://github.com/ShivaeDev/platform/blob/main/docs/framework/roadmap.md) owns composed feature fixtures, adoption and host/deployment validation. A local hook test is evidence for its own runtime and scenario.
4
+
5
+ ## Built
6
+
7
+ - [x] Opt-in `keepPreviousData` retains the last successful query value across payload changes, pending requests and failures; the default resets it. Registry replacement clears retained data, superseded responses do not replace the latest result, and disabling retention drops an inherited value. [Rendered query evidence](https://github.com/ShivaeDev/platform/blob/main/packages/effect-react/src/queryPayload.dom.spec.ts).
8
+ - [x] Public `testing.ts` render helpers supply an isolated native registry per mount, retain it on rerender, and dispose native resources and remove the container on idempotent unmount. [DOM evidence](https://github.com/ShivaeDev/platform/blob/main/packages/effect-react/src/client/testing.dom.test.tsx) also exercises query refetch after a command through a test RPC Layer; it does not establish browser networking.
9
+ - [x] Native query and action hooks with inferred input/success/failure types, retained data, pending/refresh state and explicit refresh/dispatch methods. DOM tests cover query identity changes, shared query execution and latest-dispatch interruption; compiler fixtures cover types.
10
+ - [x] A session generation owning its client, registry and subtree, with replacement/sign-out cleanup, query Unauthorized rechecking and retained screens until the auth owner changes the session.
11
+ - [x] Session cleanup across StrictMode effect replay and Activity hiding, reveal and hidden unmount, with credential rotation tested against the chosen identity key.
12
+ - [x] `useEditor` composing query, fields and save Effect, with initial loading, per-query forms, per-field refresh/saved-result merges, normalized results and in-flight edits covered by rendered tests.
13
+ - [x] `useCreate` starting a fresh form after success while retaining fields edited during schema decoding or the request; submissions through the form API settle the hooks too.
14
+ - [x] Tagged field-rejection mapping, a typed custom mapper and compile-time requirements for rejection field types that exceed the form's names; other save failures remain separately available.
15
+ - [x] A resume module using effect-contract's shared implementation, with visible browser events, an injected native source, listener disposal and native stale-while-revalidate policy covered by tests.
16
+ - [x] TanStack Router query loaders sharing the native registry, with tagged RPC failures, refresh waiting and abort handling. Render tests cover shared query execution, the route error component and previous data during navigation.
17
+
18
+ ## Next
19
+
20
+ - [ ] Add direct rendered regressions for Unauthorized action and save failures invoking the session callback. The implementation calls it, but existing boundary assertions exercise query failures.
21
+ - [ ] Add explicit result-state regressions for a successful `undefined` value and causes containing defects or interruption before making stronger README guarantees about those cases.
22
+ - [ ] Verify changing hook configuration during one form lifetime before documenting any supported reconfiguration policy. Creation currently captures its initial configuration, and an editor holds submission functions per query identity.
23
+
24
+ ## Open questions
25
+
26
+ - Do consumers need an independently scoped action helper, or are distinct native atoms and actual Effects enough for imperative per-invocation outcomes? Keep native shared-action semantics unless a demonstrated use case requires more.
27
+ - What per-request registry and hydration wiring does the TanStack Router host need for SSR? Client render fixtures do not establish SSR isolation.
28
+ - Which hook configuration values should be allowed to change without replacing the form? A rule for changing schemas, runtimes, save functions or initial values needs consumer evidence and explicit lifetime tests.