march-hare 0.14.0 → 0.17.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 +46 -5
- package/dist/actions/utils.d.ts +19 -17
- package/dist/app/index.d.ts +1 -1
- package/dist/boundary/components/broadcast/utils.d.ts +37 -10
- package/dist/cache/index.d.ts +69 -30
- package/dist/cache/types.d.ts +16 -15
- package/dist/march-hare.js +6 -6
- package/dist/march-hare.js.map +1 -1
- package/dist/march-hare.umd.cjs +1 -1
- package/dist/march-hare.umd.cjs.map +1 -1
- package/dist/resource/types.d.ts +39 -4
- package/dist/resource/utils.d.ts +0 -9
- package/dist/types/index.d.ts +13 -11
- package/dist/utils/utils.d.ts +17 -0
- package/dist/utils.d.ts +0 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -394,10 +394,11 @@ Components that mount after a broadcast has already been dispatched automaticall
|
|
|
394
394
|
|
|
395
395
|
## Resource handling
|
|
396
396
|
|
|
397
|
-
For remote data, declare an `app.Resource` at module scope. The resulting handle has
|
|
397
|
+
For remote data, declare an `app.Resource` at module scope. The resulting handle has three call forms:
|
|
398
398
|
|
|
399
399
|
- `resource.user.get(params)` — synchronous cache read, returns `User | null`. Use it in model literals, JSX, or anywhere you need the cached value without triggering a fetch.
|
|
400
400
|
- `resource.user(params)` — produces an `Invocation` you pass to `context.actions.resource(...)` for the fetch path (with auto-threaded abort controller and a live handle to the per-`<Boundary>` Env).
|
|
401
|
+
- `resource.user.action(partial?)` — broadcast channeled action fired automatically after every successful fetch. Always invoke (`resource.user.action()`) before passing to `actions.useAction` or `actions.stream`; supply a subset of params to narrow the filter (more keys = stricter), or pass nothing to receive every fetch. Errors do not broadcast.
|
|
401
402
|
|
|
402
403
|
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"`):
|
|
403
404
|
|
|
@@ -461,6 +462,44 @@ export default function Profile(): React.ReactElement {
|
|
|
461
462
|
}
|
|
462
463
|
```
|
|
463
464
|
|
|
465
|
+
Every successful fetch also publishes an auto-broadcast via `resource.x.action(partial?)`. Any component — even one that never called the fetcher — can react via `actions.useAction` or render the latest payload with `actions.stream`. Call `.action()` with no arguments to match every fetch on the resource, or supply a subset of params to narrow the filter:
|
|
466
|
+
|
|
467
|
+
```tsx
|
|
468
|
+
// greeting/actions.ts
|
|
469
|
+
import { app } from "../app";
|
|
470
|
+
import * as resource from "../resources";
|
|
471
|
+
|
|
472
|
+
type Model = { greeting: string | null };
|
|
473
|
+
|
|
474
|
+
function useActions() {
|
|
475
|
+
const context = app.useContext<Model>();
|
|
476
|
+
const actions = context.useActions({ greeting: null });
|
|
477
|
+
|
|
478
|
+
actions.useAction(resource.user.action(), (context, user) => {
|
|
479
|
+
context.actions.produce(
|
|
480
|
+
({ model }) => void (model.greeting = `Welcome, ${user.name}`),
|
|
481
|
+
);
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
return actions;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
export default function Greeting() {
|
|
488
|
+
const [model] = useActions();
|
|
489
|
+
return <span>{model.greeting ?? "…"}</span>;
|
|
490
|
+
}
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
Or render the most recent payload declaratively without a local model field:
|
|
494
|
+
|
|
495
|
+
```tsx
|
|
496
|
+
{
|
|
497
|
+
actions.stream(resource.user.action(), (user) => <span>{user.name}</span>);
|
|
498
|
+
}
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
The broadcast cache is sharded by `(action, channel)`, so a late-mounting subscriber replays every cached entry whose channel satisfies its filter — a `stream` panel that mounts after the page's data has already loaded still paints with the cached value rather than waiting for the next fetch.
|
|
502
|
+
|
|
464
503
|
`context.actions.resource(invocation)` returns a chainable thenable:
|
|
465
504
|
|
|
466
505
|
- `.exceeds({ minutes: 5 })` — short-circuits when the per-params cache age is within the freshness window. Accepts a `Temporal.Duration`, a `DurationLike` object, or an ISO 8601 duration string. `Temporal` is read from the host runtime – bring a polyfill (e.g. [`@js-temporal/polyfill`](https://github.com/js-temporal/temporal-polyfill)) if your target doesn't expose it natively.
|
|
@@ -556,7 +595,7 @@ export const app = App({
|
|
|
556
595
|
get: (key) => localStorage.getItem(key),
|
|
557
596
|
set: (key, value) => localStorage.setItem(key, value),
|
|
558
597
|
remove: (key) => localStorage.removeItem(key),
|
|
559
|
-
|
|
598
|
+
keys: () => Object.keys(localStorage),
|
|
560
599
|
}),
|
|
561
600
|
});
|
|
562
601
|
```
|
|
@@ -621,15 +660,15 @@ export const app = App<AppEnv>({
|
|
|
621
660
|
get: (key) => localStorage.getItem(key),
|
|
622
661
|
set: (key, value) => localStorage.setItem(key, value),
|
|
623
662
|
remove: (key) => localStorage.removeItem(key),
|
|
624
|
-
|
|
663
|
+
keys: () => Object.keys(localStorage),
|
|
625
664
|
key: ({ env }) => env.session?.accessToken ?? "",
|
|
626
665
|
}),
|
|
627
666
|
});
|
|
628
667
|
```
|
|
629
668
|
|
|
630
|
-
Successful writes for Alice land under `alice:0:{...}`; Bob's land under `bob:0:{...}`. Return `""`, `null`, or `undefined` to skip
|
|
669
|
+
Successful writes for Alice land under `mh:alice:0:{...}`; Bob's land under `mh:bob:0:{...}`. The Cache layer prepends a global `mh:` namespace so `cache.clear()` and partial-match eviction can scope themselves to March Hare's own entries on shared backends, leaving third-party `localStorage` state untouched. Return `""`, `null`, or `undefined` from `key(context)` to skip the per-context prefix – useful for the signed-out gap.
|
|
631
670
|
|
|
632
|
-
The adapter contract is **strictly synchronous** – `get` / `set` / `remove` / `
|
|
671
|
+
The adapter contract is **strictly synchronous** – `get` / `set` / `remove` / `keys` all return immediately, with no `Promise`. The model-literal read (`{ user: resource.user.get() }`) is evaluated during render and has no place to wait. React Native projects should use [`react-native-mmkv`](https://github.com/mrousavy/react-native-mmkv), which is sync out of the box and drops straight into the contract; `AsyncStorage` is incompatible. Truly async backends (IndexedDB, `chrome.storage.local`) need a sync facade hydrated at app entry – see the [storage recipe](./recipes/storage.md).
|
|
633
672
|
|
|
634
673
|
See the [storage recipe](./recipes/storage.md) for backend adapters (React Native `react-native-mmkv`, browser `localStorage`, browser extension `chrome.storage`), sign-out purge, and the `unset` sentinel that keeps "nothing stored" distinct from "a legitimately stored null".
|
|
635
674
|
|
|
@@ -670,6 +709,8 @@ actions.dispatch(Actions.UserUpdated, user);
|
|
|
670
709
|
|
|
671
710
|
Channel values support non-nullable primitives: `string`, `number`, `boolean`, or `symbol`. By convention, use uppercase keys like `{UserId: 4}` to distinguish controller keys from payload properties.
|
|
672
711
|
|
|
712
|
+
Matching follows a single rule: every key the **subscriber** supplies must be present and equal on the dispatch channel. The subscriber's controller is the filter, more keys narrow it. Extra keys on the dispatch channel are ignored, so the dispatcher is free to be more specific than any single subscriber needs. Uncalled actions on either side bypass channel filtering entirely. See the [channeled-actions recipe](./recipes/channeled-actions.md#channel-matching) for the full matrix.
|
|
713
|
+
|
|
673
714
|
## Multicast actions
|
|
674
715
|
|
|
675
716
|
For scoped communication between component groups, declare a multicast action class and open a scope via `app.Scope<typeof MulticastActions>()`. The generic carries the multicast surface at the type level — `scope.useContext().actions.dispatch` widens to include those actions on top of the local `Actions` class, the same way `Actions.Broadcast = BroadcastActions` widens for broadcasts. Render `<scope.Boundary>` once at the root of the subtree the scope governs:
|
package/dist/actions/utils.d.ts
CHANGED
|
@@ -16,19 +16,11 @@ export declare function withGetters<P extends Props>(a: P, b: RefObject<P>): P;
|
|
|
16
16
|
* the generator function's name.
|
|
17
17
|
*/
|
|
18
18
|
export declare function isGenerator(result: unknown): result is Generator | AsyncGenerator;
|
|
19
|
-
/**
|
|
20
|
-
* Sentinel passed as the dispatch channel during mount replay. Channeled
|
|
21
|
-
* handlers check for this to skip replay — they require specific
|
|
22
|
-
* channel context and cannot meaningfully process a replay without it.
|
|
23
|
-
*
|
|
24
|
-
* @internal
|
|
25
|
-
*/
|
|
26
|
-
export declare const replay: unique symbol;
|
|
27
19
|
/**
|
|
28
20
|
* Invokes all listeners for an event and returns a promise that resolves
|
|
29
21
|
* when every handler has settled. For {@link BroadcastEmitter} instances the
|
|
30
|
-
* payload is cached
|
|
31
|
-
* see
|
|
22
|
+
* payload is cached — sharded by channel — before listeners
|
|
23
|
+
* fire so late-mounting components see every cached entry.
|
|
32
24
|
*
|
|
33
25
|
* @internal
|
|
34
26
|
*/
|
|
@@ -147,19 +139,29 @@ export declare function useDispatchers(): Dispatchers;
|
|
|
147
139
|
*/
|
|
148
140
|
export declare function useRegisterHandler<M extends Model | void, A extends Actions | void, D extends Props>(scope: React.RefObject<Scope<M, A, D>>, action: ActionId | HandlerPayload | ChanneledAction, handler: (context: HandlerContext<M, A, D>, payload: unknown) => void | Promise<void> | AsyncGenerator | Generator): void;
|
|
149
141
|
/**
|
|
150
|
-
* Checks
|
|
151
|
-
*
|
|
142
|
+
* Checks whether a dispatch channel satisfies a registered handler's
|
|
143
|
+
* channel filter. The handler's channel is the filter: every key the
|
|
144
|
+
* subscriber supplied must be present and equal on the dispatch channel.
|
|
145
|
+
* Extra keys on the dispatch channel are ignored — the dispatcher
|
|
146
|
+
* is free to be more specific than any single subscriber needs.
|
|
147
|
+
*
|
|
148
|
+
* Mental model: "the subscriber declares everything it wants matched
|
|
149
|
+
* against; the dispatcher matches what it can." A subscriber that
|
|
150
|
+
* registers no channel keys (`{}` or no channel at all) matches every
|
|
151
|
+
* dispatch on the action.
|
|
152
152
|
*
|
|
153
153
|
* @param dispatchChannel - The channel from the dispatch call (from ChanneledAction)
|
|
154
154
|
* @param registeredChannel - The channel registered with useAction
|
|
155
|
-
* @returns `true` if
|
|
155
|
+
* @returns `true` if every key on the registered channel is satisfied by the dispatch channel
|
|
156
156
|
*
|
|
157
157
|
* @example
|
|
158
158
|
* ```ts
|
|
159
|
-
* matchesChannel({ UserId: 1 }, { UserId: 1 });
|
|
160
|
-
* matchesChannel({ UserId: 1 }, { UserId:
|
|
161
|
-
* matchesChannel({ UserId: 1 }, { UserId: 1, Role: "admin" });
|
|
162
|
-
* matchesChannel({ UserId: 1
|
|
159
|
+
* matchesChannel({ UserId: 1 }, { UserId: 1 }); // true (exact)
|
|
160
|
+
* matchesChannel({ UserId: 1, Role: "admin" }, { UserId: 1 }); // true (subscriber narrower)
|
|
161
|
+
* matchesChannel({ UserId: 1 }, { UserId: 1, Role: "admin" }); // false (subscriber asked for Role, dispatch did not supply it)
|
|
162
|
+
* matchesChannel({ UserId: 1 }, { UserId: 2 }); // false (value mismatch)
|
|
163
|
+
* matchesChannel({ UserId: 1, Role: "admin" }, {}); // true (subscriber filters nothing)
|
|
164
|
+
* matchesChannel({}, { UserId: 1 }); // false (subscriber asked for UserId, dispatch did not supply it)
|
|
163
165
|
* ```
|
|
164
166
|
*/
|
|
165
167
|
export declare function matchesChannel(dispatchChannel: Filter, registeredChannel: Filter): boolean;
|
package/dist/app/index.d.ts
CHANGED
|
@@ -50,7 +50,7 @@ export type { AppHandle } from './types.js';
|
|
|
50
50
|
* get: (key) => localStorage.getItem(key),
|
|
51
51
|
* set: (key, value) => localStorage.setItem(key, value),
|
|
52
52
|
* remove: (key) => localStorage.removeItem(key),
|
|
53
|
-
*
|
|
53
|
+
* keys: () => Object.keys(localStorage),
|
|
54
54
|
* }),
|
|
55
55
|
* });
|
|
56
56
|
*
|
|
@@ -1,25 +1,51 @@
|
|
|
1
1
|
import { default as EventEmitter } from 'eventemitter3';
|
|
2
|
+
import { Filter } from '../../../types/index.js';
|
|
2
3
|
import * as React from "react";
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
* Cache entry stored per `(event, channelHash)`. The original channel
|
|
6
|
+
* object is preserved so mount replay can re-emit with the exact
|
|
7
|
+
* channel the producer used — otherwise late-mounting subscribers
|
|
8
|
+
* could not satisfy filtered-channel matching.
|
|
9
|
+
*/
|
|
10
|
+
type Entry = {
|
|
11
|
+
readonly channel: Filter | undefined;
|
|
12
|
+
readonly value: unknown;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* EventEmitter subclass that caches the latest payload per `(event,
|
|
16
|
+
* channel)` pair. When a broadcast or multicast action is dispatched,
|
|
17
|
+
* the payload is stored so that late-mounting components can replay it
|
|
18
|
+
* via {@link useLifecycles} and handlers can read it via
|
|
19
|
+
* `context.actions.final()`. Cache entries are keyed by both the
|
|
20
|
+
* action and the channel object — multi-channel actions retain
|
|
21
|
+
* one entry per channel rather than overwriting on every dispatch.
|
|
10
22
|
*/
|
|
11
23
|
export declare class BroadcastEmitter extends EventEmitter {
|
|
12
24
|
private cache;
|
|
25
|
+
private latest;
|
|
13
26
|
emit(event: string | symbol, ...args: unknown[]): boolean;
|
|
14
27
|
/**
|
|
15
|
-
* Cache a value for a given event without emitting to
|
|
16
|
-
* Used by {@link emitAsync} to preserve caching when
|
|
28
|
+
* Cache a value for a given event/channel pair without emitting to
|
|
29
|
+
* listeners. Used by {@link emitAsync} to preserve caching when
|
|
30
|
+
* bypassing `emit()`.
|
|
17
31
|
*/
|
|
18
|
-
setCache(event: string | symbol, value: unknown): void;
|
|
32
|
+
setCache(event: string | symbol, value: unknown, channel: Filter | undefined): void;
|
|
19
33
|
/**
|
|
20
|
-
* Retrieve the
|
|
34
|
+
* Retrieve the most recently cached payload across every channel for
|
|
35
|
+
* a given event. Returns `undefined` if no value has ever been
|
|
36
|
+
* dispatched. Use {@link getCachedAll} when channel-specific replay
|
|
37
|
+
* is required — this overload exists for `context.actions
|
|
38
|
+
* .final()`, which resolves to the most recent broadcast regardless
|
|
39
|
+
* of channel.
|
|
21
40
|
*/
|
|
22
41
|
getCached(event: string | symbol): unknown;
|
|
42
|
+
/**
|
|
43
|
+
* Iterate every cached `(channel, value)` pair for a given event.
|
|
44
|
+
* Mount replay walks this to re-emit per-channel values so that
|
|
45
|
+
* late-mounting subscribers see the same broadcasts they would have
|
|
46
|
+
* received had they been mounted at dispatch time.
|
|
47
|
+
*/
|
|
48
|
+
getCachedAll(event: string | symbol): Iterable<Entry>;
|
|
23
49
|
/**
|
|
24
50
|
* Emit without caching the payload. Used by the framework to publish
|
|
25
51
|
* fire-and-forget events (such as `Lifecycle.Fault`) where late-mounting
|
|
@@ -37,3 +63,4 @@ export declare const Context: React.Context<BroadcastEmitter>;
|
|
|
37
63
|
* @returns The BroadcastEmitter instance for distributed actions.
|
|
38
64
|
*/
|
|
39
65
|
export declare function useBroadcast(): BroadcastEmitter;
|
|
66
|
+
export {};
|
package/dist/cache/index.d.ts
CHANGED
|
@@ -13,23 +13,49 @@ export type CacheContext<E extends object> = {
|
|
|
13
13
|
readonly env: E;
|
|
14
14
|
};
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* with
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* (e.g. one cache slot per access token, locale, or tenant id) can
|
|
26
|
-
* coexist in the same backing store. Return `""`, `null`, or
|
|
27
|
-
* `undefined` to skip prefixing — useful for "not signed in"
|
|
28
|
-
* gaps where the scope is genuinely empty.
|
|
16
|
+
* Optional `key(context)` callback shared by both {@link CacheConfig}
|
|
17
|
+
* variants. Derives a per-context cache scope. Called every time a
|
|
18
|
+
* cache key is assembled with the same `{ env }` shape an
|
|
19
|
+
* `app.Resource` fetcher receives; the returned string is prepended
|
|
20
|
+
* to the per-resource namespace and params so different scopes
|
|
21
|
+
* (e.g. one cache slot per access token, locale, or tenant id) can
|
|
22
|
+
* coexist in the same backing store. Return `""`, `null`, or
|
|
23
|
+
* `undefined` to skip prefixing — useful for "not signed in"
|
|
24
|
+
* gaps where the scope is genuinely empty.
|
|
29
25
|
*/
|
|
30
|
-
|
|
26
|
+
type CacheKeyOnly<E extends object> = {
|
|
31
27
|
readonly key?: (context: CacheContext<E>) => string | null | undefined;
|
|
28
|
+
readonly get?: never;
|
|
29
|
+
readonly set?: never;
|
|
30
|
+
readonly remove?: never;
|
|
31
|
+
readonly keys?: never;
|
|
32
32
|
};
|
|
33
|
+
type CacheAdapter<E extends object> = Adapter & {
|
|
34
|
+
readonly key?: (context: CacheContext<E>) => string | null | undefined;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Configuration accepted by the {@link Cache} factory. A discriminated
|
|
38
|
+
* union with two shapes:
|
|
39
|
+
*
|
|
40
|
+
* - **Adapter-only / adapter + `key`** — supply the four
|
|
41
|
+
* adapter methods (`get`/`set`/`remove`/`keys`) and an optional
|
|
42
|
+
* `key(context)` callback. The cache is backed by the adapter;
|
|
43
|
+
* persistence is the caller's responsibility. There is no `clear`
|
|
44
|
+
* — the Cache layer synthesises one by iterating `keys()`
|
|
45
|
+
* and removing only entries inside the `mh:` namespace it owns,
|
|
46
|
+
* so third-party state on shared backends stays untouched.
|
|
47
|
+
* - **`key`-only** — supply just `key(context)` (no adapter
|
|
48
|
+
* methods at all). The cache falls back to a per-instance in-memory
|
|
49
|
+
* adapter but still scopes every slot by the live Env, which is
|
|
50
|
+
* useful for tests, ephemeral state, or signed-out sessions where
|
|
51
|
+
* you want env-scoped isolation without touching storage.
|
|
52
|
+
*
|
|
53
|
+
* The split is enforced by the union: passing a partial adapter
|
|
54
|
+
* (e.g. just `get`) is a type error — either all four required
|
|
55
|
+
* adapter methods are present or none of them are. {@link Cache} also
|
|
56
|
+
* accepts no argument at all for a plain in-memory cache.
|
|
57
|
+
*/
|
|
58
|
+
export type CacheConfig<E extends object> = CacheAdapter<E> | CacheKeyOnly<E>;
|
|
33
59
|
/**
|
|
34
60
|
* Persistence-aware cache for a single {@link Resource}. Wraps a
|
|
35
61
|
* **strictly synchronous** {@link Adapter} (localStorage, MMKV,
|
|
@@ -55,11 +81,12 @@ export type CacheConfig<E extends object> = Adapter & {
|
|
|
55
81
|
* persistent store; when supplied, the adapter is the **only** tier
|
|
56
82
|
* — the Cache does not maintain a separate in-memory mirror.
|
|
57
83
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
84
|
+
* `key(context)` is independently optional. Pass it alongside the
|
|
85
|
+
* adapter methods to scope a persistent cache by the live Env, or on
|
|
86
|
+
* its own (with no adapter) for an env-scoped in-memory cache. The
|
|
87
|
+
* adapter methods are an all-or-nothing group: supply all four
|
|
88
|
+
* (`get`/`set`/`remove`/`keys`) or none. Supplying a partial adapter
|
|
89
|
+
* is a type error.
|
|
63
90
|
*
|
|
64
91
|
* The `E` generic lives on the {@link Cache} factory and on
|
|
65
92
|
* {@link CacheConfig}: it parameterises the `key(context)` callback
|
|
@@ -80,7 +107,7 @@ export type CacheConfig<E extends object> = Adapter & {
|
|
|
80
107
|
* get: (key) => localStorage.getItem(key),
|
|
81
108
|
* set: (key, value) => localStorage.setItem(key, value),
|
|
82
109
|
* remove: (key) => localStorage.removeItem(key),
|
|
83
|
-
*
|
|
110
|
+
* keys: () => Object.keys(localStorage),
|
|
84
111
|
* });
|
|
85
112
|
*
|
|
86
113
|
* // Multi-tenant: writes go under `${accessToken}:…`.
|
|
@@ -89,7 +116,12 @@ export type CacheConfig<E extends object> = Adapter & {
|
|
|
89
116
|
* get: (key) => localStorage.getItem(key),
|
|
90
117
|
* set: (key, value) => localStorage.setItem(key, value),
|
|
91
118
|
* remove: (key) => localStorage.removeItem(key),
|
|
92
|
-
*
|
|
119
|
+
* keys: () => Object.keys(localStorage),
|
|
120
|
+
* key: ({ env }) => env.session?.accessToken ?? "",
|
|
121
|
+
* });
|
|
122
|
+
*
|
|
123
|
+
* // Env-scoped in-memory cache: no adapter, just `key`.
|
|
124
|
+
* const cache = Cache<AppEnv>({
|
|
93
125
|
* key: ({ env }) => env.session?.accessToken ?? "",
|
|
94
126
|
* });
|
|
95
127
|
*
|
|
@@ -160,11 +192,18 @@ export type Cache = {
|
|
|
160
192
|
scope(env: Env | undefined): string;
|
|
161
193
|
};
|
|
162
194
|
/**
|
|
163
|
-
* Constructs a {@link Cache} from `config`. The config object
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
195
|
+
* Constructs a {@link Cache} from `config`. The config object is a
|
|
196
|
+
* discriminated union of two shapes:
|
|
197
|
+
*
|
|
198
|
+
* - Adapter methods (`get`/`set`/`remove`/`keys`) plus an optional
|
|
199
|
+
* `key(context)`.
|
|
200
|
+
* - Just `key(context)` — no adapter methods at all, which falls
|
|
201
|
+
* back to a per-instance in-memory adapter while still scoping every
|
|
202
|
+
* slot by the live Env.
|
|
203
|
+
*
|
|
204
|
+
* Supplying a partial adapter (e.g. just `get` and `set`, or omitting
|
|
205
|
+
* `keys`) is a type error: the adapter members are an all-or-nothing
|
|
206
|
+
* group. Omit `config` entirely for a plain in-memory cache.
|
|
168
207
|
*
|
|
169
208
|
* When `key` is supplied, it runs each time the Resource layer
|
|
170
209
|
* assembles a cache key, receiving the same `{ env }` shape an
|
|
@@ -175,8 +214,8 @@ export type Cache = {
|
|
|
175
214
|
* to the loose {@link Env} record so callers that don't scope by
|
|
176
215
|
* env can keep using `Cache({ ...adapter })` without supplying a
|
|
177
216
|
* generic.
|
|
178
|
-
* @param config Optional
|
|
179
|
-
*
|
|
180
|
-
*
|
|
217
|
+
* @param config Optional config literal. Omit for an in-memory cache;
|
|
218
|
+
* supply adapter methods for a persisted cache; supply `key` to
|
|
219
|
+
* scope writes by the live Env (with or without an adapter).
|
|
181
220
|
*/
|
|
182
|
-
export declare function Cache<E extends object = Env>(
|
|
221
|
+
export declare function Cache<E extends object = Env>(options?: CacheConfig<E>): Cache;
|
package/dist/cache/types.d.ts
CHANGED
|
@@ -49,20 +49,21 @@ export type Adapter = {
|
|
|
49
49
|
*/
|
|
50
50
|
readonly remove: (key: string) => void;
|
|
51
51
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
52
|
+
* Enumerator over every key the adapter currently knows about.
|
|
53
|
+
* **Strictly sync.** Required — partial-match evictions
|
|
54
|
+
* (`resource(...).evict(where)` and `nuke(where)`) sweep these keys
|
|
55
|
+
* in the current tick, so an adapter that can't enumerate cannot
|
|
56
|
+
* support eviction. `localStorage` exposes this via
|
|
57
|
+
* `Object.keys(localStorage)`; MMKV via `getAllKeys()`. If your
|
|
58
|
+
* backend genuinely cannot enumerate (write-only audit logs, opaque
|
|
59
|
+
* SDK facades), implement `keys` as `() => []` to declare that
|
|
60
|
+
* choice explicitly and accept the no-op eviction behaviour.
|
|
61
|
+
*
|
|
62
|
+
* The Cache layer prepends every key it persists with `mh:`, so
|
|
63
|
+
* `keys` may return entries outside that namespace (e.g. other
|
|
64
|
+
* `localStorage` writes on the same origin). The wrapper filters
|
|
65
|
+
* before reading, removing, or clearing, so non-`mh:` keys are
|
|
66
|
+
* always safe — the adapter need not narrow its enumeration.
|
|
58
67
|
*/
|
|
59
|
-
readonly
|
|
60
|
-
/**
|
|
61
|
-
* Optional enumerator over every key the adapter currently knows
|
|
62
|
-
* about. **Strictly sync** when implemented — partial-match
|
|
63
|
-
* evictions sweep these keys in the current tick. `localStorage`
|
|
64
|
-
* exposes this via `Object.keys(localStorage)`; MMKV via
|
|
65
|
-
* `getAllKeys()`.
|
|
66
|
-
*/
|
|
67
|
-
readonly keys?: () => Iterable<string>;
|
|
68
|
+
readonly keys: () => Iterable<string>;
|
|
68
69
|
};
|
package/dist/march-hare.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import{jsx as e,jsxs as t}from"react/jsx-runtime";import*as n from"react";import{G as r,A as o}from"@mobily/ts-belt";import{immerable as s,enablePatches as c,Immer as i,produce as a}from"immer";function u(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var l,f={exports:{}};const d=/* @__PURE__ */u((l||(l=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 i=new o(r,s||e,c),a=n?n+t:t;return e._events[a]?e._events[a].fn?e._events[a]=[e._events[a],i]:e._events[a].push(i):(e._events[a]=i,e._eventsCount++),e}function c(e,t){0===--e._eventsCount?e._events=new r:delete e._events[t]}function i(){this._events=new r,this._eventsCount=0}Object.create&&(r.prototype=/* @__PURE__ */Object.create(null),(new r).__proto__||(n=!1)),i.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},i.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},i.prototype.listenerCount=function(e){var t=this._events[n?n+e:e];return t?t.fn?1:t.length:0},i.prototype.emit=function(e,t,r,o,s,c){var i=n?n+e:e;if(!this._events[i])return!1;var a,u,l=this._events[i],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,a=new Array(f-1);u<f;u++)a[u-1]=arguments[u];l.fn.apply(l.context,a)}else{var d,
|
|
2
|
-
return e(
|
|
3
|
-
return e(
|
|
1
|
+
import{jsx as e,jsxs as t}from"react/jsx-runtime";import*as n from"react";import{G as r,A as o}from"@mobily/ts-belt";import{immerable as s,enablePatches as c,Immer as i,produce as a}from"immer";function u(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var l,f={exports:{}};const d=/* @__PURE__ */u((l||(l=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 i=new o(r,s||e,c),a=n?n+t:t;return e._events[a]?e._events[a].fn?e._events[a]=[e._events[a],i]:e._events[a].push(i):(e._events[a]=i,e._eventsCount++),e}function c(e,t){0===--e._eventsCount?e._events=new r:delete e._events[t]}function i(){this._events=new r,this._eventsCount=0}Object.create&&(r.prototype=/* @__PURE__ */Object.create(null),(new r).__proto__||(n=!1)),i.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},i.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},i.prototype.listenerCount=function(e){var t=this._events[n?n+e:e];return t?t.fn?1:t.length:0},i.prototype.emit=function(e,t,r,o,s,c){var i=n?n+e:e;if(!this._events[i])return!1;var a,u,l=this._events[i],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,a=new Array(f-1);u<f;u++)a[u-1]=arguments[u];l.fn.apply(l.context,a)}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(!a)for(d=1,a=new Array(f-1);d<f;d++)a[d-1]=arguments[d];l[u].fn.apply(l[u].context,a)}}return!0},i.prototype.on=function(e,t,n){return s(this,e,t,n,!1)},i.prototype.once=function(e,t,n){return s(this,e,t,n,!0)},i.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 i=this._events[s];if(i.fn)i.fn!==t||o&&!i.once||r&&i.context!==r||c(this,s);else{for(var a=0,u=[],l=i.length;a<l;a++)(i[a].fn!==t||o&&!i[a].once||r&&i[a].context!==r)&&u.push(i[a]);u.length?this._events[s]=1===u.length?u[0]:u:c(this,s)}return this},i.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},i.prototype.off=i.prototype.removeListener,i.prototype.addListener=i.prototype.on,i.prefixed=n,i.EventEmitter=i,e.exports=i}(f)),f.exports));class h extends d{cache=/* @__PURE__ */new Map;latest=/* @__PURE__ */new Map;emit(e,...t){return this.setCache(e,t[0],void 0),super.emit(e,...t)}setCache(e,t,n){const r=this.cache.get(e)??/* @__PURE__ */new Map,o={channel:n,value:t};r.set(function(e){return void 0===e?"":JSON.stringify(e)}(n),o),this.cache.set(e,r),this.latest.set(e,o)}getCached(e){return this.latest.get(e)?.value}getCachedAll(e){return this.cache.get(e)?.values()??[]}fire(e,...t){return super.emit(e,...t)}}const p=n.createContext(new h);function m(){return n.useContext(p)}function y({children:t}){const r=n.useMemo(()=>new h,[]);/* @__PURE__ */
|
|
2
|
+
return e(p.Provider,{value:r,children:t})}const v=n.createContext(/* @__PURE__ */new Set);function b({children:t}){const r=n.useMemo(()=>/* @__PURE__ */new Set,[]);/* @__PURE__ */
|
|
3
|
+
return e(v.Provider,{value:r,children:t})}const g=n.createContext({current:{}});function w(){const e=n.useContext(g);return n.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.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 O=(e="")=>`march-hare.action/${e}`,P=(e="")=>`march-hare.action/broadcast/${e}`,j=(e="")=>`march-hare.action/multicast/${e}`,S=(e="")=>`march-hare.action.lifecycle/${e}`;class E{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");static Lifecycle=/* @__PURE__ */Symbol("march-hare.brand/Lifecycle")}function x(e){const t=/* @__PURE__ */Symbol(`march-hare.action.lifecycle/${e}`),n=function(n){return{[E.Action]:t,[E.Payload]:void 0,[E.Channel]:n,[E.Name]:e,channel:n}};return Object.defineProperty(n,E.Action,{value:t,enumerable:!1}),Object.defineProperty(n,E.Payload,{value:void 0,enumerable:!1}),Object.defineProperty(n,E.Name,{value:e,enumerable:!1}),Object.defineProperty(n,E.Lifecycle,{value:e,enumerable:!1}),n}const C=Symbol(P("Fault")),M=Symbol(P("Env"));class k{static Mount(){return x("Mount")}static Paint(){return x("Paint")}static Unmount(){return x("Unmount")}static Error(){return x("Error")}static Update(){return x("Update")}static Fault=(()=>{const e={};return Object.defineProperty(e,E.Action,{value:C,enumerable:!1}),Object.defineProperty(e,E.Payload,{value:void 0,enumerable:!1}),Object.defineProperty(e,E.Broadcast,{value:!0,enumerable:!1}),Object.defineProperty(e,E.Name,{value:"Fault",enumerable:!1}),e})();static Env=(()=>{const e={};return Object.defineProperty(e,E.Action,{value:M,enumerable:!1}),Object.defineProperty(e,E.Payload,{value:void 0,enumerable:!1}),Object.defineProperty(e,E.Broadcast,{value:!0,enumerable:!1}),Object.defineProperty(e,E.Name,{value:"Env",enumerable:!1}),e})()}var N=/* @__PURE__ */(e=>(e.Unicast="unicast",e.Broadcast="broadcast",e.Multicast="multicast",e))(N||{}),A=/* @__PURE__ */(e=>(e.Mounting="mounting",e.Mounted="mounted",e.Unmounting="unmounting",e.Unmounted="unmounted",e))(A||{});function _({initial:t,children:o}){const s=n.useRef(t),c=m();return r.isUndefined(c.getCached(M))&&c.setCache(M,s.current,void 0),/* @__PURE__ */e(g.Provider,{value:s,children:o})}const R=n.createContext(/* @__PURE__ */new WeakMap);function U({children:t}){const r=n.useMemo(()=>/* @__PURE__ */new WeakMap,[]);/* @__PURE__ */
|
|
4
4
|
return e(R.Provider,{value:r,children:t})}const L=n.createContext(()=>{});function W({tap:t,children:r}){const o=n.useRef(t);n.useLayoutEffect(()=>{o.current=t},[t]);const s=n.useMemo(()=>e=>o.current?.(e),[]);/* @__PURE__ */
|
|
5
5
|
return e(L.Provider,{value:s,children:r})}const B=n.createContext(/* @__PURE__ */new Map);function $({env:t,tap:r,children:o}){const s=n.useMemo(()=>/* @__PURE__ */new Map,[]);/* @__PURE__ */
|
|
6
|
-
return e(y,{children:/* @__PURE__ */e(B.Provider,{value:s,children:/* @__PURE__ */e(_,{initial:t??{},children:/* @__PURE__ */e(v,{children:/* @__PURE__ */e(W,{tap:r,children:/* @__PURE__ */e(U,{children:o})})})})})})}const T=e=>"symbol"==typeof e;function F(e){return r.isString(e)||T(e)?e:(r.isObject(e)||r.isFunction(e))&&E.Action in e?e[E.Action]:e}function z(e){if(r.isString(e))return e.startsWith(O());if(T(e))return e.description?.startsWith(O())??!1;if(r.isObject(e)||r.isFunction(e)){if(E.Broadcast in e&&e[E.Broadcast])return!0;if(E.Action in e){const t=e[E.Action];return t.description?.startsWith(O())??!1}}return!1}function D(e){return r.isObject(e)&&E.Channel in e&&"channel"in e}function H(e){const t=F(e),n=T(t)?t.description??"":t;return n.startsWith(S())&&n.slice(S().length)||null}function I(e){if(r.isString(e))return e.startsWith(j());if(T(e))return e.description?.startsWith(j())??!1;if(r.isObject(e)||r.isFunction(e)){if(E.Multicast in e&&e[E.Multicast])return!0;if(E.Action in e){const t=e[E.Action];return t.description?.startsWith(j())??!1}}return!1}const J=(e="",t=M.Unicast)=>{const n=t===M.Broadcast?Symbol(O(e)):t===M.Multicast?Symbol(j(e)):Symbol(P(e)),r=function(t){return{[E.Action]:n,[E.Payload]:void 0,[E.Channel]:t,[E.Name]:e,channel:t}};return Object.defineProperty(r,E.Action,{value:n,enumerable:!1}),Object.defineProperty(r,E.Payload,{value:void 0,enumerable:!1}),Object.defineProperty(r,E.Name,{value:e,enumerable:!1}),t===M.Broadcast&&Object.defineProperty(r,E.Broadcast,{value:!0,enumerable:!1}),t===M.Multicast&&Object.defineProperty(r,E.Multicast,{value:!0,enumerable:!1}),r},G=Symbol(((e="")=>`march-hare/replay${e}`)());function K(e,t,...n){e instanceof p&&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 V(e,t){for(const n of e.keys())if(H(n)===t)return n;return null}const q=/* @__PURE__ */Symbol("march-hare.unset");function Q(){const[,e]=n.useReducer(e=>e+1,0);return e}function X(){return{data:q,at:null}}function Y(e,t){return{data:e,at:t}}var Z=/* @__PURE__ */(e=>(e[e.Aborted=0]="Aborted",e[e.Errored=1]="Errored",e))(Z||{});class ee extends Error{name="AbortError";constructor(e="Aborted"){super(e)}}const te=n.createContext(null);function ne(e){const t=r.isUndefined(e)?function(){const e=/* @__PURE__ */new Map;return{get:t=>e.get(t)??null,set:(t,n)=>{e.set(t,n)},remove:t=>{e.delete(t)},clear:()=>{e.clear()},keys:()=>e.keys()}}():e,n=e?.key;return{get(e){try{const n=t.get(e);if(r.isNull(n))return X();const o=JSON.parse(n);return Y(o.data,Temporal.Instant.from(o.at))}catch{return X()}},set(e,n){if(n.data!==q&&!r.isNull(n.at))try{t.set(e,JSON.stringify({data:n.data,at:n.at.toString()}))}catch{return}},remove(e){try{t.remove(e)}catch{return}},clear(){try{t.clear()}catch{return}},keys(){try{return t.keys?.()??[]}catch{return[]}},scope(e){if(r.isUndefined(n)||r.isNullable(e))return"";try{const t=n({env:e});return r.isNullable(t)?"":t}catch{return""}}}}const re=/* @__PURE__ */new WeakMap,oe=/* @__PURE__ */new Map,se=[];function ce(e,t,n,o){const s=r.isNull(n)?"":`${n}:`,c=(e,n)=>{const r=t.scope(e);return`${""===r?"":`${r}:`}${s}${function(e){return JSON.stringify(e)}(n)}`},i=(e,n)=>{const o=t.get(c(n,e));return o.data===q||r.isNull(o.at)?{data:q,at:null}:{data:o.data,at:o.at}},a=(n,r,o,s)=>e({env:n,controller:r,params:o,dispatch:s}).then(e=>(t.set(c(n,o),Y(e,Temporal.Now.instant())),e)),u=e=>{const n=o(),r=t.scope(n),c=""===r?s:`${r}:${s}`,i=Object.entries(e);for(const o of[...t.keys()])if(o.startsWith(c))try{const e=JSON.parse(o.slice(c.length));i.every(([t,n])=>e[t]===n)&&t.remove(o)}catch{continue}};function l(e){return{run:a,read:e=>i(e,o()),evict:u,params:e??{}}}return se.push(u),Object.defineProperty(l,"get",{value:function(e){const{data:t}=i(e??{},o());return t===q?null:t},enumerable:!1}),l}function ie(e,t,n){const o=e,s=n??(()=>{});return r.isUndefined(t)?ce(o,function(e){const t=re.get(e);if(r.isNotNullable(t))return t;const n=ne();return re.set(e,n),n}(o),null,s):ce(o,t,function(e){const t=oe.get(e);if(r.isNotNullable(t))return t;const n=String(oe.size);return oe.set(e,n),n}(o),s)}let ae=(e=21)=>{let t="",n=crypto.getRandomValues(new Uint8Array(e|=0));for(;e--;)t+="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict"[63&n[e]];return t};var ue=/* @__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))(ue||{}),le=/* @__PURE__ */(e=>(e[e.Produce=0]="Produce",e[e.Hydrate=1]="Hydrate",e))(le||{}),fe=/* @__PURE__ */(e=>(e.Property="property",e.Process="process",e.Value="value",e.Operation="operation",e))(fe||{});class de{[s]=!0;static keys=new Set(Object.values(fe));property=null;process=null;value;operation;constructor(e,t){this.value=e,this.operation=t}assign(e,t){const n=new de(this.value,this.operation);return n.property=e,n.process=t,n}}class pe{static immer=(()=>{c();const e=new i;return e.setAutoFreeze(!1),e})();static tag="κ";static id=ae}const he=/* @__PURE__ */Symbol("Box");function me(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 ye(e){if(r.isNullable(e)||ge(e))return e;if(r.isArray(e))return e.map(e=>ye(e));if(r.isObject(e)&&ve(e)){const t=Object.entries(e).map(([e,t])=>[e,ye(t)]);return{...Object.fromEntries(t),[pe.tag]:e[pe.tag]??pe.id()}}return e}function be(e){if(Array.isArray(e))return e.filter(e=>pe.tag in e).map(e=>e[pe.tag]??"").join(",");const t=e[pe.tag];if(t)return t;try{return JSON.stringify(e)}catch{return`[unserializable:${typeof e}]`}}function ve(e){const t=Object.getPrototypeOf(e);return t===Object.prototype||null===t}function ge(e){return r.isNullable(e)||r.isString(e)||r.isNumber(e)||r.isBoolean(e)||"symbol"==typeof e||"bigint"==typeof e}function we(e){return r.isObject(e)&&he in e}function Pe(e,t,n,o,s,c){return function i(a,u=t.path){if(a instanceof de){const t=me(n,u.join("."));if(Object.entries(a).filter(([e,t])=>!de.keys.has(e)&&t instanceof de).forEach(([e,t])=>i(t,u.concat(e))),ge(a.value)){if(e===le.Hydrate)return a.value;const i=u.slice(0,-1),l=i.length>0?me(n,i.join(".")):n;return r.isNullable(l)||Oe(l,a,u.at(-1),o,s,c),t??a.value}if(e===le.Hydrate){const e=ye(i(a.value,u));return Oe(e,a,null,o,s,c),e}const l=t??ye(a.value);return Oe(l,a,null,o,s,c),r.isNullable(t)?l:(i(a.value,u),t)}if(r.isArray(a))return a.map((e,t)=>i(e,u.concat(t)));if(r.isObject(a)&&!ve(a))return a;if(r.isObject(a)){const t=Object.entries(a).map(([e,t])=>[e,i(t,u.concat(e))]),n=Object.fromEntries(t);if(e===le.Hydrate){const e=ye(n);return Object.entries(a).forEach(([t,n])=>{n instanceof de&&ge(n.value)&&Oe(e,n,t,o,s,c)}),e}return n}return a}(t.value)}function Oe(e,t,n,r,o,s){const c=s(e),i=o.get(c)??[];o.set(c,[t.assign(n,r),...i])}class je{#e={};#t;#n=/* @__PURE__ */new Map;#r=/* @__PURE__ */new Set;#o=!1;constructor(e=be){this.#t=e}static pk(){return ae()}static"κ"=je.pk;annotate(e,t){return new de(t,e)}"δ"=this.annotate;get model(){return this.#e}get inspect(){return function(e,t,n,s,c){function i(s){const c=s.at(-1),i=me(e(),s),a=s.slice(0,-1),u=o.isNotEmpty(a)?me(e(),a):e();return[...r.isObject(i)||r.isArray(i)?t.get(n(i))?.filter(e=>r.isNullable(e.property))??[]:[],...r.isObject(u)?t.get(n(u))?.filter(e=>e.property===c)??[]:[]]}return function t(n){return new Proxy(()=>{},{get:(r,a)=>"pending"===a?()=>!o.isEmpty(i(n)):"remaining"===a?()=>o.length(i(n)):"box"===a?()=>({value:me(e(),n),inspect:t(n),[he]:!0}):"is"===a?e=>i(n).some(t=>0!==(t.operation&e)):"draft"===a?()=>o.head(i(n))?.value??me(e(),n):"settled"===a?()=>new Promise(t=>{if(o.isEmpty(i(n)))return t(me(e(),n));const r=()=>{o.isEmpty(i(n))&&(c(r),t(me(e(),n)))};s(r)}):t([...n,String(a)])})}([])}(()=>this.#e,this.#n,this.#t,e=>this.#r.add(e),e=>this.#r.delete(e))}hydrate(e){return this.#o=!0,this.#s(le.Hydrate,()=>e)}produce(e){if(!this.#o)throw new Error("State must be hydrated using hydrate() before calling produce()");return this.#s(le.Produce,e)}#s(e,t){const n=/* @__PURE__ */Symbol("process"),[,r]=pe.immer.produceWithPatches(this.#e,t);return this.#e=r.reduce((t,r)=>pe.immer.applyPatches(t,[{...r,value:Pe(e,r,t,n,this.#n,this.#t)}]),this.#e),this.#e=ye(this.#e),this.#c(),n}prune(e){this.#n.forEach((t,n)=>{const r=t.filter(t=>t.process!==e);o.isEmpty(r)?this.#n.delete(n):this.#n.set(n,r)}),this.#c()}#c(){this.#r.forEach(e=>e())}observe(e){const t=()=>e(this.#e);return this.#r.add(t),()=>this.#r.delete(t)}}function Se({action:e,renderer:t}){const o=m(),s=n.useContext(B),c=Q(),i=n.useMemo(()=>{const t=s.get(e);if(t)return t;const n=new je,c=o.getCached(e);r.isNotNullable(c)&&n.hydrate({value:c});const i={state:n,listeners:/* @__PURE__ */new Set};return s.set(e,i),i},[e,o,s]);n.useLayoutEffect(()=>{function t(e){i.state.hydrate({value:e}),i.listeners.forEach(e=>e())}return i.listeners.add(c),o.on(e,t),()=>{i.listeners.delete(c),o.off(e,t)}},[e,o,i]);const a=i.state.model?.value;return r.isNullable(a)?null:t(a,i.state.inspect.value)}function Ee(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 xe(e,t,n){const{cursor:r,key:o}=Ee(e,t);r[o]=n}function Ce(e){return(t,n)=>{t.actions.produce(t=>{xe(t.model,e,n)})}}function Ne(e){return t=>{t.actions.produce(t=>{!function(e,t){const{cursor:n,key:r}=Ee(e,t);n[r]=!n[r]}(t.model,e)})}}function ke(e,t){return n=>{n.actions.produce(n=>{xe(n.model,e,t)})}}function Me(){const e=n.useRef(null);return n.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 s=function(...e){const t=r.isUndefined(e[0])||r.isFunction(e[0])?{}:e[0],s=r.isFunction(e[0])?e[0]:e[1]??(()=>({})),c=m(),i=n.useContext(te),u=n.useContext(b),l=w(),f=n.useContext(g),p=n.useContext(R),h=n.useContext(L),y=Q(),v=n.useRef(!1),O=n.useRef(null),j=n.useRef(new je);v.current||(v.current=!0,O.current=j.current.hydrate(t));const[S,E]=n.useState(()=>j.current.model),x=function(e){const t=n.useRef(e);return t.current=e,n.useMemo(()=>{return n=t,Object.keys(e).reduce((e,t)=>(Object.defineProperty(e,t,{get:()=>n.current[t],enumerable:!0}),e),{});var n},[e])}(s()),k=n.useMemo(()=>new d,[]),M=n.useRef({handlers:/* @__PURE__ */new Map});M.current.handlers=/* @__PURE__ */new Map;const _=function(){const e=n.useRef(/* @__PURE__ */new Set),t=n.useRef(/* @__PURE__ */new Set);return n.useMemo(()=>({broadcast:e.current,multicast:t.current}),[])}(),U=n.useRef(A.Mounting),W=n.useRef(/* @__PURE__ */new Set),B=n.useRef(0),$=n.useCallback((e,t,n)=>{const o=new AbortController,s={controller:o,action:e,payload:t};return u.add(s),W.current.add(s),{model:j.current.model,get phase(){return U.current},task:s,data:x,tasks:u,env:l,actions:{produce(e){if(o.signal.aborted)return;const t=f.current,r=j.current.produce(t=>{f.current=a(f.current,n=>{e({model:t,inspect:j.current.inspect,env:n})})});E(j.current.model),f.current!==t&&c.emit(N,f.current),n.processes.add(r),O.current&&(n.processes.add(O.current),O.current=null)},dispatch(e,t){if(o.signal.aborted)return Promise.resolve();const n=F(e),r=D(e)?e.channel:void 0;return I(e)?i?K(i.emitter,n,t,r):Promise.resolve():K(z(e)?c:k,n,t,r)},annotate:(e,t=ue.Update)=>j.current.annotate(t,e),get inspect(){return j.current.inspect},resource:Object.assign(function(e){const t=(e,t)=>{if(o.signal.aborted)return Promise.resolve();const n=e,r=F(n);return I(n)?i?K(i.emitter,r,t,void 0):Promise.resolve():z(n)?K(c,r,t,void 0):Promise.resolve()},n={exceedsWindow:null,isolated:!1},s={then:(s,c)=>(()=>{if(r.isNotNullable(n.exceedsWindow)){const{data:t,at:o}=e.read(e.params);if(t!==q&&r.isNotNullable(o)){const e=Temporal.Now.instant().since(o),r=Temporal.Duration.from(n.exceedsWindow);if(Temporal.Duration.compare(e,r)<=0)return Promise.resolve(t)}}if(n.isolated)return e.run(l,o,e.params,t);let s=p.get(e.run);r.isUndefined(s)&&(s=/* @__PURE__ */new Map,p.set(e.run,s));const c=s,i=JSON.stringify(e.params);let a=c.get(i);if(r.isUndefined(a)){const n=new AbortController,r={controller:n,refs:0};r.promise=e.run(l,n,e.params,t).finally(()=>{c.delete(i)}),c.set(i,r),a=r}const u=a;u.refs+=1;const f=()=>{u.refs-=1,0===u.refs&&(c.delete(i),u.controller.abort(o.signal.reason))};if(o.signal.aborted)f();else{o.signal.addEventListener("abort",f,{once:!0});const e=()=>o.signal.removeEventListener("abort",f);u.promise.then(e,e)}return d=u.promise,h=o.signal,new Promise((e,t)=>{if(h.aborted)return void t(h.reason);const n=()=>t(h.reason);h.addEventListener("abort",n,{once:!0}),d.then(t=>{h.removeEventListener("abort",n),e(t)},e=>{h.removeEventListener("abort",n),t(e)})});var d,h})().then(s,c),exceeds:e=>(n.exceedsWindow=e,s),isolated:()=>(n.isolated=!0,s),evict(t){e.evict(t??e.params)}};return s},{nuke:e=>function(e){const t=e??{};for(const n of se)n(t)}(e)}),async final(e){if(o.signal.aborted)return null;const t=F(e),n=I(e)?i?.emitter??null:c;if(!n)return null;const s=n.getCached(t);if(r.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()})}),n.getCached(t)??null},peek(e){if(o.signal.aborted)return null;const t=F(e),n=I(e)?i?.emitter??null:c;return n?n.getCached(t)??null:null}}}},[S]);n.useLayoutEffect(()=>{function e(e,t,n){return function(o,s){const a=n();if(s===G&&r.isNotNullable(a))return;if(r.isNotNullable(s)&&s!==G&&r.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 l={processes:/* @__PURE__ */new Set},d=Promise.withResolvers(),p=$(e,o,l),m=function(e){const t=F(e),n=r.isString(t)?t:t.description??"";return n.startsWith(P())&&n.slice(n.lastIndexOf("/")+1)||"unknown"}(e),b=performance.now(),v=j.current.model,g=f.current;let w,O=!1;function S(){const e=j.current.model,t=f.current;return{model:v===e?null:{before:v,after:e},env:g===t?null:{before:g,after:t}}}function E(){const t=s===G?void 0:s;return I(e)?i?K(i.emitter,e,o,t):Promise.resolve():K(z(e)?c:k,e,o,t)}function x(e){O=!0;const t=V(M.current.handlers,"Error"),n=r.isNotNullable(t),s=function(e){return e instanceof Error&&"AbortError"===e.name?Z.Aborted:Z.Errored}(e),i=function(e){return e instanceof Error?e:new Error(String(e))}(e),a={reason:s,error:i,action:m,handled:n,tasks:u,retry:E};c.fire(C,a),n&&t&&k.emit(t,a),h({stage:"end",result:"error",action:{name:m,payload:o},details:{task:p.task,elapsed:performance.now()-b,mutations:S(),error:i,reason:s}})}function N(){for(const e of u)if(e===p.task){u.delete(e),W.current.delete(e);break}l.processes.forEach(e=>j.current.prune(e)),l.processes.size>0&&y(),O||h({stage:"end",result:"success",action:{name:m,payload:o},details:{task:p.task,elapsed:performance.now()-b,mutations:S()}}),d.resolve()}h({stage:"start",action:{name:m,payload:o},details:{task:p.task}});try{w=t(p,o)}catch(A){return x(A),N(),d.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}(w)?((async()=>{for await(const e of w);})().catch(x).finally(N),d.promise):(Promise.resolve(w).catch(x).finally(N),d.promise)}}B.current++;const t=/* @__PURE__ */new Set;return M.current.handlers.forEach((n,r)=>{for(const{getChannel:o,handler:s}of n){const n=e(r,s,o);if(I(r)){if(i){const e=i.emitter;e.on(r,n),t.add(()=>e.off(r,n))}k.on(r,n),_.multicast.add(r),t.add(()=>k.off(r,n))}else z(r)?(c.on(r,n),k.on(r,n),_.broadcast.add(r),t.add(()=>{c.off(r,n),k.off(r,n)})):(k.on(r,n),t.add(()=>k.off(r,n)))}}),()=>{const e=++B.current,n=new Set(t);queueMicrotask(()=>{if(B.current!==e){for(const e of n)e();return}for(const e of W.current)e.controller.abort(new ee("Component unmounted")),u.delete(e);W.current.clear(),U.current=A.Unmounting;const t=V(M.current.handlers,"Unmount");t&&k.emit(t),U.current=A.Unmounted;for(const e of n)e()})}},[k]),function({unicast:e,broadcast:t,dispatchers:s,scope:c,phase:i,data:a,handlers:u}){const l=n.useRef(null),f=n.useRef(!1);n.useLayoutEffect(()=>{if(i.current===A.Mounted)return;i.current=A.Mounting;const n=V(u,"Mount");n&&e.emit(n),s.broadcast.forEach(n=>{const o=t.getCached(n);r.isNullable(o)||e.emit(n,o,G)}),c&&s.multicast.forEach(t=>{const n=c.emitter.getCached(t);r.isNullable(n)||e.emit(t,n,G)}),i.current=A.Mounted,f.current=!1},[]),n.useEffect(()=>{if(f.current)return;f.current=!0;const t=V(u,"Paint");t&&e.emit(t)},[]),n.useLayoutEffect(()=>{if(r.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,a);if(o.isNotEmpty(Object.keys(t))){const n=V(u,"Update");n&&e.emit(n,t)}}l.current=a},[a,e])}({unicast:k,broadcast:c,dispatchers:_,scope:i,phase:U,data:s(),handlers:M.current.handlers});const T=n.useMemo(()=>({dispatch(e,t){const n=F(e),r=D(e)?e.channel:void 0;return I(e)?i?K(i.emitter,n,t,r):Promise.resolve():K(z(e)?c:k,n,t,r)},get inspect(){return j.current.inspect},stream:(e,t)=>n.createElement(Se,{action:F(e),renderer:t})}),[S,k]),H=n.useMemo(()=>[S,T,x],[S,T,x]);return H.useAction=(e,t)=>{!function(e,t,r){const o=n.useRef(r);n.useLayoutEffect(()=>{o.current=r});const s=n.useRef(t);n.useLayoutEffect(()=>{s.current=t});const c=n.useCallback((e,t)=>o.current(e,t),[]),i=n.useCallback(()=>D(s.current)?s.current.channel:void 0,[]),a=F(t),u=e.current.handlers.get(a)??/* @__PURE__ */new Set;0===u.size&&e.current.handlers.set(a,u),u.add({getChannel:i,handler:c})}(M,e,t)},H.dispatch=H[1].dispatch,H}(...t);return e.current=s.dispatch,s},with:{update:e=>Ce(e),invert:e=>Ne(e),always:(e,t)=>ke(e,t)}}),[])}function Ae(t,r){return{Boundary:function({children:t}){const r=n.useMemo(()=>({id:/* @__PURE__ */Symbol("march-hare.scope/instance"),emitter:new p}),[]);/* @__PURE__ */
|
|
7
|
-
return e(te.Provider,{value:r,children:t})},useContext:function(){return
|
|
6
|
+
return e(y,{children:/* @__PURE__ */e(B.Provider,{value:s,children:/* @__PURE__ */e(_,{initial:t??{},children:/* @__PURE__ */e(b,{children:/* @__PURE__ */e(W,{tap:r,children:/* @__PURE__ */e(U,{children:o})})})})})})}const T=e=>"symbol"==typeof e;function F(e){return r.isString(e)||T(e)?e:(r.isObject(e)||r.isFunction(e))&&E.Action in e?e[E.Action]:e}function z(e){if(r.isString(e))return e.startsWith(P());if(T(e))return e.description?.startsWith(P())??!1;if(r.isObject(e)||r.isFunction(e)){if(E.Broadcast in e&&e[E.Broadcast])return!0;if(E.Action in e){const t=e[E.Action];return t.description?.startsWith(P())??!1}}return!1}function J(e){return r.isObject(e)&&E.Channel in e&&"channel"in e}function D(e){const t=F(e),n=T(t)?t.description??"":t;return n.startsWith(S())&&n.slice(S().length)||null}function H(e){if(r.isString(e))return e.startsWith(j());if(T(e))return e.description?.startsWith(j())??!1;if(r.isObject(e)||r.isFunction(e)){if(E.Multicast in e&&e[E.Multicast])return!0;if(E.Action in e){const t=e[E.Action];return t.description?.startsWith(j())??!1}}return!1}const I=(e="",t=N.Unicast)=>{const n=t===N.Broadcast?Symbol(P(e)):t===N.Multicast?Symbol(j(e)):Symbol(O(e)),r=function(t){return{[E.Action]:n,[E.Payload]:void 0,[E.Channel]:t,[E.Name]:e,channel:t}};return Object.defineProperty(r,E.Action,{value:n,enumerable:!1}),Object.defineProperty(r,E.Payload,{value:void 0,enumerable:!1}),Object.defineProperty(r,E.Name,{value:e,enumerable:!1}),t===N.Broadcast&&Object.defineProperty(r,E.Broadcast,{value:!0,enumerable:!1}),t===N.Multicast&&Object.defineProperty(r,E.Multicast,{value:!0,enumerable:!1}),r};function G(e,t,...n){e instanceof h&&e.setCache(t,n[0],n[1]);const r=e.listeners(t);return 0===r.length?Promise.resolve():Promise.all(r.map(e=>Promise.resolve(e(...n)))).then(()=>{})}function K(e,t){for(const n of e.keys())if(D(n)===t)return n;return null}const V=/* @__PURE__ */Symbol("march-hare.unset"),q="mh:";function Q(){const[,e]=n.useReducer(e=>e+1,0);return e}function X(){return{data:V,at:null}}function Y(e,t){return{data:e,at:t}}var Z=/* @__PURE__ */(e=>(e[e.Aborted=0]="Aborted",e[e.Errored=1]="Errored",e))(Z||{});class ee extends Error{name="AbortError";constructor(e="Aborted"){super(e)}}const te=n.createContext(null);function ne(e){const t=function(e){return!r.isUndefined(e)&&"get"in e&&"function"==typeof e.get}(e)?e:function(){const e=/* @__PURE__ */new Map;return{get:t=>e.get(t)??null,set:(t,n)=>{e.set(t,n)},remove:t=>{e.delete(t)},keys:()=>e.keys()}}(),n=e?.key;function o(){try{return Array.from(t.keys()).reduce((e,t)=>t.startsWith(q)?[...e,t]:e,[])}catch{return[]}}return{get(e){try{const n=t.get(q+e);if(r.isNull(n))return X();const o=JSON.parse(n);return Y(o.data,Temporal.Instant.from(o.at))}catch{return X()}},set(e,n){if(n.data!==V&&!r.isNull(n.at))try{t.set(q+e,JSON.stringify({data:n.data,at:n.at.toString()}))}catch{return}},remove(e){try{t.remove(q+e)}catch{return}},clear(){for(const e of o())try{t.remove(e)}catch{continue}},*keys(){for(const e of o())yield e.slice(3)},scope(e){if(r.isUndefined(n)||r.isNullable(e))return"";try{const t=n({env:e});return r.isNullable(t)?"":t}catch{return""}}}}const re=/* @__PURE__ */new WeakMap,oe=/* @__PURE__ */new Map,se=[];function ce(e,t,n,o){const s=r.isNull(n)?"":`${n}:`,c=(e,n)=>{const r=t.scope(e);return`${""===r?"":`${r}:`}${s}${function(e){return JSON.stringify(e)}(n)}`},i=I(`resource:${e.name||"anonymous"}`,N.Broadcast),a=(e,n)=>{const o=t.get(c(n,e));return o.data===V||r.isNull(o.at)?{data:V,at:null}:{data:o.data,at:o.at}},u=(n,r,o,s)=>e({env:n,controller:r,params:o,dispatch:s}).then(e=>(t.set(c(n,o),Y(e,Temporal.Now.instant())),s(i(o),e),e)),l=e=>{const n=o(),r=t.scope(n),c=""===r?s:`${r}:${s}`,i=Object.entries(e);for(const o of[...t.keys()])if(o.startsWith(c))try{const e=JSON.parse(o.slice(c.length));i.every(([t,n])=>e[t]===n)&&t.remove(o)}catch{continue}};function f(e){return{run:u,read:e=>a(e,o()),evict:l,params:e??{}}}return se.push(l),Object.defineProperty(f,"get",{value:function(e){const{data:t}=a(e??{},o());return t===V?null:t},enumerable:!1}),Object.defineProperty(f,"action",{value:function(e){return i(e??{})},enumerable:!1}),f}function ie(e,t,n){const o=e,s=n??(()=>{});return r.isUndefined(t)?ce(o,function(e){const t=re.get(e);if(r.isNotNullable(t))return t;const n=ne();return re.set(e,n),n}(o),null,s):ce(o,t,function(e){const t=oe.get(e);if(r.isNotNullable(t))return t;const n=String(oe.size);return oe.set(e,n),n}(o),s)}let ae=(e=21)=>{let t="",n=crypto.getRandomValues(new Uint8Array(e|=0));for(;e--;)t+="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict"[63&n[e]];return t};var ue=/* @__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))(ue||{}),le=/* @__PURE__ */(e=>(e[e.Produce=0]="Produce",e[e.Hydrate=1]="Hydrate",e))(le||{}),fe=/* @__PURE__ */(e=>(e.Property="property",e.Process="process",e.Value="value",e.Operation="operation",e))(fe||{});class de{[s]=!0;static keys=new Set(Object.values(fe));property=null;process=null;value;operation;constructor(e,t){this.value=e,this.operation=t}assign(e,t){const n=new de(this.value,this.operation);return n.property=e,n.process=t,n}}class he{static immer=(()=>{c();const e=new i;return e.setAutoFreeze(!1),e})();static tag="κ";static id=ae}const pe=/* @__PURE__ */Symbol("Box");function me(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 ye(e){if(r.isNullable(e)||ge(e))return e;if(r.isArray(e))return e.map(e=>ye(e));if(r.isObject(e)&&be(e)){const t=Object.entries(e).map(([e,t])=>[e,ye(t)]);return{...Object.fromEntries(t),[he.tag]:e[he.tag]??he.id()}}return e}function ve(e){if(Array.isArray(e))return e.filter(e=>he.tag in e).map(e=>e[he.tag]??"").join(",");const t=e[he.tag];if(t)return t;try{return JSON.stringify(e)}catch{return`[unserializable:${typeof e}]`}}function be(e){const t=Object.getPrototypeOf(e);return t===Object.prototype||null===t}function ge(e){return r.isNullable(e)||r.isString(e)||r.isNumber(e)||r.isBoolean(e)||"symbol"==typeof e||"bigint"==typeof e}function we(e){return r.isObject(e)&&pe in e}function Oe(e,t,n,o,s,c){return function i(a,u=t.path){if(a instanceof de){const t=me(n,u.join("."));if(Object.entries(a).filter(([e,t])=>!de.keys.has(e)&&t instanceof de).forEach(([e,t])=>i(t,u.concat(e))),ge(a.value)){if(e===le.Hydrate)return a.value;const i=u.slice(0,-1),l=i.length>0?me(n,i.join(".")):n;return r.isNullable(l)||Pe(l,a,u.at(-1),o,s,c),t??a.value}if(e===le.Hydrate){const e=ye(i(a.value,u));return Pe(e,a,null,o,s,c),e}const l=t??ye(a.value);return Pe(l,a,null,o,s,c),r.isNullable(t)?l:(i(a.value,u),t)}if(r.isArray(a))return a.map((e,t)=>i(e,u.concat(t)));if(r.isObject(a)&&!be(a))return a;if(r.isObject(a)){const t=Object.entries(a).map(([e,t])=>[e,i(t,u.concat(e))]),n=Object.fromEntries(t);if(e===le.Hydrate){const e=ye(n);return Object.entries(a).forEach(([t,n])=>{n instanceof de&&ge(n.value)&&Pe(e,n,t,o,s,c)}),e}return n}return a}(t.value)}function Pe(e,t,n,r,o,s){const c=s(e),i=o.get(c)??[];o.set(c,[t.assign(n,r),...i])}class je{#e={};#t;#n=/* @__PURE__ */new Map;#r=/* @__PURE__ */new Set;#o=!1;constructor(e=ve){this.#t=e}static pk(){return ae()}static"κ"=je.pk;annotate(e,t){return new de(t,e)}"δ"=this.annotate;get model(){return this.#e}get inspect(){return function(e,t,n,s,c){function i(s){const c=s.at(-1),i=me(e(),s),a=s.slice(0,-1),u=o.isNotEmpty(a)?me(e(),a):e();return[...r.isObject(i)||r.isArray(i)?t.get(n(i))?.filter(e=>r.isNullable(e.property))??[]:[],...r.isObject(u)?t.get(n(u))?.filter(e=>e.property===c)??[]:[]]}return function t(n){return new Proxy(()=>{},{get:(r,a)=>"pending"===a?()=>!o.isEmpty(i(n)):"remaining"===a?()=>o.length(i(n)):"box"===a?()=>({value:me(e(),n),inspect:t(n),[pe]:!0}):"is"===a?e=>i(n).some(t=>0!==(t.operation&e)):"draft"===a?()=>o.head(i(n))?.value??me(e(),n):"settled"===a?()=>new Promise(t=>{if(o.isEmpty(i(n)))return t(me(e(),n));const r=()=>{o.isEmpty(i(n))&&(c(r),t(me(e(),n)))};s(r)}):t([...n,String(a)])})}([])}(()=>this.#e,this.#n,this.#t,e=>this.#r.add(e),e=>this.#r.delete(e))}hydrate(e){return this.#o=!0,this.#s(le.Hydrate,()=>e)}produce(e){if(!this.#o)throw new Error("State must be hydrated using hydrate() before calling produce()");return this.#s(le.Produce,e)}#s(e,t){const n=/* @__PURE__ */Symbol("process"),[,r]=he.immer.produceWithPatches(this.#e,t);return this.#e=r.reduce((t,r)=>he.immer.applyPatches(t,[{...r,value:Oe(e,r,t,n,this.#n,this.#t)}]),this.#e),this.#e=ye(this.#e),this.#c(),n}prune(e){this.#n.forEach((t,n)=>{const r=t.filter(t=>t.process!==e);o.isEmpty(r)?this.#n.delete(n):this.#n.set(n,r)}),this.#c()}#c(){this.#r.forEach(e=>e())}observe(e){const t=()=>e(this.#e);return this.#r.add(t),()=>this.#r.delete(t)}}function Se({action:e,renderer:t}){const o=m(),s=n.useContext(B),c=Q(),i=n.useMemo(()=>{const t=s.get(e);if(t)return t;const n=new je,c=o.getCached(e);r.isNotNullable(c)&&n.hydrate({value:c});const i={state:n,listeners:/* @__PURE__ */new Set};return s.set(e,i),i},[e,o,s]);n.useLayoutEffect(()=>{function t(e){i.state.hydrate({value:e}),i.listeners.forEach(e=>e())}return i.listeners.add(c),o.on(e,t),()=>{i.listeners.delete(c),o.off(e,t)}},[e,o,i]);const a=i.state.model?.value;return r.isNullable(a)?null:t(a,i.state.inspect.value)}function Ee(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 xe(e,t,n){const{cursor:r,key:o}=Ee(e,t);r[o]=n}function Ce(e){return(t,n)=>{t.actions.produce(t=>{xe(t.model,e,n)})}}function Me(e){return t=>{t.actions.produce(t=>{!function(e,t){const{cursor:n,key:r}=Ee(e,t);n[r]=!n[r]}(t.model,e)})}}function ke(e,t){return n=>{n.actions.produce(n=>{xe(n.model,e,t)})}}function Ne(){const e=n.useRef(null);return n.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 s=function(...e){const t=r.isUndefined(e[0])||r.isFunction(e[0])?{}:e[0],s=r.isFunction(e[0])?e[0]:e[1]??(()=>({})),c=m(),i=n.useContext(te),u=n.useContext(v),l=w(),f=n.useContext(g),h=n.useContext(R),p=n.useContext(L),y=Q(),b=n.useRef(!1),P=n.useRef(null),j=n.useRef(new je);b.current||(b.current=!0,P.current=j.current.hydrate(t));const[S,E]=n.useState(()=>j.current.model),x=function(e){const t=n.useRef(e);return t.current=e,n.useMemo(()=>{return n=t,Object.keys(e).reduce((e,t)=>(Object.defineProperty(e,t,{get:()=>n.current[t],enumerable:!0}),e),{});var n},[e])}(s()),k=n.useMemo(()=>new d,[]),N=n.useRef({handlers:/* @__PURE__ */new Map});N.current.handlers=/* @__PURE__ */new Map;const _=function(){const e=n.useRef(/* @__PURE__ */new Set),t=n.useRef(/* @__PURE__ */new Set);return n.useMemo(()=>({broadcast:e.current,multicast:t.current}),[])}(),U=n.useRef(A.Mounting),W=n.useRef(/* @__PURE__ */new Set),B=n.useRef(0),$=n.useCallback((e,t,n)=>{const o=new AbortController,s={controller:o,action:e,payload:t};return u.add(s),W.current.add(s),{get model(){return j.current.model},get phase(){return U.current},task:s,data:x,tasks:u,env:l,actions:{produce(e){if(o.signal.aborted)return;const t=f.current,r=j.current.produce(t=>{f.current=a(f.current,n=>{e({model:t,inspect:j.current.inspect,env:n})})});E(j.current.model),f.current!==t&&c.emit(M,f.current),n.processes.add(r),P.current&&(n.processes.add(P.current),P.current=null)},dispatch(e,t){if(o.signal.aborted)return Promise.resolve();const n=F(e),r=J(e)?e.channel:void 0;return H(e)?i?G(i.emitter,n,t,r):Promise.resolve():G(z(e)?c:k,n,t,r)},annotate:(e,t=ue.Update)=>j.current.annotate(t,e),get inspect(){return j.current.inspect},resource:Object.assign(function(e){const t=(e,t)=>{const n=e,r=F(n),o=J(n)?n.channel:void 0;return H(n)?i?G(i.emitter,r,t,o):Promise.resolve():z(n)?G(c,r,t,o):Promise.resolve()},n={exceedsWindow:null,isolated:!1},s={then:(s,c)=>(()=>{if(r.isNotNullable(n.exceedsWindow)){const{data:t,at:o}=e.read(e.params);if(t!==V&&r.isNotNullable(o)){const e=Temporal.Now.instant().since(o),r=Temporal.Duration.from(n.exceedsWindow);if(Temporal.Duration.compare(e,r)<=0)return Promise.resolve(t)}}if(n.isolated)return e.run(l,o,e.params,t);let s=h.get(e.run);r.isUndefined(s)&&(s=/* @__PURE__ */new Map,h.set(e.run,s));const c=s,i=JSON.stringify(e.params);let a=c.get(i);if(r.isUndefined(a)){const n=new AbortController,r={controller:n,refs:0};r.promise=e.run(l,n,e.params,t).finally(()=>{c.delete(i)}),c.set(i,r),a=r}const u=a;u.refs+=1;const f=()=>{u.refs-=1,0===u.refs&&(c.delete(i),u.controller.abort(o.signal.reason))};if(o.signal.aborted)f();else{o.signal.addEventListener("abort",f,{once:!0});const e=()=>o.signal.removeEventListener("abort",f);u.promise.then(e,e)}return d=u.promise,p=o.signal,new Promise((e,t)=>{if(p.aborted)return void t(p.reason);const n=()=>t(p.reason);p.addEventListener("abort",n,{once:!0}),d.then(t=>{p.removeEventListener("abort",n),e(t)},e=>{p.removeEventListener("abort",n),t(e)})});var d,p})().then(s,c),exceeds:e=>(n.exceedsWindow=e,s),isolated:()=>(n.isolated=!0,s),evict(t){e.evict(t??e.params)}};return s},{nuke:e=>function(e){const t=e??{};for(const n of se)n(t)}(e)}),async final(e){if(o.signal.aborted)return null;const t=F(e),n=H(e)?i?.emitter??null:c;if(!n)return null;const s=n.getCached(t);if(r.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()})}),n.getCached(t)??null},peek(e){if(o.signal.aborted)return null;const t=F(e),n=H(e)?i?.emitter??null:c;return n?n.getCached(t)??null:null}}}},[S]);n.useLayoutEffect(()=>{function e(e,t,n){return function(o,s){const a=n();if(r.isNotNullable(s)&&r.isNotNullable(a)&&!function(e,t){for(const n of Object.keys(t))if(e[n]!==t[n])return!1;return!0}(s,a))return;const l={processes:/* @__PURE__ */new Set},d=Promise.withResolvers(),h=$(e,o,l),m=function(e){const t=F(e),n=r.isString(t)?t:t.description??"";return n.startsWith(O())&&n.slice(n.lastIndexOf("/")+1)||"unknown"}(e),v=performance.now(),b=j.current.model,g=f.current;let w,P=!1;function S(){const e=j.current.model,t=f.current;return{model:b===e?null:{before:b,after:e},env:g===t?null:{before:g,after:t}}}function E(){return H(e)?i?G(i.emitter,e,o,s):Promise.resolve():G(z(e)?c:k,e,o,s)}function x(e){P=!0;const t=K(N.current.handlers,"Error"),n=r.isNotNullable(t),s=function(e){return e instanceof Error&&"AbortError"===e.name?Z.Aborted:Z.Errored}(e),i=function(e){return e instanceof Error?e:new Error(String(e))}(e),a={reason:s,error:i,action:m,handled:n,tasks:u,retry:E};c.fire(C,a),n&&t&&k.emit(t,a),p({stage:"end",result:"error",action:{name:m,payload:o},details:{task:h.task,elapsed:performance.now()-v,mutations:S(),error:i,reason:s}})}function M(){for(const e of u)if(e===h.task){u.delete(e),W.current.delete(e);break}l.processes.forEach(e=>j.current.prune(e)),l.processes.size>0&&y(),P||p({stage:"end",result:"success",action:{name:m,payload:o},details:{task:h.task,elapsed:performance.now()-v,mutations:S()}}),d.resolve()}p({stage:"start",action:{name:m,payload:o},details:{task:h.task}});try{w=t(h,o)}catch(A){return x(A),M(),d.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}(w)?((async()=>{for await(const e of w);})().catch(x).finally(M),d.promise):(Promise.resolve(w).catch(x).finally(M),d.promise)}}B.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(H(r)){if(i){const e=i.emitter;e.on(r,n),t.add(()=>e.off(r,n))}k.on(r,n),_.multicast.add(r),t.add(()=>k.off(r,n))}else z(r)?(c.on(r,n),k.on(r,n),_.broadcast.add(r),t.add(()=>{c.off(r,n),k.off(r,n)})):(k.on(r,n),t.add(()=>k.off(r,n)))}}),()=>{const e=++B.current,n=new Set(t);queueMicrotask(()=>{if(B.current!==e){for(const e of n)e();return}for(const e of W.current)e.controller.abort(new ee("Component unmounted")),u.delete(e);W.current.clear(),U.current=A.Unmounting;const t=K(N.current.handlers,"Unmount");t&&k.emit(t),U.current=A.Unmounted;for(const e of n)e()})}},[k]),function({unicast:e,broadcast:t,dispatchers:s,scope:c,phase:i,data:a,handlers:u}){const l=n.useRef(null),f=n.useRef(!1);n.useLayoutEffect(()=>{if(i.current===A.Mounted)return;i.current=A.Mounting;const n=K(u,"Mount");n&&e.emit(n),s.broadcast.forEach(n=>{for(const{channel:o,value:s}of t.getCachedAll(n))r.isNullable(s)||e.emit(n,s,o)}),c&&s.multicast.forEach(t=>{for(const{channel:n,value:o}of c.emitter.getCachedAll(t))r.isNullable(o)||e.emit(t,o,n)}),i.current=A.Mounted,f.current=!1},[]),n.useEffect(()=>{if(f.current)return;f.current=!0;const t=K(u,"Paint");t&&e.emit(t)},[]),n.useLayoutEffect(()=>{if(r.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,a);if(o.isNotEmpty(Object.keys(t))){const n=K(u,"Update");n&&e.emit(n,t)}}l.current=a},[a,e])}({unicast:k,broadcast:c,dispatchers:_,scope:i,phase:U,data:s(),handlers:N.current.handlers});const T=n.useMemo(()=>({dispatch(e,t){const n=F(e),r=J(e)?e.channel:void 0;return H(e)?i?G(i.emitter,n,t,r):Promise.resolve():G(z(e)?c:k,n,t,r)},get inspect(){return j.current.inspect},stream:(e,t)=>n.createElement(Se,{action:F(e),renderer:t})}),[S,k]),D=n.useMemo(()=>[S,T,x],[S,T,x]);return D.useAction=(e,t)=>{!function(e,t,r){const o=n.useRef(r);n.useLayoutEffect(()=>{o.current=r});const s=n.useRef(t);n.useLayoutEffect(()=>{s.current=t});const c=n.useCallback((e,t)=>o.current(e,t),[]),i=n.useCallback(()=>J(s.current)?s.current.channel:void 0,[]),a=F(t),u=e.current.handlers.get(a)??/* @__PURE__ */new Set;0===u.size&&e.current.handlers.set(a,u),u.add({getChannel:i,handler:c})}(N,e,t)},D.dispatch=D[1].dispatch,D}(...t);return e.current=s.dispatch,s},with:{update:e=>Ce(e),invert:e=>Me(e),always:(e,t)=>ke(e,t)}}),[])}function Ae(t,r){return{Boundary:function({children:t}){const r=n.useMemo(()=>({id:/* @__PURE__ */Symbol("march-hare.scope/instance"),emitter:new h}),[]);/* @__PURE__ */
|
|
7
|
+
return e(te.Provider,{value:r,children:t})},useContext:function(){return Ne()},useEnv:function(){return w()},Resource:function(e){return ie(e,t,r)}}}function _e(n){const r={current:void 0};return{Boundary:function({children:o}){/* @__PURE__ */
|
|
8
8
|
return t($,{env:n?.env,tap:n?.tap,children:[
|
|
9
|
-
/* @__PURE__ */e(Re,{holder:r}),o]})},useContext:function(){return
|
|
9
|
+
/* @__PURE__ */e(Re,{holder:r}),o]})},useContext:function(){return Ne()},useEnv:function(){return w()},Resource:function(e){return ie(e,n?.cache,()=>r.current)},Scope:()=>Ae(n?.cache,()=>r.current)}}function Re({holder:e}){const t=w();return e.current=t,null}const Ue={Update:e=>Ce(e),Invert:e=>Me(e),Always:(e,t)=>ke(e,t)},Le=new je;function We(e,t=ue.Update){return Le.annotate(t,e)}function Be(e,t){return new Promise((n,r)=>{if(t?.aborted)return void r(new ee);const o=setTimeout(n,e);t?.addEventListener("abort",()=>{clearTimeout(o),r(new ee)},{once:!0})})}async function $e(e,t,n){if(t?.aborted)throw new ee;for(;;){if(await n())return;await Be(e,t)}}function Te(e){return e?Boolean(e&&"symbol"!=typeof e):/* @__PURE__ */Symbol(`pk.${Date.now()}.${crypto.randomUUID()}`)}const Fe=/* @__PURE__ */Object.freeze(/* @__PURE__ */Object.defineProperty({__proto__:null,pk:Te,poll:$e,sleep:Be,unset:V,"ζ":Be,"κ":Te,"π":$e},Symbol.toStringTag,{value:"Module"})),ze=/* @__PURE__ */Object.freeze(/* @__PURE__ */Object.defineProperty({__proto__:null,Resource:function(e){return ie(e)},Scope:function(){return Ae()},useContext:function(){return Ne()},useEnv:function(){return w()}},Symbol.toStringTag,{value:"Module"}));export{ee as Aborted,I as Action,_e as App,$ as Boundary,ne as Cache,N as Distribution,k as Lifecycle,ue as Op,ue as Operation,Z as Reason,je as State,Ue as With,We as annotate,we as isBox,ze as shared,Fe as utils};
|
|
10
10
|
//# sourceMappingURL=march-hare.js.map
|