march-hare 0.9.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.
- package/README.md +17 -8
- package/dist/actions/index.d.ts +3 -3
- package/dist/app/index.d.ts +25 -5
- package/dist/app/types.d.ts +44 -11
- package/dist/boundary/components/tap/index.d.ts +36 -0
- package/dist/boundary/components/tap/types.d.ts +150 -0
- package/dist/boundary/components/tap/utils.d.ts +14 -0
- package/dist/boundary/index.d.ts +1 -1
- package/dist/boundary/types.d.ts +45 -3
- package/dist/context/index.d.ts +8 -6
- package/dist/index.d.ts +1 -0
- package/dist/march-hare.js +6 -5
- package/dist/march-hare.umd.cjs +1 -1
- package/dist/resource/index.d.ts +5 -2
- package/dist/resource/types.d.ts +6 -3
- package/dist/resource/utils.d.ts +1 -1
- package/dist/types/index.d.ts +33 -21
- package/dist/with/index.d.ts +91 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,6 +47,7 @@ For advanced topics, see the [recipes directory](./recipes/).
|
|
|
47
47
|
- Declarative lifecycle hooks without `useEffect`.
|
|
48
48
|
- Centralised error handling via the global `Lifecycle.Fault` broadcast.
|
|
49
49
|
- View-side reactivity for the per-`<app.Boundary>` Env via the global `Lifecycle.Env` broadcast.
|
|
50
|
+
- Observability hook via `<app.Boundary tap={...}>` – fires for every handler dispatch and its terminal (`success` or `error`). See the [tap recipe](./recipes/tap.md).
|
|
50
51
|
- React Native compatible – uses [eventemitter3](https://github.com/primus/eventemitter3) for cross-platform pub/sub.
|
|
51
52
|
|
|
52
53
|
## Getting started
|
|
@@ -148,7 +149,7 @@ function useActions() {
|
|
|
148
149
|
void (model.name = context.actions.annotate(model.name, Op.Update)),
|
|
149
150
|
);
|
|
150
151
|
|
|
151
|
-
// Auto-threads context.task.controller and the Env
|
|
152
|
+
// Auto-threads context.task.controller and the live Env handle.
|
|
152
153
|
const user = await context.actions.resource(resource.user());
|
|
153
154
|
|
|
154
155
|
context.actions.produce(({ model }) => void (model.name = user.name));
|
|
@@ -392,7 +393,7 @@ Components that mount after a broadcast has already been dispatched automaticall
|
|
|
392
393
|
|
|
393
394
|
## Remote data with `Resource`
|
|
394
395
|
|
|
395
|
-
For remote data, declare an `app.Resource` at module scope. `resource.user(params)` is the unified call form — it returns the sync cache read (`User | null`) and primes a slot that `context.actions.resource(resource.user(params))` consumes for the fetch path (with auto-threaded abort controller and Env
|
|
396
|
+
For remote data, declare an `app.Resource` at module scope. `resource.user(params)` is the unified call form — it returns the sync cache read (`User | null`) and primes a slot that `context.actions.resource(resource.user(params))` consumes for the fetch path (with auto-threaded abort controller and a live handle to the per-`<Boundary>` Env). Every successful fetch caches the response in a module-level slot keyed by the fetcher and the stringified params, so different param-sets are independent. Keep all resources in `resources.ts` and pull the whole module in as a namespace (`import * as resource from "./resources"`):
|
|
396
397
|
|
|
397
398
|
```ts
|
|
398
399
|
// resources.ts
|
|
@@ -706,7 +707,7 @@ See the [multicast recipe](./recipes/multicast-actions.md) for more details.
|
|
|
706
707
|
|
|
707
708
|
## Global data
|
|
708
709
|
|
|
709
|
-
For coordinating between async handlers and threading ambient values (session tokens, locale, feature flags, current operational mode) without re-rendering the JSX tree on every dot read, use the per-`<app.Boundary>` `Env`. Declare your env shape inline on `App({ env })`, read via dot notation (`env.session`, `context.env.locale`), and write via `context.actions.produce(({ env }) => { ... })` — the same Immer-style recipe used for the model. Every `app.Resource` fetcher also receives a
|
|
710
|
+
For coordinating between async handlers and threading ambient values (session tokens, locale, feature flags, current operational mode) without re-rendering the JSX tree on every dot read, use the per-`<app.Boundary>` `Env`. Declare your env shape inline on `App({ env })`, read via dot notation (`env.session`, `context.env.locale`), and write via `context.actions.produce(({ env }) => { ... })` — the same Immer-style recipe used for the model. Every `app.Resource` fetcher also receives a live read-only handle to the Env on its args object — the same `Proxy` as `context.env`, so dot reads stay fresh across `await` boundaries inside the fetcher. When the view side needs to react to Env changes, subscribe to the global `Lifecycle.Env` broadcast — `actions.useAction(Lifecycle.Env, handler)` for handler-level work and `actions.stream(Lifecycle.Env, (env) => ...)` for JSX. Both seed from the initial Env on mount.
|
|
710
711
|
|
|
711
712
|
```ts
|
|
712
713
|
// app.ts
|
|
@@ -790,10 +791,10 @@ Multiple `App` instances can coexist in the same tree — each `<app.Boundar
|
|
|
790
791
|
|
|
791
792
|
## Toggling boolean state
|
|
792
793
|
|
|
793
|
-
Toggling boolean UI state – modals, sidebars, drawers – is one of the most common patterns. Bind a unicast action to a boolean field on the model with `
|
|
794
|
+
Toggling boolean UI state – modals, sidebars, drawers – is one of the most common patterns. Bind a unicast action to a boolean field on the model with `context.with.invert`:
|
|
794
795
|
|
|
795
796
|
```tsx
|
|
796
|
-
import { Action
|
|
797
|
+
import { Action } from "march-hare";
|
|
797
798
|
import { app } from "./app";
|
|
798
799
|
|
|
799
800
|
type Model = {
|
|
@@ -815,8 +816,11 @@ function useActions() {
|
|
|
815
816
|
const context = app.useContext<Model, typeof Actions>();
|
|
816
817
|
const actions = context.useActions(model);
|
|
817
818
|
|
|
818
|
-
actions.useAction(
|
|
819
|
-
|
|
819
|
+
actions.useAction(
|
|
820
|
+
Actions.TogglePaymentDialog,
|
|
821
|
+
context.with.invert("paymentDialog"),
|
|
822
|
+
);
|
|
823
|
+
actions.useAction(Actions.ToggleSidebar, context.with.invert("sidebar"));
|
|
820
824
|
|
|
821
825
|
return actions;
|
|
822
826
|
}
|
|
@@ -835,4 +839,9 @@ export default function Shell(): React.ReactElement {
|
|
|
835
839
|
}
|
|
836
840
|
```
|
|
837
841
|
|
|
838
|
-
`
|
|
842
|
+
`context.with.invert` only compiles when the leaf at the path is a boolean on the model; sibling helpers cover other common shapes:
|
|
843
|
+
|
|
844
|
+
- `context.with.update(key)` — binds the dispatched payload to a model leaf; the payload type must match `Get<Model, key>`.
|
|
845
|
+
- `context.with.always(key, value)` — pins the leaf to a fixed value, ignoring any dispatched payload. Handy for Open/Close, Show/Hide pairs where each action pins the same field to a known value.
|
|
846
|
+
|
|
847
|
+
All three accept lodash-style dotted paths (`"a.b.c"`) and array indices (`"items.0.name"`); keys autocomplete from the model declared on `useContext<Model, …>()`. The top-level `With.Update` / `With.Invert` / `With.Always` import is kept for call sites that don't have a typed `context` in scope.
|
package/dist/actions/index.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ import { Model, Props, Actions, UseActions } from '../types/index';
|
|
|
20
20
|
* @template M The model type representing the component's state.
|
|
21
21
|
* @template AC The actions class containing action definitions.
|
|
22
22
|
* @template D The data type for reactive external values.
|
|
23
|
-
* @param
|
|
23
|
+
* @param model The initial model state.
|
|
24
24
|
* @param getData Optional function that returns reactive values as data.
|
|
25
25
|
* Values returned are accessible via `context.data` in action handlers,
|
|
26
26
|
* always reflecting the latest values even after await operations.
|
|
@@ -39,8 +39,8 @@ import { Model, Props, Actions, UseActions } from '../types/index';
|
|
|
39
39
|
* Model,
|
|
40
40
|
* typeof Actions,
|
|
41
41
|
* { query: string }
|
|
42
|
-
* >(
|
|
42
|
+
* >(model, () => ({ query: props.query }));
|
|
43
43
|
* ```
|
|
44
44
|
*/
|
|
45
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>(
|
|
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>;
|
package/dist/app/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Env } from '../boundary/components/env/index';
|
|
|
2
2
|
import { Actions, Model, Props } from '../types/index';
|
|
3
3
|
import { Scope } from '../scope/index';
|
|
4
4
|
import { AppContextHandle, AppResource } from './types';
|
|
5
|
+
import { Tap } from '../boundary/components/tap/types';
|
|
5
6
|
import * as React from "react";
|
|
6
7
|
export type { AppArgs, AppContextHandle, AppFetcher, AppResource, } from './types';
|
|
7
8
|
/**
|
|
@@ -10,15 +11,20 @@ export type { AppArgs, AppContextHandle, AppFetcher, AppResource, } from './type
|
|
|
10
11
|
*/
|
|
11
12
|
export type App<S extends object> = {
|
|
12
13
|
/**
|
|
13
|
-
* Boundary component for this App.
|
|
14
|
-
* `env`
|
|
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.
|
|
15
19
|
*/
|
|
16
20
|
readonly Boundary: React.FC<{
|
|
21
|
+
env?: S;
|
|
22
|
+
tap?: Tap;
|
|
17
23
|
children: React.ReactNode;
|
|
18
24
|
}>;
|
|
19
25
|
/**
|
|
20
26
|
* Hook returning a stable `Context` handle. The handle's
|
|
21
|
-
* `context.useActions(
|
|
27
|
+
* `context.useActions(model?, getData?)` materialises the
|
|
22
28
|
* component's `[model, actions, data]` tuple. Every handler's
|
|
23
29
|
* `context.env` is typed as `S`.
|
|
24
30
|
*/
|
|
@@ -62,17 +68,30 @@ export type App<S extends object> = {
|
|
|
62
68
|
* Each `<app.Boundary>` instance owns its own Env, so different `App`s
|
|
63
69
|
* can coexist in the same tree with completely independent shapes.
|
|
64
70
|
*
|
|
71
|
+
* Pass `tap` to subscribe to every action handler's dispatch / settle /
|
|
72
|
+
* error inside the boundary — 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
|
+
*
|
|
65
77
|
* @example
|
|
66
78
|
* ```tsx
|
|
67
|
-
* import { App } from "march-hare";
|
|
79
|
+
* import { App, type Tapped } from "march-hare";
|
|
68
80
|
*
|
|
69
81
|
* type Session = { accessToken: string };
|
|
70
82
|
*
|
|
83
|
+
* function tap(event: Tapped) {
|
|
84
|
+
* if (event.type === "error") {
|
|
85
|
+
* Sentry.captureException(event.error, { tags: { action: event.action } });
|
|
86
|
+
* }
|
|
87
|
+
* }
|
|
88
|
+
*
|
|
71
89
|
* export const app = App({
|
|
72
90
|
* env: {
|
|
73
91
|
* session: null as Session | null,
|
|
74
92
|
* operating: "idle" as "idle" | "signing-out",
|
|
75
93
|
* },
|
|
94
|
+
* tap,
|
|
76
95
|
* });
|
|
77
96
|
*
|
|
78
97
|
* // Root render.
|
|
@@ -108,5 +127,6 @@ export type App<S extends object> = {
|
|
|
108
127
|
* ```
|
|
109
128
|
*/
|
|
110
129
|
export declare function App<S extends object = Env>(config?: {
|
|
111
|
-
env
|
|
130
|
+
env?: S;
|
|
131
|
+
tap?: Tap;
|
|
112
132
|
}): App<S>;
|
package/dist/app/types.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { Args, ResourceHandle } from '../resource/types';
|
|
|
2
2
|
import { Cache } from '../cache/index';
|
|
3
3
|
import { Actions, Context, Model, Props, UseActions } from '../types/index';
|
|
4
4
|
import { Data } from '../actions/types';
|
|
5
|
+
import { Env } from '../boundary/components/env/index';
|
|
6
|
+
import { WithHandle } from '../with/index';
|
|
5
7
|
/**
|
|
6
8
|
* Args object passed to an `app.Resource` fetcher. Same shape as the
|
|
7
9
|
* base `Resource` fetcher's args but with `env` typed as `S`.
|
|
@@ -25,25 +27,56 @@ export type AppResource<S> = {
|
|
|
25
27
|
readonly Cachable: <T, P extends object = Record<never, never>>(cache: Cache, fetcher: AppFetcher<S, T, P>) => ResourceHandle<T, P>;
|
|
26
28
|
};
|
|
27
29
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* @internal
|
|
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
33
|
*/
|
|
34
|
-
type
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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>;
|
|
39
42
|
/**
|
|
40
43
|
* `Context` handle returned by `app.useContext()`. Mirrors the base
|
|
41
|
-
* {@link Context} but
|
|
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.
|
|
42
53
|
*/
|
|
43
54
|
export type AppContextHandle<M, AC, D, S> = {
|
|
55
|
+
/**
|
|
56
|
+
* Stable dispatch surface available before `useActions(...)` runs.
|
|
57
|
+
* Exposes only `dispatch(action, payload?)` — 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
|
+
*/
|
|
44
62
|
readonly actions: {
|
|
45
63
|
dispatch: Context<M extends Model | void ? M : void, AC extends Actions | void ? AC : void, D extends Props ? D : Props>["actions"]["dispatch"];
|
|
46
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 — the callback re-runs every render so handlers reading
|
|
78
|
+
* `context.data` always see fresh values across `await` boundaries.
|
|
79
|
+
*/
|
|
47
80
|
readonly useActions: AppUseActions<M, AC, D, S>;
|
|
48
81
|
};
|
|
49
82
|
export {};
|
|
@@ -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 — 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` — React's sanctioned write-after-commit
|
|
16
|
+
* window — 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 — 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} — the observer signature.
|
|
34
|
+
* @see {@link Tapped} — the discriminated union of event shapes.
|
|
35
|
+
*/
|
|
36
|
+
export declare function Tappable({ tap, children }: Props): React.ReactNode;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { Reason } from '../../../error/types';
|
|
2
|
+
import { Task } from '../tasks/types';
|
|
3
|
+
import type * as React from "react";
|
|
4
|
+
/**
|
|
5
|
+
* Identity of a handler invocation: the action being handled and the
|
|
6
|
+
* payload supplied at dispatch. Appears on every {@link Tapped} event
|
|
7
|
+
* as `event.action`, alongside the `stage` / `result` discriminators.
|
|
8
|
+
*/
|
|
9
|
+
export type Invocation = {
|
|
10
|
+
/** Human-readable name of the action being handled. */
|
|
11
|
+
readonly name: string;
|
|
12
|
+
/** Payload supplied at dispatch (typed `unknown`; cast at the call site). */
|
|
13
|
+
readonly payload: unknown;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Failure fields layered onto the `details` sub-object of the
|
|
17
|
+
* `end:error` variant of {@link Tapped}. Groups the thrown error with
|
|
18
|
+
* the {@link Reason} the dispatch pipeline classified it as —
|
|
19
|
+
* consumers branching on `Aborted` vs. `Errored` read both off the
|
|
20
|
+
* same place. The error variant's `details` is `Failure` merged with
|
|
21
|
+
* the common end-of-handler shape (`{ mutations }`), so a helper that
|
|
22
|
+
* takes a `Failure` accepts the error-variant `details` directly.
|
|
23
|
+
*/
|
|
24
|
+
export type Failure = {
|
|
25
|
+
/** The Error instance thrown by the handler (or the abort cause). */
|
|
26
|
+
readonly error: Error;
|
|
27
|
+
/** Library classification of the failure: `Aborted` or `Errored`. */
|
|
28
|
+
readonly reason: Reason;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Reference snapshot for a single mutation surface (model or env)
|
|
32
|
+
* captured at handler start and end. The library compares references
|
|
33
|
+
* — Immer / Immertation produce a new reference iff a `produce`
|
|
34
|
+
* call actually committed a change, so reference inequality is a
|
|
35
|
+
* precise "did something change" signal with no deep-diff cost.
|
|
36
|
+
*
|
|
37
|
+
* Consumers diff `before` against `after` themselves (using the
|
|
38
|
+
* library's `changes` helper, a third-party diff lib, or a custom
|
|
39
|
+
* walk) when they need the per-field delta.
|
|
40
|
+
*/
|
|
41
|
+
export type Snapshot<T> = {
|
|
42
|
+
readonly before: T;
|
|
43
|
+
readonly after: T;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Per-handler summary of which mutation surfaces changed during the
|
|
47
|
+
* invocation. `null` on a surface means "reference unchanged" —
|
|
48
|
+
* the handler did not commit any `produce` call that touched that
|
|
49
|
+
* surface. A non-null value carries the before/after references.
|
|
50
|
+
*
|
|
51
|
+
* Values are typed `unknown` at this seam because the library doesn't
|
|
52
|
+
* know the model or env shape; cast inside the tap callback at the
|
|
53
|
+
* boundary where you know your `App`'s typed shape.
|
|
54
|
+
*
|
|
55
|
+
* Note on nested dispatches: if a handler awaits a sibling dispatch
|
|
56
|
+
* that itself mutates the same surface, the outer handler's
|
|
57
|
+
* `mutations` reflects the net change observed across its lifetime
|
|
58
|
+
* — including downstream effects. This is usually what you want
|
|
59
|
+
* for tracing; if you need to attribute changes to the originating
|
|
60
|
+
* handler precisely, branch on the `action` field as well.
|
|
61
|
+
*/
|
|
62
|
+
export type Mutations = {
|
|
63
|
+
readonly model: Snapshot<unknown> | null;
|
|
64
|
+
readonly env: Snapshot<unknown> | null;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Lifecycle event published by the tap. One pair of events is emitted
|
|
68
|
+
* per handler invocation, not per dispatch — a broadcast that
|
|
69
|
+
* reaches five subscribers produces five `start` events plus one `end`
|
|
70
|
+
* event per subscriber.
|
|
71
|
+
*
|
|
72
|
+
* The shape is a two-level discrimination:
|
|
73
|
+
*
|
|
74
|
+
* - **`stage`** — `"start"` (handler just began) or `"end"`
|
|
75
|
+
* (handler completed).
|
|
76
|
+
* - **`result`** — on `end` only, `"success"` or `"error"`
|
|
77
|
+
* describing the outcome. Mutually exclusive: the same handler
|
|
78
|
+
* invocation never produces both a success and an error.
|
|
79
|
+
*
|
|
80
|
+
* Per handler, exactly one of these two pairs is emitted:
|
|
81
|
+
* - **Succeeded:** `stage: "start"` → `stage: "end", result: "success"`.
|
|
82
|
+
* - **Failed:** `stage: "start"` → `stage: "end", result: "error"`.
|
|
83
|
+
*
|
|
84
|
+
* The {@link Invocation} identity (`action`, `payload`) and the
|
|
85
|
+
* discriminators (`stage`, `result`) sit at the top level so consumers
|
|
86
|
+
* can route and label events without diving into `details`. Everything
|
|
87
|
+
* else — the task handle, timing, mutation summary, failure
|
|
88
|
+
* info — lives on the `details` sub-object.
|
|
89
|
+
*
|
|
90
|
+
* `start` events carry just the {@link Task} in `details`;
|
|
91
|
+
* `end:success` adds `elapsed` and {@link Mutations}; `end:error`
|
|
92
|
+
* adds those plus the {@link Failure} fields.
|
|
93
|
+
*
|
|
94
|
+
* `elapsed` is measured in milliseconds against `performance.now()`,
|
|
95
|
+
* captured the moment the `end` event fires.
|
|
96
|
+
*/
|
|
97
|
+
export type Tapped = {
|
|
98
|
+
/** Action being handled: `{ name, payload }`. */
|
|
99
|
+
readonly action: Invocation;
|
|
100
|
+
} & ({
|
|
101
|
+
readonly stage: "start";
|
|
102
|
+
readonly details: {
|
|
103
|
+
readonly task: Task;
|
|
104
|
+
};
|
|
105
|
+
} | {
|
|
106
|
+
readonly stage: "end";
|
|
107
|
+
readonly result: "success";
|
|
108
|
+
readonly details: {
|
|
109
|
+
readonly task: Task;
|
|
110
|
+
readonly elapsed: number;
|
|
111
|
+
readonly mutations: Mutations;
|
|
112
|
+
};
|
|
113
|
+
} | {
|
|
114
|
+
readonly stage: "end";
|
|
115
|
+
readonly result: "error";
|
|
116
|
+
readonly details: Failure & {
|
|
117
|
+
readonly task: Task;
|
|
118
|
+
readonly elapsed: number;
|
|
119
|
+
readonly mutations: Mutations;
|
|
120
|
+
};
|
|
121
|
+
});
|
|
122
|
+
/**
|
|
123
|
+
* Observer callback invoked for every action handler lifecycle event
|
|
124
|
+
* inside the surrounding `<Boundary>`. Synchronous — do not
|
|
125
|
+
* perform expensive or async work here; defer to a queue, ring buffer,
|
|
126
|
+
* or external transport if the receiver is slow.
|
|
127
|
+
*
|
|
128
|
+
* Tap is intended for cross-cutting observability concerns: analytics,
|
|
129
|
+
* audit logging, browser-extension devtools, Sentry breadcrumbs,
|
|
130
|
+
* replay traces for bug reports. Use {@link Lifecycle.Fault} for
|
|
131
|
+
* in-band error recovery; the two are independent.
|
|
132
|
+
*/
|
|
133
|
+
export type Tap = (event: Tapped) => void;
|
|
134
|
+
/**
|
|
135
|
+
* Props accepted by the internal {@link Tappable} provider. The provider
|
|
136
|
+
* stores the supplied `tap` callback behind a ref so a parent re-render
|
|
137
|
+
* that changes the callback identity does not invalidate the React
|
|
138
|
+
* context value — every dispatch reads the latest callback at
|
|
139
|
+
* fire time.
|
|
140
|
+
*/
|
|
141
|
+
export type Props = {
|
|
142
|
+
/**
|
|
143
|
+
* Observer invoked for every action handler lifecycle event inside
|
|
144
|
+
* the surrounding `<Boundary>`. Omit (or pass `undefined`) to
|
|
145
|
+
* disable observation — the dispatch path then pays only the
|
|
146
|
+
* cost of a single function call per handler invocation.
|
|
147
|
+
*/
|
|
148
|
+
tap?: Tap;
|
|
149
|
+
children: React.ReactNode;
|
|
150
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Tap } from './types';
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
/**
|
|
4
|
+
* React context carrying the active {@link Tap} observer for the
|
|
5
|
+
* surrounding `<Boundary>`. Defaults to a no-op so `useTap()` callers
|
|
6
|
+
* never need to null-check.
|
|
7
|
+
*/
|
|
8
|
+
export declare const Context: React.Context<Tap>;
|
|
9
|
+
/**
|
|
10
|
+
* Hook returning the active tap observer. Always returns a callable
|
|
11
|
+
* — if no `<Boundary tap={...}>` is mounted above, calls are
|
|
12
|
+
* silent no-ops with no allocation cost beyond the function call itself.
|
|
13
|
+
*/
|
|
14
|
+
export declare function useTap(): Tap;
|
package/dist/boundary/index.d.ts
CHANGED
package/dist/boundary/types.d.ts
CHANGED
|
@@ -1,12 +1,54 @@
|
|
|
1
1
|
import { Env } from './components/env/types';
|
|
2
|
+
import { Tap } from './components/tap/types';
|
|
2
3
|
import type * as React from "react";
|
|
4
|
+
/**
|
|
5
|
+
* Props accepted by the bare `<Boundary>` provider.
|
|
6
|
+
*
|
|
7
|
+
* Most applications declare these once via {@link App} and let the
|
|
8
|
+
* generated `<app.Boundary>` thread them through — the bare
|
|
9
|
+
* Boundary is exposed for advanced use (custom wrappers, isolated test
|
|
10
|
+
* renders) where the loose {@link Env} record type is sufficient and
|
|
11
|
+
* the typed `app.useContext` / `app.useEnv` surface isn't needed.
|
|
12
|
+
*
|
|
13
|
+
* All fields are optional. Omit them all and the Boundary still wraps
|
|
14
|
+
* its subtree with the Broadcaster, Env, Tasks, and Tap providers
|
|
15
|
+
* required by every March Hare hook — just with a default empty
|
|
16
|
+
* env and a no-op tap.
|
|
17
|
+
*/
|
|
3
18
|
export type Props = {
|
|
4
19
|
/**
|
|
5
20
|
* Initial value of the per-Boundary {@link Env}. Prefer `App({ env })`
|
|
6
|
-
* — it infers the Env shape and threads it through
|
|
7
|
-
* `app.useEnv`, and `app.Resource
|
|
8
|
-
*
|
|
21
|
+
* — it infers the Env shape `S` and threads it through
|
|
22
|
+
* `app.useContext`, `app.useEnv`, and `app.Resource`, so handler
|
|
23
|
+
* `context.env` is typed accordingly. Pass `env` directly here only
|
|
24
|
+
* for advanced cases where the loose record type is sufficient.
|
|
25
|
+
*
|
|
26
|
+
* The value is captured on mount; subsequent prop updates do not
|
|
27
|
+
* replace the live env. Mutations during the boundary's lifetime
|
|
28
|
+
* flow through `context.actions.produce(({ env }) => { ... })`.
|
|
9
29
|
*/
|
|
10
30
|
env?: Env;
|
|
31
|
+
/**
|
|
32
|
+
* Observer invoked for every action handler lifecycle event inside
|
|
33
|
+
* this Boundary. One event fires per handler invocation, not per
|
|
34
|
+
* dispatch — a broadcast that reaches five subscribers
|
|
35
|
+
* produces five `dispatch` events, five `settle` events, and an
|
|
36
|
+
* `error` event for any of the five that throws.
|
|
37
|
+
*
|
|
38
|
+
* The callback is synchronous: it blocks the dispatch path until it
|
|
39
|
+
* returns. Defer slow work (network transports, file IO) to a queue
|
|
40
|
+
* or idle callback rather than running it inline.
|
|
41
|
+
*
|
|
42
|
+
* Typical uses: analytics histograms, audit-log ring buffers,
|
|
43
|
+
* Sentry breadcrumbs, replay traces for bug reports. For in-band
|
|
44
|
+
* error recovery use {@link Lifecycle.Fault} instead — the two
|
|
45
|
+
* channels are independent. See `recipes/tap.md`.
|
|
46
|
+
*/
|
|
47
|
+
tap?: Tap;
|
|
48
|
+
/**
|
|
49
|
+
* Subtree that should receive the boundary's broadcast, env, tasks,
|
|
50
|
+
* and tap providers. Every March Hare hook called inside this
|
|
51
|
+
* subtree resolves against this boundary's context.
|
|
52
|
+
*/
|
|
11
53
|
children: React.ReactNode;
|
|
12
54
|
};
|
package/dist/context/index.d.ts
CHANGED
|
@@ -6,15 +6,17 @@ import { Actions, Context as ContextHandle, Model, Props } from '../types/index'
|
|
|
6
6
|
* dispatch callback at construction time, while the value that library
|
|
7
7
|
* returns must flow back into the controller's data callback.
|
|
8
8
|
*
|
|
9
|
-
* The handle exposes `dispatch(action, payload?)
|
|
9
|
+
* The handle exposes `dispatch(action, payload?)`, a `useActions(...)`
|
|
10
10
|
* method that materialises the component-local model and reactive data
|
|
11
11
|
* — the M and D pair of `useContext<M, AC, D>` — and
|
|
12
12
|
* returns the `[model, actions, data]` tuple with `useAction`, `dispatch`,
|
|
13
|
-
* `inspect`, and `stream` attached
|
|
14
|
-
* `
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* render
|
|
13
|
+
* `inspect`, and `stream` attached, plus `with` — a bag of handler
|
|
14
|
+
* factories (`update`/`invert`/`always`) typed against the declared model
|
|
15
|
+
* and accepting lodash-style dotted paths and array indices. The first
|
|
16
|
+
* invocation of `context.actions.dispatch(...)` must come from an event
|
|
17
|
+
* handler — not synchronously during render — because the
|
|
18
|
+
* underlying dispatch target is wired up when `context.useActions(...)`
|
|
19
|
+
* runs in the same render pass.
|
|
18
20
|
*
|
|
19
21
|
* @template M The model type representing the component's state.
|
|
20
22
|
* @template AC The actions class containing action definitions.
|
package/dist/index.d.ts
CHANGED
|
@@ -13,3 +13,4 @@ export type { Fault } from './error/index';
|
|
|
13
13
|
export type { Adapter } from './cache/index';
|
|
14
14
|
export type { Box } from 'immertation';
|
|
15
15
|
export type { Pk, Task, Tasks, Maybe, Handler, Handlers, } from './types/index';
|
|
16
|
+
export type { Tap, Tapped, Invocation, Failure, Mutations, Snapshot, } from './boundary/components/tap/types';
|
package/dist/march-hare.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import{jsx as e}from"react/jsx-runtime";import*as t from"react";import{G as n,A as r}from"@mobily/ts-belt";import{immerable as o,enablePatches as s,Immer as c,produce as a}from"immer";function i(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var u,l={exports:{}};const f=/* @__PURE__ */i((u||(u=1,function(e){var t=Object.prototype.hasOwnProperty,n="~";function r(){}function o(e,t,n){this.fn=e,this.context=t,this.once=n||!1}function s(e,t,r,s,c){if("function"!=typeof r)throw new TypeError("The listener must be a function");var a=new o(r,s||e,c),i=n?n+t:t;return e._events[i]?e._events[i].fn?e._events[i]=[e._events[i],a]:e._events[i].push(a):(e._events[i]=a,e._eventsCount++),e}function c(e,t){0===--e._eventsCount?e._events=new r:delete e._events[t]}function a(){this._events=new r,this._eventsCount=0}Object.create&&(r.prototype=/* @__PURE__ */Object.create(null),(new r).__proto__||(n=!1)),a.prototype.eventNames=function(){var e,r,o=[];if(0===this._eventsCount)return o;for(r in e=this._events)t.call(e,r)&&o.push(n?r.slice(1):r);return Object.getOwnPropertySymbols?o.concat(Object.getOwnPropertySymbols(e)):o},a.prototype.listeners=function(e){var t=this._events[n?n+e:e];if(!t)return[];if(t.fn)return[t.fn];for(var r=0,o=t.length,s=new Array(o);r<o;r++)s[r]=t[r].fn;return s},a.prototype.listenerCount=function(e){var t=this._events[n?n+e:e];return t?t.fn?1:t.length:0},a.prototype.emit=function(e,t,r,o,s,c){var a=n?n+e:e;if(!this._events[a])return!1;var i,u,l=this._events[a],f=arguments.length;if(l.fn){switch(l.once&&this.removeListener(e,l.fn,void 0,!0),f){case 1:return l.fn.call(l.context),!0;case 2:return l.fn.call(l.context,t),!0;case 3:return l.fn.call(l.context,t,r),!0;case 4:return l.fn.call(l.context,t,r,o),!0;case 5:return l.fn.call(l.context,t,r,o,s),!0;case 6:return l.fn.call(l.context,t,r,o,s,c),!0}for(u=1,i=new Array(f-1);u<f;u++)i[u-1]=arguments[u];l.fn.apply(l.context,i)}else{var d,h=l.length;for(u=0;u<h;u++)switch(l[u].once&&this.removeListener(e,l[u].fn,void 0,!0),f){case 1:l[u].fn.call(l[u].context);break;case 2:l[u].fn.call(l[u].context,t);break;case 3:l[u].fn.call(l[u].context,t,r);break;case 4:l[u].fn.call(l[u].context,t,r,o);break;default:if(!i)for(d=1,i=new Array(f-1);d<f;d++)i[d-1]=arguments[d];l[u].fn.apply(l[u].context,i)}}return!0},a.prototype.on=function(e,t,n){return s(this,e,t,n,!1)},a.prototype.once=function(e,t,n){return s(this,e,t,n,!0)},a.prototype.removeListener=function(e,t,r,o){var s=n?n+e:e;if(!this._events[s])return this;if(!t)return c(this,s),this;var a=this._events[s];if(a.fn)a.fn!==t||o&&!a.once||r&&a.context!==r||c(this,s);else{for(var i=0,u=[],l=a.length;i<l;i++)(a[i].fn!==t||o&&!a[i].once||r&&a[i].context!==r)&&u.push(a[i]);u.length?this._events[s]=1===u.length?u[0]:u:c(this,s)}return this},a.prototype.removeAllListeners=function(e){var t;return e?this._events[t=n?n+e:e]&&c(this,t):(this._events=new r,this._eventsCount=0),this},a.prototype.off=a.prototype.removeListener,a.prototype.addListener=a.prototype.on,a.prefixed=n,a.EventEmitter=a,e.exports=a}(l)),l.exports));class d extends f{cache=/* @__PURE__ */new Map;emit(e,...t){return this.cache.set(e,t[0]),super.emit(e,...t)}setCache(e,t){this.cache.set(e,t)}getCached(e){return this.cache.get(e)}fire(e,...t){return super.emit(e,...t)}}const h=t.createContext(new d);function p(){return t.useContext(h)}function m({children:n}){const r=t.useMemo(()=>new d,[]);/* @__PURE__ */
|
|
2
2
|
return e(h.Provider,{value:r,children:n})}const b=t.createContext(/* @__PURE__ */new Set);function y({children:n}){const r=t.useMemo(()=>/* @__PURE__ */new Set,[]);/* @__PURE__ */
|
|
3
|
-
return e(b.Provider,{value:r,children:n})}const v=t.createContext({current:{}});function g(){const e=t.useContext(v);return t.useMemo(()=>new Proxy({},{get:(t,n)=>Reflect.get(e.current,n),has:(t,n)=>n in e.current,ownKeys:()=>Reflect.ownKeys(e.current),getOwnPropertyDescriptor(t,r){const o=Object.getOwnPropertyDescriptor(e.current,r);if(!n.isUndefined(o))return{...o,configurable:!0}},set(){throw new TypeError("Env is read-only outside `context.actions.produce`. Mutate via produce(({ env }) => { env.x = ... }) instead.")}}),[e])}const w=(e="")=>`march-hare.action/${e}`,O=(e="")=>`march-hare.action/broadcast/${e}`,P=(e="")=>`march-hare.action/multicast/${e}`,j=(e="")=>`march-hare.action.lifecycle/${e}`;class
|
|
4
|
-
return e(_.Provider,{value:r,children:n})}function
|
|
5
|
-
return e(m,{children:/* @__PURE__ */e(A,{initial:t??{},children:/* @__PURE__ */e(y,{children:/* @__PURE__ */e(U,{children:n})})})})}const L=e=>"symbol"==typeof e;function T(e){return n.isString(e)||L(e)?e:(n.isObject(e)||n.isFunction(e))&&S.Action in e?e[S.Action]:e}function W(e){if(n.isString(e))return e.startsWith(O());if(L(e))return e.description?.startsWith(O())??!1;if(n.isObject(e)||n.isFunction(e)){if(S.Broadcast in e&&e[S.Broadcast])return!0;if(S.Action in e){const t=e[S.Action];return t.description?.startsWith(O())??!1}}return!1}function $(e){const t=T(e),r=n.isString(t)?t:t.description??"";return r.startsWith(w())&&r.slice(r.lastIndexOf("/")+1)||"unknown"}function B(e){return n.isObject(e)&&S.Channel in e&&"channel"in e}function F(e){const t=T(e),n=L(t)?t.description??"":t;return n.startsWith(j())&&n.slice(j().length)||null}function D(e){if(n.isString(e))return e.startsWith(P());if(L(e))return e.description?.startsWith(P())??!1;if(n.isObject(e)||n.isFunction(e)){if(S.Multicast in e&&e[S.Multicast])return!0;if(S.Action in e){const t=e[S.Action];return t.description?.startsWith(P())??!1}}return!1}const H=(e="",t=M.Unicast)=>{const n=t===M.Broadcast?Symbol(O(e)):t===M.Multicast?Symbol(P(e)):Symbol(w(e)),r=function(t){return{[S.Action]:n,[S.Payload]:void 0,[S.Channel]:t,[S.Name]:e,channel:t}};return Object.defineProperty(r,S.Action,{value:n,enumerable:!1}),Object.defineProperty(r,S.Payload,{value:void 0,enumerable:!1}),Object.defineProperty(r,S.Name,{value:e,enumerable:!1}),t===M.Broadcast&&Object.defineProperty(r,S.Broadcast,{value:!0,enumerable:!1}),t===M.Multicast&&Object.defineProperty(r,S.Multicast,{value:!0,enumerable:!1}),r},I=Symbol(((e="")=>`march-hare/replay${e}`)());function J(e,t,...n){e instanceof d&&e.setCache(t,n[0]);const r=e.listeners(t);return 0===r.length?Promise.resolve():Promise.all(r.map(e=>Promise.resolve(e(...n)))).then(()=>{})}function z(e,t){for(const n of e.keys())if(F(n)===t)return n;return null}const q=/* @__PURE__ */Symbol("march-hare.unset");function G(){const[,e]=t.useReducer(e=>e+1,0);return e}function K(){return{data:q,at:null,else:e=>e}}function V(e,t){return{data:e,at:t,else:t=>e}}var Q=/* @__PURE__ */(e=>(e[e.Aborted=0]="Aborted",e[e.Errored=1]="Errored",e))(Q||{});class X extends Error{name="AbortError";constructor(e="Aborted"){super(e)}}function Y(e){return e instanceof Error?e:new Error(String(e))}const Z=t.createContext(null);function ee(e){const t=/* @__PURE__ */new Map,r=e??{get:e=>t.get(e)??null,set:(e,n)=>{t.set(e,n)},remove:e=>{t.delete(e)},clear:()=>{t.clear()}};return{get(e){try{const t=r.get(e);if(n.isNull(t))return K();const o=JSON.parse(t);return V(o.data,Temporal.Instant.from(o.at))}catch{return K()}},set(e,t){if(t.data===q||n.isNull(t.at))return!1;try{return r.set(e,JSON.stringify({data:t.data,at:t.at.toString()})),!0}catch{return!1}},remove(e){r.remove(e)},clear(){r.clear()}}}function te(e,t){return new Promise((n,r)=>{if(t?.aborted)return void r(new X);const o=setTimeout(n,e);t?.addEventListener("abort",()=>{clearTimeout(o),r(new X)},{once:!0})})}async function ne(e,t,n){if(t?.aborted)throw new X;for(;;){if(await n())return;await te(e,t)}}function re(e){return e?Boolean(e&&"symbol"!=typeof e):/* @__PURE__ */Symbol(`pk.${Date.now()}.${crypto.randomUUID()}`)}const oe=/* @__PURE__ */Object.freeze(/* @__PURE__ */Object.defineProperty({__proto__:null,pk:re,poll:ne,sleep:te,unset:q,"ζ":te,"κ":re,"π":ne},Symbol.toStringTag,{value:"Module"})),se=/* @__PURE__ */new WeakMap;function ce(e){return JSON.stringify(e)}let ae=null;function ie(){if(n.isNull(ae))throw new Error("context.actions.resource(...) and context.actions.resource.set(...) must be called with a fresh resource invocation, e.g. context.actions.resource(resource.cat({ id: 5 })).");const e=ae;return ae=null,e}function ue(e,t){const r=e=>{const r=t.get(ce(e));return r.data===q||n.isNull(r.at)?{data:q,at:null}:{data:r.data,at:r.at}},o=(n,r,o,s)=>e({env:n,controller:r,params:o,dispatch:s}).then(e=>(t.set(ce(o),V(e,Temporal.Now.instant())),e)),s=(e,n,r)=>{t.set(ce(e),V(n,r))};return function(e){const t=e??{};ae={run:o,read:r,seed:s,params:t},queueMicrotask(()=>{n.isNotNullable(ae)&&ae.params===t&&(ae=null)});const{data:c}=r(t);return c===q?null:c}}function le(e){return ue(e,function(e){let t=se.get(e);return n.isUndefined(t)&&(t=ee(),se.set(e,t)),t}(e))}(le||(le={})).Cachable=function(e,t){return ue(t,e)};const fe=/* @__PURE__ */Symbol("march-hare.coalesce/default");function de(e,t){return new Promise((n,r)=>{if(t.aborted)return void r(t.reason);const o=()=>r(t.reason);t.addEventListener("abort",o,{once:!0}),e.then(e=>{t.removeEventListener("abort",o),n(e)},e=>{t.removeEventListener("abort",o),r(e)})})}let he=(e=21)=>{let t="",n=crypto.getRandomValues(new Uint8Array(e|=0));for(;e--;)t+="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict"[63&n[e]];return t};var pe=/* @__PURE__ */(e=>(e[e.Add=1]="Add",e[e.Remove=2]="Remove",e[e.Update=4]="Update",e[e.Move=8]="Move",e[e.Replace=16]="Replace",e[e.Sort=32]="Sort",e[e.Create=64]="Create",e[e.Fetch=128]="Fetch",e[e.Clone=256]="Clone",e[e.Archive=512]="Archive",e[e.Restore=1024]="Restore",e[e.Merge=2048]="Merge",e[e.Reorder=4096]="Reorder",e[e.Sync=8192]="Sync",e[e.Publish=16384]="Publish",e[e.Link=32768]="Link",e[e.Unlink=65536]="Unlink",e[e.Lock=131072]="Lock",e[e.Unlock=262144]="Unlock",e[e.Import=524288]="Import",e[e.Export=1048576]="Export",e[e.Transfer=2097152]="Transfer",e))(pe||{}),me=/* @__PURE__ */(e=>(e[e.Produce=0]="Produce",e[e.Hydrate=1]="Hydrate",e))(me||{}),be=/* @__PURE__ */(e=>(e.Property="property",e.Process="process",e.Value="value",e.Operation="operation",e))(be||{});class ye{[o]=!0;static keys=new Set(Object.values(be));property=null;process=null;value;operation;constructor(e,t){this.value=e,this.operation=t}assign(e,t){const n=new ye(this.value,this.operation);return n.property=e,n.process=t,n}}class ve{static immer=(()=>{s();const e=new c;return e.setAutoFreeze(!1),e})();static tag="κ";static id=he}function ge(e,t){const n="string"==typeof t?""===t?[]:t.split("."):t;let r=e;for(const o of n){if(null==r)return;r=r[o]}return r}function we(e){if(n.isNullable(e)||je(e))return e;if(n.isArray(e))return e.map(e=>we(e));if(n.isObject(e)&&Pe(e)){const t=Object.entries(e).map(([e,t])=>[e,we(t)]);return{...Object.fromEntries(t),[ve.tag]:e[ve.tag]??ve.id()}}return e}function Oe(e){if(Array.isArray(e))return e.filter(e=>ve.tag in e).map(e=>e[ve.tag]??"").join(",");const t=e[ve.tag];if(t)return t;try{return JSON.stringify(e)}catch{return`[unserializable:${typeof e}]`}}function Pe(e){const t=Object.getPrototypeOf(e);return t===Object.prototype||null===t}function je(e){return n.isNullable(e)||n.isString(e)||n.isNumber(e)||n.isBoolean(e)||"symbol"==typeof e||"bigint"==typeof e}function Se(e,t,r,o,s,c){return function a(i,u=t.path){if(i instanceof ye){const t=ge(r,u.join("."));if(Object.entries(i).filter(([e,t])=>!ye.keys.has(e)&&t instanceof ye).forEach(([e,t])=>a(t,u.concat(e))),je(i.value)){if(e===me.Hydrate)return i.value;const a=u.slice(0,-1),l=a.length>0?ge(r,a.join(".")):r;return n.isNullable(l)||xe(l,i,u.at(-1),o,s,c),t??i.value}if(e===me.Hydrate){const e=we(a(i.value,u));return xe(e,i,null,o,s,c),e}const l=t??we(i.value);return xe(l,i,null,o,s,c),n.isNullable(t)?l:(a(i.value,u),t)}if(n.isArray(i))return i.map((e,t)=>a(e,u.concat(t)));if(n.isObject(i)&&!Pe(i))return i;if(n.isObject(i)){const t=Object.entries(i).map(([e,t])=>[e,a(t,u.concat(e))]),n=Object.fromEntries(t);if(e===me.Hydrate){const e=we(n);return Object.entries(i).forEach(([t,n])=>{n instanceof ye&&je(n.value)&&xe(e,n,t,o,s,c)}),e}return n}return i}(t.value)}function xe(e,t,n,r,o,s){const c=s(e),a=o.get(c)??[];o.set(c,[t.assign(n,r),...a])}class Ee{#e={};#t;#n=/* @__PURE__ */new Map;#r=/* @__PURE__ */new Set;#o=!1;constructor(e=Oe){this.#t=e}static pk(){return he()}static"κ"=Ee.pk;annotate(e,t){return new ye(t,e)}"δ"=this.annotate;get model(){return this.#e}get inspect(){return function(e,t,o,s,c){function a(s){const c=s.at(-1),a=ge(e(),s),i=s.slice(0,-1),u=r.isNotEmpty(i)?ge(e(),i):e();return[...n.isObject(a)||n.isArray(a)?t.get(o(a))?.filter(e=>n.isNullable(e.property))??[]:[],...n.isObject(u)?t.get(o(u))?.filter(e=>e.property===c)??[]:[]]}return function t(n){return new Proxy(()=>{},{get:(o,i)=>"pending"===i?()=>!r.isEmpty(a(n)):"remaining"===i?()=>r.length(a(n)):"box"===i?()=>({value:ge(e(),n),inspect:t(n)}):"is"===i?e=>a(n).some(t=>0!==(t.operation&e)):"draft"===i?()=>r.head(a(n))?.value??ge(e(),n):"settled"===i?()=>new Promise(t=>{if(r.isEmpty(a(n)))return t(ge(e(),n));const o=()=>{r.isEmpty(a(n))&&(c(o),t(ge(e(),n)))};s(o)}):t([...n,String(i)])})}([])}(()=>this.#e,this.#n,this.#t,e=>this.#r.add(e),e=>this.#r.delete(e))}hydrate(e){return this.#o=!0,this.#s(me.Hydrate,()=>e)}produce(e){if(!this.#o)throw new Error("State must be hydrated using hydrate() before calling produce()");return this.#s(me.Produce,e)}#s(e,t){const n=/* @__PURE__ */Symbol("process"),[,r]=ve.immer.produceWithPatches(this.#e,t);return this.#e=r.reduce((t,r)=>ve.immer.applyPatches(t,[{...r,value:Se(e,r,t,n,this.#n,this.#t)}]),this.#e),this.#e=we(this.#e),this.#c(),n}prune(e){this.#n.forEach((t,n)=>{const o=t.filter(t=>t.process!==e);r.isEmpty(o)?this.#n.delete(n):this.#n.set(n,o)}),this.#c()}#c(){this.#r.forEach(e=>e())}observe(e){const t=()=>e(this.#e);return this.#r.add(t),()=>this.#r.delete(t)}}const Ce=t.createContext(/* @__PURE__ */new Map);function Ne({action:e,renderer:r}){const o=p(),s=t.useContext(Ce),c=G(),a=t.useMemo(()=>{const t=s.get(e);if(t)return t;const r=new Ee,c=o.getCached(e);n.isNotNullable(c)&&r.hydrate({value:c});const a={state:r,listeners:/* @__PURE__ */new Set};return s.set(e,a),a},[e,o,s]);t.useLayoutEffect(()=>{function t(e){a.state.hydrate({value:e}),a.listeners.forEach(e=>e())}return a.listeners.add(c),o.on(e,t),()=>{a.listeners.delete(c),o.off(e,t)}},[e,o,a]);const i=a.state.model?.value;return n.isNullable(i)?null:r(i,a.state.inspect.value)}function Me(){const e=t.useRef(null);return t.useMemo(()=>({actions:{dispatch:function(t,n){const r=e.current;if(!r)throw new Error("march-hare: useContext handle dispatched before its paired context.useActions(...) ran. Call context.actions.dispatch from event handlers, not synchronously during render.");return r(t,n)}},useActions:function(...o){const s=function(...e){const o=n.isUndefined(e[0])||n.isFunction(e[0])?{}:e[0],s=n.isFunction(e[0])?e[0]:e[1]??(()=>({})),c=p(),i=t.useContext(Z),u=t.useContext(b),l=g(),d=t.useContext(v),h=t.useContext(_),m=G(),y=t.useRef(!1),w=t.useRef(null),O=t.useRef(new Ee);y.current||(y.current=!0,w.current=O.current.hydrate(o));const[P,j]=t.useState(()=>O.current.model),S=function(e){const n=t.useRef(e);return n.current=e,t.useMemo(()=>{return t=n,Object.keys(e).reduce((e,n)=>(Object.defineProperty(e,n,{get:()=>t.current[n],enumerable:!0}),e),{});var t},[e])}(s()),x=t.useMemo(()=>new f,[]),N=t.useRef({handlers:/* @__PURE__ */new Map});N.current.handlers=/* @__PURE__ */new Map;const M=function(){const e=t.useRef(/* @__PURE__ */new Set),n=t.useRef(/* @__PURE__ */new Set);return t.useMemo(()=>({broadcast:e.current,multicast:n.current}),[])}(),A=t.useRef(k.Mounting),U=t.useRef(/* @__PURE__ */new Set),R=t.useRef(0),L=t.useCallback((e,t,r)=>{const o=new AbortController,s={controller:o,action:e,payload:t};return u.add(s),U.current.add(s),{model:O.current.model,get phase(){return A.current},task:s,data:S,tasks:u,env:l,actions:{produce(e){if(o.signal.aborted)return;const t=d.current,n=O.current.produce(t=>{d.current=a(d.current,n=>{e({model:t,inspect:O.current.inspect,env:n})})});j(O.current.model),d.current!==t&&c.emit(C,d.current),r.processes.add(n),w.current&&(r.processes.add(w.current),w.current=null)},dispatch(e,t){if(o.signal.aborted)return Promise.resolve();const n=T(e),r=B(e)?e.channel:void 0;return D(e)?i?J(i.emitter,n,t,r):Promise.resolve():J(W(e)?c:x,n,t,r)},annotate:(e,t=pe.Update)=>O.current.annotate(t,e),get inspect(){return O.current.inspect},resource:Object.assign(function(e){const t=ie(),r=(e,t)=>{if(o.signal.aborted)return Promise.resolve();const n=e,r=T(n);return D(n)?i?J(i.emitter,r,t,void 0):Promise.resolve():W(n)?J(c,r,t,void 0):Promise.resolve()},s={exceedsWindow:null,coalesceToken:void 0},a={then:(e,c)=>(()=>{if(n.isNotNullable(s.exceedsWindow)){const{data:e,at:r}=t.read(t.params);if(e!==q&&n.isNotNullable(r)){const t=Temporal.Now.instant().since(r),n=Temporal.Duration.from(s.exceedsWindow);if(Temporal.Duration.compare(t,n)<=0)return Promise.resolve(e)}}if(n.isUndefined(s.coalesceToken))return t.run(d.current,o,t.params,r);let e=h.get(t.run);n.isUndefined(e)&&(e=/* @__PURE__ */new Map,h.set(t.run,e));const c=e,a=`${JSON.stringify(t.params)}|${function(e){switch(typeof e){case"string":return`s:${e}`;case"number":return`n:${e}`;case"bigint":return`i:${e.toString()}`;case"boolean":return`b:${e}`;case"symbol":return`y:${e.description??String(e)}`;default:return`o:${JSON.stringify(e)}`}}(s.coalesceToken)}`,i=c.get(a);if(i)return de(i,o.signal);const u=new AbortController,l=t.run(d.current,u,t.params,r).finally(()=>{c.delete(a)});return c.set(a,l),de(l,o.signal)})().then(e,c),exceeds:e=>(s.exceedsWindow=e,a),coalesce:e=>(s.coalesceToken=e??fe,a)};return a},{set:(e,t)=>{const n=ie();n.seed(n.params,t,Temporal.Now.instant())}}),async final(e){if(o.signal.aborted)return null;const t=T(e),r=D(e)?i?.emitter??null:c;if(!r)return null;const s=r.getCached(t);if(n.isUndefined(s))return null;const a=O.current.inspect;return a.pending()&&await new Promise((e,t)=>{if(o.signal.aborted)return void t(o.signal.reason);const n=()=>t(o.signal.reason);o.signal.addEventListener("abort",n,{once:!0}),a.settled().then(()=>{o.signal.removeEventListener("abort",n),e()})}),r.getCached(t)??null},peek(e){if(o.signal.aborted)return null;const t=T(e),n=D(e)?i?.emitter??null:c;return n?n.getCached(t)??null:null}}}},[P]);t.useLayoutEffect(()=>{function e(e,t,r){return function(o,s){const a=r();if(s===I&&n.isNotNullable(a))return;if(n.isNotNullable(s)&&s!==I&&n.isNotNullable(a)&&!function(e,t){for(const n of Object.keys(e))if(t[n]!==e[n])return!1;return!0}(s,a))return;const i={processes:/* @__PURE__ */new Set},l=Promise.withResolvers(),f=L(e,o,i);function d(t){const r=z(N.current.handlers,"Error"),o=n.isNotNullable(r),s={reason:(a=t,a instanceof Error&&"AbortError"===a.name?Q.Aborted:Q.Errored),error:Y(t),action:$(e),handled:o,tasks:u};var a;c.fire(E,s),o&&r&&x.emit(r,s)}function h(){for(const e of u)if(e===f.task){u.delete(e),U.current.delete(e);break}i.processes.forEach(e=>O.current.prune(e)),i.processes.size>0&&m(),l.resolve()}let p;try{p=t(f,o)}catch(b){return d(b),void h()}if(!function(e){if(!e||"object"!=typeof e)return!1;const t=Object.prototype.toString.call(e);return"[object Generator]"===t||"[object AsyncGenerator]"===t}(p))return Promise.resolve(p).catch(d).finally(h),l.promise;(async()=>{for await(const e of p);})().catch(d).finally(h)}}R.current++;const t=/* @__PURE__ */new Set;return N.current.handlers.forEach((n,r)=>{for(const{getChannel:o,handler:s}of n){const n=e(r,s,o);if(D(r)){if(i){const e=i.emitter;e.on(r,n),t.add(()=>e.off(r,n))}x.on(r,n),M.multicast.add(r),t.add(()=>x.off(r,n))}else W(r)?(c.on(r,n),x.on(r,n),M.broadcast.add(r),t.add(()=>{c.off(r,n),x.off(r,n)})):(x.on(r,n),t.add(()=>x.off(r,n)))}}),()=>{const e=++R.current,n=new Set(t);queueMicrotask(()=>{if(R.current!==e){for(const e of n)e();return}for(const e of U.current)e.controller.abort(),u.delete(e);U.current.clear(),A.current=k.Unmounting;const t=z(N.current.handlers,"Unmount");t&&x.emit(t),A.current=k.Unmounted;for(const e of n)e()})}},[x]),function({unicast:e,broadcast:o,dispatchers:s,scope:c,phase:a,data:i,handlers:u}){const l=t.useRef(null);t.useLayoutEffect(()=>{if(a.current===k.Mounted)return;a.current=k.Mounting;const t=z(u,"Mount");t&&e.emit(t),s.broadcast.forEach(t=>{const r=o.getCached(t);n.isNullable(r)||e.emit(t,r,I)}),c&&s.multicast.forEach(t=>{const r=c.emitter.getCached(t);n.isNullable(r)||e.emit(t,r,I)}),a.current=k.Mounted},[]),t.useLayoutEffect(()=>{if(n.isNotNullable(l.current)){const t=function(e,t){return Object.keys(t).reduce((n,r)=>e[r]!==t[r]?{...n,[r]:t[r]}:n,{})}(l.current,i);if(r.isNotEmpty(Object.keys(t))){const n=z(u,"Update");n&&e.emit(n,t)}}l.current=i},[i,e])}({unicast:x,broadcast:c,dispatchers:M,scope:i,phase:A,data:s(),handlers:N.current.handlers});const F=t.useMemo(()=>({dispatch(e,t){const n=T(e),r=B(e)?e.channel:void 0;return D(e)?i?J(i.emitter,n,t,r):Promise.resolve():J(W(e)?c:x,n,t,r)},get inspect(){return O.current.inspect},stream:(e,n)=>t.createElement(Ne,{action:T(e),renderer:n})}),[P,x]),H=t.useMemo(()=>[P,F,S],[P,F,S]);return H.useAction=(e,n)=>{!function(e,n,r){const o=t.useRef(r);t.useLayoutEffect(()=>{o.current=r});const s=t.useRef(n);t.useLayoutEffect(()=>{s.current=n});const c=t.useCallback((e,t)=>o.current(e,t),[]),a=t.useCallback(()=>B(s.current)?s.current.channel:void 0,[]),i=T(n),u=e.current.handlers.get(i)??/* @__PURE__ */new Set;0===u.size&&e.current.handlers.set(i,u),u.add({getChannel:a,handler:c})}(N,e,n)},H.dispatch=H[1].dispatch,H}(...o);return e.current=s.dispatch,s}}),[])}function ke(n){return{Boundary:function({children:t}){/* @__PURE__ */
|
|
6
|
-
return e(R,{env:n?.env,children:t})},useContext:function(){return Me()},useEnv:function(){return g()},Resource:Object.assign(function(e){return le(e)},{Cachable:(e,t)=>le.Cachable(e,t)}),Scope:()=>({Boundary:function({children:n}){const r=t.useMemo(()=>({id:/* @__PURE__ */Symbol("march-hare.scope/instance"),emitter:new d}),[]);/* @__PURE__ */
|
|
7
|
-
return e(
|
|
3
|
+
return e(b.Provider,{value:r,children:n})}const v=t.createContext({current:{}});function g(){const e=t.useContext(v);return t.useMemo(()=>new Proxy({},{get:(t,n)=>Reflect.get(e.current,n),has:(t,n)=>n in e.current,ownKeys:()=>Reflect.ownKeys(e.current),getOwnPropertyDescriptor(t,r){const o=Object.getOwnPropertyDescriptor(e.current,r);if(!n.isUndefined(o))return{...o,configurable:!0}},set(){throw new TypeError("Env is read-only outside `context.actions.produce`. Mutate via produce(({ env }) => { env.x = ... }) instead.")}}),[e])}const w=(e="")=>`march-hare.action/${e}`,O=(e="")=>`march-hare.action/broadcast/${e}`,P=(e="")=>`march-hare.action/multicast/${e}`,j=(e="")=>`march-hare.action.lifecycle/${e}`;class x{static Payload=/* @__PURE__ */Symbol("march-hare.brand/Payload");static Broadcast=/* @__PURE__ */Symbol("march-hare.brand/Broadcast");static Multicast=/* @__PURE__ */Symbol("march-hare.brand/Multicast");static Action=/* @__PURE__ */Symbol("march-hare.brand/Action");static Channel=/* @__PURE__ */Symbol("march-hare.brand/Channel");static Name=/* @__PURE__ */Symbol("march-hare.brand/Name")}function S(e){const t=/* @__PURE__ */Symbol(`march-hare.action.lifecycle/${e}`),n=function(n){return{[x.Action]:t,[x.Payload]:void 0,[x.Channel]:n,[x.Name]:e,channel:n}};return Object.defineProperty(n,x.Action,{value:t,enumerable:!1}),Object.defineProperty(n,x.Payload,{value:void 0,enumerable:!1}),Object.defineProperty(n,x.Name,{value:e,enumerable:!1}),n}const E=Symbol(O("Fault")),C=Symbol(O("Env"));class k{static Mount(){return S("Mount")}static Unmount(){return S("Unmount")}static Error(){return S("Error")}static Update(){return S("Update")}static Fault=(()=>{const e={};return Object.defineProperty(e,x.Action,{value:E,enumerable:!1}),Object.defineProperty(e,x.Payload,{value:void 0,enumerable:!1}),Object.defineProperty(e,x.Broadcast,{value:!0,enumerable:!1}),Object.defineProperty(e,x.Name,{value:"Fault",enumerable:!1}),e})();static Env=(()=>{const e={};return Object.defineProperty(e,x.Action,{value:C,enumerable:!1}),Object.defineProperty(e,x.Payload,{value:void 0,enumerable:!1}),Object.defineProperty(e,x.Broadcast,{value:!0,enumerable:!1}),Object.defineProperty(e,x.Name,{value:"Env",enumerable:!1}),e})()}var N=/* @__PURE__ */(e=>(e.Unicast="unicast",e.Broadcast="broadcast",e.Multicast="multicast",e))(N||{}),M=/* @__PURE__ */(e=>(e.Mounting="mounting",e.Mounted="mounted",e.Unmounting="unmounting",e.Unmounted="unmounted",e))(M||{});function A({initial:r,children:o}){const s=t.useRef(r),c=p();return n.isUndefined(c.getCached(C))&&c.setCache(C,s.current),/* @__PURE__ */e(v.Provider,{value:s,children:o})}const _=t.createContext(/* @__PURE__ */new WeakMap);function R({children:n}){const r=t.useMemo(()=>/* @__PURE__ */new WeakMap,[]);/* @__PURE__ */
|
|
4
|
+
return e(_.Provider,{value:r,children:n})}const U=t.createContext(()=>{});function L({tap:n,children:r}){const o=t.useRef(n);t.useLayoutEffect(()=>{o.current=n},[n]);const s=t.useMemo(()=>e=>o.current?.(e),[]);/* @__PURE__ */
|
|
5
|
+
return e(U.Provider,{value:s,children:r})}function T({env:t,tap:n,children:r}){/* @__PURE__ */
|
|
6
|
+
return e(m,{children:/* @__PURE__ */e(A,{initial:t??{},children:/* @__PURE__ */e(y,{children:/* @__PURE__ */e(L,{tap:n,children:/* @__PURE__ */e(R,{children:r})})})})})}const W=e=>"symbol"==typeof e;function $(e){return n.isString(e)||W(e)?e:(n.isObject(e)||n.isFunction(e))&&x.Action in e?e[x.Action]:e}function B(e){if(n.isString(e))return e.startsWith(O());if(W(e))return e.description?.startsWith(O())??!1;if(n.isObject(e)||n.isFunction(e)){if(x.Broadcast in e&&e[x.Broadcast])return!0;if(x.Action in e){const t=e[x.Action];return t.description?.startsWith(O())??!1}}return!1}function F(e){return n.isObject(e)&&x.Channel in e&&"channel"in e}function D(e){const t=$(e),n=W(t)?t.description??"":t;return n.startsWith(j())&&n.slice(j().length)||null}function H(e){if(n.isString(e))return e.startsWith(P());if(W(e))return e.description?.startsWith(P())??!1;if(n.isObject(e)||n.isFunction(e)){if(x.Multicast in e&&e[x.Multicast])return!0;if(x.Action in e){const t=e[x.Action];return t.description?.startsWith(P())??!1}}return!1}const I=(e="",t=N.Unicast)=>{const n=t===N.Broadcast?Symbol(O(e)):t===N.Multicast?Symbol(P(e)):Symbol(w(e)),r=function(t){return{[x.Action]:n,[x.Payload]:void 0,[x.Channel]:t,[x.Name]:e,channel:t}};return Object.defineProperty(r,x.Action,{value:n,enumerable:!1}),Object.defineProperty(r,x.Payload,{value:void 0,enumerable:!1}),Object.defineProperty(r,x.Name,{value:e,enumerable:!1}),t===N.Broadcast&&Object.defineProperty(r,x.Broadcast,{value:!0,enumerable:!1}),t===N.Multicast&&Object.defineProperty(r,x.Multicast,{value:!0,enumerable:!1}),r},J=Symbol(((e="")=>`march-hare/replay${e}`)());function z(e,t,...n){e instanceof d&&e.setCache(t,n[0]);const r=e.listeners(t);return 0===r.length?Promise.resolve():Promise.all(r.map(e=>Promise.resolve(e(...n)))).then(()=>{})}function q(e,t){for(const n of e.keys())if(D(n)===t)return n;return null}const G=/* @__PURE__ */Symbol("march-hare.unset");function K(){const[,e]=t.useReducer(e=>e+1,0);return e}function V(){return{data:G,at:null,else:e=>e}}function Q(e,t){return{data:e,at:t,else:t=>e}}var X=/* @__PURE__ */(e=>(e[e.Aborted=0]="Aborted",e[e.Errored=1]="Errored",e))(X||{});class Y extends Error{name="AbortError";constructor(e="Aborted"){super(e)}}const Z=t.createContext(null);function ee(e){const t=/* @__PURE__ */new Map,r=e??{get:e=>t.get(e)??null,set:(e,n)=>{t.set(e,n)},remove:e=>{t.delete(e)},clear:()=>{t.clear()}};return{get(e){try{const t=r.get(e);if(n.isNull(t))return V();const o=JSON.parse(t);return Q(o.data,Temporal.Instant.from(o.at))}catch{return V()}},set(e,t){if(t.data===G||n.isNull(t.at))return!1;try{return r.set(e,JSON.stringify({data:t.data,at:t.at.toString()})),!0}catch{return!1}},remove(e){r.remove(e)},clear(){r.clear()}}}function te(e,t){return new Promise((n,r)=>{if(t?.aborted)return void r(new Y);const o=setTimeout(n,e);t?.addEventListener("abort",()=>{clearTimeout(o),r(new Y)},{once:!0})})}async function ne(e,t,n){if(t?.aborted)throw new Y;for(;;){if(await n())return;await te(e,t)}}function re(e){return e?Boolean(e&&"symbol"!=typeof e):/* @__PURE__ */Symbol(`pk.${Date.now()}.${crypto.randomUUID()}`)}const oe=/* @__PURE__ */Object.freeze(/* @__PURE__ */Object.defineProperty({__proto__:null,pk:re,poll:ne,sleep:te,unset:G,"ζ":te,"κ":re,"π":ne},Symbol.toStringTag,{value:"Module"})),se=/* @__PURE__ */new WeakMap;function ce(e){return JSON.stringify(e)}let ae=null;function ie(){if(n.isNull(ae))throw new Error("context.actions.resource(...) and context.actions.resource.set(...) must be called with a fresh resource invocation, e.g. context.actions.resource(resource.cat({ id: 5 })).");const e=ae;return ae=null,e}function ue(e,t){const r=e=>{const r=t.get(ce(e));return r.data===G||n.isNull(r.at)?{data:G,at:null}:{data:r.data,at:r.at}},o=(n,r,o,s)=>e({env:n,controller:r,params:o,dispatch:s}).then(e=>(t.set(ce(o),Q(e,Temporal.Now.instant())),e)),s=(e,n,r)=>{t.set(ce(e),Q(n,r))};return function(e){const t=e??{};ae={run:o,read:r,seed:s,params:t},queueMicrotask(()=>{n.isNotNullable(ae)&&ae.params===t&&(ae=null)});const{data:c}=r(t);return c===G?null:c}}function le(e){return ue(e,function(e){let t=se.get(e);return n.isUndefined(t)&&(t=ee(),se.set(e,t)),t}(e))}(le||(le={})).Cachable=function(e,t){return ue(t,e)};const fe=/* @__PURE__ */Symbol("march-hare.coalesce/default");function de(e,t){return new Promise((n,r)=>{if(t.aborted)return void r(t.reason);const o=()=>r(t.reason);t.addEventListener("abort",o,{once:!0}),e.then(e=>{t.removeEventListener("abort",o),n(e)},e=>{t.removeEventListener("abort",o),r(e)})})}let he=(e=21)=>{let t="",n=crypto.getRandomValues(new Uint8Array(e|=0));for(;e--;)t+="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict"[63&n[e]];return t};var pe=/* @__PURE__ */(e=>(e[e.Add=1]="Add",e[e.Remove=2]="Remove",e[e.Update=4]="Update",e[e.Move=8]="Move",e[e.Replace=16]="Replace",e[e.Sort=32]="Sort",e[e.Create=64]="Create",e[e.Fetch=128]="Fetch",e[e.Clone=256]="Clone",e[e.Archive=512]="Archive",e[e.Restore=1024]="Restore",e[e.Merge=2048]="Merge",e[e.Reorder=4096]="Reorder",e[e.Sync=8192]="Sync",e[e.Publish=16384]="Publish",e[e.Link=32768]="Link",e[e.Unlink=65536]="Unlink",e[e.Lock=131072]="Lock",e[e.Unlock=262144]="Unlock",e[e.Import=524288]="Import",e[e.Export=1048576]="Export",e[e.Transfer=2097152]="Transfer",e))(pe||{}),me=/* @__PURE__ */(e=>(e[e.Produce=0]="Produce",e[e.Hydrate=1]="Hydrate",e))(me||{}),be=/* @__PURE__ */(e=>(e.Property="property",e.Process="process",e.Value="value",e.Operation="operation",e))(be||{});class ye{[o]=!0;static keys=new Set(Object.values(be));property=null;process=null;value;operation;constructor(e,t){this.value=e,this.operation=t}assign(e,t){const n=new ye(this.value,this.operation);return n.property=e,n.process=t,n}}class ve{static immer=(()=>{s();const e=new c;return e.setAutoFreeze(!1),e})();static tag="κ";static id=he}function ge(e,t){const n="string"==typeof t?""===t?[]:t.split("."):t;let r=e;for(const o of n){if(null==r)return;r=r[o]}return r}function we(e){if(n.isNullable(e)||je(e))return e;if(n.isArray(e))return e.map(e=>we(e));if(n.isObject(e)&&Pe(e)){const t=Object.entries(e).map(([e,t])=>[e,we(t)]);return{...Object.fromEntries(t),[ve.tag]:e[ve.tag]??ve.id()}}return e}function Oe(e){if(Array.isArray(e))return e.filter(e=>ve.tag in e).map(e=>e[ve.tag]??"").join(",");const t=e[ve.tag];if(t)return t;try{return JSON.stringify(e)}catch{return`[unserializable:${typeof e}]`}}function Pe(e){const t=Object.getPrototypeOf(e);return t===Object.prototype||null===t}function je(e){return n.isNullable(e)||n.isString(e)||n.isNumber(e)||n.isBoolean(e)||"symbol"==typeof e||"bigint"==typeof e}function xe(e,t,r,o,s,c){return function a(i,u=t.path){if(i instanceof ye){const t=ge(r,u.join("."));if(Object.entries(i).filter(([e,t])=>!ye.keys.has(e)&&t instanceof ye).forEach(([e,t])=>a(t,u.concat(e))),je(i.value)){if(e===me.Hydrate)return i.value;const a=u.slice(0,-1),l=a.length>0?ge(r,a.join(".")):r;return n.isNullable(l)||Se(l,i,u.at(-1),o,s,c),t??i.value}if(e===me.Hydrate){const e=we(a(i.value,u));return Se(e,i,null,o,s,c),e}const l=t??we(i.value);return Se(l,i,null,o,s,c),n.isNullable(t)?l:(a(i.value,u),t)}if(n.isArray(i))return i.map((e,t)=>a(e,u.concat(t)));if(n.isObject(i)&&!Pe(i))return i;if(n.isObject(i)){const t=Object.entries(i).map(([e,t])=>[e,a(t,u.concat(e))]),n=Object.fromEntries(t);if(e===me.Hydrate){const e=we(n);return Object.entries(i).forEach(([t,n])=>{n instanceof ye&&je(n.value)&&Se(e,n,t,o,s,c)}),e}return n}return i}(t.value)}function Se(e,t,n,r,o,s){const c=s(e),a=o.get(c)??[];o.set(c,[t.assign(n,r),...a])}class Ee{#e={};#t;#n=/* @__PURE__ */new Map;#r=/* @__PURE__ */new Set;#o=!1;constructor(e=Oe){this.#t=e}static pk(){return he()}static"κ"=Ee.pk;annotate(e,t){return new ye(t,e)}"δ"=this.annotate;get model(){return this.#e}get inspect(){return function(e,t,o,s,c){function a(s){const c=s.at(-1),a=ge(e(),s),i=s.slice(0,-1),u=r.isNotEmpty(i)?ge(e(),i):e();return[...n.isObject(a)||n.isArray(a)?t.get(o(a))?.filter(e=>n.isNullable(e.property))??[]:[],...n.isObject(u)?t.get(o(u))?.filter(e=>e.property===c)??[]:[]]}return function t(n){return new Proxy(()=>{},{get:(o,i)=>"pending"===i?()=>!r.isEmpty(a(n)):"remaining"===i?()=>r.length(a(n)):"box"===i?()=>({value:ge(e(),n),inspect:t(n)}):"is"===i?e=>a(n).some(t=>0!==(t.operation&e)):"draft"===i?()=>r.head(a(n))?.value??ge(e(),n):"settled"===i?()=>new Promise(t=>{if(r.isEmpty(a(n)))return t(ge(e(),n));const o=()=>{r.isEmpty(a(n))&&(c(o),t(ge(e(),n)))};s(o)}):t([...n,String(i)])})}([])}(()=>this.#e,this.#n,this.#t,e=>this.#r.add(e),e=>this.#r.delete(e))}hydrate(e){return this.#o=!0,this.#s(me.Hydrate,()=>e)}produce(e){if(!this.#o)throw new Error("State must be hydrated using hydrate() before calling produce()");return this.#s(me.Produce,e)}#s(e,t){const n=/* @__PURE__ */Symbol("process"),[,r]=ve.immer.produceWithPatches(this.#e,t);return this.#e=r.reduce((t,r)=>ve.immer.applyPatches(t,[{...r,value:xe(e,r,t,n,this.#n,this.#t)}]),this.#e),this.#e=we(this.#e),this.#c(),n}prune(e){this.#n.forEach((t,n)=>{const o=t.filter(t=>t.process!==e);r.isEmpty(o)?this.#n.delete(n):this.#n.set(n,o)}),this.#c()}#c(){this.#r.forEach(e=>e())}observe(e){const t=()=>e(this.#e);return this.#r.add(t),()=>this.#r.delete(t)}}const Ce=t.createContext(/* @__PURE__ */new Map);function ke({action:e,renderer:r}){const o=p(),s=t.useContext(Ce),c=K(),a=t.useMemo(()=>{const t=s.get(e);if(t)return t;const r=new Ee,c=o.getCached(e);n.isNotNullable(c)&&r.hydrate({value:c});const a={state:r,listeners:/* @__PURE__ */new Set};return s.set(e,a),a},[e,o,s]);t.useLayoutEffect(()=>{function t(e){a.state.hydrate({value:e}),a.listeners.forEach(e=>e())}return a.listeners.add(c),o.on(e,t),()=>{a.listeners.delete(c),o.off(e,t)}},[e,o,a]);const i=a.state.model?.value;return n.isNullable(i)?null:r(i,a.state.inspect.value)}function Ne(e,t){const n=t.split(".");let r=e;for(let o=0;o<n.length-1;o++)r=r[n[o]];return{cursor:r,key:n[n.length-1]}}function Me(e,t,n){const{cursor:r,key:o}=Ne(e,t);r[o]=n}function Ae(e){return(t,n)=>{t.actions.produce(t=>{Me(t.model,e,n)})}}function _e(e){return t=>{t.actions.produce(t=>{!function(e,t){const{cursor:n,key:r}=Ne(e,t);n[r]=!n[r]}(t.model,e)})}}function Re(e,t){return n=>{n.actions.produce(n=>{Me(n.model,e,t)})}}const Ue={Update:e=>Ae(e),Invert:e=>_e(e),Always:(e,t)=>Re(e,t)};function Le(){const e=t.useRef(null);return t.useMemo(()=>({actions:{dispatch:function(t,n){const r=e.current;if(!r)throw new Error("march-hare: useContext handle dispatched before its paired context.useActions(...) ran. Call context.actions.dispatch from event handlers, not synchronously during render.");return r(t,n)}},useActions:function(...o){const s=function(...e){const o=n.isUndefined(e[0])||n.isFunction(e[0])?{}:e[0],s=n.isFunction(e[0])?e[0]:e[1]??(()=>({})),c=p(),i=t.useContext(Z),u=t.useContext(b),l=g(),d=t.useContext(v),h=t.useContext(_),m=t.useContext(U),y=K(),O=t.useRef(!1),P=t.useRef(null),j=t.useRef(new Ee);O.current||(O.current=!0,P.current=j.current.hydrate(o));const[x,S]=t.useState(()=>j.current.model),k=function(e){const n=t.useRef(e);return n.current=e,t.useMemo(()=>{return t=n,Object.keys(e).reduce((e,n)=>(Object.defineProperty(e,n,{get:()=>t.current[n],enumerable:!0}),e),{});var t},[e])}(s()),N=t.useMemo(()=>new f,[]),A=t.useRef({handlers:/* @__PURE__ */new Map});A.current.handlers=/* @__PURE__ */new Map;const R=function(){const e=t.useRef(/* @__PURE__ */new Set),n=t.useRef(/* @__PURE__ */new Set);return t.useMemo(()=>({broadcast:e.current,multicast:n.current}),[])}(),L=t.useRef(M.Mounting),T=t.useRef(/* @__PURE__ */new Set),W=t.useRef(0),D=t.useCallback((e,t,r)=>{const o=new AbortController,s={controller:o,action:e,payload:t};return u.add(s),T.current.add(s),{model:j.current.model,get phase(){return L.current},task:s,data:k,tasks:u,env:l,actions:{produce(e){if(o.signal.aborted)return;const t=d.current,n=j.current.produce(t=>{d.current=a(d.current,n=>{e({model:t,inspect:j.current.inspect,env:n})})});S(j.current.model),d.current!==t&&c.emit(C,d.current),r.processes.add(n),P.current&&(r.processes.add(P.current),P.current=null)},dispatch(e,t){if(o.signal.aborted)return Promise.resolve();const n=$(e),r=F(e)?e.channel:void 0;return H(e)?i?z(i.emitter,n,t,r):Promise.resolve():z(B(e)?c:N,n,t,r)},annotate:(e,t=pe.Update)=>j.current.annotate(t,e),get inspect(){return j.current.inspect},resource:Object.assign(function(e){const t=ie(),r=(e,t)=>{if(o.signal.aborted)return Promise.resolve();const n=e,r=$(n);return H(n)?i?z(i.emitter,r,t,void 0):Promise.resolve():B(n)?z(c,r,t,void 0):Promise.resolve()},s={exceedsWindow:null,coalesceToken:void 0},a={then:(e,c)=>(()=>{if(n.isNotNullable(s.exceedsWindow)){const{data:e,at:r}=t.read(t.params);if(e!==G&&n.isNotNullable(r)){const t=Temporal.Now.instant().since(r),n=Temporal.Duration.from(s.exceedsWindow);if(Temporal.Duration.compare(t,n)<=0)return Promise.resolve(e)}}if(n.isUndefined(s.coalesceToken))return t.run(l,o,t.params,r);let e=h.get(t.run);n.isUndefined(e)&&(e=/* @__PURE__ */new Map,h.set(t.run,e));const c=e,a=`${JSON.stringify(t.params)}|${function(e){switch(typeof e){case"string":return`s:${e}`;case"number":return`n:${e}`;case"bigint":return`i:${e.toString()}`;case"boolean":return`b:${e}`;case"symbol":return`y:${e.description??String(e)}`;default:return`o:${JSON.stringify(e)}`}}(s.coalesceToken)}`,i=c.get(a);if(i)return de(i,o.signal);const u=new AbortController,f=t.run(l,u,t.params,r).finally(()=>{c.delete(a)});return c.set(a,f),de(f,o.signal)})().then(e,c),exceeds:e=>(s.exceedsWindow=e,a),coalesce:e=>(s.coalesceToken=e??fe,a)};return a},{set:(e,t)=>{const n=ie();n.seed(n.params,t,Temporal.Now.instant())}}),async final(e){if(o.signal.aborted)return null;const t=$(e),r=H(e)?i?.emitter??null:c;if(!r)return null;const s=r.getCached(t);if(n.isUndefined(s))return null;const a=j.current.inspect;return a.pending()&&await new Promise((e,t)=>{if(o.signal.aborted)return void t(o.signal.reason);const n=()=>t(o.signal.reason);o.signal.addEventListener("abort",n,{once:!0}),a.settled().then(()=>{o.signal.removeEventListener("abort",n),e()})}),r.getCached(t)??null},peek(e){if(o.signal.aborted)return null;const t=$(e),n=H(e)?i?.emitter??null:c;return n?n.getCached(t)??null:null}}}},[x]);t.useLayoutEffect(()=>{function e(e,t,r){return function(o,s){const a=r();if(s===J&&n.isNotNullable(a))return;if(n.isNotNullable(s)&&s!==J&&n.isNotNullable(a)&&!function(e,t){for(const n of Object.keys(e))if(t[n]!==e[n])return!1;return!0}(s,a))return;const i={processes:/* @__PURE__ */new Set},l=Promise.withResolvers(),f=D(e,o,i),h=function(e){const t=$(e),r=n.isString(t)?t:t.description??"";return r.startsWith(w())&&r.slice(r.lastIndexOf("/")+1)||"unknown"}(e),p=performance.now(),b=j.current.model,v=d.current;let g,O=!1;function P(){const e=j.current.model,t=d.current;return{model:b===e?null:{before:b,after:e},env:v===t?null:{before:v,after:t}}}function x(e){O=!0;const t=q(A.current.handlers,"Error"),r=n.isNotNullable(t),s=function(e){return e instanceof Error&&"AbortError"===e.name?X.Aborted:X.Errored}(e),a=function(e){return e instanceof Error?e:new Error(String(e))}(e),i={reason:s,error:a,action:h,handled:r,tasks:u};c.fire(E,i),r&&t&&N.emit(t,i),m({stage:"end",result:"error",action:{name:h,payload:o},details:{task:f.task,elapsed:performance.now()-p,mutations:P(),error:a,reason:s}})}function S(){for(const e of u)if(e===f.task){u.delete(e),T.current.delete(e);break}i.processes.forEach(e=>j.current.prune(e)),i.processes.size>0&&y(),O||m({stage:"end",result:"success",action:{name:h,payload:o},details:{task:f.task,elapsed:performance.now()-p,mutations:P()}}),l.resolve()}m({stage:"start",action:{name:h,payload:o},details:{task:f.task}});try{g=t(f,o)}catch(C){return x(C),S(),l.promise}return function(e){if(!e||"object"!=typeof e)return!1;const t=Object.prototype.toString.call(e);return"[object Generator]"===t||"[object AsyncGenerator]"===t}(g)?((async()=>{for await(const e of g);})().catch(x).finally(S),l.promise):(Promise.resolve(g).catch(x).finally(S),l.promise)}}W.current++;const t=/* @__PURE__ */new Set;return A.current.handlers.forEach((n,r)=>{for(const{getChannel:o,handler:s}of n){const n=e(r,s,o);if(H(r)){if(i){const e=i.emitter;e.on(r,n),t.add(()=>e.off(r,n))}N.on(r,n),R.multicast.add(r),t.add(()=>N.off(r,n))}else B(r)?(c.on(r,n),N.on(r,n),R.broadcast.add(r),t.add(()=>{c.off(r,n),N.off(r,n)})):(N.on(r,n),t.add(()=>N.off(r,n)))}}),()=>{const e=++W.current,n=new Set(t);queueMicrotask(()=>{if(W.current!==e){for(const e of n)e();return}for(const e of T.current)e.controller.abort(),u.delete(e);T.current.clear(),L.current=M.Unmounting;const t=q(A.current.handlers,"Unmount");t&&N.emit(t),L.current=M.Unmounted;for(const e of n)e()})}},[N]),function({unicast:e,broadcast:o,dispatchers:s,scope:c,phase:a,data:i,handlers:u}){const l=t.useRef(null);t.useLayoutEffect(()=>{if(a.current===M.Mounted)return;a.current=M.Mounting;const t=q(u,"Mount");t&&e.emit(t),s.broadcast.forEach(t=>{const r=o.getCached(t);n.isNullable(r)||e.emit(t,r,J)}),c&&s.multicast.forEach(t=>{const r=c.emitter.getCached(t);n.isNullable(r)||e.emit(t,r,J)}),a.current=M.Mounted},[]),t.useLayoutEffect(()=>{if(n.isNotNullable(l.current)){const t=function(e,t){return Object.keys(t).reduce((n,r)=>e[r]!==t[r]?{...n,[r]:t[r]}:n,{})}(l.current,i);if(r.isNotEmpty(Object.keys(t))){const n=q(u,"Update");n&&e.emit(n,t)}}l.current=i},[i,e])}({unicast:N,broadcast:c,dispatchers:R,scope:i,phase:L,data:s(),handlers:A.current.handlers});const I=t.useMemo(()=>({dispatch(e,t){const n=$(e),r=F(e)?e.channel:void 0;return H(e)?i?z(i.emitter,n,t,r):Promise.resolve():z(B(e)?c:N,n,t,r)},get inspect(){return j.current.inspect},stream:(e,n)=>t.createElement(ke,{action:$(e),renderer:n})}),[x,N]),V=t.useMemo(()=>[x,I,k],[x,I,k]);return V.useAction=(e,n)=>{!function(e,n,r){const o=t.useRef(r);t.useLayoutEffect(()=>{o.current=r});const s=t.useRef(n);t.useLayoutEffect(()=>{s.current=n});const c=t.useCallback((e,t)=>o.current(e,t),[]),a=t.useCallback(()=>F(s.current)?s.current.channel:void 0,[]),i=$(n),u=e.current.handlers.get(i)??/* @__PURE__ */new Set;0===u.size&&e.current.handlers.set(i,u),u.add({getChannel:a,handler:c})}(A,e,n)},V.dispatch=V[1].dispatch,V}(...o);return e.current=s.dispatch,s},with:{update:e=>Ae(e),invert:e=>_e(e),always:(e,t)=>Re(e,t)}}),[])}function Te(n){return{Boundary:function({env:t,tap:r,children:o}){/* @__PURE__ */
|
|
7
|
+
return e(T,{env:t??n?.env,tap:r??n?.tap,children:o})},useContext:function(){return Le()},useEnv:function(){return g()},Resource:Object.assign(function(e){return le(e)},{Cachable:(e,t)=>le.Cachable(e,t)}),Scope:()=>({Boundary:function({children:n}){const r=t.useMemo(()=>({id:/* @__PURE__ */Symbol("march-hare.scope/instance"),emitter:new d}),[]);/* @__PURE__ */
|
|
8
|
+
return e(Z.Provider,{value:r,children:n})},useContext:function(){return Le()},useEnv:function(){return g()},Resource:Object.assign(function(e){return le(e)},{Cachable:(e,t)=>le.Cachable(e,t)})})}}const We=new Ee;function $e(e,t=pe.Update){return We.annotate(t,e)}export{Y as Aborted,I as Action,Te as App,T as Boundary,ee as Cache,N as Distribution,k as Lifecycle,pe as Op,pe as Operation,X as Reason,le as Resource,Ee as State,Ue as With,$e as annotate,oe as utils};
|
package/dist/march-hare.umd.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var global,factory;global=this,factory=function(e,t,n,r,o){"use strict";function s(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e)for(const n in e)if("default"!==n){const r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:()=>e[n]})}return t.default=e,Object.freeze(t)}const c=s(n);function a(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var i,u={exports:{}},l=(i||(i=1,function(e){var t=Object.prototype.hasOwnProperty,n="~";function r(){}function o(e,t,n){this.fn=e,this.context=t,this.once=n||!1}function s(e,t,r,s,c){if("function"!=typeof r)throw new TypeError("The listener must be a function");var a=new o(r,s||e,c),i=n?n+t:t;return e._events[i]?e._events[i].fn?e._events[i]=[e._events[i],a]:e._events[i].push(a):(e._events[i]=a,e._eventsCount++),e}function c(e,t){0===--e._eventsCount?e._events=new r:delete e._events[t]}function a(){this._events=new r,this._eventsCount=0}Object.create&&(r.prototype=Object.create(null),(new r).__proto__||(n=!1)),a.prototype.eventNames=function(){var e,r,o=[];if(0===this._eventsCount)return o;for(r in e=this._events)t.call(e,r)&&o.push(n?r.slice(1):r);return Object.getOwnPropertySymbols?o.concat(Object.getOwnPropertySymbols(e)):o},a.prototype.listeners=function(e){var t=this._events[n?n+e:e];if(!t)return[];if(t.fn)return[t.fn];for(var r=0,o=t.length,s=new Array(o);r<o;r++)s[r]=t[r].fn;return s},a.prototype.listenerCount=function(e){var t=this._events[n?n+e:e];return t?t.fn?1:t.length:0},a.prototype.emit=function(e,t,r,o,s,c){var a=n?n+e:e;if(!this._events[a])return!1;var i,u,l=this._events[a],f=arguments.length;if(l.fn){switch(l.once&&this.removeListener(e,l.fn,void 0,!0),f){case 1:return l.fn.call(l.context),!0;case 2:return l.fn.call(l.context,t),!0;case 3:return l.fn.call(l.context,t,r),!0;case 4:return l.fn.call(l.context,t,r,o),!0;case 5:return l.fn.call(l.context,t,r,o,s),!0;case 6:return l.fn.call(l.context,t,r,o,s,c),!0}for(u=1,i=new Array(f-1);u<f;u++)i[u-1]=arguments[u];l.fn.apply(l.context,i)}else{var d,h=l.length;for(u=0;u<h;u++)switch(l[u].once&&this.removeListener(e,l[u].fn,void 0,!0),f){case 1:l[u].fn.call(l[u].context);break;case 2:l[u].fn.call(l[u].context,t);break;case 3:l[u].fn.call(l[u].context,t,r);break;case 4:l[u].fn.call(l[u].context,t,r,o);break;default:if(!i)for(d=1,i=new Array(f-1);d<f;d++)i[d-1]=arguments[d];l[u].fn.apply(l[u].context,i)}}return!0},a.prototype.on=function(e,t,n){return s(this,e,t,n,!1)},a.prototype.once=function(e,t,n){return s(this,e,t,n,!0)},a.prototype.removeListener=function(e,t,r,o){var s=n?n+e:e;if(!this._events[s])return this;if(!t)return c(this,s),this;var a=this._events[s];if(a.fn)a.fn!==t||o&&!a.once||r&&a.context!==r||c(this,s);else{for(var i=0,u=[],l=a.length;i<l;i++)(a[i].fn!==t||o&&!a[i].once||r&&a[i].context!==r)&&u.push(a[i]);u.length?this._events[s]=1===u.length?u[0]:u:c(this,s)}return this},a.prototype.removeAllListeners=function(e){var t;return e?this._events[t=n?n+e:e]&&c(this,t):(this._events=new r,this._eventsCount=0),this},a.prototype.off=a.prototype.removeListener,a.prototype.addListener=a.prototype.on,a.prefixed=n,a.EventEmitter=a,e.exports=a}(u)),u.exports);const f=a(l);class d extends f{cache=new Map;emit(e,...t){return this.cache.set(e,t[0]),super.emit(e,...t)}setCache(e,t){this.cache.set(e,t)}getCached(e){return this.cache.get(e)}fire(e,...t){return super.emit(e,...t)}}const h=c.createContext(new d);function p(){return c.useContext(h)}function m({children:e}){const n=c.useMemo(()=>new d,[]);return t.jsx(h.Provider,{value:n,children:e})}const b=c.createContext(new Set);function y({children:e}){const n=c.useMemo(()=>new Set,[]);return t.jsx(b.Provider,{value:n,children:e})}const v=c.createContext({current:{}});function g(){const e=c.useContext(v);return c.useMemo(()=>new Proxy({},{get:(t,n)=>Reflect.get(e.current,n),has:(t,n)=>n in e.current,ownKeys:()=>Reflect.ownKeys(e.current),getOwnPropertyDescriptor(t,n){const o=Object.getOwnPropertyDescriptor(e.current,n);if(!r.G.isUndefined(o))return{...o,configurable:!0}},set(){throw new TypeError("Env is read-only outside `context.actions.produce`. Mutate via produce(({ env }) => { env.x = ... }) instead.")}}),[e])}const w=(e="")=>`march-hare.action/${e}`,j=(e="")=>`march-hare.action/broadcast/${e}`,O=(e="")=>`march-hare.action/multicast/${e}`,x=(e="")=>`march-hare.action.lifecycle/${e}`;class P{static Payload=Symbol("march-hare.brand/Payload");static Broadcast=Symbol("march-hare.brand/Broadcast");static Multicast=Symbol("march-hare.brand/Multicast");static Action=Symbol("march-hare.brand/Action");static Channel=Symbol("march-hare.brand/Channel");static Name=Symbol("march-hare.brand/Name")}function S(e){const t=Symbol(`march-hare.action.lifecycle/${e}`),n=function(n){return{[P.Action]:t,[P.Payload]:void 0,[P.Channel]:n,[P.Name]:e,channel:n}};return Object.defineProperty(n,P.Action,{value:t,enumerable:!1}),Object.defineProperty(n,P.Payload,{value:void 0,enumerable:!1}),Object.defineProperty(n,P.Name,{value:e,enumerable:!1}),n}const E=Symbol(j("Fault")),C=Symbol(j("Env"));class G{static Mount(){return S("Mount")}static Unmount(){return S("Unmount")}static Error(){return S("Error")}static Update(){return S("Update")}static Fault=(()=>{const e={};return Object.defineProperty(e,P.Action,{value:E,enumerable:!1}),Object.defineProperty(e,P.Payload,{value:void 0,enumerable:!1}),Object.defineProperty(e,P.Broadcast,{value:!0,enumerable:!1}),Object.defineProperty(e,P.Name,{value:"Fault",enumerable:!1}),e})();static Env=(()=>{const e={};return Object.defineProperty(e,P.Action,{value:C,enumerable:!1}),Object.defineProperty(e,P.Payload,{value:void 0,enumerable:!1}),Object.defineProperty(e,P.Broadcast,{value:!0,enumerable:!1}),Object.defineProperty(e,P.Name,{value:"Env",enumerable:!1}),e})()}var A=(e=>(e.Unicast="unicast",e.Broadcast="broadcast",e.Multicast="multicast",e))(A||{}),M=(e=>(e.Mounting="mounting",e.Mounted="mounted",e.Unmounting="unmounting",e.Unmounted="unmounted",e))(M||{});function N({initial:e,children:n}){const o=c.useRef(e),s=p();return r.G.isUndefined(s.getCached(C))&&s.setCache(C,o.current),t.jsx(v.Provider,{value:o,children:n})}const k=c.createContext(new WeakMap);function R({children:e}){const n=c.useMemo(()=>new WeakMap,[]);return t.jsx(k.Provider,{value:n,children:e})}function _({env:e,children:n}){return t.jsx(m,{children:t.jsx(N,{initial:e??{},children:t.jsx(y,{children:t.jsx(R,{children:n})})})})}const U=e=>"symbol"==typeof e;function L(e){return r.G.isString(e)||U(e)?e:(r.G.isObject(e)||r.G.isFunction(e))&&P.Action in e?e[P.Action]:e}function T(e){if(r.G.isString(e))return e.startsWith(j());if(U(e))return e.description?.startsWith(j())??!1;if(r.G.isObject(e)||r.G.isFunction(e)){if(P.Broadcast in e&&e[P.Broadcast])return!0;if(P.Action in e){const t=e[P.Action];return t.description?.startsWith(j())??!1}}return!1}function W(e){const t=L(e),n=r.G.isString(t)?t:t.description??"";return n.startsWith(w())&&n.slice(n.lastIndexOf("/")+1)||"unknown"}function B(e){return r.G.isObject(e)&&P.Channel in e&&"channel"in e}function $(e){const t=L(e),n=U(t)?t.description??"":t;return n.startsWith(x())&&n.slice(x().length)||null}function F(e){if(r.G.isString(e))return e.startsWith(O());if(U(e))return e.description?.startsWith(O())??!1;if(r.G.isObject(e)||r.G.isFunction(e)){if(P.Multicast in e&&e[P.Multicast])return!0;if(P.Action in e){const t=e[P.Action];return t.description?.startsWith(O())??!1}}return!1}const D=Symbol(((e="")=>`march-hare/replay${e}`)());function I(e,t,...n){e instanceof d&&e.setCache(t,n[0]);const r=e.listeners(t);return 0===r.length?Promise.resolve():Promise.all(r.map(e=>Promise.resolve(e(...n)))).then(()=>{})}function H(e,t){for(const n of e.keys())if($(n)===t)return n;return null}const q=Symbol("march-hare.unset");function z(){const[,e]=c.useReducer(e=>e+1,0);return e}function J(){return{data:q,at:null,else:e=>e}}function K(e,t){return{data:e,at:t,else:t=>e}}var V=(e=>(e[e.Aborted=0]="Aborted",e[e.Errored=1]="Errored",e))(V||{});class Q extends Error{name="AbortError";constructor(e="Aborted"){super(e)}}function X(e){return e instanceof Error?e:new Error(String(e))}const Y=c.createContext(null);function Z(e){const t=new Map,n=e??{get:e=>t.get(e)??null,set:(e,n)=>{t.set(e,n)},remove:e=>{t.delete(e)},clear:()=>{t.clear()}};return{get(e){try{const t=n.get(e);if(r.G.isNull(t))return J();const o=JSON.parse(t);return K(o.data,Temporal.Instant.from(o.at))}catch{return J()}},set(e,t){if(t.data===q||r.G.isNull(t.at))return!1;try{return n.set(e,JSON.stringify({data:t.data,at:t.at.toString()})),!0}catch{return!1}},remove(e){n.remove(e)},clear(){n.clear()}}}function ee(e,t){return new Promise((n,r)=>{if(t?.aborted)return void r(new Q);const o=setTimeout(n,e);t?.addEventListener("abort",()=>{clearTimeout(o),r(new Q)},{once:!0})})}async function te(e,t,n){if(t?.aborted)throw new Q;for(;;){if(await n())return;await ee(e,t)}}function ne(e){return e?Boolean(e&&"symbol"!=typeof e):Symbol(`pk.${Date.now()}.${crypto.randomUUID()}`)}const re=Object.freeze(Object.defineProperty({__proto__:null,pk:ne,poll:te,sleep:ee,unset:q,"ζ":ee,"κ":ne,"π":te},Symbol.toStringTag,{value:"Module"})),oe=new WeakMap;function se(e){return JSON.stringify(e)}let ce=null;function ae(){if(r.G.isNull(ce))throw new Error("context.actions.resource(...) and context.actions.resource.set(...) must be called with a fresh resource invocation, e.g. context.actions.resource(resource.cat({ id: 5 })).");const e=ce;return ce=null,e}function ie(e,t){const n=e=>{const n=t.get(se(e));return n.data===q||r.G.isNull(n.at)?{data:q,at:null}:{data:n.data,at:n.at}},o=(n,r,o,s)=>e({env:n,controller:r,params:o,dispatch:s}).then(e=>(t.set(se(o),K(e,Temporal.Now.instant())),e)),s=(e,n,r)=>{t.set(se(e),K(n,r))};return function(e){const t=e??{};ce={run:o,read:n,seed:s,params:t},queueMicrotask(()=>{r.G.isNotNullable(ce)&&ce.params===t&&(ce=null)});const{data:c}=n(t);return c===q?null:c}}function ue(e){return ie(e,function(e){let t=oe.get(e);return r.G.isUndefined(t)&&(t=Z(),oe.set(e,t)),t}(e))}(ue||(ue={})).Cachable=function(e,t){return ie(t,e)};const le=Symbol("march-hare.coalesce/default");function fe(e,t){return new Promise((n,r)=>{if(t.aborted)return void r(t.reason);const o=()=>r(t.reason);t.addEventListener("abort",o,{once:!0}),e.then(e=>{t.removeEventListener("abort",o),n(e)},e=>{t.removeEventListener("abort",o),r(e)})})}let de=(e=21)=>{let t="",n=crypto.getRandomValues(new Uint8Array(e|=0));for(;e--;)t+="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict"[63&n[e]];return t};var he=(e=>(e[e.Add=1]="Add",e[e.Remove=2]="Remove",e[e.Update=4]="Update",e[e.Move=8]="Move",e[e.Replace=16]="Replace",e[e.Sort=32]="Sort",e[e.Create=64]="Create",e[e.Fetch=128]="Fetch",e[e.Clone=256]="Clone",e[e.Archive=512]="Archive",e[e.Restore=1024]="Restore",e[e.Merge=2048]="Merge",e[e.Reorder=4096]="Reorder",e[e.Sync=8192]="Sync",e[e.Publish=16384]="Publish",e[e.Link=32768]="Link",e[e.Unlink=65536]="Unlink",e[e.Lock=131072]="Lock",e[e.Unlock=262144]="Unlock",e[e.Import=524288]="Import",e[e.Export=1048576]="Export",e[e.Transfer=2097152]="Transfer",e))(he||{}),pe=(e=>(e[e.Produce=0]="Produce",e[e.Hydrate=1]="Hydrate",e))(pe||{}),me=(e=>(e.Property="property",e.Process="process",e.Value="value",e.Operation="operation",e))(me||{});class be{[o.immerable]=!0;static keys=new Set(Object.values(me));property=null;process=null;value;operation;constructor(e,t){this.value=e,this.operation=t}assign(e,t){const n=new be(this.value,this.operation);return n.property=e,n.process=t,n}}class ye{static immer=(()=>{o.enablePatches();const e=new o.Immer;return e.setAutoFreeze(!1),e})();static tag="κ";static id=de}function ve(e,t){const n="string"==typeof t?""===t?[]:t.split("."):t;let r=e;for(const o of n){if(null==r)return;r=r[o]}return r}function ge(e){if(r.G.isNullable(e)||Oe(e))return e;if(r.G.isArray(e))return e.map(e=>ge(e));if(r.G.isObject(e)&&je(e)){const t=Object.entries(e).map(([e,t])=>[e,ge(t)]);return{...Object.fromEntries(t),[ye.tag]:e[ye.tag]??ye.id()}}return e}function we(e){if(Array.isArray(e))return e.filter(e=>ye.tag in e).map(e=>e[ye.tag]??"").join(",");const t=e[ye.tag];if(t)return t;try{return JSON.stringify(e)}catch{return`[unserializable:${typeof e}]`}}function je(e){const t=Object.getPrototypeOf(e);return t===Object.prototype||null===t}function Oe(e){return r.G.isNullable(e)||r.G.isString(e)||r.G.isNumber(e)||r.G.isBoolean(e)||"symbol"==typeof e||"bigint"==typeof e}function xe(e,t,n,o,s,c){return function a(i,u=t.path){if(i instanceof be){const t=ve(n,u.join("."));if(Object.entries(i).filter(([e,t])=>!be.keys.has(e)&&t instanceof be).forEach(([e,t])=>a(t,u.concat(e))),Oe(i.value)){if(e===pe.Hydrate)return i.value;const a=u.slice(0,-1),l=a.length>0?ve(n,a.join(".")):n;return r.G.isNullable(l)||Pe(l,i,u.at(-1),o,s,c),t??i.value}if(e===pe.Hydrate){const e=ge(a(i.value,u));return Pe(e,i,null,o,s,c),e}const l=t??ge(i.value);return Pe(l,i,null,o,s,c),r.G.isNullable(t)?l:(a(i.value,u),t)}if(r.G.isArray(i))return i.map((e,t)=>a(e,u.concat(t)));if(r.G.isObject(i)&&!je(i))return i;if(r.G.isObject(i)){const t=Object.entries(i).map(([e,t])=>[e,a(t,u.concat(e))]),n=Object.fromEntries(t);if(e===pe.Hydrate){const e=ge(n);return Object.entries(i).forEach(([t,n])=>{n instanceof be&&Oe(n.value)&&Pe(e,n,t,o,s,c)}),e}return n}return i}(t.value)}function Pe(e,t,n,r,o,s){const c=s(e),a=o.get(c)??[];o.set(c,[t.assign(n,r),...a])}class Se{#e={};#t;#n=new Map;#r=new Set;#o=!1;constructor(e=we){this.#t=e}static pk(){return de()}static"κ"=Se.pk;annotate(e,t){return new be(t,e)}"δ"=this.annotate;get model(){return this.#e}get inspect(){return function(e,t,n,o,s){function c(o){const s=o.at(-1),c=ve(e(),o),a=o.slice(0,-1),i=r.A.isNotEmpty(a)?ve(e(),a):e();return[...r.G.isObject(c)||r.G.isArray(c)?t.get(n(c))?.filter(e=>r.G.isNullable(e.property))??[]:[],...r.G.isObject(i)?t.get(n(i))?.filter(e=>e.property===s)??[]:[]]}return function t(n){return new Proxy(()=>{},{get:(a,i)=>"pending"===i?()=>!r.A.isEmpty(c(n)):"remaining"===i?()=>r.A.length(c(n)):"box"===i?()=>({value:ve(e(),n),inspect:t(n)}):"is"===i?e=>c(n).some(t=>0!==(t.operation&e)):"draft"===i?()=>r.A.head(c(n))?.value??ve(e(),n):"settled"===i?()=>new Promise(t=>{if(r.A.isEmpty(c(n)))return t(ve(e(),n));const a=()=>{r.A.isEmpty(c(n))&&(s(a),t(ve(e(),n)))};o(a)}):t([...n,String(i)])})}([])}(()=>this.#e,this.#n,this.#t,e=>this.#r.add(e),e=>this.#r.delete(e))}hydrate(e){return this.#o=!0,this.#s(pe.Hydrate,()=>e)}produce(e){if(!this.#o)throw new Error("State must be hydrated using hydrate() before calling produce()");return this.#s(pe.Produce,e)}#s(e,t){const n=Symbol("process"),[,r]=ye.immer.produceWithPatches(this.#e,t);return this.#e=r.reduce((t,r)=>ye.immer.applyPatches(t,[{...r,value:xe(e,r,t,n,this.#n,this.#t)}]),this.#e),this.#e=ge(this.#e),this.#c(),n}prune(e){this.#n.forEach((t,n)=>{const o=t.filter(t=>t.process!==e);r.A.isEmpty(o)?this.#n.delete(n):this.#n.set(n,o)}),this.#c()}#c(){this.#r.forEach(e=>e())}observe(e){const t=()=>e(this.#e);return this.#r.add(t),()=>this.#r.delete(t)}}const Ee=c.createContext(new Map);function Ce({action:e,renderer:t}){const n=p(),o=c.useContext(Ee),s=z(),a=c.useMemo(()=>{const t=o.get(e);if(t)return t;const s=new Se,c=n.getCached(e);r.G.isNotNullable(c)&&s.hydrate({value:c});const a={state:s,listeners:new Set};return o.set(e,a),a},[e,n,o]);c.useLayoutEffect(()=>{function t(e){a.state.hydrate({value:e}),a.listeners.forEach(e=>e())}return a.listeners.add(s),n.on(e,t),()=>{a.listeners.delete(s),n.off(e,t)}},[e,n,a]);const i=a.state.model?.value;return r.G.isNullable(i)?null:t(i,a.state.inspect.value)}function Ge(){const e=c.useRef(null);return c.useMemo(()=>({actions:{dispatch:function(t,n){const r=e.current;if(!r)throw new Error("march-hare: useContext handle dispatched before its paired context.useActions(...) ran. Call context.actions.dispatch from event handlers, not synchronously during render.");return r(t,n)}},useActions:function(...t){const n=function(...e){const t=r.G.isUndefined(e[0])||r.G.isFunction(e[0])?{}:e[0],n=r.G.isFunction(e[0])?e[0]:e[1]??(()=>({})),s=p(),a=c.useContext(Y),i=c.useContext(b),u=g(),l=c.useContext(v),d=c.useContext(k),h=z(),m=c.useRef(!1),y=c.useRef(null),w=c.useRef(new Se);m.current||(m.current=!0,y.current=w.current.hydrate(t));const[j,O]=c.useState(()=>w.current.model),x=function(e){const t=c.useRef(e);return t.current=e,c.useMemo(()=>{return n=t,Object.keys(e).reduce((e,t)=>(Object.defineProperty(e,t,{get:()=>n.current[t],enumerable:!0}),e),{});var n},[e])}(n()),P=c.useMemo(()=>new f,[]),S=c.useRef({handlers:new Map});S.current.handlers=new Map;const G=function(){const e=c.useRef(new Set),t=c.useRef(new Set);return c.useMemo(()=>({broadcast:e.current,multicast:t.current}),[])}(),A=c.useRef(M.Mounting),N=c.useRef(new Set),R=c.useRef(0),_=c.useCallback((e,t,n)=>{const c=new AbortController,f={controller:c,action:e,payload:t};return i.add(f),N.current.add(f),{model:w.current.model,get phase(){return A.current},task:f,data:x,tasks:i,env:u,actions:{produce(e){if(c.signal.aborted)return;const t=l.current,r=w.current.produce(t=>{l.current=o.produce(l.current,n=>{e({model:t,inspect:w.current.inspect,env:n})})});O(w.current.model),l.current!==t&&s.emit(C,l.current),n.processes.add(r),y.current&&(n.processes.add(y.current),y.current=null)},dispatch(e,t){if(c.signal.aborted)return Promise.resolve();const n=L(e),r=B(e)?e.channel:void 0;return F(e)?a?I(a.emitter,n,t,r):Promise.resolve():I(T(e)?s:P,n,t,r)},annotate:(e,t=he.Update)=>w.current.annotate(t,e),get inspect(){return w.current.inspect},resource:Object.assign(function(e){const t=ae(),n=(e,t)=>{if(c.signal.aborted)return Promise.resolve();const n=e,r=L(n);return F(n)?a?I(a.emitter,r,t,void 0):Promise.resolve():T(n)?I(s,r,t,void 0):Promise.resolve()},o={exceedsWindow:null,coalesceToken:void 0},i={then:(e,s)=>(()=>{if(r.G.isNotNullable(o.exceedsWindow)){const{data:e,at:n}=t.read(t.params);if(e!==q&&r.G.isNotNullable(n)){const t=Temporal.Now.instant().since(n),r=Temporal.Duration.from(o.exceedsWindow);if(Temporal.Duration.compare(t,r)<=0)return Promise.resolve(e)}}if(r.G.isUndefined(o.coalesceToken))return t.run(l.current,c,t.params,n);let e=d.get(t.run);r.G.isUndefined(e)&&(e=new Map,d.set(t.run,e));const s=e,a=`${JSON.stringify(t.params)}|${function(e){switch(typeof e){case"string":return`s:${e}`;case"number":return`n:${e}`;case"bigint":return`i:${e.toString()}`;case"boolean":return`b:${e}`;case"symbol":return`y:${e.description??String(e)}`;default:return`o:${JSON.stringify(e)}`}}(o.coalesceToken)}`,i=s.get(a);if(i)return fe(i,c.signal);const u=new AbortController,f=t.run(l.current,u,t.params,n).finally(()=>{s.delete(a)});return s.set(a,f),fe(f,c.signal)})().then(e,s),exceeds:e=>(o.exceedsWindow=e,i),coalesce:e=>(o.coalesceToken=e??le,i)};return i},{set:(e,t)=>{const n=ae();n.seed(n.params,t,Temporal.Now.instant())}}),async final(e){if(c.signal.aborted)return null;const t=L(e),n=F(e)?a?.emitter??null:s;if(!n)return null;const o=n.getCached(t);if(r.G.isUndefined(o))return null;const i=w.current.inspect;return i.pending()&&await new Promise((e,t)=>{if(c.signal.aborted)return void t(c.signal.reason);const n=()=>t(c.signal.reason);c.signal.addEventListener("abort",n,{once:!0}),i.settled().then(()=>{c.signal.removeEventListener("abort",n),e()})}),n.getCached(t)??null},peek(e){if(c.signal.aborted)return null;const t=L(e),n=F(e)?a?.emitter??null:s;return n?n.getCached(t)??null:null}}}},[j]);c.useLayoutEffect(()=>{function e(e,t,n){return function(o,c){const a=n();if(c===D&&r.G.isNotNullable(a))return;if(r.G.isNotNullable(c)&&c!==D&&r.G.isNotNullable(a)&&!function(e,t){for(const n of Object.keys(e))if(t[n]!==e[n])return!1;return!0}(c,a))return;const u={processes:new Set},l=Promise.withResolvers(),f=_(e,o,u);function d(t){const n=H(S.current.handlers,"Error"),o=r.G.isNotNullable(n),c={reason:(a=t,a instanceof Error&&"AbortError"===a.name?V.Aborted:V.Errored),error:X(t),action:W(e),handled:o,tasks:i};var a;s.fire(E,c),o&&n&&P.emit(n,c)}function p(){for(const e of i)if(e===f.task){i.delete(e),N.current.delete(e);break}u.processes.forEach(e=>w.current.prune(e)),u.processes.size>0&&h(),l.resolve()}let m;try{m=t(f,o)}catch(b){return d(b),void p()}if(!function(e){if(!e||"object"!=typeof e)return!1;const t=Object.prototype.toString.call(e);return"[object Generator]"===t||"[object AsyncGenerator]"===t}(m))return Promise.resolve(m).catch(d).finally(p),l.promise;(async()=>{for await(const e of m);})().catch(d).finally(p)}}R.current++;const t=new Set;return S.current.handlers.forEach((n,r)=>{for(const{getChannel:o,handler:c}of n){const n=e(r,c,o);if(F(r)){if(a){const e=a.emitter;e.on(r,n),t.add(()=>e.off(r,n))}P.on(r,n),G.multicast.add(r),t.add(()=>P.off(r,n))}else T(r)?(s.on(r,n),P.on(r,n),G.broadcast.add(r),t.add(()=>{s.off(r,n),P.off(r,n)})):(P.on(r,n),t.add(()=>P.off(r,n)))}}),()=>{const e=++R.current,n=new Set(t);queueMicrotask(()=>{if(R.current!==e){for(const e of n)e();return}for(const e of N.current)e.controller.abort(),i.delete(e);N.current.clear(),A.current=M.Unmounting;const t=H(S.current.handlers,"Unmount");t&&P.emit(t),A.current=M.Unmounted;for(const e of n)e()})}},[P]),function({unicast:e,broadcast:t,dispatchers:n,scope:o,phase:s,data:a,handlers:i}){const u=c.useRef(null);c.useLayoutEffect(()=>{if(s.current===M.Mounted)return;s.current=M.Mounting;const c=H(i,"Mount");c&&e.emit(c),n.broadcast.forEach(n=>{const o=t.getCached(n);r.G.isNullable(o)||e.emit(n,o,D)}),o&&n.multicast.forEach(t=>{const n=o.emitter.getCached(t);r.G.isNullable(n)||e.emit(t,n,D)}),s.current=M.Mounted},[]),c.useLayoutEffect(()=>{if(r.G.isNotNullable(u.current)){const t=function(e,t){return Object.keys(t).reduce((n,r)=>e[r]!==t[r]?{...n,[r]:t[r]}:n,{})}(u.current,a);if(r.A.isNotEmpty(Object.keys(t))){const n=H(i,"Update");n&&e.emit(n,t)}}u.current=a},[a,e])}({unicast:P,broadcast:s,dispatchers:G,scope:a,phase:A,data:n(),handlers:S.current.handlers});const U=c.useMemo(()=>({dispatch(e,t){const n=L(e),r=B(e)?e.channel:void 0;return F(e)?a?I(a.emitter,n,t,r):Promise.resolve():I(T(e)?s:P,n,t,r)},get inspect(){return w.current.inspect},stream:(e,t)=>c.createElement(Ce,{action:L(e),renderer:t})}),[j,P]),$=c.useMemo(()=>[j,U,x],[j,U,x]);return $.useAction=(e,t)=>{!function(e,t,n){const r=c.useRef(n);c.useLayoutEffect(()=>{r.current=n});const o=c.useRef(t);c.useLayoutEffect(()=>{o.current=t});const s=c.useCallback((e,t)=>r.current(e,t),[]),a=c.useCallback(()=>B(o.current)?o.current.channel:void 0,[]),i=L(t),u=e.current.handlers.get(i)??new Set;0===u.size&&e.current.handlers.set(i,u),u.add({getChannel:a,handler:s})}(S,e,t)},$.dispatch=$[1].dispatch,$}(...t);return e.current=n.dispatch,n}}),[])}const Ae=new Se;e.Aborted=Q,e.Action=(e="",t=A.Unicast)=>{const n=t===A.Broadcast?Symbol(j(e)):t===A.Multicast?Symbol(O(e)):Symbol(w(e)),r=function(t){return{[P.Action]:n,[P.Payload]:void 0,[P.Channel]:t,[P.Name]:e,channel:t}};return Object.defineProperty(r,P.Action,{value:n,enumerable:!1}),Object.defineProperty(r,P.Payload,{value:void 0,enumerable:!1}),Object.defineProperty(r,P.Name,{value:e,enumerable:!1}),t===A.Broadcast&&Object.defineProperty(r,P.Broadcast,{value:!0,enumerable:!1}),t===A.Multicast&&Object.defineProperty(r,P.Multicast,{value:!0,enumerable:!1}),r},e.App=function(e){return{Boundary:function({children:n}){return t.jsx(_,{env:e?.env,children:n})},useContext:function(){return Ge()},useEnv:function(){return g()},Resource:Object.assign(function(e){return ue(e)},{Cachable:(e,t)=>ue.Cachable(e,t)}),Scope:()=>({Boundary:function({children:e}){const n=c.useMemo(()=>({id:Symbol("march-hare.scope/instance"),emitter:new d}),[]);return t.jsx(Y.Provider,{value:n,children:e})},useContext:function(){return Ge()},useEnv:function(){return g()},Resource:Object.assign(function(e){return ue(e)},{Cachable:(e,t)=>ue.Cachable(e,t)})})}},e.Boundary=_,e.Cache=Z,e.Distribution=A,e.Lifecycle=G,e.Op=he,e.Operation=he,e.Reason=V,e.Resource=ue,e.State=Se,e.With={Update:e=>(t,n)=>{t.actions.produce(t=>{t.model[e]=n})},Invert:e=>t=>{t.actions.produce(t=>{t.model[e]=!t.model[e]})}},e.annotate=function(e,t=he.Update){return Ae.annotate(t,e)},e.utils=re,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})},"object"==typeof exports&&"undefined"!=typeof module?factory(exports,require("react/jsx-runtime"),require("react"),require("@mobily/ts-belt"),require("immer")):"function"==typeof define&&define.amd?define(["exports","react/jsx-runtime","react","@mobily/ts-belt","immer"],factory):factory((global="undefined"!=typeof globalThis?globalThis:global||self).MarchHare={},global.jsxRuntime,global.React,global.TsBelt,global.Immer);
|
|
1
|
+
var global,factory;global=this,factory=function(e,t,n,r,o){"use strict";function s(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e)for(const n in e)if("default"!==n){const r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:()=>e[n]})}return t.default=e,Object.freeze(t)}const c=s(n);function a(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var i,u={exports:{}},l=(i||(i=1,function(e){var t=Object.prototype.hasOwnProperty,n="~";function r(){}function o(e,t,n){this.fn=e,this.context=t,this.once=n||!1}function s(e,t,r,s,c){if("function"!=typeof r)throw new TypeError("The listener must be a function");var a=new o(r,s||e,c),i=n?n+t:t;return e._events[i]?e._events[i].fn?e._events[i]=[e._events[i],a]:e._events[i].push(a):(e._events[i]=a,e._eventsCount++),e}function c(e,t){0===--e._eventsCount?e._events=new r:delete e._events[t]}function a(){this._events=new r,this._eventsCount=0}Object.create&&(r.prototype=Object.create(null),(new r).__proto__||(n=!1)),a.prototype.eventNames=function(){var e,r,o=[];if(0===this._eventsCount)return o;for(r in e=this._events)t.call(e,r)&&o.push(n?r.slice(1):r);return Object.getOwnPropertySymbols?o.concat(Object.getOwnPropertySymbols(e)):o},a.prototype.listeners=function(e){var t=this._events[n?n+e:e];if(!t)return[];if(t.fn)return[t.fn];for(var r=0,o=t.length,s=new Array(o);r<o;r++)s[r]=t[r].fn;return s},a.prototype.listenerCount=function(e){var t=this._events[n?n+e:e];return t?t.fn?1:t.length:0},a.prototype.emit=function(e,t,r,o,s,c){var a=n?n+e:e;if(!this._events[a])return!1;var i,u,l=this._events[a],f=arguments.length;if(l.fn){switch(l.once&&this.removeListener(e,l.fn,void 0,!0),f){case 1:return l.fn.call(l.context),!0;case 2:return l.fn.call(l.context,t),!0;case 3:return l.fn.call(l.context,t,r),!0;case 4:return l.fn.call(l.context,t,r,o),!0;case 5:return l.fn.call(l.context,t,r,o,s),!0;case 6:return l.fn.call(l.context,t,r,o,s,c),!0}for(u=1,i=new Array(f-1);u<f;u++)i[u-1]=arguments[u];l.fn.apply(l.context,i)}else{var d,p=l.length;for(u=0;u<p;u++)switch(l[u].once&&this.removeListener(e,l[u].fn,void 0,!0),f){case 1:l[u].fn.call(l[u].context);break;case 2:l[u].fn.call(l[u].context,t);break;case 3:l[u].fn.call(l[u].context,t,r);break;case 4:l[u].fn.call(l[u].context,t,r,o);break;default:if(!i)for(d=1,i=new Array(f-1);d<f;d++)i[d-1]=arguments[d];l[u].fn.apply(l[u].context,i)}}return!0},a.prototype.on=function(e,t,n){return s(this,e,t,n,!1)},a.prototype.once=function(e,t,n){return s(this,e,t,n,!0)},a.prototype.removeListener=function(e,t,r,o){var s=n?n+e:e;if(!this._events[s])return this;if(!t)return c(this,s),this;var a=this._events[s];if(a.fn)a.fn!==t||o&&!a.once||r&&a.context!==r||c(this,s);else{for(var i=0,u=[],l=a.length;i<l;i++)(a[i].fn!==t||o&&!a[i].once||r&&a[i].context!==r)&&u.push(a[i]);u.length?this._events[s]=1===u.length?u[0]:u:c(this,s)}return this},a.prototype.removeAllListeners=function(e){var t;return e?this._events[t=n?n+e:e]&&c(this,t):(this._events=new r,this._eventsCount=0),this},a.prototype.off=a.prototype.removeListener,a.prototype.addListener=a.prototype.on,a.prefixed=n,a.EventEmitter=a,e.exports=a}(u)),u.exports);const f=a(l);class d extends f{cache=new Map;emit(e,...t){return this.cache.set(e,t[0]),super.emit(e,...t)}setCache(e,t){this.cache.set(e,t)}getCached(e){return this.cache.get(e)}fire(e,...t){return super.emit(e,...t)}}const p=c.createContext(new d);function h(){return c.useContext(p)}function m({children:e}){const n=c.useMemo(()=>new d,[]);return t.jsx(p.Provider,{value:n,children:e})}const b=c.createContext(new Set);function y({children:e}){const n=c.useMemo(()=>new Set,[]);return t.jsx(b.Provider,{value:n,children:e})}const v=c.createContext({current:{}});function g(){const e=c.useContext(v);return c.useMemo(()=>new Proxy({},{get:(t,n)=>Reflect.get(e.current,n),has:(t,n)=>n in e.current,ownKeys:()=>Reflect.ownKeys(e.current),getOwnPropertyDescriptor(t,n){const o=Object.getOwnPropertyDescriptor(e.current,n);if(!r.G.isUndefined(o))return{...o,configurable:!0}},set(){throw new TypeError("Env is read-only outside `context.actions.produce`. Mutate via produce(({ env }) => { env.x = ... }) instead.")}}),[e])}const w=(e="")=>`march-hare.action/${e}`,j=(e="")=>`march-hare.action/broadcast/${e}`,x=(e="")=>`march-hare.action/multicast/${e}`,O=(e="")=>`march-hare.action.lifecycle/${e}`;class P{static Payload=Symbol("march-hare.brand/Payload");static Broadcast=Symbol("march-hare.brand/Broadcast");static Multicast=Symbol("march-hare.brand/Multicast");static Action=Symbol("march-hare.brand/Action");static Channel=Symbol("march-hare.brand/Channel");static Name=Symbol("march-hare.brand/Name")}function S(e){const t=Symbol(`march-hare.action.lifecycle/${e}`),n=function(n){return{[P.Action]:t,[P.Payload]:void 0,[P.Channel]:n,[P.Name]:e,channel:n}};return Object.defineProperty(n,P.Action,{value:t,enumerable:!1}),Object.defineProperty(n,P.Payload,{value:void 0,enumerable:!1}),Object.defineProperty(n,P.Name,{value:e,enumerable:!1}),n}const C=Symbol(j("Fault")),E=Symbol(j("Env"));class G{static Mount(){return S("Mount")}static Unmount(){return S("Unmount")}static Error(){return S("Error")}static Update(){return S("Update")}static Fault=(()=>{const e={};return Object.defineProperty(e,P.Action,{value:C,enumerable:!1}),Object.defineProperty(e,P.Payload,{value:void 0,enumerable:!1}),Object.defineProperty(e,P.Broadcast,{value:!0,enumerable:!1}),Object.defineProperty(e,P.Name,{value:"Fault",enumerable:!1}),e})();static Env=(()=>{const e={};return Object.defineProperty(e,P.Action,{value:E,enumerable:!1}),Object.defineProperty(e,P.Payload,{value:void 0,enumerable:!1}),Object.defineProperty(e,P.Broadcast,{value:!0,enumerable:!1}),Object.defineProperty(e,P.Name,{value:"Env",enumerable:!1}),e})()}var A=(e=>(e.Unicast="unicast",e.Broadcast="broadcast",e.Multicast="multicast",e))(A||{}),M=(e=>(e.Mounting="mounting",e.Mounted="mounted",e.Unmounting="unmounting",e.Unmounted="unmounted",e))(M||{});function k({initial:e,children:n}){const o=c.useRef(e),s=h();return r.G.isUndefined(s.getCached(E))&&s.setCache(E,o.current),t.jsx(v.Provider,{value:o,children:n})}const N=c.createContext(new WeakMap);function R({children:e}){const n=c.useMemo(()=>new WeakMap,[]);return t.jsx(N.Provider,{value:n,children:e})}const _=c.createContext(()=>{});function U({tap:e,children:n}){const r=c.useRef(e);c.useLayoutEffect(()=>{r.current=e},[e]);const o=c.useMemo(()=>e=>r.current?.(e),[]);return t.jsx(_.Provider,{value:o,children:n})}function L({env:e,tap:n,children:r}){return t.jsx(m,{children:t.jsx(k,{initial:e??{},children:t.jsx(y,{children:t.jsx(U,{tap:n,children:t.jsx(R,{children:r})})})})})}const T=e=>"symbol"==typeof e;function W(e){return r.G.isString(e)||T(e)?e:(r.G.isObject(e)||r.G.isFunction(e))&&P.Action in e?e[P.Action]:e}function B(e){if(r.G.isString(e))return e.startsWith(j());if(T(e))return e.description?.startsWith(j())??!1;if(r.G.isObject(e)||r.G.isFunction(e)){if(P.Broadcast in e&&e[P.Broadcast])return!0;if(P.Action in e){const t=e[P.Action];return t.description?.startsWith(j())??!1}}return!1}function $(e){return r.G.isObject(e)&&P.Channel in e&&"channel"in e}function F(e){const t=W(e),n=T(t)?t.description??"":t;return n.startsWith(O())&&n.slice(O().length)||null}function D(e){if(r.G.isString(e))return e.startsWith(x());if(T(e))return e.description?.startsWith(x())??!1;if(r.G.isObject(e)||r.G.isFunction(e)){if(P.Multicast in e&&e[P.Multicast])return!0;if(P.Action in e){const t=e[P.Action];return t.description?.startsWith(x())??!1}}return!1}const I=Symbol(((e="")=>`march-hare/replay${e}`)());function H(e,t,...n){e instanceof d&&e.setCache(t,n[0]);const r=e.listeners(t);return 0===r.length?Promise.resolve():Promise.all(r.map(e=>Promise.resolve(e(...n)))).then(()=>{})}function q(e,t){for(const n of e.keys())if(F(n)===t)return n;return null}const z=Symbol("march-hare.unset");function J(){const[,e]=c.useReducer(e=>e+1,0);return e}function K(){return{data:z,at:null,else:e=>e}}function V(e,t){return{data:e,at:t,else:t=>e}}var Q=(e=>(e[e.Aborted=0]="Aborted",e[e.Errored=1]="Errored",e))(Q||{});class X extends Error{name="AbortError";constructor(e="Aborted"){super(e)}}const Y=c.createContext(null);function Z(e){const t=new Map,n=e??{get:e=>t.get(e)??null,set:(e,n)=>{t.set(e,n)},remove:e=>{t.delete(e)},clear:()=>{t.clear()}};return{get(e){try{const t=n.get(e);if(r.G.isNull(t))return K();const o=JSON.parse(t);return V(o.data,Temporal.Instant.from(o.at))}catch{return K()}},set(e,t){if(t.data===z||r.G.isNull(t.at))return!1;try{return n.set(e,JSON.stringify({data:t.data,at:t.at.toString()})),!0}catch{return!1}},remove(e){n.remove(e)},clear(){n.clear()}}}function ee(e,t){return new Promise((n,r)=>{if(t?.aborted)return void r(new X);const o=setTimeout(n,e);t?.addEventListener("abort",()=>{clearTimeout(o),r(new X)},{once:!0})})}async function te(e,t,n){if(t?.aborted)throw new X;for(;;){if(await n())return;await ee(e,t)}}function ne(e){return e?Boolean(e&&"symbol"!=typeof e):Symbol(`pk.${Date.now()}.${crypto.randomUUID()}`)}const re=Object.freeze(Object.defineProperty({__proto__:null,pk:ne,poll:te,sleep:ee,unset:z,"ζ":ee,"κ":ne,"π":te},Symbol.toStringTag,{value:"Module"})),oe=new WeakMap;function se(e){return JSON.stringify(e)}let ce=null;function ae(){if(r.G.isNull(ce))throw new Error("context.actions.resource(...) and context.actions.resource.set(...) must be called with a fresh resource invocation, e.g. context.actions.resource(resource.cat({ id: 5 })).");const e=ce;return ce=null,e}function ie(e,t){const n=e=>{const n=t.get(se(e));return n.data===z||r.G.isNull(n.at)?{data:z,at:null}:{data:n.data,at:n.at}},o=(n,r,o,s)=>e({env:n,controller:r,params:o,dispatch:s}).then(e=>(t.set(se(o),V(e,Temporal.Now.instant())),e)),s=(e,n,r)=>{t.set(se(e),V(n,r))};return function(e){const t=e??{};ce={run:o,read:n,seed:s,params:t},queueMicrotask(()=>{r.G.isNotNullable(ce)&&ce.params===t&&(ce=null)});const{data:c}=n(t);return c===z?null:c}}function ue(e){return ie(e,function(e){let t=oe.get(e);return r.G.isUndefined(t)&&(t=Z(),oe.set(e,t)),t}(e))}(ue||(ue={})).Cachable=function(e,t){return ie(t,e)};const le=Symbol("march-hare.coalesce/default");function fe(e,t){return new Promise((n,r)=>{if(t.aborted)return void r(t.reason);const o=()=>r(t.reason);t.addEventListener("abort",o,{once:!0}),e.then(e=>{t.removeEventListener("abort",o),n(e)},e=>{t.removeEventListener("abort",o),r(e)})})}let de=(e=21)=>{let t="",n=crypto.getRandomValues(new Uint8Array(e|=0));for(;e--;)t+="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict"[63&n[e]];return t};var pe=(e=>(e[e.Add=1]="Add",e[e.Remove=2]="Remove",e[e.Update=4]="Update",e[e.Move=8]="Move",e[e.Replace=16]="Replace",e[e.Sort=32]="Sort",e[e.Create=64]="Create",e[e.Fetch=128]="Fetch",e[e.Clone=256]="Clone",e[e.Archive=512]="Archive",e[e.Restore=1024]="Restore",e[e.Merge=2048]="Merge",e[e.Reorder=4096]="Reorder",e[e.Sync=8192]="Sync",e[e.Publish=16384]="Publish",e[e.Link=32768]="Link",e[e.Unlink=65536]="Unlink",e[e.Lock=131072]="Lock",e[e.Unlock=262144]="Unlock",e[e.Import=524288]="Import",e[e.Export=1048576]="Export",e[e.Transfer=2097152]="Transfer",e))(pe||{}),he=(e=>(e[e.Produce=0]="Produce",e[e.Hydrate=1]="Hydrate",e))(he||{}),me=(e=>(e.Property="property",e.Process="process",e.Value="value",e.Operation="operation",e))(me||{});class be{[o.immerable]=!0;static keys=new Set(Object.values(me));property=null;process=null;value;operation;constructor(e,t){this.value=e,this.operation=t}assign(e,t){const n=new be(this.value,this.operation);return n.property=e,n.process=t,n}}class ye{static immer=(()=>{o.enablePatches();const e=new o.Immer;return e.setAutoFreeze(!1),e})();static tag="κ";static id=de}function ve(e,t){const n="string"==typeof t?""===t?[]:t.split("."):t;let r=e;for(const o of n){if(null==r)return;r=r[o]}return r}function ge(e){if(r.G.isNullable(e)||xe(e))return e;if(r.G.isArray(e))return e.map(e=>ge(e));if(r.G.isObject(e)&&je(e)){const t=Object.entries(e).map(([e,t])=>[e,ge(t)]);return{...Object.fromEntries(t),[ye.tag]:e[ye.tag]??ye.id()}}return e}function we(e){if(Array.isArray(e))return e.filter(e=>ye.tag in e).map(e=>e[ye.tag]??"").join(",");const t=e[ye.tag];if(t)return t;try{return JSON.stringify(e)}catch{return`[unserializable:${typeof e}]`}}function je(e){const t=Object.getPrototypeOf(e);return t===Object.prototype||null===t}function xe(e){return r.G.isNullable(e)||r.G.isString(e)||r.G.isNumber(e)||r.G.isBoolean(e)||"symbol"==typeof e||"bigint"==typeof e}function Oe(e,t,n,o,s,c){return function a(i,u=t.path){if(i instanceof be){const t=ve(n,u.join("."));if(Object.entries(i).filter(([e,t])=>!be.keys.has(e)&&t instanceof be).forEach(([e,t])=>a(t,u.concat(e))),xe(i.value)){if(e===he.Hydrate)return i.value;const a=u.slice(0,-1),l=a.length>0?ve(n,a.join(".")):n;return r.G.isNullable(l)||Pe(l,i,u.at(-1),o,s,c),t??i.value}if(e===he.Hydrate){const e=ge(a(i.value,u));return Pe(e,i,null,o,s,c),e}const l=t??ge(i.value);return Pe(l,i,null,o,s,c),r.G.isNullable(t)?l:(a(i.value,u),t)}if(r.G.isArray(i))return i.map((e,t)=>a(e,u.concat(t)));if(r.G.isObject(i)&&!je(i))return i;if(r.G.isObject(i)){const t=Object.entries(i).map(([e,t])=>[e,a(t,u.concat(e))]),n=Object.fromEntries(t);if(e===he.Hydrate){const e=ge(n);return Object.entries(i).forEach(([t,n])=>{n instanceof be&&xe(n.value)&&Pe(e,n,t,o,s,c)}),e}return n}return i}(t.value)}function Pe(e,t,n,r,o,s){const c=s(e),a=o.get(c)??[];o.set(c,[t.assign(n,r),...a])}class Se{#e={};#t;#n=new Map;#r=new Set;#o=!1;constructor(e=we){this.#t=e}static pk(){return de()}static"κ"=Se.pk;annotate(e,t){return new be(t,e)}"δ"=this.annotate;get model(){return this.#e}get inspect(){return function(e,t,n,o,s){function c(o){const s=o.at(-1),c=ve(e(),o),a=o.slice(0,-1),i=r.A.isNotEmpty(a)?ve(e(),a):e();return[...r.G.isObject(c)||r.G.isArray(c)?t.get(n(c))?.filter(e=>r.G.isNullable(e.property))??[]:[],...r.G.isObject(i)?t.get(n(i))?.filter(e=>e.property===s)??[]:[]]}return function t(n){return new Proxy(()=>{},{get:(a,i)=>"pending"===i?()=>!r.A.isEmpty(c(n)):"remaining"===i?()=>r.A.length(c(n)):"box"===i?()=>({value:ve(e(),n),inspect:t(n)}):"is"===i?e=>c(n).some(t=>0!==(t.operation&e)):"draft"===i?()=>r.A.head(c(n))?.value??ve(e(),n):"settled"===i?()=>new Promise(t=>{if(r.A.isEmpty(c(n)))return t(ve(e(),n));const a=()=>{r.A.isEmpty(c(n))&&(s(a),t(ve(e(),n)))};o(a)}):t([...n,String(i)])})}([])}(()=>this.#e,this.#n,this.#t,e=>this.#r.add(e),e=>this.#r.delete(e))}hydrate(e){return this.#o=!0,this.#s(he.Hydrate,()=>e)}produce(e){if(!this.#o)throw new Error("State must be hydrated using hydrate() before calling produce()");return this.#s(he.Produce,e)}#s(e,t){const n=Symbol("process"),[,r]=ye.immer.produceWithPatches(this.#e,t);return this.#e=r.reduce((t,r)=>ye.immer.applyPatches(t,[{...r,value:Oe(e,r,t,n,this.#n,this.#t)}]),this.#e),this.#e=ge(this.#e),this.#c(),n}prune(e){this.#n.forEach((t,n)=>{const o=t.filter(t=>t.process!==e);r.A.isEmpty(o)?this.#n.delete(n):this.#n.set(n,o)}),this.#c()}#c(){this.#r.forEach(e=>e())}observe(e){const t=()=>e(this.#e);return this.#r.add(t),()=>this.#r.delete(t)}}const Ce=c.createContext(new Map);function Ee({action:e,renderer:t}){const n=h(),o=c.useContext(Ce),s=J(),a=c.useMemo(()=>{const t=o.get(e);if(t)return t;const s=new Se,c=n.getCached(e);r.G.isNotNullable(c)&&s.hydrate({value:c});const a={state:s,listeners:new Set};return o.set(e,a),a},[e,n,o]);c.useLayoutEffect(()=>{function t(e){a.state.hydrate({value:e}),a.listeners.forEach(e=>e())}return a.listeners.add(s),n.on(e,t),()=>{a.listeners.delete(s),n.off(e,t)}},[e,n,a]);const i=a.state.model?.value;return r.G.isNullable(i)?null:t(i,a.state.inspect.value)}function Ge(e,t){const n=t.split(".");let r=e;for(let o=0;o<n.length-1;o++)r=r[n[o]];return{cursor:r,key:n[n.length-1]}}function Ae(e,t,n){const{cursor:r,key:o}=Ge(e,t);r[o]=n}function Me(e){return(t,n)=>{t.actions.produce(t=>{Ae(t.model,e,n)})}}function ke(e){return t=>{t.actions.produce(t=>{!function(e,t){const{cursor:n,key:r}=Ge(e,t);n[r]=!n[r]}(t.model,e)})}}function Ne(e,t){return n=>{n.actions.produce(n=>{Ae(n.model,e,t)})}}const Re={Update:e=>Me(e),Invert:e=>ke(e),Always:(e,t)=>Ne(e,t)};function _e(){const e=c.useRef(null);return c.useMemo(()=>({actions:{dispatch:function(t,n){const r=e.current;if(!r)throw new Error("march-hare: useContext handle dispatched before its paired context.useActions(...) ran. Call context.actions.dispatch from event handlers, not synchronously during render.");return r(t,n)}},useActions:function(...t){const n=function(...e){const t=r.G.isUndefined(e[0])||r.G.isFunction(e[0])?{}:e[0],n=r.G.isFunction(e[0])?e[0]:e[1]??(()=>({})),s=h(),a=c.useContext(Y),i=c.useContext(b),u=g(),l=c.useContext(v),d=c.useContext(N),p=c.useContext(_),m=J(),y=c.useRef(!1),j=c.useRef(null),x=c.useRef(new Se);y.current||(y.current=!0,j.current=x.current.hydrate(t));const[O,P]=c.useState(()=>x.current.model),S=function(e){const t=c.useRef(e);return t.current=e,c.useMemo(()=>{return n=t,Object.keys(e).reduce((e,t)=>(Object.defineProperty(e,t,{get:()=>n.current[t],enumerable:!0}),e),{});var n},[e])}(n()),G=c.useMemo(()=>new f,[]),A=c.useRef({handlers:new Map});A.current.handlers=new Map;const k=function(){const e=c.useRef(new Set),t=c.useRef(new Set);return c.useMemo(()=>({broadcast:e.current,multicast:t.current}),[])}(),R=c.useRef(M.Mounting),U=c.useRef(new Set),L=c.useRef(0),T=c.useCallback((e,t,n)=>{const c=new AbortController,f={controller:c,action:e,payload:t};return i.add(f),U.current.add(f),{model:x.current.model,get phase(){return R.current},task:f,data:S,tasks:i,env:u,actions:{produce(e){if(c.signal.aborted)return;const t=l.current,r=x.current.produce(t=>{l.current=o.produce(l.current,n=>{e({model:t,inspect:x.current.inspect,env:n})})});P(x.current.model),l.current!==t&&s.emit(E,l.current),n.processes.add(r),j.current&&(n.processes.add(j.current),j.current=null)},dispatch(e,t){if(c.signal.aborted)return Promise.resolve();const n=W(e),r=$(e)?e.channel:void 0;return D(e)?a?H(a.emitter,n,t,r):Promise.resolve():H(B(e)?s:G,n,t,r)},annotate:(e,t=pe.Update)=>x.current.annotate(t,e),get inspect(){return x.current.inspect},resource:Object.assign(function(e){const t=ae(),n=(e,t)=>{if(c.signal.aborted)return Promise.resolve();const n=e,r=W(n);return D(n)?a?H(a.emitter,r,t,void 0):Promise.resolve():B(n)?H(s,r,t,void 0):Promise.resolve()},o={exceedsWindow:null,coalesceToken:void 0},i={then:(e,s)=>(()=>{if(r.G.isNotNullable(o.exceedsWindow)){const{data:e,at:n}=t.read(t.params);if(e!==z&&r.G.isNotNullable(n)){const t=Temporal.Now.instant().since(n),r=Temporal.Duration.from(o.exceedsWindow);if(Temporal.Duration.compare(t,r)<=0)return Promise.resolve(e)}}if(r.G.isUndefined(o.coalesceToken))return t.run(u,c,t.params,n);let e=d.get(t.run);r.G.isUndefined(e)&&(e=new Map,d.set(t.run,e));const s=e,a=`${JSON.stringify(t.params)}|${function(e){switch(typeof e){case"string":return`s:${e}`;case"number":return`n:${e}`;case"bigint":return`i:${e.toString()}`;case"boolean":return`b:${e}`;case"symbol":return`y:${e.description??String(e)}`;default:return`o:${JSON.stringify(e)}`}}(o.coalesceToken)}`,i=s.get(a);if(i)return fe(i,c.signal);const l=new AbortController,f=t.run(u,l,t.params,n).finally(()=>{s.delete(a)});return s.set(a,f),fe(f,c.signal)})().then(e,s),exceeds:e=>(o.exceedsWindow=e,i),coalesce:e=>(o.coalesceToken=e??le,i)};return i},{set:(e,t)=>{const n=ae();n.seed(n.params,t,Temporal.Now.instant())}}),async final(e){if(c.signal.aborted)return null;const t=W(e),n=D(e)?a?.emitter??null:s;if(!n)return null;const o=n.getCached(t);if(r.G.isUndefined(o))return null;const i=x.current.inspect;return i.pending()&&await new Promise((e,t)=>{if(c.signal.aborted)return void t(c.signal.reason);const n=()=>t(c.signal.reason);c.signal.addEventListener("abort",n,{once:!0}),i.settled().then(()=>{c.signal.removeEventListener("abort",n),e()})}),n.getCached(t)??null},peek(e){if(c.signal.aborted)return null;const t=W(e),n=D(e)?a?.emitter??null:s;return n?n.getCached(t)??null:null}}}},[O]);c.useLayoutEffect(()=>{function e(e,t,n){return function(o,c){const a=n();if(c===I&&r.G.isNotNullable(a))return;if(r.G.isNotNullable(c)&&c!==I&&r.G.isNotNullable(a)&&!function(e,t){for(const n of Object.keys(e))if(t[n]!==e[n])return!1;return!0}(c,a))return;const u={processes:new Set},f=Promise.withResolvers(),d=T(e,o,u),h=function(e){const t=W(e),n=r.G.isString(t)?t:t.description??"";return n.startsWith(w())&&n.slice(n.lastIndexOf("/")+1)||"unknown"}(e),b=performance.now(),y=x.current.model,v=l.current;let g,j=!1;function O(){const e=x.current.model,t=l.current;return{model:y===e?null:{before:y,after:e},env:v===t?null:{before:v,after:t}}}function P(e){j=!0;const t=q(A.current.handlers,"Error"),n=r.G.isNotNullable(t),c=function(e){return e instanceof Error&&"AbortError"===e.name?Q.Aborted:Q.Errored}(e),a=function(e){return e instanceof Error?e:new Error(String(e))}(e),u={reason:c,error:a,action:h,handled:n,tasks:i};s.fire(C,u),n&&t&&G.emit(t,u),p({stage:"end",result:"error",action:{name:h,payload:o},details:{task:d.task,elapsed:performance.now()-b,mutations:O(),error:a,reason:c}})}function S(){for(const e of i)if(e===d.task){i.delete(e),U.current.delete(e);break}u.processes.forEach(e=>x.current.prune(e)),u.processes.size>0&&m(),j||p({stage:"end",result:"success",action:{name:h,payload:o},details:{task:d.task,elapsed:performance.now()-b,mutations:O()}}),f.resolve()}p({stage:"start",action:{name:h,payload:o},details:{task:d.task}});try{g=t(d,o)}catch(E){return P(E),S(),f.promise}return function(e){if(!e||"object"!=typeof e)return!1;const t=Object.prototype.toString.call(e);return"[object Generator]"===t||"[object AsyncGenerator]"===t}(g)?((async()=>{for await(const e of g);})().catch(P).finally(S),f.promise):(Promise.resolve(g).catch(P).finally(S),f.promise)}}L.current++;const t=new Set;return A.current.handlers.forEach((n,r)=>{for(const{getChannel:o,handler:c}of n){const n=e(r,c,o);if(D(r)){if(a){const e=a.emitter;e.on(r,n),t.add(()=>e.off(r,n))}G.on(r,n),k.multicast.add(r),t.add(()=>G.off(r,n))}else B(r)?(s.on(r,n),G.on(r,n),k.broadcast.add(r),t.add(()=>{s.off(r,n),G.off(r,n)})):(G.on(r,n),t.add(()=>G.off(r,n)))}}),()=>{const e=++L.current,n=new Set(t);queueMicrotask(()=>{if(L.current!==e){for(const e of n)e();return}for(const e of U.current)e.controller.abort(),i.delete(e);U.current.clear(),R.current=M.Unmounting;const t=q(A.current.handlers,"Unmount");t&&G.emit(t),R.current=M.Unmounted;for(const e of n)e()})}},[G]),function({unicast:e,broadcast:t,dispatchers:n,scope:o,phase:s,data:a,handlers:i}){const u=c.useRef(null);c.useLayoutEffect(()=>{if(s.current===M.Mounted)return;s.current=M.Mounting;const c=q(i,"Mount");c&&e.emit(c),n.broadcast.forEach(n=>{const o=t.getCached(n);r.G.isNullable(o)||e.emit(n,o,I)}),o&&n.multicast.forEach(t=>{const n=o.emitter.getCached(t);r.G.isNullable(n)||e.emit(t,n,I)}),s.current=M.Mounted},[]),c.useLayoutEffect(()=>{if(r.G.isNotNullable(u.current)){const t=function(e,t){return Object.keys(t).reduce((n,r)=>e[r]!==t[r]?{...n,[r]:t[r]}:n,{})}(u.current,a);if(r.A.isNotEmpty(Object.keys(t))){const n=q(i,"Update");n&&e.emit(n,t)}}u.current=a},[a,e])}({unicast:G,broadcast:s,dispatchers:k,scope:a,phase:R,data:n(),handlers:A.current.handlers});const F=c.useMemo(()=>({dispatch(e,t){const n=W(e),r=$(e)?e.channel:void 0;return D(e)?a?H(a.emitter,n,t,r):Promise.resolve():H(B(e)?s:G,n,t,r)},get inspect(){return x.current.inspect},stream:(e,t)=>c.createElement(Ee,{action:W(e),renderer:t})}),[O,G]),K=c.useMemo(()=>[O,F,S],[O,F,S]);return K.useAction=(e,t)=>{!function(e,t,n){const r=c.useRef(n);c.useLayoutEffect(()=>{r.current=n});const o=c.useRef(t);c.useLayoutEffect(()=>{o.current=t});const s=c.useCallback((e,t)=>r.current(e,t),[]),a=c.useCallback(()=>$(o.current)?o.current.channel:void 0,[]),i=W(t),u=e.current.handlers.get(i)??new Set;0===u.size&&e.current.handlers.set(i,u),u.add({getChannel:a,handler:s})}(A,e,t)},K.dispatch=K[1].dispatch,K}(...t);return e.current=n.dispatch,n},with:{update:e=>Me(e),invert:e=>ke(e),always:(e,t)=>Ne(e,t)}}),[])}const Ue=new Se;e.Aborted=X,e.Action=(e="",t=A.Unicast)=>{const n=t===A.Broadcast?Symbol(j(e)):t===A.Multicast?Symbol(x(e)):Symbol(w(e)),r=function(t){return{[P.Action]:n,[P.Payload]:void 0,[P.Channel]:t,[P.Name]:e,channel:t}};return Object.defineProperty(r,P.Action,{value:n,enumerable:!1}),Object.defineProperty(r,P.Payload,{value:void 0,enumerable:!1}),Object.defineProperty(r,P.Name,{value:e,enumerable:!1}),t===A.Broadcast&&Object.defineProperty(r,P.Broadcast,{value:!0,enumerable:!1}),t===A.Multicast&&Object.defineProperty(r,P.Multicast,{value:!0,enumerable:!1}),r},e.App=function(e){return{Boundary:function({env:n,tap:r,children:o}){return t.jsx(L,{env:n??e?.env,tap:r??e?.tap,children:o})},useContext:function(){return _e()},useEnv:function(){return g()},Resource:Object.assign(function(e){return ue(e)},{Cachable:(e,t)=>ue.Cachable(e,t)}),Scope:()=>({Boundary:function({children:e}){const n=c.useMemo(()=>({id:Symbol("march-hare.scope/instance"),emitter:new d}),[]);return t.jsx(Y.Provider,{value:n,children:e})},useContext:function(){return _e()},useEnv:function(){return g()},Resource:Object.assign(function(e){return ue(e)},{Cachable:(e,t)=>ue.Cachable(e,t)})})}},e.Boundary=L,e.Cache=Z,e.Distribution=A,e.Lifecycle=G,e.Op=pe,e.Operation=pe,e.Reason=Q,e.Resource=ue,e.State=Se,e.With=Re,e.annotate=function(e,t=pe.Update){return Ue.annotate(t,e)},e.utils=re,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})},"object"==typeof exports&&"undefined"!=typeof module?factory(exports,require("react/jsx-runtime"),require("react"),require("@mobily/ts-belt"),require("immer")):"function"==typeof define&&define.amd?define(["exports","react/jsx-runtime","react","@mobily/ts-belt","immer"],factory):factory((global="undefined"!=typeof globalThis?globalThis:global||self).MarchHare={},global.jsxRuntime,global.React,global.TsBelt,global.Immer);
|
package/dist/resource/index.d.ts
CHANGED
|
@@ -19,8 +19,11 @@ export declare function consumePending(): PendingCall;
|
|
|
19
19
|
*
|
|
20
20
|
* The fetcher receives a single `context` argument carrying `env`,
|
|
21
21
|
* `controller`, `params`, and a broadcast/multicast-only `dispatch`.
|
|
22
|
-
*
|
|
23
|
-
*
|
|
22
|
+
* `env` is a live handle — dot reads inside the fetcher
|
|
23
|
+
* always see the latest per-`<Boundary>` Env, even after `await`
|
|
24
|
+
* boundaries. Every successful fetch writes through to a per-resource
|
|
25
|
+
* in-memory cache; pair with {@link Resource.Cachable} to persist
|
|
26
|
+
* across reloads.
|
|
24
27
|
*
|
|
25
28
|
* Concurrent calls fire fresh requests by default. Opt in to in-flight
|
|
26
29
|
* sharing per call via `.coalesce(key)` on the thenable returned from
|
package/dist/resource/types.d.ts
CHANGED
|
@@ -13,8 +13,11 @@ export type Dispatch = {
|
|
|
13
13
|
/**
|
|
14
14
|
* `context` object passed to every {@link Fetcher}.
|
|
15
15
|
*
|
|
16
|
-
* - `env` —
|
|
17
|
-
*
|
|
16
|
+
* - `env` — live read-only handle to the per-`<Boundary>` Env.
|
|
17
|
+
* Dot reads always reflect the latest value, even across `await`
|
|
18
|
+
* boundaries inside the fetcher — the handle is a `Proxy`
|
|
19
|
+
* that delegates property access to the live ref, identical to
|
|
20
|
+
* the `context.env` exposed to action handlers.
|
|
18
21
|
* - `controller` — the `AbortController` auto-threaded from the
|
|
19
22
|
* calling handler's `context.task.controller`. Pass
|
|
20
23
|
* `controller.signal` to `fetch`/`ky`/`EventSource`, or call
|
|
@@ -33,7 +36,7 @@ export type Args<P extends object = Record<never, never>> = {
|
|
|
33
36
|
};
|
|
34
37
|
/**
|
|
35
38
|
* Fetcher signature accepted by `Resource`. Receives a single `context`
|
|
36
|
-
* argument carrying
|
|
39
|
+
* argument carrying a live Env handle, the abort controller, params,
|
|
37
40
|
* and a broadcast/multicast-only `dispatch`.
|
|
38
41
|
*/
|
|
39
42
|
export type Fetcher<T, P extends object = Record<never, never>> = (context: Args<P>) => Promise<T>;
|
package/dist/resource/utils.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export declare function defaultCache(fetcher: object): Cache;
|
|
|
20
20
|
/**
|
|
21
21
|
* Stable string key derived from the call-site `params`. Two calls with
|
|
22
22
|
* the same logical params (same key order, same primitive values) hit
|
|
23
|
-
* the same slot. JSON.stringify is sufficient for the
|
|
23
|
+
* the same slot. JSON.stringify is sufficient for the March Hare params
|
|
24
24
|
* convention (primitive-leaf objects); callers who need order-stable
|
|
25
25
|
* keying should normalise the object before passing it in.
|
|
26
26
|
*
|
package/dist/types/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { ActionId, Task, Tasks } from '../boundary/components/tasks/types';
|
|
|
3
3
|
import { Fault } from '../error/types';
|
|
4
4
|
import { Env } from '../boundary/components/env/index';
|
|
5
5
|
import { Coalesce } from '../resource/types';
|
|
6
|
+
import { WithHandle } from '../with/index';
|
|
6
7
|
import * as React from "react";
|
|
7
8
|
/**
|
|
8
9
|
* Chainable handle returned from `context.actions.resource(invocation)`.
|
|
@@ -482,16 +483,19 @@ export type Filter = Record<string, string | number | bigint | boolean | symbol>
|
|
|
482
483
|
*/
|
|
483
484
|
export type ActionOrChanneled<A extends HandlerPayload = HandlerPayload> = A | ChanneledAction;
|
|
484
485
|
/**
|
|
485
|
-
*
|
|
486
|
-
*
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
*
|
|
491
|
-
*
|
|
486
|
+
* Type guard that produces a compile-time error if an async function is
|
|
487
|
+
* passed. Used to enforce synchronous callbacks in `produce()`.
|
|
488
|
+
*
|
|
489
|
+
* The `[F]` tuple wrapping prevents distribution over function unions, and
|
|
490
|
+
* checking the actual signature (`(...args: never[]) => Promise<unknown>`)
|
|
491
|
+
* sidesteps TypeScript's lenient `Promise<void>`→`void` assignability that
|
|
492
|
+
* would otherwise let an async recipe satisfy a `(draft) => void`
|
|
493
|
+
* constraint. Async F collapses the argument type to `never`, which no
|
|
494
|
+
* function value can satisfy.
|
|
495
|
+
*
|
|
492
496
|
* @internal
|
|
493
497
|
*/
|
|
494
|
-
type AssertSync<F> =
|
|
498
|
+
type AssertSync<F> = [F] extends [(...args: never[]) => Promise<unknown>] ? never : F;
|
|
495
499
|
/**
|
|
496
500
|
* Base type for data props passed to useActions.
|
|
497
501
|
* Represents any object that can be captured as reactive data.
|
|
@@ -509,17 +513,17 @@ export type Actions = object;
|
|
|
509
513
|
export type Result = {
|
|
510
514
|
processes: Set<Process>;
|
|
511
515
|
};
|
|
512
|
-
export type HandlerContext<M extends Model | void, AC extends Actions | void, D extends Props = Props> = {
|
|
516
|
+
export type HandlerContext<M extends Model | void, AC extends Actions | void, D extends Props = Props, S extends Env = Env> = {
|
|
513
517
|
readonly model: DeepReadonly<M>;
|
|
514
518
|
readonly phase: Phase;
|
|
515
519
|
readonly task: Task;
|
|
516
520
|
readonly data: DeepReadonly<D>;
|
|
517
521
|
readonly tasks: ReadonlySet<Task>;
|
|
518
|
-
readonly env:
|
|
522
|
+
readonly env: Readonly<S>;
|
|
519
523
|
readonly actions: {
|
|
520
524
|
produce<F extends (draft: {
|
|
521
525
|
model: M;
|
|
522
|
-
env:
|
|
526
|
+
env: S;
|
|
523
527
|
readonly inspect: Readonly<Inspect<M>>;
|
|
524
528
|
}) => void>(ƒ: F & AssertSync<F>): void;
|
|
525
529
|
dispatch(action: NoPayloadActions<Dispatchable<AC>>): Promise<void>;
|
|
@@ -550,7 +554,7 @@ export type HandlerContext<M extends Model | void, AC extends Actions | void, D
|
|
|
550
554
|
* @example
|
|
551
555
|
* ```tsx
|
|
552
556
|
* const [model, actions, data] = useActions<Model, typeof Actions, Data>(
|
|
553
|
-
*
|
|
557
|
+
* model,
|
|
554
558
|
* () => ({ user, theme }),
|
|
555
559
|
* );
|
|
556
560
|
*
|
|
@@ -578,7 +582,7 @@ export type HandlerContext<M extends Model | void, AC extends Actions | void, D
|
|
|
578
582
|
*
|
|
579
583
|
* @see {@link Handlers} for the recommended HKT pattern
|
|
580
584
|
*/
|
|
581
|
-
export type Handler<M extends Model | void, AC extends Actions | void, K extends keyof AC & string, D extends Props = Props> = (context: HandlerContext<M, AC, D>, ...args: [Payload<AC[K] & HandlerPayload<unknown>>] extends [never] ? [] : [payload: Payload<AC[K] & HandlerPayload<unknown>>]) => void | Promise<void> | AsyncGenerator | Generator;
|
|
585
|
+
export type Handler<M extends Model | void, AC extends Actions | void, K extends keyof AC & string, D extends Props = Props, S extends Env = Env> = (context: HandlerContext<M, AC, D, S>, ...args: [Payload<AC[K] & HandlerPayload<unknown>>] extends [never] ? [] : [payload: Payload<AC[K] & HandlerPayload<unknown>>]) => void | Promise<void> | AsyncGenerator | Generator;
|
|
582
586
|
/**
|
|
583
587
|
* String keys of `AC` excluding inherited `prototype` from class constructors.
|
|
584
588
|
* When action containers are classes (`typeof MyActions`), TypeScript includes
|
|
@@ -667,10 +671,10 @@ export type WithPayloadActions<U> = Exclude<U, {
|
|
|
667
671
|
* export const handlePaymentSent: H["Broadcast"]["PaymentSent"] = (context) => { ... };
|
|
668
672
|
* ```
|
|
669
673
|
*/
|
|
670
|
-
export type Handlers<M extends Model | void, AC extends Actions | void, D extends Props = Props, RootAC extends Actions | void = AC> = {
|
|
671
|
-
[K in OwnKeys<AC>]: OwnKeys<AC[K]> extends never ? (context: HandlerContext<M, RootAC, D>, ...args: [Payload<AC[K] & HandlerPayload<unknown>>] extends [never] ? [] : [payload: Payload<AC[K] & HandlerPayload<unknown>>]) => void | Promise<void> | AsyncGenerator | Generator : Handlers<M, AC[K] & Actions, D, RootAC>;
|
|
674
|
+
export type Handlers<M extends Model | void, AC extends Actions | void, D extends Props = Props, RootAC extends Actions | void = AC, S extends Env = Env> = {
|
|
675
|
+
[K in OwnKeys<AC>]: OwnKeys<AC[K]> extends never ? (context: HandlerContext<M, RootAC, D, S>, ...args: [Payload<AC[K] & HandlerPayload<unknown>>] extends [never] ? [] : [payload: Payload<AC[K] & HandlerPayload<unknown>>]) => void | Promise<void> | AsyncGenerator | Generator : Handlers<M, AC[K] & Actions, D, RootAC, S>;
|
|
672
676
|
};
|
|
673
|
-
export type UseActions<M extends Model | void, AC extends Actions | void, D extends Props = Props> = [
|
|
677
|
+
export type UseActions<M extends Model | void, AC extends Actions | void, D extends Props = Props, S extends Env = Env> = [
|
|
674
678
|
Readonly<M>,
|
|
675
679
|
{
|
|
676
680
|
/**
|
|
@@ -703,6 +707,7 @@ export type UseActions<M extends Model | void, AC extends Actions | void, D exte
|
|
|
703
707
|
* );
|
|
704
708
|
* ```
|
|
705
709
|
*/
|
|
710
|
+
stream(action: typeof Lifecycle.Env, renderer: (value: Readonly<S>, inspect: Inspect<S>) => React.ReactNode): React.ReactNode;
|
|
706
711
|
stream<T extends object>(action: BroadcastPayload<T>, renderer: (value: T, inspect: Inspect<T>) => React.ReactNode): React.ReactNode;
|
|
707
712
|
},
|
|
708
713
|
DeepReadonly<D>
|
|
@@ -743,8 +748,9 @@ export type UseActions<M extends Model | void, AC extends Actions | void, D exte
|
|
|
743
748
|
* });
|
|
744
749
|
* ```
|
|
745
750
|
*/
|
|
746
|
-
useAction(action: NoPayloadActions<Subscribable<AC>>, handler: (context: HandlerContext<M, AC, D>) => void | Promise<void> | AsyncGenerator | Generator): void;
|
|
747
|
-
useAction
|
|
751
|
+
useAction(action: NoPayloadActions<Subscribable<AC>>, handler: (context: HandlerContext<M, AC, D, S>) => void | Promise<void> | AsyncGenerator | Generator): void;
|
|
752
|
+
useAction(action: typeof Lifecycle.Env, handler: (context: HandlerContext<M, AC, D, S>, env: Readonly<S>) => void | Promise<void> | AsyncGenerator | Generator): void;
|
|
753
|
+
useAction<A extends WithPayloadActions<Subscribable<AC>>>(action: A, handler: (context: HandlerContext<M, AC, D, S>, payload: Payload<A>) => void | Promise<void> | AsyncGenerator | Generator): void;
|
|
748
754
|
};
|
|
749
755
|
/**
|
|
750
756
|
* Stable, typed dispatch function for the actions class `AC`. Same call
|
|
@@ -767,10 +773,16 @@ export type Dispatch<AC extends Actions | void> = {
|
|
|
767
773
|
* `React.Context` — it's the March Hare action surface returned by
|
|
768
774
|
* the `useContext` hook of this library.
|
|
769
775
|
*/
|
|
770
|
-
export type Context<M extends Model | void, AC extends Actions | void, D extends Props = Props> = {
|
|
776
|
+
export type Context<M extends Model | void, AC extends Actions | void, D extends Props = Props, S extends Env = Env> = {
|
|
771
777
|
readonly actions: {
|
|
772
778
|
dispatch: Dispatch<AC>;
|
|
773
779
|
};
|
|
774
|
-
|
|
775
|
-
|
|
780
|
+
/**
|
|
781
|
+
* Typed bag of handler factories bound to `M`. Methods accept lodash-style
|
|
782
|
+
* dotted paths with array indices (e.g. `"a.b.c"`, `"items.0.id"`) and
|
|
783
|
+
* autocomplete from the model. See {@link WithHandle}.
|
|
784
|
+
*/
|
|
785
|
+
readonly with: WithHandle<M>;
|
|
786
|
+
useActions(getData?: () => D): UseActions<M, AC, D, S>;
|
|
787
|
+
useActions(model: M extends void ? never : M, getData?: () => D): UseActions<M, AC, D, S>;
|
|
776
788
|
};
|
package/dist/with/index.d.ts
CHANGED
|
@@ -1,40 +1,111 @@
|
|
|
1
1
|
import { Actions, HandlerContext, Model, Props } from '../types/index';
|
|
2
|
+
import { Env } from '../boundary/components/env/index';
|
|
3
|
+
type Primitive = string | number | bigint | boolean | symbol | null | undefined;
|
|
4
|
+
type Depth = [never, 0, 1, 2, 3, 4, 5];
|
|
2
5
|
/**
|
|
3
|
-
*
|
|
6
|
+
* Lodash-style dotted paths reachable from `T`. Yields `"a"`, `"a.b"`,
|
|
7
|
+
* `"items.0"`, `"items.0.name"`, etc. Recursion is capped at depth 5 to
|
|
8
|
+
* keep the type-checker tractable on deeply nested models.
|
|
4
9
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
10
|
+
* @template T The object type to enumerate paths from.
|
|
11
|
+
* @template D Recursion budget (internal).
|
|
12
|
+
*/
|
|
13
|
+
export type Paths<T, D extends number = 5> = [D] extends [never] ? never : T extends Primitive ? never : T extends ReadonlyArray<infer U> ? `${number}` | (U extends Primitive ? never : `${number}.${Paths<U, Depth[D]>}`) : T extends object ? {
|
|
14
|
+
[K in Extract<keyof T, string>]: T[K] extends Primitive ? K : K | `${K}.${Paths<T[K], Depth[D]>}`;
|
|
15
|
+
}[Extract<keyof T, string>] : never;
|
|
16
|
+
/**
|
|
17
|
+
* Subset of {@link Paths} whose leaf type is `boolean`. Used by
|
|
18
|
+
* `context.with.invert` (and the legacy {@link With.Invert}) to restrict the
|
|
19
|
+
* key to togglable fields only.
|
|
20
|
+
*
|
|
21
|
+
* @template T The object type to enumerate boolean leaves from.
|
|
22
|
+
* @template D Recursion budget (internal).
|
|
23
|
+
*/
|
|
24
|
+
export type BooleanPaths<T, D extends number = 5> = [D] extends [never] ? never : T extends Primitive ? never : T extends ReadonlyArray<infer U> ? (U extends boolean ? `${number}` : never) | (U extends Primitive ? never : `${number}.${BooleanPaths<U, Depth[D]>}`) : T extends object ? {
|
|
25
|
+
[K in Extract<keyof T, string>]: T[K] extends boolean ? K : T[K] extends Primitive ? never : `${K}.${BooleanPaths<T[K], Depth[D]>}`;
|
|
26
|
+
}[Extract<keyof T, string>] : never;
|
|
27
|
+
/**
|
|
28
|
+
* Resolves the leaf type at a dotted path on `T`. `Get<{a:{b:number}},"a.b">`
|
|
29
|
+
* is `number`; `Get<{items: string[]},"items.0">` is `string`.
|
|
7
30
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
31
|
+
* @template T The object type to walk.
|
|
32
|
+
* @template P The dotted path string.
|
|
33
|
+
*/
|
|
34
|
+
export type Get<T, P extends string> = P extends `${infer Head}.${infer Tail}` ? T extends ReadonlyArray<infer U> ? Head extends `${number}` ? Get<U, Tail> : never : Head extends keyof T ? Get<T[Head], Tail> : never : T extends ReadonlyArray<infer U> ? P extends `${number}` ? U : never : P extends keyof T ? T[P] : never;
|
|
35
|
+
/**
|
|
36
|
+
* Returned by `context.with` — a typed bag of handler factories
|
|
37
|
+
* bound to the model `M` declared in `useContext<M, …>()`. Methods accept
|
|
38
|
+
* lodash-style dotted paths (`"a.b.c"`) with array indices (`"items.0.id"`).
|
|
39
|
+
*
|
|
40
|
+
* - `update(key)` — assigns the dispatched payload to `model[key]`.
|
|
41
|
+
* - `invert(key)` — flips a boolean leaf at `model[key]`.
|
|
42
|
+
* - `always(key, value)` — assigns a fixed `value` to `model[key]`,
|
|
43
|
+
* ignoring any dispatched payload.
|
|
44
|
+
*
|
|
45
|
+
* @template M The model type to bind keys against.
|
|
46
|
+
*/
|
|
47
|
+
export type WithHandle<M> = M extends Model ? {
|
|
48
|
+
update<K extends Paths<M>>(key: K): <A extends Actions | void, D extends Props, S extends Env = Env>(context: HandlerContext<M, A, D, S>, payload: Get<M, K>) => void;
|
|
49
|
+
invert<K extends BooleanPaths<M>>(key: K): <A extends Actions | void, D extends Props, S extends Env = Env>(context: HandlerContext<M, A, D, S>) => void;
|
|
50
|
+
always<K extends Paths<M>>(key: K, value: Get<M, K>): <A extends Actions | void, D extends Props, S extends Env = Env>(context: HandlerContext<M, A, D, S>) => void;
|
|
51
|
+
} : Record<string, never>;
|
|
52
|
+
/**
|
|
53
|
+
* Builds the {@link WithHandle} object returned via `context.with`. The
|
|
54
|
+
* runtime is identical for any model — only the call-site types differ.
|
|
55
|
+
*
|
|
56
|
+
* @internal
|
|
57
|
+
*/
|
|
58
|
+
export declare function bindWith<M extends Model | void>(): WithHandle<M>;
|
|
59
|
+
/**
|
|
60
|
+
* Handler factories that wire an action directly to a model field. Prefer
|
|
61
|
+
* `context.with` from `useContext<Model>()` for first-class autocompletion
|
|
62
|
+
* over dotted paths; this top-level form is kept for callers that don't have
|
|
63
|
+
* a typed `context` in scope.
|
|
64
|
+
*
|
|
65
|
+
* - {@link With.Update} assigns the dispatched payload to a model path.
|
|
66
|
+
* - {@link With.Invert} flips a boolean leaf at a model path.
|
|
67
|
+
* - {@link With.Always} assigns a fixed value to a model path, ignoring any
|
|
68
|
+
* dispatched payload.
|
|
69
|
+
*
|
|
70
|
+
* Keys may be lodash-style dotted paths (`"a.b.c"`) and support array
|
|
71
|
+
* indices (`"items.0.name"`). The model type is inferred at handler-bind
|
|
72
|
+
* time; an invalid path or mismatched payload fails to compile when the
|
|
73
|
+
* handler is registered with `useAction`.
|
|
10
74
|
*
|
|
11
75
|
* @example
|
|
12
76
|
* ```ts
|
|
13
77
|
* import { With } from "march-hare";
|
|
14
78
|
*
|
|
15
|
-
* type Model = {
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* }
|
|
79
|
+
* type Model = {
|
|
80
|
+
* name: string;
|
|
81
|
+
* sidebar: boolean;
|
|
82
|
+
* nested: { open: boolean };
|
|
83
|
+
* items: { id: number }[];
|
|
84
|
+
* };
|
|
21
85
|
*
|
|
22
86
|
* actions.useAction(Actions.SetName, With.Update("name"));
|
|
87
|
+
* actions.useAction(Actions.SetFirstId, With.Update("items.0.id"));
|
|
23
88
|
* actions.useAction(Actions.ToggleSidebar, With.Invert("sidebar"));
|
|
89
|
+
* actions.useAction(Actions.ToggleNested, With.Invert("nested.open"));
|
|
90
|
+
* actions.useAction(Actions.Open, With.Always("nested.open", true));
|
|
24
91
|
* ```
|
|
25
92
|
*/
|
|
26
93
|
export declare const With: {
|
|
27
94
|
/**
|
|
28
|
-
* Returns a handler that assigns the action payload to
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
|
|
95
|
+
* Returns a handler that assigns the action payload to the model leaf at
|
|
96
|
+
* the given lodash-style path. The payload type must match `Get<M, K>`,
|
|
97
|
+
* and the path must exist on the model.
|
|
98
|
+
*/
|
|
99
|
+
Update<K extends string>(key: K): <M extends Model, A extends Actions | void, D extends Props, P extends K extends Paths<M> ? Get<M, K> : never, S extends Env = Env>(context: HandlerContext<M, A, D, S>, payload: P) => void;
|
|
100
|
+
/**
|
|
101
|
+
* Returns a handler that inverts a boolean leaf at the given lodash-style
|
|
102
|
+
* path. The leaf must be a `boolean` on the model.
|
|
32
103
|
*/
|
|
33
|
-
|
|
104
|
+
Invert<K extends string>(key: K): <M extends Model, A extends Actions | void, D extends Props, S extends Env = Env>(context: K extends BooleanPaths<M> ? HandlerContext<M, A, D, S> : never) => void;
|
|
34
105
|
/**
|
|
35
|
-
* Returns a handler that
|
|
36
|
-
*
|
|
37
|
-
* Type-checks at the call site: `model[key]` must be a boolean.
|
|
106
|
+
* Returns a handler that assigns a fixed `value` to the model leaf at the
|
|
107
|
+
* given lodash-style path. The dispatched payload (if any) is ignored.
|
|
38
108
|
*/
|
|
39
|
-
|
|
109
|
+
Always<K extends string, V>(key: K, value: V): <M extends Model, A extends Actions | void, D extends Props, S extends Env = Env>(context: K extends Paths<M> ? V extends Get<M, K> ? HandlerContext<M, A, D, S> : never : never) => void;
|
|
40
110
|
};
|
|
111
|
+
export {};
|