chizu 0.2.72 → 0.4.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 +99 -84
- package/dist/boundary/components/broadcast/utils.d.ts +7 -1
- package/dist/boundary/components/scope/index.d.ts +25 -21
- package/dist/boundary/components/scope/types.d.ts +2 -2
- package/dist/boundary/index.d.ts +1 -1
- package/dist/chizu.js +6 -8
- package/dist/chizu.umd.cjs +1 -1
- package/dist/error/index.d.ts +1 -23
- package/dist/error/types.d.ts +12 -28
- package/dist/error/utils.d.ts +1 -11
- package/dist/hooks/utils.d.ts +8 -0
- package/dist/index.d.ts +4 -3
- package/dist/resource/index.d.ts +78 -0
- package/dist/types/index.d.ts +78 -94
- package/dist/utils/index.d.ts +0 -17
- package/dist/utils.d.ts +26 -0
- package/package.json +4 -1
- package/dist/boundary/components/cache/index.d.ts +0 -13
- package/dist/boundary/components/cache/types.d.ts +0 -19
- package/dist/boundary/components/cache/utils.d.ts +0 -12
- package/dist/cache/index.d.ts +0 -77
package/dist/error/index.d.ts
CHANGED
|
@@ -1,24 +1,2 @@
|
|
|
1
|
-
import { Props } from './types';
|
|
2
|
-
export { useError } from './utils';
|
|
3
1
|
export { Reason, AbortError, TimeoutError, DisallowedError } from './types';
|
|
4
|
-
export type { Fault
|
|
5
|
-
/**
|
|
6
|
-
* Error boundary component for catching and handling errors from actions.
|
|
7
|
-
*
|
|
8
|
-
* @template E Custom error types to include in the handler's error union.
|
|
9
|
-
* @param props.handler - The error handler function to call when an error occurs.
|
|
10
|
-
* @param props.children - The children to render within the error boundary.
|
|
11
|
-
* @returns The children wrapped in an error context provider.
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```tsx
|
|
15
|
-
* <Error<ApiError | ValidationError>
|
|
16
|
-
* handler={({ error }) => {
|
|
17
|
-
* if (error instanceof ApiError) handleApiError(error);
|
|
18
|
-
* }}
|
|
19
|
-
* >
|
|
20
|
-
* <App />
|
|
21
|
-
* </Error>
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
export declare function Error<E extends Error = never>({ handler, children, }: Props<E>): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export type { Fault } from './types';
|
package/dist/error/types.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
2
1
|
import { Task } from '../boundary/components/tasks/types.ts';
|
|
3
2
|
/**
|
|
4
3
|
* Reasons why an action error occurred.
|
|
@@ -57,6 +56,13 @@ export declare class DisallowedError extends Error {
|
|
|
57
56
|
}
|
|
58
57
|
/**
|
|
59
58
|
* Details about an error that occurred during action execution.
|
|
59
|
+
*
|
|
60
|
+
* Faults are delivered through the global `Lifecycle.Fault` broadcast.
|
|
61
|
+
* Subscribe with `actions.useAction(Lifecycle.Fault, handler)` near the
|
|
62
|
+
* root of your application for app-level concerns (logging, sign-out on
|
|
63
|
+
* auth failure, abort cascades). For component-local recovery, use a
|
|
64
|
+
* `Lifecycle.Error()` factory instead.
|
|
65
|
+
*
|
|
60
66
|
* @template E Custom error types to include in the union with Error.
|
|
61
67
|
*/
|
|
62
68
|
export type Fault<E extends Error = never> = {
|
|
@@ -74,35 +80,13 @@ export type Fault<E extends Error = never> = {
|
|
|
74
80
|
* (e.g., on 403/500 responses to prevent cascading failures).
|
|
75
81
|
*
|
|
76
82
|
* @example
|
|
77
|
-
* ```
|
|
78
|
-
*
|
|
79
|
-
* if (reason === Reason.Errored) {
|
|
80
|
-
*
|
|
81
|
-
* for (const task of tasks) {
|
|
82
|
-
* task.controller.abort();
|
|
83
|
-
* }
|
|
84
|
-
* // Trigger re-authentication flow
|
|
83
|
+
* ```ts
|
|
84
|
+
* actions.useAction(Lifecycle.Fault, (context, fault) => {
|
|
85
|
+
* if (fault.reason === Reason.Errored) {
|
|
86
|
+
* for (const task of fault.tasks) task.controller.abort();
|
|
85
87
|
* }
|
|
86
|
-
* }
|
|
87
|
-
* {children}
|
|
88
|
-
* </Error>
|
|
88
|
+
* });
|
|
89
89
|
* ```
|
|
90
90
|
*/
|
|
91
91
|
tasks: ReadonlySet<Task>;
|
|
92
92
|
};
|
|
93
|
-
/**
|
|
94
|
-
* Catcher function called when an action error occurs.
|
|
95
|
-
* @template E Custom error types to include in the union with Error.
|
|
96
|
-
* @param details Information about the error.
|
|
97
|
-
*/
|
|
98
|
-
export type Catcher<E extends Error = never> = (details: Fault<E>) => void;
|
|
99
|
-
/**
|
|
100
|
-
* Props for the Error boundary component.
|
|
101
|
-
* @template E Custom error types to include in the union with Error.
|
|
102
|
-
*/
|
|
103
|
-
export type Props<E extends Error = never> = {
|
|
104
|
-
/** Catcher function called when an action error occurs. */
|
|
105
|
-
handler: Catcher<E>;
|
|
106
|
-
/** Child components to wrap with error handling. */
|
|
107
|
-
children?: ReactNode;
|
|
108
|
-
};
|
package/dist/error/utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Reason } from './types.ts';
|
|
2
2
|
/**
|
|
3
3
|
* Determines the error reason based on what was thrown.
|
|
4
4
|
*
|
|
@@ -13,13 +13,3 @@ export declare function getReason(error: unknown): Reason;
|
|
|
13
13
|
* @returns An Error instance (original if already Error, wrapped otherwise).
|
|
14
14
|
*/
|
|
15
15
|
export declare function getError(error: unknown): Error;
|
|
16
|
-
/**
|
|
17
|
-
* React context for handling errors that occur within actions.
|
|
18
|
-
*/
|
|
19
|
-
export declare const ErrorContext: import('react').Context<Catcher | undefined>;
|
|
20
|
-
/**
|
|
21
|
-
* Hook to access the error handler from the nearest Error provider.
|
|
22
|
-
*
|
|
23
|
-
* @returns The error handler function, or undefined if not within an Error provider.
|
|
24
|
-
*/
|
|
25
|
-
export declare function useError(): Catcher | undefined;
|
package/dist/hooks/utils.d.ts
CHANGED
|
@@ -16,6 +16,14 @@ 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;
|
|
19
27
|
/**
|
|
20
28
|
* Invokes all listeners for an event and returns a promise that resolves
|
|
21
29
|
* when every handler has settled. For {@link BroadcastEmitter} instances the
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
export { Action } from './action/index.ts';
|
|
2
|
-
export { Entry } from './cache/index.ts';
|
|
3
2
|
export { Distribution, Lifecycle } from './types/index.ts';
|
|
4
|
-
export {
|
|
3
|
+
export { Reason, AbortError, TimeoutError, DisallowedError, } from './error/index.ts';
|
|
5
4
|
export { Operation, Op, State } from 'immertation';
|
|
6
5
|
export { annotate } from './annotate/index.ts';
|
|
7
6
|
export { Boundary } from './boundary/index.tsx';
|
|
8
7
|
export { Regulators } from './boundary/components/regulators/index.tsx';
|
|
9
8
|
export { Scope, withScope } from './boundary/components/scope/index.tsx';
|
|
10
9
|
export { useActions, With } from './hooks/index.ts';
|
|
10
|
+
export { Resource } from './resource/index.ts';
|
|
11
|
+
export type { ResourceHandle, ResourceDispatch, ResourceSuccess, ResourceFailure, } from './resource/index.ts';
|
|
11
12
|
export * as utils from './utils/index.ts';
|
|
12
13
|
export type { Box } from 'immertation';
|
|
13
|
-
export type { Fault
|
|
14
|
+
export type { Fault } from './error/index.ts';
|
|
14
15
|
export type { Pk, Task, Tasks, Handlers, Meta } from './types/index.ts';
|
|
15
16
|
export type { Regulator } from './boundary/components/regulators/index.tsx';
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { BroadcastPayload, ChanneledAction, Filter, Props } from '../types/index.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Fan-out dispatcher passed to a {@link Resource}'s `onSuccess` and
|
|
4
|
+
* `onError` callbacks. Restricted to broadcast actions (and channeled
|
|
5
|
+
* broadcasts) because resource-level events have no single owning
|
|
6
|
+
* component to scope unicast or multicast to.
|
|
7
|
+
*/
|
|
8
|
+
export type ResourceDispatch = {
|
|
9
|
+
<P>(action: BroadcastPayload<P>, payload?: P): Promise<void>;
|
|
10
|
+
<P, C extends Filter>(action: ChanneledAction<P, C>, payload?: P): Promise<void>;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Context passed to a {@link Resource}'s `onSuccess` callback after a
|
|
14
|
+
* successful fetch.
|
|
15
|
+
*/
|
|
16
|
+
export type ResourceSuccess<T> = {
|
|
17
|
+
/** The resolved value from the fetcher. */
|
|
18
|
+
readonly response: T;
|
|
19
|
+
/** The reactive `data` proxy of the component that triggered the fetch. */
|
|
20
|
+
readonly data: Props;
|
|
21
|
+
/** Pre-bound dispatcher for the surrounding Boundary's broadcaster. */
|
|
22
|
+
readonly dispatch: ResourceDispatch;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Context passed to a {@link Resource}'s `onError` callback after a
|
|
26
|
+
* failed fetch.
|
|
27
|
+
*/
|
|
28
|
+
export type ResourceFailure<E> = {
|
|
29
|
+
/** The thrown error, narrowed to the second generic on `Resource`. */
|
|
30
|
+
readonly error: E;
|
|
31
|
+
/** The reactive `data` proxy of the component that triggered the fetch. */
|
|
32
|
+
readonly data: Props;
|
|
33
|
+
/** Pre-bound dispatcher for the surrounding Boundary's broadcaster. */
|
|
34
|
+
readonly dispatch: ResourceDispatch;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Module-scope handle returned by {@link Resource}. Pass to
|
|
38
|
+
* `actions.useResource(handle)` inside a component to obtain a fetcher
|
|
39
|
+
* thunk bound to the surrounding Boundary's broadcaster and the
|
|
40
|
+
* component's reactive `data`.
|
|
41
|
+
*/
|
|
42
|
+
export type ResourceHandle<T, E = Error, Args extends readonly unknown[] = []> = {
|
|
43
|
+
readonly key: string;
|
|
44
|
+
/** @internal */
|
|
45
|
+
readonly fetch: (dispatch: ResourceDispatch, data: Props, ...args: Args) => Promise<T>;
|
|
46
|
+
/** @internal — phantom marker so TS distinguishes by error type */
|
|
47
|
+
readonly _error?: E;
|
|
48
|
+
/** @internal — phantom marker so TS distinguishes by args */
|
|
49
|
+
readonly _args?: Args;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Defines a remote resource — declare at module scope and consume via
|
|
53
|
+
* `actions.useResource(handle)`. Mirrors the {@link Action} factory pattern:
|
|
54
|
+
* the declaration is a value, not a hook.
|
|
55
|
+
*
|
|
56
|
+
* The fetcher may take arguments. The thunk returned by `actions.useResource`
|
|
57
|
+
* forwards them, and in-flight dedup keys per arg-tuple – so
|
|
58
|
+
* `fetchPage(null)` and `fetchPage("abc")` run independently, while two
|
|
59
|
+
* concurrent `fetchPage("abc")` calls share one network request.
|
|
60
|
+
*
|
|
61
|
+
* Once a fetch resolves, the next call with the same args fetches anew –
|
|
62
|
+
* there is no stale cache. Coordination across components happens via the
|
|
63
|
+
* broadcast actions dispatched in `onSuccess` / `onError`.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* import { Resource } from "chizu";
|
|
68
|
+
*
|
|
69
|
+
* export const feed = Resource<Page<Item>, ApiError, [cursor: string | null]>(
|
|
70
|
+
* "feed",
|
|
71
|
+
* (cursor) =>
|
|
72
|
+
* http.get("feed", { searchParams: { cursor: cursor ?? "" } }).json(),
|
|
73
|
+
* ({ response, dispatch }) =>
|
|
74
|
+
* dispatch(Actions.Broadcast.PageLoaded, response),
|
|
75
|
+
* );
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export declare function Resource<T, E = Error, Args extends readonly unknown[] = []>(key: string, fetcher: (...args: Args) => Promise<T>, onSuccess?: (context: ResourceSuccess<T>) => void, onError?: (context: ResourceFailure<E>) => void): ResourceHandle<T, E, Args>;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { Operation, Process, Inspect, Box } from 'immertation';
|
|
2
2
|
import { ActionId, Task, Tasks } from '../boundary/components/tasks/types.ts';
|
|
3
|
-
import { Option } from '@mobily/ts-belt/Option';
|
|
4
|
-
import { Result as TsBeltResult } from '@mobily/ts-belt/Result';
|
|
5
3
|
import { Regulator } from '../boundary/components/regulators/types.ts';
|
|
6
4
|
import { Fault } from '../error/types.ts';
|
|
7
5
|
import * as React from "react";
|
|
@@ -58,43 +56,15 @@ export declare class Brand {
|
|
|
58
56
|
static readonly Channel: unique symbol;
|
|
59
57
|
/** Node capture events used by Lifecycle.Node */
|
|
60
58
|
static readonly Node: unique symbol;
|
|
61
|
-
/** Identifies cache entry identifiers created with Entry() */
|
|
62
|
-
static readonly Cache: unique symbol;
|
|
63
59
|
}
|
|
64
60
|
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* @internal
|
|
69
|
-
*/
|
|
70
|
-
declare const CacheValueBrand: unique symbol;
|
|
71
|
-
/**
|
|
72
|
-
* A branded cache entry identifier.
|
|
73
|
-
*
|
|
74
|
-
* When the second type parameter `C` is provided, the entry becomes callable
|
|
75
|
-
* to produce channeled identifiers for independent cache slots per channel.
|
|
61
|
+
* Internal symbol for the global `Lifecycle.Fault` broadcast. Exposed so the
|
|
62
|
+
* dispatch pipeline can fire faults and short-circuit the regulator policy
|
|
63
|
+
* without depending on the `Lifecycle` class at runtime.
|
|
76
64
|
*
|
|
77
|
-
* @
|
|
78
|
-
* @template C - The channel type for channeled entries (defaults to never).
|
|
79
|
-
*/
|
|
80
|
-
export type CacheId<T = unknown, C extends Filter = never> = {
|
|
81
|
-
readonly [Brand.Cache]: symbol;
|
|
82
|
-
readonly [CacheValueBrand]?: (t: T) => T;
|
|
83
|
-
} & ([C] extends [never] ? unknown : {
|
|
84
|
-
(channel: C): ChanneledCacheId<T, C>;
|
|
85
|
-
});
|
|
86
|
-
/**
|
|
87
|
-
* Result of calling a channeled cache entry with a channel argument.
|
|
88
|
-
* Contains the entry identity and the channel data for scoped cache access.
|
|
89
|
-
*
|
|
90
|
-
* @template T - The cached value type.
|
|
91
|
-
* @template C - The channel type.
|
|
65
|
+
* @internal
|
|
92
66
|
*/
|
|
93
|
-
export
|
|
94
|
-
readonly [Brand.Cache]: symbol;
|
|
95
|
-
readonly [CacheValueBrand]?: (t: T) => T;
|
|
96
|
-
readonly channel: C;
|
|
97
|
-
};
|
|
67
|
+
export declare const FaultSymbol: unique symbol;
|
|
98
68
|
/**
|
|
99
69
|
* Factory functions for lifecycle actions.
|
|
100
70
|
*
|
|
@@ -116,6 +86,9 @@ export type ChanneledCacheId<T = unknown, C = unknown> = {
|
|
|
116
86
|
* // Now regulating Lifecycle.Mount only blocks THIS component's mount:
|
|
117
87
|
* context.regulator.disallow(Actions.Mount);
|
|
118
88
|
* ```
|
|
89
|
+
*
|
|
90
|
+
* `Lifecycle.Fault` is a singleton broadcast (not a factory). All components
|
|
91
|
+
* subscribe to the same shared symbol to receive global fault notifications.
|
|
119
92
|
*/
|
|
120
93
|
export declare class Lifecycle {
|
|
121
94
|
/** Creates a Mount lifecycle action. Triggered once on component mount (`useLayoutEffect`). */
|
|
@@ -151,6 +124,28 @@ export declare class Lifecycle {
|
|
|
151
124
|
static Node(): HandlerPayload<unknown, {
|
|
152
125
|
Name: string;
|
|
153
126
|
}>;
|
|
127
|
+
/**
|
|
128
|
+
* Global fault broadcast. Receives a `Fault` whenever any action in the
|
|
129
|
+
* `<Boundary>` errors, times out, is supplanted, or is blocked by the
|
|
130
|
+
* regulator. Subscribe via `actions.useAction(Lifecycle.Fault, handler)`.
|
|
131
|
+
*
|
|
132
|
+
* Unlike the per-component `Lifecycle.Error()` factory, `Fault` is a single
|
|
133
|
+
* shared broadcast — every subscriber points at the same symbol. The
|
|
134
|
+
* regulator policy does not apply to `Fault`; faults always reach
|
|
135
|
+
* subscribers so error visibility cannot be silenced.
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```tsx
|
|
139
|
+
* const actions = useActions<void, typeof Actions>();
|
|
140
|
+
*
|
|
141
|
+
* actions.useAction(Lifecycle.Fault, (context, fault) => {
|
|
142
|
+
* if (fault.reason === Reason.Errored) {
|
|
143
|
+
* console.error(`Action "${fault.action}" failed`, fault.error);
|
|
144
|
+
* }
|
|
145
|
+
* });
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
static Fault: BroadcastPayload<Fault>;
|
|
154
149
|
}
|
|
155
150
|
/**
|
|
156
151
|
* Distribution modes for actions.
|
|
@@ -274,8 +269,12 @@ export type ChanneledAction<P = unknown, C = unknown> = {
|
|
|
274
269
|
* Broadcast actions are sent to all mounted components. Values are cached so that
|
|
275
270
|
* late-mounting components receive the most recent payload.
|
|
276
271
|
*
|
|
272
|
+
* Late-mounting components receive the most recent cached payload via their
|
|
273
|
+
* `useAction` handler during mount. Use `peek()` in a `Lifecycle.Mount` handler
|
|
274
|
+
* to check whether a cached value exists before performing default fetches.
|
|
275
|
+
*
|
|
277
276
|
* This type extends `HandlerPayload<P, C>` with an additional brand to enforce at compile-time
|
|
278
|
-
* that only broadcast actions can be passed to `context.actions.
|
|
277
|
+
* that only broadcast actions can be passed to `context.actions.resolution()`.
|
|
279
278
|
*
|
|
280
279
|
* @template P - The payload type for the action
|
|
281
280
|
* @template C - The channel type for channeled dispatches (defaults to never)
|
|
@@ -284,8 +283,8 @@ export type ChanneledAction<P = unknown, C = unknown> = {
|
|
|
284
283
|
* ```ts
|
|
285
284
|
* const SignedOut = Action<User>("SignedOut", Distribution.Broadcast);
|
|
286
285
|
*
|
|
287
|
-
* //
|
|
288
|
-
* const user = await context.actions.
|
|
286
|
+
* // Resolve the latest value inside a handler
|
|
287
|
+
* const user = await context.actions.resolution(SignedOut);
|
|
289
288
|
* ```
|
|
290
289
|
*/
|
|
291
290
|
export type BroadcastPayload<P = unknown, C extends Filter = never> = HandlerPayload<P, C> & {
|
|
@@ -297,18 +296,19 @@ export type BroadcastPayload<P = unknown, C extends Filter = never> = HandlerPay
|
|
|
297
296
|
*
|
|
298
297
|
* When dispatching a multicast action, you MUST provide the scope name as the third argument:
|
|
299
298
|
* ```ts
|
|
300
|
-
* actions.dispatch(Actions.Multicast.Update, payload, { scope:
|
|
299
|
+
* actions.dispatch(Actions.Multicast.Update, payload, { scope: Actions.Multicast.Scope });
|
|
301
300
|
* ```
|
|
302
301
|
*
|
|
303
|
-
* Components receive multicast events only if they are descendants of a `<Scope
|
|
302
|
+
* Components receive multicast events only if they are descendants of a `<Scope of={...}>`.
|
|
304
303
|
*
|
|
305
304
|
* @template P - The payload type for the action
|
|
306
305
|
* @template C - The channel type for channeled dispatches (defaults to never)
|
|
307
306
|
*
|
|
308
307
|
* @example
|
|
309
308
|
* ```tsx
|
|
310
|
-
* // Define multicast actions
|
|
309
|
+
* // Define multicast actions and their scope together
|
|
311
310
|
* class MulticastActions {
|
|
311
|
+
* static Scope = "Counter" as const;
|
|
312
312
|
* static Update = Action<number>("Update", Distribution.Multicast);
|
|
313
313
|
* }
|
|
314
314
|
*
|
|
@@ -318,13 +318,13 @@ export type BroadcastPayload<P = unknown, C extends Filter = never> = HandlerPay
|
|
|
318
318
|
* }
|
|
319
319
|
*
|
|
320
320
|
* // In JSX - create a named scope boundary
|
|
321
|
-
* <Scope
|
|
321
|
+
* <Scope of={MulticastActions.Scope}>
|
|
322
322
|
* <CounterA />
|
|
323
323
|
* <CounterB />
|
|
324
324
|
* </Scope>
|
|
325
325
|
*
|
|
326
|
-
* // Inside CounterA - dispatch to all components in
|
|
327
|
-
* actions.dispatch(Actions.Multicast.Update, 42, { scope:
|
|
326
|
+
* // Inside CounterA - dispatch to all components in the scope
|
|
327
|
+
* actions.dispatch(Actions.Multicast.Update, 42, { scope: Actions.Multicast.Scope });
|
|
328
328
|
* // CounterA and CounterB both receive the event
|
|
329
329
|
* ```
|
|
330
330
|
*/
|
|
@@ -336,7 +336,11 @@ export type MulticastPayload<P = unknown, C extends Filter = never> = HandlerPay
|
|
|
336
336
|
* Required when dispatching a multicast action.
|
|
337
337
|
*/
|
|
338
338
|
export type MulticastOptions = {
|
|
339
|
-
/**
|
|
339
|
+
/**
|
|
340
|
+
* Scope name. By convention this is a `static Scope` literal co-located
|
|
341
|
+
* with the multicast action declarations (e.g. `Actions.Multicast.Scope`),
|
|
342
|
+
* giving every call site a single source of truth.
|
|
343
|
+
*/
|
|
340
344
|
scope: string;
|
|
341
345
|
};
|
|
342
346
|
/**
|
|
@@ -665,45 +669,6 @@ export type HandlerContext<M extends Model | void, _AC extends Actions | void, D
|
|
|
665
669
|
}) => void>(ƒ: F & AssertSync<F>): void;
|
|
666
670
|
dispatch(action: ActionOrChanneled, payload?: unknown, options?: MulticastOptions): Promise<void>;
|
|
667
671
|
annotate<T>(operation: Operation, value: T): T;
|
|
668
|
-
/**
|
|
669
|
-
* Fetches a value from the cache or executes the callback if not cached / expired.
|
|
670
|
-
*
|
|
671
|
-
* The callback must return an `Option<T>` or `Result<T, E>`. Only `Some` / `Ok`
|
|
672
|
-
* values are stored; `None` / `Error` results are skipped and `{ data: null }`
|
|
673
|
-
* is returned. Exactly one layer of `Option` / `Result` is unwrapped.
|
|
674
|
-
*
|
|
675
|
-
* @param entry - The cache entry identifier (from `Entry()`).
|
|
676
|
-
* @param ttl - Time-to-live in milliseconds.
|
|
677
|
-
* @param fn - Async callback that produces the value to cache.
|
|
678
|
-
* @returns An object with `data` set to the cached or freshly-fetched value, or `null`.
|
|
679
|
-
*
|
|
680
|
-
* @example
|
|
681
|
-
* ```ts
|
|
682
|
-
* const { data } = await context.actions.cacheable(
|
|
683
|
-
* CacheStore.Pairs,
|
|
684
|
-
* 30_000,
|
|
685
|
-
* async () => Some(await api.fetchPairs()),
|
|
686
|
-
* );
|
|
687
|
-
* ```
|
|
688
|
-
*/
|
|
689
|
-
cacheable<T>(entry: CacheId<T> | ChanneledCacheId<T>, ttl: number, fn: () => Promise<Option<T>>): Promise<{
|
|
690
|
-
data: T | null;
|
|
691
|
-
}>;
|
|
692
|
-
cacheable<T>(entry: CacheId<T> | ChanneledCacheId<T>, ttl: number, fn: () => Promise<TsBeltResult<T, unknown>>): Promise<{
|
|
693
|
-
data: T | null;
|
|
694
|
-
}>;
|
|
695
|
-
/**
|
|
696
|
-
* Removes a cached value from the store.
|
|
697
|
-
*
|
|
698
|
-
* @param entry - The cache entry identifier to invalidate.
|
|
699
|
-
*
|
|
700
|
-
* @example
|
|
701
|
-
* ```ts
|
|
702
|
-
* context.actions.invalidate(CacheStore.Pairs);
|
|
703
|
-
* context.actions.invalidate(CacheStore.User({ UserId: 5 }));
|
|
704
|
-
* ```
|
|
705
|
-
*/
|
|
706
|
-
invalidate(entry: CacheId<unknown> | ChanneledCacheId<unknown>): void;
|
|
707
672
|
/**
|
|
708
673
|
* Feature toggle methods for mutating boolean flags on the model.
|
|
709
674
|
*
|
|
@@ -720,20 +685,21 @@ export type HandlerContext<M extends Model | void, _AC extends Actions | void, D
|
|
|
720
685
|
invert<K extends keyof FeatureFlags<M>>(name: K): void;
|
|
721
686
|
};
|
|
722
687
|
/**
|
|
723
|
-
*
|
|
688
|
+
* Returns the resolved broadcast or multicast value, waiting for any
|
|
689
|
+
* pending annotations to settle before resolving.
|
|
724
690
|
*
|
|
725
691
|
* If a value has already been dispatched it resolves immediately.
|
|
726
692
|
* Otherwise it waits until the next dispatch of the action.
|
|
727
693
|
* Resolves with `null` if the task is aborted before a value arrives.
|
|
728
694
|
*
|
|
729
|
-
* @param action - The broadcast or multicast action to
|
|
730
|
-
* @param options - For multicast actions, must include `{ scope:
|
|
695
|
+
* @param action - The broadcast or multicast action to resolve.
|
|
696
|
+
* @param options - For multicast actions, must include `{ scope: MulticastActions }`.
|
|
731
697
|
* @returns The dispatched value, or `null` if aborted.
|
|
732
698
|
*
|
|
733
699
|
* @example
|
|
734
700
|
* ```ts
|
|
735
701
|
* actions.useAction(Actions.FetchPosts, async (context) => {
|
|
736
|
-
* const user = await context.actions.
|
|
702
|
+
* const user = await context.actions.resolution(Actions.Broadcast.User);
|
|
737
703
|
* if (!user) return;
|
|
738
704
|
* const posts = await fetchPosts(user.id, {
|
|
739
705
|
* signal: context.task.controller.signal,
|
|
@@ -742,15 +708,15 @@ export type HandlerContext<M extends Model | void, _AC extends Actions | void, D
|
|
|
742
708
|
* });
|
|
743
709
|
* ```
|
|
744
710
|
*/
|
|
745
|
-
|
|
746
|
-
|
|
711
|
+
resolution<T>(action: BroadcastPayload<T>): Promise<T | null>;
|
|
712
|
+
resolution<T>(action: MulticastPayload<T>, options: MulticastOptions): Promise<T | null>;
|
|
747
713
|
/**
|
|
748
714
|
* Returns the latest broadcast or multicast value immediately without
|
|
749
715
|
* waiting for annotations to settle. Use this when you need the current
|
|
750
716
|
* cached value and do not need to wait for pending operations to complete.
|
|
751
717
|
*
|
|
752
718
|
* @param action - The broadcast or multicast action to peek at.
|
|
753
|
-
* @param options - For multicast actions, must include `{ scope:
|
|
719
|
+
* @param options - For multicast actions, must include `{ scope: MulticastActions }`.
|
|
754
720
|
* @returns The cached value, or `null` if no value has been dispatched.
|
|
755
721
|
*
|
|
756
722
|
* @example
|
|
@@ -854,14 +820,14 @@ export type UseActions<M extends Model | void, AC extends Actions | void, D exte
|
|
|
854
820
|
/**
|
|
855
821
|
* Dispatches an action with an optional payload.
|
|
856
822
|
*
|
|
857
|
-
* For multicast actions, you MUST provide the scope as the third argument:
|
|
823
|
+
* For multicast actions, you MUST provide the scope carrier as the third argument:
|
|
858
824
|
* ```ts
|
|
859
|
-
* actions.dispatch(Actions.Multicast.Update, payload, { scope:
|
|
825
|
+
* actions.dispatch(Actions.Multicast.Update, payload, { scope: Actions.Multicast });
|
|
860
826
|
* ```
|
|
861
827
|
*
|
|
862
828
|
* @param action - The action to dispatch
|
|
863
829
|
* @param payload - The payload to send with the action
|
|
864
|
-
* @param options - For multicast actions, must include `{ scope:
|
|
830
|
+
* @param options - For multicast actions, must include `{ scope: MulticastActions }`
|
|
865
831
|
*/
|
|
866
832
|
dispatch<P>(action: HandlerPayload<P>, payload?: P, options?: MulticastOptions): Promise<void>;
|
|
867
833
|
dispatch<P>(action: BroadcastPayload<P>, payload?: P, options?: MulticastOptions): Promise<void>;
|
|
@@ -975,4 +941,22 @@ export type UseActions<M extends Model | void, AC extends Actions | void, D exte
|
|
|
975
941
|
* ```
|
|
976
942
|
*/
|
|
977
943
|
useAction<A extends ActionId | HandlerPayload | ChanneledAction>(action: A, handler: (context: HandlerContext<M, AC, D>, ...args: [Payload<A>] extends [never] ? [] : [payload: Payload<A>]) => void | Promise<void> | AsyncGenerator | Generator): void;
|
|
944
|
+
/**
|
|
945
|
+
* Connects a {@link Resource} declared at module scope to this component.
|
|
946
|
+
* Returns a thunk that fetches fresh data on every call – concurrent
|
|
947
|
+
* calls share the in-flight promise. The Resource's `onSuccess` and
|
|
948
|
+
* `onError` callbacks receive `(response, data, dispatch)` where `data`
|
|
949
|
+
* is this component's reactive `data` proxy.
|
|
950
|
+
*
|
|
951
|
+
* @example
|
|
952
|
+
* ```ts
|
|
953
|
+
* const fetchUser = actions.useResource(resources.user);
|
|
954
|
+
*
|
|
955
|
+
* actions.useAction(Actions.Mount, async (context) => {
|
|
956
|
+
* const data = await fetchUser();
|
|
957
|
+
* context.actions.produce(({ model }) => { model.user = data; });
|
|
958
|
+
* });
|
|
959
|
+
* ```
|
|
960
|
+
*/
|
|
961
|
+
useResource<T, E, Args extends readonly unknown[]>(resource: import('../resource/index.ts').ResourceHandle<T, E, Args>): (...args: Args) => Promise<T>;
|
|
978
962
|
};
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,21 +1,4 @@
|
|
|
1
1
|
import { Pk } from '../types/index.ts';
|
|
2
|
-
/**
|
|
3
|
-
* Configuration constants for Chizu action symbols.
|
|
4
|
-
*/
|
|
5
|
-
export declare const config: {
|
|
6
|
-
/** Prefix for all Chizu action symbols. */
|
|
7
|
-
actionPrefix: string;
|
|
8
|
-
/** Prefix for broadcast action symbols. */
|
|
9
|
-
broadcastActionPrefix: string;
|
|
10
|
-
/** Prefix for multicast action symbols. */
|
|
11
|
-
multicastActionPrefix: string;
|
|
12
|
-
/** Prefix for channeled action symbols. */
|
|
13
|
-
channelPrefix: string;
|
|
14
|
-
/** Prefix for cache operation symbols. */
|
|
15
|
-
cachePrefix: string;
|
|
16
|
-
/** Prefix for lifecycle action symbols. */
|
|
17
|
-
lifecyclePrefix: string;
|
|
18
|
-
};
|
|
19
2
|
/**
|
|
20
3
|
* Returns a promise that resolves after the specified number of milliseconds.
|
|
21
4
|
* The sleep will reject with an AbortError when the signal is aborted,
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal symbol description factories. Each function returns a namespaced
|
|
3
|
+
* string suitable for `Symbol()` descriptions or `startsWith` checks.
|
|
4
|
+
*
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
export declare const describe: {
|
|
8
|
+
/** Unicast action description. `describe.action("Fetch")` → `"chizu.action/Fetch"` */
|
|
9
|
+
action: (name?: string) => string;
|
|
10
|
+
/** Broadcast action description. `describe.broadcast("User")` → `"chizu.action/broadcast/User"` */
|
|
11
|
+
broadcast: (name?: string) => string;
|
|
12
|
+
/** Multicast action description. `describe.multicast("Update")` → `"chizu.action/multicast/Update"` */
|
|
13
|
+
multicast: (name?: string) => string;
|
|
14
|
+
/** Channeled action description. `describe.channel("user")` → `"chizu.channel/user"` */
|
|
15
|
+
channel: (name?: string) => string;
|
|
16
|
+
/** Cache entry description. `describe.cache("users")` → `"chizu.cache/users"` */
|
|
17
|
+
cache: (name?: string) => string;
|
|
18
|
+
/** Lifecycle action description. `describe.lifecycle("Mount")` → `"chizu.action.lifecycle/Mount"` */
|
|
19
|
+
lifecycle: (name?: string) => string;
|
|
20
|
+
/** Mount replay sentinel description. Used to create the {@link replay} symbol. */
|
|
21
|
+
replay: (name?: string) => string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Flat record used for shallow property comparison in {@link changes}.
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
1
27
|
type Changes = Record<string, unknown>;
|
|
2
28
|
/**
|
|
3
29
|
* Get high-level changed paths between two objects.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chizu",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"packageManager": "yarn@1.22.22",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"@testing-library/dom": "^10.4.1",
|
|
48
48
|
"@testing-library/jest-dom": "^6.9.1",
|
|
49
49
|
"@testing-library/react": "^16.3.2",
|
|
50
|
+
"@types/dom-navigation": "^1.0.7",
|
|
50
51
|
"@types/lodash": "^4.17.23",
|
|
51
52
|
"@types/ramda": "^0.31.1",
|
|
52
53
|
"@types/react": "^19.2.14",
|
|
@@ -70,6 +71,7 @@
|
|
|
70
71
|
"jest": "^30.2.0",
|
|
71
72
|
"jest-environment-jsdom": "^30.2.0",
|
|
72
73
|
"jsdom": "^28.1.0",
|
|
74
|
+
"ky": "^2.0.2",
|
|
73
75
|
"lodash": "^4.17.23",
|
|
74
76
|
"lucide-react": "^0.564.0",
|
|
75
77
|
"madge": "^8.0.0",
|
|
@@ -80,6 +82,7 @@
|
|
|
80
82
|
"react-flip-numbers": "^3.0.9",
|
|
81
83
|
"react-router-dom": "^7.13.0",
|
|
82
84
|
"react-test-renderer": "^19.2.4",
|
|
85
|
+
"react-wayfinder": "^0.1.3",
|
|
83
86
|
"rollup-plugin-visualizer": "^6.0.5",
|
|
84
87
|
"terser": "^5.46.0",
|
|
85
88
|
"traverse": "^0.6.11",
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { Props } from './types.ts';
|
|
2
|
-
import * as React from "react";
|
|
3
|
-
export { useCacheStore } from './utils.ts';
|
|
4
|
-
export type { CacheContext } from './types.ts';
|
|
5
|
-
/**
|
|
6
|
-
* Creates a new cache context for storing values from `context.actions.cacheable`.
|
|
7
|
-
* Automatically included in `<Boundary>`. Only needed directly if you want to
|
|
8
|
-
* isolate a cache context.
|
|
9
|
-
*
|
|
10
|
-
* @param props.children - The children to render within the cache context.
|
|
11
|
-
* @returns The children wrapped in a cache context provider.
|
|
12
|
-
*/
|
|
13
|
-
export declare function CacheProvider({ children }: Props): React.ReactNode;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type * as React from "react";
|
|
2
|
-
/**
|
|
3
|
-
* A single cached value with its expiry timestamp.
|
|
4
|
-
*/
|
|
5
|
-
export type CacheItem = {
|
|
6
|
-
value: unknown;
|
|
7
|
-
expiry: number;
|
|
8
|
-
};
|
|
9
|
-
/**
|
|
10
|
-
* Store for cached values.
|
|
11
|
-
* Keyed by a deterministic string derived from the entry symbol and channel.
|
|
12
|
-
*/
|
|
13
|
-
export type CacheContext = Map<string, CacheItem>;
|
|
14
|
-
/**
|
|
15
|
-
* Props for the CacheProvider component.
|
|
16
|
-
*/
|
|
17
|
-
export type Props = {
|
|
18
|
-
children: React.ReactNode;
|
|
19
|
-
};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { CacheContext } from './types.ts';
|
|
2
|
-
import * as React from "react";
|
|
3
|
-
/**
|
|
4
|
-
* React context for the shared cache store.
|
|
5
|
-
*/
|
|
6
|
-
export declare const Context: React.Context<CacheContext>;
|
|
7
|
-
/**
|
|
8
|
-
* Hook to access the cache store from context.
|
|
9
|
-
*
|
|
10
|
-
* @returns The cache Map from the nearest CacheProvider.
|
|
11
|
-
*/
|
|
12
|
-
export declare function useCacheStore(): CacheContext;
|