march-hare 0.8.0 → 0.10.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 (41) hide show
  1. package/README.md +491 -211
  2. package/dist/actions/index.d.ts +46 -0
  3. package/dist/{hooks → actions}/utils.d.ts +0 -39
  4. package/dist/app/index.d.ts +132 -0
  5. package/dist/app/types.d.ts +82 -0
  6. package/dist/boundary/components/broadcast/utils.d.ts +1 -1
  7. package/dist/boundary/components/env/index.d.ts +26 -0
  8. package/dist/boundary/components/env/types.d.ts +11 -0
  9. package/dist/boundary/components/env/utils.d.ts +36 -0
  10. package/dist/boundary/components/scope/index.d.ts +1 -39
  11. package/dist/boundary/components/scope/types.d.ts +17 -13
  12. package/dist/boundary/components/scope/utils.d.ts +12 -8
  13. package/dist/boundary/components/sharing/index.d.ts +43 -0
  14. package/dist/boundary/components/tap/index.d.ts +36 -0
  15. package/dist/boundary/components/tap/types.d.ts +150 -0
  16. package/dist/boundary/components/tap/utils.d.ts +14 -0
  17. package/dist/boundary/index.d.ts +10 -10
  18. package/dist/boundary/types.d.ts +46 -14
  19. package/dist/cache/index.d.ts +4 -4
  20. package/dist/coalesce/index.d.ts +57 -0
  21. package/dist/context/index.d.ts +41 -0
  22. package/dist/context/types.d.ts +14 -0
  23. package/dist/error/index.d.ts +1 -1
  24. package/dist/error/types.d.ts +8 -19
  25. package/dist/index.d.ts +9 -13
  26. package/dist/march-hare.js +8 -5
  27. package/dist/march-hare.umd.cjs +1 -1
  28. package/dist/resource/index.d.ts +55 -78
  29. package/dist/resource/types.d.ts +87 -11
  30. package/dist/resource/utils.d.ts +1 -1
  31. package/dist/scope/index.d.ts +63 -0
  32. package/dist/scope/types.d.ts +55 -0
  33. package/dist/types/index.d.ts +108 -58
  34. package/dist/utils/index.d.ts +6 -5
  35. package/dist/with/index.d.ts +111 -0
  36. package/package.json +1 -1
  37. package/dist/boundary/components/store/index.d.ts +0 -41
  38. package/dist/boundary/components/store/types.d.ts +0 -11
  39. package/dist/boundary/components/store/utils.d.ts +0 -64
  40. package/dist/hooks/index.d.ts +0 -83
  41. /package/dist/{hooks → actions}/types.d.ts +0 -0
