march-hare 0.13.10 → 0.14.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 +6 -4
- package/dist/action/utils.d.ts +3 -3
- package/dist/actions/utils.d.ts +4 -0
- package/dist/boundary/components/sharing/index.d.ts +24 -9
- package/dist/coalesce/index.d.ts +3 -43
- package/dist/march-hare.js +7 -7
- 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/index.d.ts +8 -4
- package/dist/resource/types.d.ts +0 -8
- package/dist/types/index.d.ts +48 -37
- package/package.json +1 -1
package/dist/resource/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { ResourceHandle } from './types.js';
|
|
|
2
2
|
import { Cache } from './utils.js';
|
|
3
3
|
import { AppFetcher } from '../app/types.js';
|
|
4
4
|
import { Env } from '../boundary/components/env/types.js';
|
|
5
|
-
export type {
|
|
5
|
+
export type { Fetcher, Invocation, ResourceHandle } from './types.js';
|
|
6
6
|
/**
|
|
7
7
|
* Evicts cache entries across every Resource constructed in the
|
|
8
8
|
* current process. Resources register themselves on declaration, so
|
|
@@ -44,9 +44,13 @@ export declare function nuke(where?: object): void;
|
|
|
44
44
|
* Standalone `shared.Resource` declarations always use an in-memory
|
|
45
45
|
* cache — reach for `app.Resource` when persistence is required.
|
|
46
46
|
*
|
|
47
|
-
* Concurrent calls
|
|
48
|
-
*
|
|
49
|
-
*
|
|
47
|
+
* Concurrent calls with the same `(Resource, params)` share a single
|
|
48
|
+
* in-flight fetch by default — one network request, every caller
|
|
49
|
+
* resolves with the same payload. The underlying work is refcounted: if
|
|
50
|
+
* every caller aborts, the shared `AbortController` is aborted too.
|
|
51
|
+
* Chain `.isolated()` on the thenable returned from
|
|
52
|
+
* `context.actions.resource(...)` to opt out (own controller, own
|
|
53
|
+
* request) for the rare cases that need it.
|
|
50
54
|
*
|
|
51
55
|
* @template E The Env shape (or union) the fetcher's `context.env` is
|
|
52
56
|
* typed against.
|
package/dist/resource/types.d.ts
CHANGED
|
@@ -40,14 +40,6 @@ export type Args<P extends object = Record<never, never>> = {
|
|
|
40
40
|
* and a broadcast/multicast-only `dispatch`.
|
|
41
41
|
*/
|
|
42
42
|
export type Fetcher<T, P extends object = Record<never, never>> = (context: Args<P>) => Promise<T>;
|
|
43
|
-
/**
|
|
44
|
-
* Per-call coalescing token. Two callers with the same Resource, same
|
|
45
|
-
* structural params, and equal `Coalesce` value share a single in-flight
|
|
46
|
-
* promise; different tokens (or different params) fire independent
|
|
47
|
-
* fetches. Primitives compose naturally via stringification; objects
|
|
48
|
-
* are serialised with `JSON.stringify`.
|
|
49
|
-
*/
|
|
50
|
-
export type Coalesce = string | number | bigint | boolean | symbol | object;
|
|
51
43
|
/**
|
|
52
44
|
* Config form accepted by `Resource`. The fetcher shorthand
|
|
53
45
|
* `Resource(fetcher)` is equivalent to `Resource({ fetch: fetcher })`.
|
package/dist/types/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { Operation, Process, Inspect as ImmInspect, Box } from 'immertation';
|
|
|
2
2
|
import { ActionId, Task, Tasks } from '../boundary/components/tasks/types.js';
|
|
3
3
|
import { Fault } from '../error/types.js';
|
|
4
4
|
import { Env } from '../boundary/components/env/types.js';
|
|
5
|
-
import {
|
|
5
|
+
import { Invocation } from '../resource/types.js';
|
|
6
6
|
import { WithHandle } from '../with/types.js';
|
|
7
7
|
import * as React from "react";
|
|
8
8
|
/**
|
|
@@ -26,25 +26,22 @@ type ValueAt<T, K extends PropertyKey> = T extends unknown ? K extends keyof T ?
|
|
|
26
26
|
export type Inspect<T, D extends number = 8> = ImmInspect<T> & ([D] extends [0] ? object : {
|
|
27
27
|
[K in UnionKeys<T> as ValueAt<T, K> extends (...args: unknown[]) => unknown ? never : K]: Inspect<ValueAt<T, K>, DepthLimiter[D]>;
|
|
28
28
|
});
|
|
29
|
-
/**
|
|
30
|
-
* Chainable handle returned from `context.actions.resource(invocation)`.
|
|
31
|
-
*
|
|
32
|
-
* - `.exceeds(duration)` short-circuits the fetch when the per-params
|
|
33
|
-
* cache age is within the supplied freshness window.
|
|
34
|
-
* - `.coalesce(token)` opts the call into in-flight sharing: any other
|
|
35
|
-
* caller with the same Resource, same structural params, and equal
|
|
36
|
-
* `token` joins the same promise.
|
|
37
|
-
*
|
|
38
|
-
* Awaiting the handle (`await context.actions.resource(...)`) triggers
|
|
39
|
-
* the fetch with whichever options have been set on the chain.
|
|
40
|
-
*/
|
|
41
29
|
/**
|
|
42
30
|
* Fetch-configured chain returned from `.exceeds(...)` and
|
|
43
|
-
* `.
|
|
31
|
+
* `.isolated()`. Awaiting the chain runs the fetch with whichever
|
|
44
32
|
* options are set; `.evict()` is intentionally absent because the
|
|
45
33
|
* "configured a fetch then evicted instead" sequence has no coherent
|
|
46
34
|
* meaning — eviction is always available off the bare
|
|
47
35
|
* `context.actions.resource(...)` call.
|
|
36
|
+
*
|
|
37
|
+
* Concurrent callers with the same `(Resource, params)` automatically
|
|
38
|
+
* share a single in-flight fetch — one network request, every
|
|
39
|
+
* caller resolves with the same payload. The shared fetch runs on a
|
|
40
|
+
* detached `AbortController` so one caller's abort never cancels work
|
|
41
|
+
* other callers are still waiting on; when every caller has released
|
|
42
|
+
* (their `context.task.controller` aborted) the shared controller is
|
|
43
|
+
* aborted too. Chain `.isolated()` to opt out for the rare case that
|
|
44
|
+
* needs an independent request.
|
|
48
45
|
*/
|
|
49
46
|
export type ResourceFetch<T> = PromiseLike<T> & {
|
|
50
47
|
/**
|
|
@@ -54,22 +51,25 @@ export type ResourceFetch<T> = PromiseLike<T> & {
|
|
|
54
51
|
*/
|
|
55
52
|
readonly exceeds: (duration: Temporal.DurationLike) => ResourceFetch<T>;
|
|
56
53
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
54
|
+
* Opt this call out of the default `(Resource, params)` coalesce
|
|
55
|
+
* path. The fetch fires as an independent network request against
|
|
56
|
+
* the caller's own `context.task.controller` — no joining of
|
|
57
|
+
* any in-flight fetch, no refcounted detached controller. Aborting
|
|
58
|
+
* the caller's task cancels the network exactly as a regular fetch
|
|
59
|
+
* would.
|
|
62
60
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
61
|
+
* Reach for this only when two callers need parallel fetches with
|
|
62
|
+
* byte-identical params and the difference in intent genuinely can't
|
|
63
|
+
* be modelled by differing params. The default is almost always
|
|
64
|
+
* what you want.
|
|
65
65
|
*/
|
|
66
|
-
readonly
|
|
66
|
+
readonly isolated: () => ResourceFetch<T>;
|
|
67
67
|
};
|
|
68
68
|
/**
|
|
69
69
|
* Chainable handle returned from `context.actions.resource(invocation)`.
|
|
70
|
-
* Either resolve to the fetched value (`.exceeds`/`.
|
|
70
|
+
* Either resolve to the fetched value (`.exceeds`/`.isolated` + await)
|
|
71
71
|
* or drop the cache slot (`.evict`) — the two paths are mutually
|
|
72
|
-
* exclusive, so once `.exceeds` or `.
|
|
72
|
+
* exclusive, so once `.exceeds` or `.isolated` runs the chain narrows
|
|
73
73
|
* to {@link ResourceFetch} and `.evict` is no longer available.
|
|
74
74
|
*/
|
|
75
75
|
export type ResourceCall<T> = ResourceFetch<T> & {
|
|
@@ -175,9 +175,10 @@ export declare class Brand {
|
|
|
175
175
|
static readonly Name: unique symbol;
|
|
176
176
|
/**
|
|
177
177
|
* Phantom brand identifying lifecycle actions returned by
|
|
178
|
-
* `Lifecycle.Mount()`, `Lifecycle.
|
|
179
|
-
* `Lifecycle.Update()`. Carries the lifecycle's
|
|
180
|
-
* `useAction` can pick distinct overloads — in
|
|
178
|
+
* `Lifecycle.Mount()`, `Lifecycle.Paint()`, `Lifecycle.Unmount()`,
|
|
179
|
+
* `Lifecycle.Error()`, and `Lifecycle.Update()`. Carries the lifecycle's
|
|
180
|
+
* literal kind so that `useAction` can pick distinct overloads — in
|
|
181
|
+
* particular,
|
|
181
182
|
* `Lifecycle.Update` resolves its payload to `Partial<DeepReadonly<D>>`
|
|
182
183
|
* against the surrounding `useActions` data generic instead of the
|
|
183
184
|
* factory-level `Record<string, unknown>` placeholder. Without this
|
|
@@ -213,6 +214,7 @@ export declare const EnvSymbol: unique symbol;
|
|
|
213
214
|
* ```ts
|
|
214
215
|
* export class Actions {
|
|
215
216
|
* static Mount = Lifecycle.Mount();
|
|
217
|
+
* static Paint = Lifecycle.Paint();
|
|
216
218
|
* static Unmount = Lifecycle.Unmount();
|
|
217
219
|
* static Error = Lifecycle.Error();
|
|
218
220
|
* static Update = Lifecycle.Update();
|
|
@@ -229,6 +231,14 @@ export declare const EnvSymbol: unique symbol;
|
|
|
229
231
|
export declare class Lifecycle {
|
|
230
232
|
/** Creates a Mount lifecycle action. Triggered once on component mount (`useLayoutEffect`). */
|
|
231
233
|
static Mount(): LifecyclePayload<never, never, "Mount">;
|
|
234
|
+
/**
|
|
235
|
+
* Creates a Paint lifecycle action. Triggered once after the browser has
|
|
236
|
+
* committed the first frame (`useEffect`). Pairs with {@link Lifecycle.Mount}
|
|
237
|
+
* (pre-paint) — use Paint for work that should not delay the first
|
|
238
|
+
* paint: analytics “viewed” events, focus management, scroll-into-view,
|
|
239
|
+
* non-blocking prefetch, etc.
|
|
240
|
+
*/
|
|
241
|
+
static Paint(): LifecyclePayload<never, never, "Paint">;
|
|
232
242
|
/** Creates an Unmount lifecycle action. Triggered when the component unmounts. */
|
|
233
243
|
static Unmount(): LifecyclePayload<never, never, "Unmount">;
|
|
234
244
|
/** Creates an Error lifecycle action. Triggered when an action throws. Receives `Fault` as payload. */
|
|
@@ -407,20 +417,21 @@ export type HandlerPayload<P = unknown, C extends Filter = never, Name extends s
|
|
|
407
417
|
(channel: C): ChanneledAction<P, C, Name>;
|
|
408
418
|
});
|
|
409
419
|
/**
|
|
410
|
-
* Branded type returned by `Lifecycle.Mount`, `Lifecycle.
|
|
411
|
-
* `Lifecycle.Error`, and `Lifecycle.Update`.
|
|
412
|
-
* `HandlerPayload` but carries a phantom
|
|
413
|
-
* is the lifecycle's literal kind. The
|
|
414
|
-
* `Handlers` resolve `Lifecycle.Update`'s
|
|
415
|
-
* (against the surrounding `useActions`
|
|
416
|
-
* factory-level `Record<string, unknown>`
|
|
417
|
-
* `Action<P>("Update")` would have
|
|
418
|
-
* so it falls into the generic
|
|
420
|
+
* Branded type returned by `Lifecycle.Mount`, `Lifecycle.Paint`,
|
|
421
|
+
* `Lifecycle.Unmount`, `Lifecycle.Error`, and `Lifecycle.Update`.
|
|
422
|
+
* Structurally identical to a `HandlerPayload` but carries a phantom
|
|
423
|
+
* `Brand.Lifecycle` brand whose value is the lifecycle's literal kind. The
|
|
424
|
+
* brand is what lets `useAction` and `Handlers` resolve `Lifecycle.Update`'s
|
|
425
|
+
* payload to `Partial<DeepReadonly<D>>` (against the surrounding `useActions`
|
|
426
|
+
* data generic) instead of the factory-level `Record<string, unknown>`
|
|
427
|
+
* placeholder — a user-defined `Action<P>("Update")` would have
|
|
428
|
+
* `Name = "Update"` but no `Brand.Lifecycle`, so it falls into the generic
|
|
429
|
+
* payload overload as expected.
|
|
419
430
|
*
|
|
420
431
|
* @template P Payload type for the lifecycle.
|
|
421
432
|
* @template C Channel filter (always `never` for lifecycles — they are
|
|
422
433
|
* not channeled).
|
|
423
|
-
* @template Name Literal name (`"Mount"`, `"Unmount"`, `"Error"`, `"Update"`).
|
|
434
|
+
* @template Name Literal name (`"Mount"`, `"Paint"`, `"Unmount"`, `"Error"`, `"Update"`).
|
|
424
435
|
*/
|
|
425
436
|
export type LifecyclePayload<P = unknown, C extends Filter = never, Name extends string = string> = HandlerPayload<P, C, Name> & {
|
|
426
437
|
readonly [Brand.Lifecycle]: Name;
|