@@ -0,0 +1,46 @@
1
+ import { Data } from './types';
2
+ import { Model, Props, Actions, UseActions } from '../types/index';
3
+ /**
4
+ * A hook for managing state with actions.
5
+ *
6
+ * Call `useActions` first, then use `actions.useAction` to bind handlers
7
+ * to action symbols. Types are pre-baked from the generic parameters, so
8
+ * no additional type annotations are needed on handler calls.
9
+ *
10
+ * The hook returns a result containing:
11
+ * 1. The current model state
12
+ * 2. An actions object with `dispatch`, `inspect`, and `useAction`
13
+ * 3. A read-only snapshot of the data values produced by `getData` —
14
+ * the same values handlers read via `context.data`, exposed here for
15
+ * JSX consumption so the view and the handler share one named source.
16
+ *
17
+ * The `inspect` property provides access to Immertation's annotation system,
18
+ * allowing you to check for pending operations on model properties.
19
+ *
20
+ * @template M The model type representing the component's state.
21
+ * @template AC The actions class containing action definitions.
22
+ * @template D The data type for reactive external values.
23
+ * @param model The initial model state.
24
+ * @param getData Optional function that returns reactive values as data.
25
+ * Values returned are accessible via `context.data` in action handlers,
26
+ * always reflecting the latest values even after await operations.
27
+ * @returns A result `[model, actions, data]` with pre-typed `useAction` method.
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * // Basic usage
32
+ * const [model, actions] = useActions<Model, typeof Actions>(model);
33
+ *
34
+ * // Without a model (actions-only)
35
+ * const [, actions] = useActions<void, typeof Actions>();
36
+ *
37
+ * // With reactive data &mdash; consumed in JSX and handlers alike.
38
+ * const [model, actions, data] = useActions<
39
+ * Model,
40
+ * typeof Actions,
41
+ * { query: string }
42
+ * >(model, () => ({ query: props.query }));
43
+ * ```
44
+ */
45
+ export declare function useActions<M extends void = void, A extends Actions | void = void, D extends Props = Props>(getData?: Data<D>): UseActions<M, A, D>;
46
+ export declare function useActions<M extends Model, A extends Actions | void = void, D extends Props = Props>(model: M, getData?: Data<D>): UseActions<M, A, D>;
@@ -63,45 +63,6 @@ export declare function useLifecycles({ unicast, broadcast, dispatchers, scope,
63
63
  * @returns A memoized data proxy of the object.
64
64
  */
65
65
  export declare function useData<P extends Props>(props: P): P;
66
- /**
67
- * Handler factories that wire an action directly to a model field.
68
- *
69
- * - {@link With.Update} assigns the dispatched payload to `model[key]`.
70
- * - {@link With.Invert} flips a boolean field on `model[key]`.
71
- *
72
- * Both are typed so the call site fails to compile when `key` is missing or
73
- * has an incompatible type.
74
- *
75
- * @example
76
- * ```ts
77
- * import { With } from "march-hare";
78
- *
79
- * type Model = { name: string; sidebar: boolean };
80
- *
81
- * class Actions {
82
- * static SetName = Action<string>("SetName");
83
- * static ToggleSidebar = Action("ToggleSidebar");
84
- * }
85
- *
86
- * actions.useAction(Actions.SetName, With.Update("name"));
87
- * actions.useAction(Actions.ToggleSidebar, With.Invert("sidebar"));
88
- * ```
89
- */
90
- export declare const With: {
91
- /**
92
- * Returns a handler that assigns the action payload to `model[key]`.
93
- *
94
- * Type-checks at the call site: the payload type must be assignable to
95
- * the model property's type, and the key must exist on the model.
96
- */
97
- Update<K extends string>(key: K): <M extends Model, A extends Actions | void, D extends Props, P extends K extends keyof M ? M[K] : never>(context: HandlerContext<M, A, D>, payload: P) => void;
98
- /**
99
- * Returns a handler that inverts a boolean field on the model.
100
- *
101
- * Type-checks at the call site: `model[key]` must be a boolean.
102
- */
103
- Invert<K extends string>(key: K): <M extends Model & Record<K, boolean>, A extends Actions | void, D extends Props>(context: HandlerContext<M, A, D>) => void;
104
- };
105
66
  /**
106
67
  * Scans a handler registry for a lifecycle action of the given type.
107
68
  *
@@ -0,0 +1,132 @@
1
+ import { Env } from '../boundary/components/env/index';
2
+ import { Actions, Model, Props } from '../types/index';
3
+ import { Scope } from '../scope/index';
4
+ import { AppContextHandle, AppResource } from './types';
5
+ import { Tap } from '../boundary/components/tap/types';
6
+ import * as React from "react";
7
+ export type { AppArgs, AppContextHandle, AppFetcher, AppResource, } from './types';
8
+ /**
9
+ * Returned from {@link App}. Bundles the Boundary, hooks, and Resource
10
+ * factory bound to a single typed Env shape `S`.
11
+ */
12
+ export type App<S extends object> = {
13
+ /**
14
+ * Boundary component for this App. By default, wraps the subtree with
15
+ * the `env` and `tap` passed to {@link App}; either can be overridden
16
+ * at the call site via the corresponding prop, which is useful when a
17
+ * single `App` definition is rendered against different envs in tests
18
+ * or storybooks.
19
+ */
20
+ readonly Boundary: React.FC<{
21
+ env?: S;
22
+ tap?: Tap;
23
+ children: React.ReactNode;
24
+ }>;
25
+ /**
26
+ * Hook returning a stable `Context` handle. The handle's
27
+ * `context.useActions(model?, getData?)` materialises the
28
+ * component's `[model, actions, data]` tuple. Every handler's
29
+ * `context.env` is typed as `S`.
30
+ */
31
+ readonly useContext: <M extends Model | void = void, AC extends Actions | void = void, D extends Props = Props>() => AppContextHandle<M, AC, D, S>;
32
+ /**
33
+ * Read-only Proxy over the per-Boundary Env, typed as `S`. Reads use
34
+ * dot notation (`env.session`) and always reflect the latest value
35
+ * across `await` boundaries. Writes flow through
36
+ * `context.actions.produce(({ env }) => { ... })`.
37
+ */
38
+ readonly useEnv: () => Readonly<S>;
39
+ /**
40
+ * `Resource` factory bound to this App's Env. Same shape as the
41
+ * top-level `Resource`: call directly for an in-memory cache, or use
42
+ * `app.Resource.Cachable(cache, fetcher)` for persistence.
43
+ */
44
+ readonly Resource: AppResource<S>;
45
+ /**
46
+ * Opens a typed multicast scope. The generic `MulticastActions` declares
47
+ * the `Distribution.Multicast` action class (or union of classes)
48
+ * whose dispatches are routed through this scope &mdash; the
49
+ * returned handle mirrors the App surface but widens
50
+ * `useContext().actions.dispatch` to accept actions from `MulticastActions`
51
+ * on top of the local `AC` class.
52
+ *
53
+ * Render `<scope.Boundary>` to open the scope at runtime; nesting
54
+ * multiple boundaries from different `app.Scope()` calls is fine,
55
+ * each runs as an independent emitter shadowed for its subtree.
56
+ *
57
+ * The Scope handle deliberately does NOT expose a further `Scope`
58
+ * method &mdash; the multicast surface must be declared at the
59
+ * `app.Scope<MulticastActions>()` call site so the type union is explicit.
60
+ */
61
+ readonly Scope: <MulticastActions>() => Scope<S, MulticastActions>;
62
+ };
63
+ /**
64
+ * Creates an `App` &mdash; the entrypoint for a typed Env shape `S`,
65
+ * inferred from `config.env`. `App<S>` exposes `Boundary`, hooks, and
66
+ * a `Resource` factory all wired against the same shape.
67
+ *
68
+ * Each `<app.Boundary>` instance owns its own Env, so different `App`s
69
+ * can coexist in the same tree with completely independent shapes.
70
+ *
71
+ * Pass `tap` to subscribe to every action handler's dispatch / settle /
72
+ * error inside the boundary &mdash; useful for analytics, audit logging,
73
+ * Sentry breadcrumbs. See `recipes/tap.md`. Both `env` and `tap` can
74
+ * also be supplied as props on `<app.Boundary>`, where they override the
75
+ * defaults set at `App()` time (handy for test renders and storybooks).
76
+ *
77
+ * @example
78
+ * ```tsx
79
+ * import { App, type Tapped } from "march-hare";
80
+ *
81
+ * type Session = { accessToken: string };
82
+ *
83
+ * function tap(event: Tapped) {
84
+ * if (event.type === "error") {
85
+ * Sentry.captureException(event.error, { tags: { action: event.action } });
86
+ * }
87
+ * }
88
+ *
89
+ * export const app = App({
90
+ * env: {
91
+ * session: null as Session | null,
92
+ * operating: "idle" as "idle" | "signing-out",
93
+ * },
94
+ * tap,
95
+ * });
96
+ *
97
+ * // Root render.
98
+ * <app.Boundary>
99
+ * <Root />
100
+ * </app.Boundary>;
101
+ *
102
+ * // In a feature's actions.ts:
103
+ * export function useAuthActions() {
104
+ * const context = app.useContext<void, typeof Actions>();
105
+ * const actions = context.useActions();
106
+ *
107
+ * actions.useAction(Actions.SignOut, async (context) => {
108
+ * context.actions.produce(({ env }) => {
109
+ * env.session = null;
110
+ * });
111
+ * });
112
+ *
113
+ * return actions;
114
+ * }
115
+ *
116
+ * // In resources.ts:
117
+ * export const user = app.Resource<User>((context) =>
118
+ * ky
119
+ * .get("/api/user", {
120
+ * headers: context.env.session
121
+ * ? { Authorization: `Bearer ${context.env.session.accessToken}` }
122
+ * : {},
123
+ * signal: context.controller.signal,
124
+ * })
125
+ * .json<User>(),
126
+ * );
127
+ * ```
128
+ */
129
+ export declare function App<S extends object = Env>(config?: {
130
+ env?: S;
131
+ tap?: Tap;
132
+ }): App<S>;
@@ -0,0 +1,82 @@
1
+ import { Args, ResourceHandle } from '../resource/types';
2
+ import { Cache } from '../cache/index';
3
+ import { Actions, Context, Model, Props, UseActions } from '../types/index';
4
+ import { Data } from '../actions/types';
5
+ import { Env } from '../boundary/components/env/index';
6
+ import { WithHandle } from '../with/index';
7
+ /**
8
+ * Args object passed to an `app.Resource` fetcher. Same shape as the
9
+ * base `Resource` fetcher's args but with `env` typed as `S`.
10
+ */
11
+ export type AppArgs<S, P extends object = Record<never, never>> = Omit<Args<P>, "env"> & {
12
+ readonly env: Readonly<S>;
13
+ };
14
+ /**
15
+ * Fetcher signature for an `app.Resource` declaration. The fetcher's
16
+ * `context.env` is typed against the App's inferred Env shape `S`.
17
+ */
18
+ export type AppFetcher<S, T, P extends object = Record<never, never>> = (context: AppArgs<S, P>) => Promise<T>;
19
+ /**
20
+ * `app.Resource(fetcher)` &mdash; in-memory cache, no persistence.
21
+ * `app.Resource.Cachable(cache, fetcher)` &mdash; persistent cache wired
22
+ * to the supplied `Cache` adapter. Both forms type `context.env` as
23
+ * the App's Env shape.
24
+ */
25
+ export type AppResource<S> = {
26
+ <T, P extends object = Record<never, never>>(fetcher: AppFetcher<S, T, P>): ResourceHandle<T, P>;
27
+ readonly Cachable: <T, P extends object = Record<never, never>>(cache: Cache, fetcher: AppFetcher<S, T, P>) => ResourceHandle<T, P>;
28
+ };
29
+ /**
30
+ * Tuple shape returned by `context.useActions(...)` on an App-bound
31
+ * Context. Re-exports the base {@link UseActions} with the App's `S`
32
+ * threaded through every `HandlerContext` and produce draft.
33
+ */
34
+ type AppActionsResult<M, AC, D, S> = UseActions<M extends Model | void ? M : void, AC extends Actions | void ? AC : void, D extends Props ? D : Props, S extends Env ? S : Env>;
35
+ /**
36
+ * `useActions(...)` signature on the App-bound Context. Has two forms:
37
+ * void-model components omit the model argument entirely; everyone else
38
+ * passes their initial model as the first argument and an optional data
39
+ * callback as the second.
40
+ */
41
+ type AppUseActions<M, AC, D, S> = M extends void ? (getData?: Data<D & Props>) => AppActionsResult<M, AC, D, S> : (model: M, getData?: Data<D & Props>) => AppActionsResult<M, AC, D, S>;
42
+ /**
43
+ * `Context` handle returned by `app.useContext()`. Mirrors the base
44
+ * {@link Context} but threads the App's Env shape `S` through every
45
+ * handler's `context.env` and produce draft.
46
+ *
47
+ * @template M The model type for the component's state, or `void`.
48
+ * @template AC The actions class containing this component's action
49
+ * definitions, or `void` for actions-only consumers.
50
+ * @template D The reactive data type returned from the `useActions(...)`
51
+ * data callback.
52
+ * @template S The App's Env shape, supplied at `App({env})` time.
53
+ */
54
+ export type AppContextHandle<M, AC, D, S> = {
55
+ /**
56
+ * Stable dispatch surface available before `useActions(...)` runs.
57
+ * Exposes only `dispatch(action, payload?)` &mdash; useful when an
58
+ * external imperative library needs a dispatch callback at construction
59
+ * time. Inside handlers, prefer `context.actions.dispatch` for the same
60
+ * call.
61
+ */
62
+ readonly actions: {
63
+ dispatch: Context<M extends Model | void ? M : void, AC extends Actions | void ? AC : void, D extends Props ? D : Props>["actions"]["dispatch"];
64
+ };
65
+ /**
66
+ * Typed bag of handler factories bound to `M`. Methods accept
67
+ * lodash-style dotted paths and array indices; keys autocomplete from
68
+ * the model declared on `app.useContext<Model, …>()`. See
69
+ * {@link WithHandle}.
70
+ */
71
+ readonly with: WithHandle<M extends Model | void ? M : void>;
72
+ /**
73
+ * Materialises the component-local model and reactive data, returning
74
+ * the `[model, actions, data]` tuple with `useAction`, `dispatch`,
75
+ * `inspect`, and `stream` attached. Pass the initial model as the first
76
+ * argument (unless `M` is `void`) and an optional data callback as the
77
+ * second &mdash; the callback re-runs every render so handlers reading
78
+ * `context.data` always see fresh values across `await` boundaries.
79
+ */
80
+ readonly useActions: AppUseActions<M, AC, D, S>;
81
+ };
82
+ export {};
@@ -6,7 +6,7 @@ import * as React from "react";
6
6
  * When a broadcast or multicast action is dispatched, the payload is
7
7
  * stored so that late-mounting components can replay it via
8
8
  * {@link useLifecycles} and handlers can read it via
9
- * `context.actions.resolution()`.
9
+ * `context.actions.final()`.
10
10
  */
11
11
  export declare class BroadcastEmitter extends EventEmitter {
12
12
  private cache;
@@ -0,0 +1,26 @@
1
+ import { Props } from './types';
2
+ import * as React from "react";
3
+ export { useEnv } from './utils';
4
+ /**
5
+ * Loose runtime shape for the per-`<Boundary>` Env. Each {@link App}
6
+ * narrows this to its own typed env via `App<S>({ env })`; the
7
+ * loose type exists so the framework's internal plumbing
8
+ * (`<Boundary>`, `useEnv`, handler `context.env`, Resource
9
+ * fetcher `context.env`) does not need to be parametric over S.
10
+ *
11
+ * Consumers should declare their Env shape inline via `App({ env })`
12
+ * &mdash; the inferred `S` is what flows through `app.useContext`,
13
+ * `app.useEnv`, and `app.Resource`. Module augmentation of `Env`
14
+ * is no longer required.
15
+ */
16
+ export type Env = Record<string, unknown>;
17
+ /**
18
+ * Provides a per-Boundary {@link Env} value to every component inside
19
+ * the boundary. Usually wired in via the `<Boundary env={initial}>`
20
+ * prop rather than used directly.
21
+ *
22
+ * The Env is **not** reactive. Mutating it does not trigger a
23
+ * re-render. Drive view state through the model; use the Env for
24
+ * cross-handler coordination.
25
+ */
26
+ export declare function Env({ initial, children }: Props): React.ReactNode;
@@ -0,0 +1,11 @@
1
+ import { ReactNode } from 'react';
2
+ import { Env } from './index';
3
+ export type { Env } from './index';
4
+ /**
5
+ * Props for the Env provider component. Accepts the initial Env
6
+ * value that satisfies the augmented {@link Env} interface.
7
+ */
8
+ export type Props = {
9
+ initial: Env;
10
+ children: ReactNode;
11
+ };
@@ -0,0 +1,36 @@
1
+ import { RefObject } from 'react';
2
+ import { Env } from './index';
3
+ import * as React from "react";
4
+ /**
5
+ * React context exposing the per-Boundary Env ref. The ref itself is
6
+ * stable across renders &mdash; readers grab `.current` at call time.
7
+ *
8
+ * @internal
9
+ */
10
+ export declare const Context: React.Context<React.RefObject<Env>>;
11
+ /**
12
+ * Hook that returns a read-only handle to the per-Boundary {@link Env}.
13
+ * Reads use plain dot notation (`env.session`) and always reflect the
14
+ * latest value, even after `await` boundaries &mdash; the handle is a
15
+ * `Proxy` that delegates property access to the live ref.
16
+ *
17
+ * Writes are not exposed here. Mutate the Env inside an action handler
18
+ * via `context.actions.produce(({ model, env }) => { env.x = ... })`
19
+ * &mdash; the same Immer-style recipe used for the model. Mutations do
20
+ * **not** trigger a re-render; drive view state through the model.
21
+ *
22
+ * Prefer `app.useEnv()` from an {@link App} instance &mdash; the App
23
+ * factory infers the Env's shape from `app.env` and types every
24
+ * read/write against it. The bare `useEnv()` exists for non-App
25
+ * call sites and returns the loose {@link Env} record type.
26
+ */
27
+ export declare function useEnv(): Env;
28
+ /**
29
+ * Internal accessor for the per-Boundary Env ref &mdash; used by the
30
+ * Resource layer to pass a fresh snapshot to each fetcher invocation
31
+ * and by the action layer to write through `context.actions.produce`.
32
+ * Not exported from the library.
33
+ *
34
+ * @internal
35
+ */
36
+ export declare function useEnvRef(): RefObject<Env>;
@@ -1,40 +1,2 @@
1
- import { MulticastPayload } from '../../../types/index';
2
- import { ComponentType, ReactNode } from 'react';
3
- export { useScope, getScope } from './utils';
1
+ export { Context, useScope, getScope } from './utils';
4
2
  export type { ScopeEntry, ScopeContext } from './types';
5
- /**
6
- * Higher-order component that opens a multicast scope keyed by the supplied
7
- * multicast action. Components rendered inside the wrapped tree (and any
8
- * descendants) participate in the scope: every dispatch of `action` reaches
9
- * every subscriber inside this boundary.
10
- *
11
- * Each multicast action defines its own scope &mdash; pass the same action you
12
- * declared with `Distribution.Multicast` to both `withScope` and
13
- * `actions.dispatch`.
14
- *
15
- * Multicast caches the most recent dispatched value per scope so late-mounted
16
- * components can read it via `context.actions.resolution()`.
17
- *
18
- * @param action - The multicast action that opens this scope.
19
- * @param Component - The component to wrap.
20
- * @returns A component that renders the original inside a fresh scope boundary.
21
- *
22
- * @example
23
- * ```tsx
24
- * export class Scope {
25
- * static Mood = Action<Mood>("Mood", Distribution.Multicast);
26
- * }
27
- *
28
- * function Mood() {
29
- * return (
30
- * <>
31
- * <Happy />
32
- * <Sad />
33
- * </>
34
- * );
35
- * }
36
- *
37
- * export default withScope(Scope.Mood, Mood);
38
- * ```
39
- */
40
- export declare function withScope<P extends object, T = unknown>(action: MulticastPayload<T>, Component: ComponentType<P>): (props: P) => ReactNode;
@@ -1,20 +1,24 @@
1
1
  import { BroadcastEmitter } from '../broadcast/utils';
2
- import { ActionId } from '../tasks/types';
3
2
  /**
4
- * Represents a single scope in the ancestor chain. The scope key is the
5
- * action id of the multicast action that opens the scope; each multicast
6
- * action defines its own scope.
3
+ * Runtime entry for a single multicast scope opened by an
4
+ * `<app.Scope().Boundary>`. The `id` uniquely identifies this scope
5
+ * instance; the `emitter` carries every multicast event dispatched
6
+ * inside the boundary, keyed internally by the action's symbol.
7
+ *
8
+ * Nested boundaries shadow outer ones via React context.
9
+ *
10
+ * @internal
7
11
  */
8
12
  export type ScopeEntry = {
9
- /** The action id that opened this scope */
10
- action: ActionId;
11
- /** BroadcastEmitter for multicast events within this scope (caches last payload per event) */
12
- emitter: BroadcastEmitter;
13
+ readonly id: symbol;
14
+ readonly emitter: BroadcastEmitter;
13
15
  };
14
16
  /**
15
- * The scope context is a flattened map of ancestor scopes keyed by the
16
- * multicast action that opened each scope. Each `withScope` merges its entry
17
- * with the parent's map, building up a complete lookup table for O(1)
18
- * retrieval. null indicates no scope ancestor.
17
+ * The scope context. `null` when no `<app.Scope().Boundary>` exists in
18
+ * the ancestor chain; otherwise the nearest entry &mdash; React context
19
+ * shadowing makes nested boundaries override outer ones for the
20
+ * subtree.
21
+ *
22
+ * @internal
19
23
  */
20
- export type ScopeContext = Map<ActionId, ScopeEntry> | null;
24
+ export type ScopeContext = ScopeEntry | null;
@@ -1,19 +1,23 @@
1
1
  import { ScopeContext, ScopeEntry } from './types';
2
- import { ActionId } from '../tasks/types';
3
2
  import * as React from "react";
4
3
  /**
5
- * React context for the scope chain.
6
- * Starts as null (no scopes).
4
+ * React context for the nearest multicast scope. `null` at the root.
5
+ *
6
+ * @internal
7
7
  */
8
8
  export declare const Context: React.Context<ScopeContext>;
9
9
  /**
10
- * Hook to access the scope context from the nearest ancestor.
10
+ * Hook that returns the nearest multicast scope entry. `null` when
11
+ * the caller is not rendered inside any `<app.Scope().Boundary>`.
11
12
  *
12
- * @returns The scope context chain, or null if not inside any scope.
13
+ * @internal
13
14
  */
14
15
  export declare function useScope(): ScopeContext;
15
16
  /**
16
- * Looks up the scope opened by the given multicast action.
17
- * O(1) lookup from the flattened scope map.
17
+ * Pass-through accessor. Kept for the dispatch/subscribe sites that
18
+ * previously needed an action-keyed lookup; now the scope is a single
19
+ * entry (or `null`), so this returns it as-is.
20
+ *
21
+ * @internal
18
22
  */
19
- export declare function getScope(context: ScopeContext, action: ActionId): ScopeEntry | null;
23
+ export declare function getScope(context: ScopeContext): ScopeEntry | null;
@@ -0,0 +1,43 @@
1
+ import { PendingCall } from '../../../resource/index';
2
+ import * as React from "react";
3
+ /**
4
+ * Per-`<Boundary>` registry for `.coalesce(token)` sharing. Outer map
5
+ * keys on the `PendingCall.run` function identity (stable per Resource
6
+ * via the `build()` closure); inner map keys on
7
+ * `${paramsKey}|${coalesceKey(token)}`. While an entry exists every
8
+ * caller awaiting `.coalesce(token)` for the same Resource + params +
9
+ * token receives the same promise.
10
+ *
11
+ * Lifted into React context so each `<app.Boundary>` owns its own
12
+ * registry &mdash; two `App` instances in the same tree cannot collide
13
+ * on a shared token like `.coalesce("k")`.
14
+ *
15
+ * @internal
16
+ */
17
+ export type Sharing = WeakMap<PendingCall["run"], Map<string, Promise<unknown>>>;
18
+ /**
19
+ * React context exposing the per-Boundary sharing registry. The
20
+ * fallback is a fresh `WeakMap` used when `useSharing()` is read
21
+ * outside any `<Boundary>` &mdash; calls work but never share with any
22
+ * other component.
23
+ *
24
+ * @internal
25
+ */
26
+ export declare const Context: React.Context<Sharing>;
27
+ /**
28
+ * Wraps children with a Boundary-scoped sharing registry for
29
+ * `.coalesce(token)`. Rendered as part of {@link Boundary}; not
30
+ * exposed standalone.
31
+ *
32
+ * @internal
33
+ */
34
+ export declare function SharingProvider({ children, }: {
35
+ children: React.ReactNode;
36
+ }): React.ReactElement;
37
+ /**
38
+ * Hook returning the per-Boundary sharing registry. Used by the
39
+ * `.coalesce(token)` chainable inside `useActions`.
40
+ *
41
+ * @internal
42
+ */
43
+ export declare function useSharing(): Sharing;
@@ -0,0 +1,36 @@
1
+ import { Props } from './types';
2
+ import * as React from "react";
3
+ export { useTap } from './utils';
4
+ export type { Tap, Tapped, Invocation, Failure, Mutations, Snapshot, } from './types';
5
+ /**
6
+ * Internal provider that wires a {@link Tap} observer into the React
7
+ * context consumed by `useActions` during dispatch. Rendered by the
8
+ * top-level `<Boundary>` (and indirectly by `<app.Boundary>`); not
9
+ * exposed on the public surface &mdash; consumers should pass the
10
+ * callback via the `tap` prop of either boundary instead of mounting
11
+ * this provider directly.
12
+ *
13
+ * The supplied `tap` callback is held in a `useRef` and indirected
14
+ * through a stable `useMemo` wrapper. The ref is synchronised inside a
15
+ * `useLayoutEffect` &mdash; React's sanctioned write-after-commit
16
+ * window &mdash; so the provider stays compatible with Concurrent
17
+ * rendering, where the render function may be invoked more than once
18
+ * per commit and direct mutation during render would race the
19
+ * scheduler.
20
+ *
21
+ * Keeping the context value referentially constant for the lifetime of
22
+ * the boundary means callers may pass inline arrow functions without
23
+ * invalidating the dispatch pipeline on every render &mdash; the
24
+ * latest callback is read at fire time, not at provider-render time.
25
+ * When `tap` is `undefined`, the wrapper short-circuits via optional
26
+ * chaining: no allocation per event beyond the wrapper call itself.
27
+ *
28
+ * @param props.tap Observer to receive lifecycle events for every action
29
+ * handler dispatched inside the boundary. See {@link Tap}.
30
+ * @param props.children Subtree that should observe the supplied tap.
31
+ * @returns Children rendered inside the tap context provider.
32
+ *
33
+ * @see {@link Tap} &mdash; the observer signature.
34
+ * @see {@link Tapped} &mdash; the discriminated union of event shapes.
35
+ */
36
+ export declare function Tappable({ tap, children }: Props): React.ReactNode;