march-hare 0.12.1 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/README.md +66 -25
  2. package/dist/action/index.d.ts +2 -2
  3. package/dist/action/utils.d.ts +2 -2
  4. package/dist/actions/index.d.ts +2 -2
  5. package/dist/actions/types.d.ts +3 -3
  6. package/dist/actions/utils.d.ts +3 -3
  7. package/dist/app/index.d.ts +33 -87
  8. package/dist/app/types.d.ts +79 -26
  9. package/dist/boundary/components/broadcast/index.d.ts +2 -2
  10. package/dist/boundary/components/broadcast/types.d.ts +1 -1
  11. package/dist/boundary/components/consumer/components/partition/index.d.ts +1 -1
  12. package/dist/boundary/components/consumer/components/partition/types.d.ts +1 -1
  13. package/dist/boundary/components/consumer/index.d.ts +5 -5
  14. package/dist/boundary/components/consumer/types.d.ts +1 -1
  15. package/dist/boundary/components/consumer/utils.d.ts +1 -1
  16. package/dist/boundary/components/env/index.d.ts +3 -16
  17. package/dist/boundary/components/env/types.d.ts +24 -2
  18. package/dist/boundary/components/env/utils.d.ts +1 -1
  19. package/dist/boundary/components/scope/index.d.ts +2 -2
  20. package/dist/boundary/components/scope/types.d.ts +1 -1
  21. package/dist/boundary/components/scope/utils.d.ts +1 -1
  22. package/dist/boundary/components/sharing/index.d.ts +3 -3
  23. package/dist/boundary/components/tap/index.d.ts +3 -3
  24. package/dist/boundary/components/tap/types.d.ts +2 -2
  25. package/dist/boundary/components/tap/utils.d.ts +1 -1
  26. package/dist/boundary/components/tasks/index.d.ts +2 -2
  27. package/dist/boundary/components/tasks/utils.d.ts +1 -1
  28. package/dist/boundary/index.d.ts +3 -3
  29. package/dist/boundary/types.d.ts +3 -3
  30. package/dist/cache/index.d.ts +68 -12
  31. package/dist/cache/types.d.ts +33 -19
  32. package/dist/cli/bin/mh.js +10 -0
  33. package/dist/cli/lib/banner/index.js +14 -0
  34. package/dist/cli/lib/commands/app/index.js +37 -0
  35. package/dist/cli/lib/commands/feature/index.js +55 -0
  36. package/dist/cli/lib/commands/index.js +89 -0
  37. package/dist/cli/lib/commands/init/index.js +29 -0
  38. package/dist/cli/lib/commands/shared/index.js +56 -0
  39. package/dist/cli/lib/index.js +56 -0
  40. package/dist/cli/lib/parser/index.js +24 -0
  41. package/dist/cli/lib/prompt/index.js +61 -0
  42. package/dist/cli/lib/runner/index.js +46 -0
  43. package/dist/cli/lib/runner/types.js +1 -0
  44. package/dist/cli/lib/runner/utils.js +60 -0
  45. package/dist/cli/lib/types.js +1 -0
  46. package/dist/cli/lib/utils.js +20 -0
  47. package/dist/cli/templates/app/action/actions.ts.ejs.t +10 -0
  48. package/dist/cli/templates/app/action/types.ts.ejs.t +7 -0
  49. package/dist/cli/templates/app/integration/index.integration.tsx.ejs.t +13 -0
  50. package/dist/cli/templates/app/page/actions.ts.ejs.t +14 -0
  51. package/dist/cli/templates/app/page/index.tsx.ejs.t +20 -0
  52. package/dist/cli/templates/app/page/styles.ts.ejs.t +35 -0
  53. package/dist/cli/templates/app/page/types.ts.ejs.t +12 -0
  54. package/dist/cli/templates/feature/action/actions.ts.ejs.t +10 -0
  55. package/dist/cli/templates/feature/action/types.ts.ejs.t +7 -0
  56. package/dist/cli/templates/feature/multicast/types.ts.ejs.t +7 -0
  57. package/dist/cli/templates/feature/presentational/index.tsx.ejs.t +14 -0
  58. package/dist/cli/templates/feature/presentational/types.ts.ejs.t +12 -0
  59. package/dist/cli/templates/feature/presentational/utils.ts.ejs.t +8 -0
  60. package/dist/cli/templates/feature/stateful/actions.ts.ejs.t +16 -0
  61. package/dist/cli/templates/feature/stateful/index.tsx.ejs.t +19 -0
  62. package/dist/cli/templates/feature/stateful/types.ts.ejs.t +16 -0
  63. package/dist/cli/templates/feature/stateful/utils.ts.ejs.t +8 -0
  64. package/dist/cli/templates/feature/unit/index.test.tsx.ejs.t +21 -0
  65. package/dist/cli/templates/init/new/README.md.ejs.t +48 -0
  66. package/dist/cli/templates/init/new/eslint.config.js.ejs.t +88 -0
  67. package/dist/cli/templates/init/new/gitignore.ejs.t +9 -0
  68. package/dist/cli/templates/init/new/index.html.ejs.t +18 -0
  69. package/dist/cli/templates/init/new/package.json.ejs.t +54 -0
  70. package/dist/cli/templates/init/new/playwright.config.ts.ejs.t +17 -0
  71. package/dist/cli/templates/init/new/prettierrc.ejs.t +8 -0
  72. package/dist/cli/templates/init/new/src.app.index.tsx.ejs.t +14 -0
  73. package/dist/cli/templates/init/new/src.app.pages.home.actions.ts.ejs.t +16 -0
  74. package/dist/cli/templates/init/new/src.app.pages.home.index.tsx.ejs.t +30 -0
  75. package/dist/cli/templates/init/new/src.app.pages.home.integration.tsx.ejs.t +28 -0
  76. package/dist/cli/templates/init/new/src.app.pages.home.styles.ts.ejs.t +45 -0
  77. package/dist/cli/templates/init/new/src.app.pages.home.types.ts.ejs.t +12 -0
  78. package/dist/cli/templates/init/new/src.app.utils.ts.ejs.t +9 -0
  79. package/dist/cli/templates/init/new/src.features.greet.actions.ts.ejs.t +20 -0
  80. package/dist/cli/templates/init/new/src.features.greet.index.test.tsx.ejs.t +21 -0
  81. package/dist/cli/templates/init/new/src.features.greet.index.tsx.ejs.t +24 -0
  82. package/dist/cli/templates/init/new/src.features.greet.types.ts.ejs.t +18 -0
  83. package/dist/cli/templates/init/new/src.features.greet.utils.ts.ejs.t +8 -0
  84. package/dist/cli/templates/init/new/src.index.tsx.ejs.t +8 -0
  85. package/dist/cli/templates/init/new/src.shared.components.button.index.test.tsx.ejs.t +13 -0
  86. package/dist/cli/templates/init/new/src.shared.components.button.index.tsx.ejs.t +10 -0
  87. package/dist/cli/templates/init/new/src.shared.components.button.types.ts.ejs.t +6 -0
  88. package/dist/cli/templates/init/new/src.shared.resources.index.ts.ejs.t +4 -0
  89. package/dist/cli/templates/init/new/src.shared.theme.index.ts.ejs.t +51 -0
  90. package/dist/cli/templates/init/new/src.shared.types.index.ts.ejs.t +23 -0
  91. package/dist/cli/templates/init/new/src.test-setup.ts.ejs.t +10 -0
  92. package/dist/cli/templates/init/new/src.vite-env.d.ts.ejs.t +4 -0
  93. package/dist/cli/templates/init/new/tests.home.e2e.ts.ejs.t +14 -0
  94. package/dist/cli/templates/init/new/tsconfig.json.ejs.t +29 -0
  95. package/dist/cli/templates/init/new/vite.config.ts.ejs.t +17 -0
  96. package/dist/cli/templates/init/new/vitest.config.ts.ejs.t +24 -0
  97. package/dist/cli/templates/shared/component/index.tsx.ejs.t +9 -0
  98. package/dist/cli/templates/shared/component/types.ts.ejs.t +8 -0
  99. package/dist/cli/templates/shared/resource/index.ts.ejs.t +15 -0
  100. package/dist/cli/templates/shared/resource/types.ts.ejs.t +10 -0
  101. package/dist/cli/templates/shared/type-broadcast/types.ts.ejs.t +7 -0
  102. package/dist/cli/templates/shared/type-payload/types.ts.ejs.t +9 -0
  103. package/dist/cli/templates/shared/unit-component/index.test.tsx.ejs.t +13 -0
  104. package/dist/cli/templates/shared/unit-resource/index.test.ts.ejs.t +15 -0
  105. package/dist/cli/templates/shared/unit-util/index.test.ts.ejs.t +11 -0
  106. package/dist/cli/templates/shared/util/index.ts.ejs.t +6 -0
  107. package/dist/coalesce/index.d.ts +1 -1
  108. package/dist/context/index.d.ts +2 -2
  109. package/dist/error/index.d.ts +18 -1
  110. package/dist/error/types.d.ts +1 -18
  111. package/dist/error/utils.d.ts +1 -1
  112. package/dist/index.d.ts +16 -14
  113. package/dist/march-hare.js +7 -6
  114. package/dist/march-hare.js.map +1 -0
  115. package/dist/march-hare.umd.cjs +2 -1
  116. package/dist/march-hare.umd.cjs.map +1 -0
  117. package/dist/resource/index.d.ts +32 -61
  118. package/dist/resource/types.d.ts +45 -22
  119. package/dist/resource/utils.d.ts +31 -3
  120. package/dist/scope/index.d.ts +4 -64
  121. package/dist/scope/types.d.ts +8 -8
  122. package/dist/scope/utils.d.ts +12 -0
  123. package/dist/shared/index.d.ts +12 -21
  124. package/dist/types/index.d.ts +114 -29
  125. package/dist/utils/index.d.ts +3 -3
  126. package/dist/utils/types.d.ts +1 -3
  127. package/dist/utils/utils.d.ts +1 -3
  128. package/dist/with/index.d.ts +17 -62
  129. package/dist/with/types.d.ts +66 -0
  130. package/dist/with/utils.d.ts +61 -0
  131. package/package.json +21 -4
  132. package/src/cli/README.md +314 -0
@@ -1,22 +1,27 @@
1
- import { PendingCall, ResourceHandle } from './types';
2
- import { Cache } from './utils';
3
- import { AppFetcher } from '../app/types';
4
- export type { Coalesce, Fetcher, PendingCall, ResourceHandle, } from './types';
1
+ import { ResourceHandle } from './types.js';
2
+ import { Cache } from './utils.js';
3
+ import { AppFetcher } from '../app/types.js';
4
+ export type { Coalesce, Fetcher, Invocation, ResourceHandle } from './types.js';
5
5
  /**
6
- * Reads and clears the slot populated by the most recent resource
7
- * invocation. Throws when the slot is empty — the public
8
- * `.resource(...)` shape requires a fresh `resource.cat(params)` call
9
- * as its argument.
6
+ * Evicts cache entries across every Resource constructed in the
7
+ * current process. Resources register themselves on declaration, so
8
+ * `nuke` covers both `app.Resource` and `shared.Resource`. Pass a
9
+ * `where` pattern to drop only slots whose stored params satisfy the
10
+ * pattern's keys (partial match — extra keys in the stored
11
+ * params are ignored). Pass nothing to clear every known slot.
10
12
  *
11
- * @internal
13
+ * @internal Public surface lives on `context.actions.resource.nuke(...)`.
12
14
  */
13
- export declare function consumePending(): PendingCall;
15
+ export declare function nuke(where?: object): void;
14
16
  /**
15
17
  * Defines a remote resource — declared at module scope and used
16
- * directly. Exported as `shared.Resource`. Calling the returned handle
17
- * with `params` returns the sync cache value (`T | null`) and primes
18
- * the slot consumed by `context.actions.resource(...)` / `.set(...)`
19
- * for fetch and write paths.
18
+ * directly. Exported as `shared.Resource` and (via the app factory) as
19
+ * `app.Resource`. Calling the returned handle with `params` produces an
20
+ * {@link Invocation} suitable for `context.actions.resource(...)` (fetch
21
+ * path) or `context.actions.resource(...).evict(where?)` (partial-match
22
+ * invalidation). Use `.get(params)` on the handle for a synchronous
23
+ * cache read returning `T | null`. Persistence happens automatically
24
+ * when the App is declared with `App({ cache })`.
20
25
  *
21
26
  * Takes the **Env shape `E` as a mandatory first generic** —
22
27
  * `context.env` inside the fetcher is typed as `E`. Pass a union of
@@ -29,8 +34,14 @@ export declare function consumePending(): PendingCall;
29
34
  * `controller`, `params`, and a broadcast/multicast-only `dispatch`.
30
35
  * `env` is a live handle — dot reads inside the fetcher always
31
36
  * see the latest per-`<Boundary>` Env, even after `await` boundaries.
32
- * Every successful fetch writes through to a per-resource in-memory
33
- * cache; pair with {@link Resource.Cachable} to persist across reloads.
37
+ *
38
+ * Cache behaviour is decided at the App level: when `App({ cache })`
39
+ * is supplied, every `app.Resource` declaration on that App writes
40
+ * through to (and seeds from) the shared cache, isolated per resource
41
+ * by a stable module-order namespace. When the App is constructed
42
+ * without a `cache`, every resource keeps its own in-memory slot.
43
+ * Standalone `shared.Resource` declarations always use an in-memory
44
+ * cache &mdash; reach for `app.Resource` when persistence is required.
34
45
  *
35
46
  * Concurrent calls fire fresh requests by default. Opt in to in-flight
36
47
  * sharing per call via `.coalesce(key)` on the thenable returned from
@@ -58,49 +69,9 @@ export declare function consumePending(): PendingCall;
58
69
  * .json<User>(),
59
70
  * );
60
71
  * ```
72
+ *
73
+ * @internal The optional `cache` argument is reserved for `app.Resource`
74
+ * &mdash; consumers should use `App({ cache })` instead of passing it
75
+ * directly.
61
76
  */
62
- export declare function Resource<E extends object, T, P extends object = Record<never, never>>(ƒ: AppFetcher<E, T, P>): ResourceHandle<T, P>;
63
- export declare namespace Resource {
64
- /**
65
- * Cache-aware variant of {@link Resource}, exported as
66
- * `shared.Resource.Cachable`. The supplied {@link Cache} is the
67
- * **second** argument (after the Env generic) &mdash; persistence is
68
- * the headline of this form, the fetcher is the operation. Every
69
- * successful fetch writes through to the cache; first reads via the
70
- * call form auto-seed from the cache's adapter.
71
- *
72
- * Takes the same **Env shape `E` as a mandatory first generic** as
73
- * {@link Resource}. For single-app resources, prefer
74
- * `app.Resource.Cachable(cache, fetcher)` &mdash; the Env is captured
75
- * from `app` automatically.
76
- *
77
- * @template E The Env shape (or union) the fetcher's `context.env` is
78
- * typed against.
79
- * @template T The payload type the fetcher resolves to.
80
- * @template P The call-time params type.
81
- *
82
- * @example
83
- * ```ts
84
- * import { Cache, shared } from "march-hare";
85
- *
86
- * type WebEnv = { session: Session | null };
87
- *
88
- * const cache = Cache({
89
- * get: (key) => localStorage.getItem(key),
90
- * set: (key, value) => localStorage.setItem(key, value),
91
- * remove: (key) => localStorage.removeItem(key),
92
- * clear: () => localStorage.clear(),
93
- * });
94
- *
95
- * export const cat = shared.Resource.Cachable<WebEnv, Cat>(cache, async (context) =>
96
- * ky
97
- * .get("https://api.thecatapi.com/v1/images/search", {
98
- * signal: context.controller.signal,
99
- * })
100
- * .json<Cat[]>()
101
- * .then((cats) => cats[0]),
102
- * );
103
- * ```
104
- */
105
- function Cachable<E extends object, T, P extends object = Record<never, never>>(cache: Cache, ƒ: AppFetcher<E, T, P>): ResourceHandle<T, P>;
106
- }
77
+ export declare function Resource<E extends object, T, P extends object = Record<never, never>>(ƒ: AppFetcher<E, T, P>, cache?: Cache): ResourceHandle<T, P>;
@@ -1,6 +1,6 @@
1
- import { Env } from '../boundary/components/env/index';
2
- import { Cache } from '../cache/index';
3
- import { BroadcastPayload, MulticastPayload, Filter } from '../types/index';
1
+ import { Env } from '../boundary/components/env/types.js';
2
+ import { Cache } from '../cache/index.js';
3
+ import { BroadcastPayload, MulticastPayload, Filter } from '../types/index.js';
4
4
  /**
5
5
  * Dispatch surface exposed on a Resource fetcher's `context`. Restricted
6
6
  * to broadcast and multicast actions &mdash; unicast targets the calling
@@ -62,42 +62,65 @@ export type Config<T, P extends object = Record<never, never>> = {
62
62
  readonly cache?: Cache;
63
63
  };
64
64
  /**
65
- * Snapshot of the most recent resource invocation. `resource.cat(params)`
66
- * writes one of these into a module-scope slot; the next
67
- * `context.actions.resource(...)` / `.set(...)` call consumes it via
68
- * `consumePending` and then clears the slot.
65
+ * Descriptor produced by calling a Resource handle. Carries the per-call
66
+ * `params` together with the closures `context.actions.resource(...)`
67
+ * needs to run, read, or evict the slot. Pass it straight to
68
+ * `context.actions.resource(invocation)` &mdash; no module-level state
69
+ * sits between the producer and the consumer, so two synchronous calls
70
+ * to the same Resource are independent values that can be stored,
71
+ * deferred, or passed across `await` boundaries safely.
69
72
  *
70
73
  * @internal
71
74
  */
72
- export type PendingCall = {
73
- readonly run: (env: Env, controller: AbortController, params: object, dispatch: Dispatch) => Promise<unknown>;
75
+ export type Invocation<T, P extends object = Record<never, never>> = {
76
+ readonly run: (env: Env, controller: AbortController, params: object, dispatch: Dispatch) => Promise<T>;
74
77
  readonly read: (params: object) => {
75
- data: unknown;
78
+ data: T | symbol;
76
79
  at: Temporal.Instant | null;
77
80
  };
78
- readonly seed: (params: object, data: unknown, at: Temporal.Instant) => void;
79
- readonly params: object;
81
+ readonly evict: (where: object) => void;
82
+ readonly params: P;
80
83
  };
81
84
  /**
82
- * Resource handle returned by `Resource(...)` or `Resource.Cachable(...)`.
83
- * Call it with `params` to read the per-params cache slot synchronously
84
- * and prime the slot consumed by `context.actions.resource(...)` for a
85
- * follow-up fetch or `context.actions.resource.set(...)` for an
86
- * out-of-band write.
85
+ * Resource handle returned by `Resource(...)` (or its `app.Resource` /
86
+ * `shared.Resource` counterparts). Call it with `params` to produce an
87
+ * {@link Invocation} suitable for `context.actions.resource(...)`. Use
88
+ * `.get(params)` for a synchronous cache read.
89
+ *
90
+ * - `resource.cat.get({id: 5})` &mdash; sync read, returns `T | null`.
91
+ * - `context.actions.resource(resource.cat({id: 5}))` &mdash; fetch.
92
+ * - `context.actions.resource(resource.cat({id: 5})).evict()` &mdash;
93
+ * drop the `{id: 5}` slot.
94
+ * - `context.actions.resource(resource.cat()).evict({name: "Adam"})`
95
+ * &mdash; evict every cached `cat` entry whose stored params include
96
+ * `name: "Adam"`, regardless of other keys.
97
+ * - `context.actions.resource.nuke({id: 5})` &mdash; partial-match
98
+ * eviction across every resource on the App; nuke with no argument
99
+ * clears every known slot.
87
100
  *
88
101
  * ```ts
89
102
  * // Sync cache read in a model literal.
90
- * { cat: resource.cat({ id: 5 }) }
103
+ * { cat: resource.cat.get({ id: 5 }) }
91
104
  *
92
105
  * // Fetch with `.exceeds(...)` for cache-aware refresh.
93
106
  * await context.actions
94
107
  * .resource(resource.cat({ id: 5 }))
95
108
  * .exceeds({ minutes: 5 });
96
109
  *
97
- * // Write through to the per-params cache slot.
98
- * context.actions.resource.set(resource.cat({ id: 5 }), data);
110
+ * // Evict the {id: 5} slot.
111
+ * context.actions.resource(resource.cat({ id: 5 })).evict();
99
112
  * ```
100
113
  */
101
- export type ResourceHandle<T, P extends object = Record<never, never>> = [
114
+ export type ResourceHandle<T, P extends object = Record<never, never>> = ([
102
115
  keyof P
103
- ] extends [never] ? (params?: P) => T | null : (params: P) => T | null;
116
+ ] extends [never] ? (params?: P) => Invocation<T, P> : (params: P) => Invocation<T, P>) & {
117
+ readonly get: [keyof P] extends [never] ? (params?: P) => T | null : (params: P) => T | null;
118
+ };
119
+ /**
120
+ * Drops cache slots whose stored params match the supplied `where`
121
+ * pattern. Each Resource registers one of these on declaration so
122
+ * `nuke(where)` can iterate them.
123
+ *
124
+ * @internal
125
+ */
126
+ export type ResourceEvictor = (where: object) => void;
@@ -1,6 +1,7 @@
1
- import { Cache } from '../cache/index';
2
- import { unset } from '../utils/index';
3
- export { Cache } from '../cache/index';
1
+ import { Cache } from '../cache/index.js';
2
+ import { unset } from '../utils/utils.js';
3
+ import { Fetcher, ResourceEvictor, ResourceHandle } from './types.js';
4
+ export { Cache } from '../cache/index.js';
4
5
  /**
5
6
  * Default in-memory `Cache` used when {@link Resource} is constructed
6
7
  * without an explicit one. Each fetcher gets its own slot via the
@@ -35,3 +36,30 @@ export declare function key(params: object): string;
35
36
  export declare const config: {
36
37
  readonly unset: typeof unset;
37
38
  };
39
+ /**
40
+ * Per-Resource eviction callbacks. Each `Resource` declaration registers
41
+ * one entry on construction; the public `nuke(...)` (defined in
42
+ * {@link "./index.ts"}) iterates them to drop cache slots across every
43
+ * Resource in the process.
44
+ *
45
+ * @internal
46
+ */
47
+ export declare const evictors: Array<ResourceEvictor>;
48
+ /**
49
+ * Mints the next namespace id for an app-shared cache. Each `app.Resource`
50
+ * declaration consumes one id so the shared {@link Cache} can keep
51
+ * resource-specific slots from colliding on shared params keys.
52
+ *
53
+ * @internal
54
+ */
55
+ export declare function nextResourceId(fetcher: object): string;
56
+ /**
57
+ * Allocates the per-Resource closure shared by `app.Resource` and
58
+ * `shared.Resource`. The returned callable produces an
59
+ * {@link Invocation} on every call &mdash; pass it to
60
+ * `context.actions.resource(...)` for fetch/evict. `.get(params)` reads
61
+ * the per-params cache slot synchronously.
62
+ *
63
+ * @internal
64
+ */
65
+ export declare function build<T, P extends object>(ƒ: Fetcher<T, P>, backing: Cache, namespace: string | null): ResourceHandle<T, P>;
@@ -1,66 +1,6 @@
1
- import { AppContextHandle, AppResource } from '../app/types';
2
- import { Actions, Model, Props } from '../types/index';
3
- import * as React from "react";
4
- /**
5
- * Handle returned by `app.Scope<MulticastActions>()`. Mirrors the {@link App}
6
- * surface (`Boundary`, `useContext`, `useEnv`, `Resource`) but typed
7
- * against a specific multicast action surface `MulticastActions` and the
8
- * enclosing App's Env shape `S`.
9
- *
10
- * Notably absent: a nested `Scope` method. Nesting scopes is supported
11
- * at the React-tree level &mdash; just render two `<scope.Boundary>`s
12
- * &mdash; but each scope must come from a distinct
13
- * `app.Scope<MulticastActions>()` call so that its multicast surface is
14
- * declared up-front.
15
- *
16
- * @template S The enclosing App's Env shape.
17
- * @template MulticastActions The multicast Actions class (or union of
18
- * classes) this scope's `useContext().actions.dispatch` is allowed
19
- * to fire.
20
- */
21
- export type Scope<S extends object, MulticastActions> = {
22
- /**
23
- * Boundary component. Wrap a subtree to open a fresh multicast scope
24
- * &mdash; every `Distribution.Multicast` action dispatched inside this
25
- * subtree routes through this boundary's emitter, and every handler
26
- * subscribed via `scope.useContext().useActions(...)` on that subtree
27
- * receives the event.
28
- *
29
- * Each render of `<scope.Boundary>` opens a distinct scope instance;
30
- * unmounting tears the emitter down.
31
- */
32
- readonly Boundary: React.FC<{
33
- children: React.ReactNode;
34
- }>;
35
- /**
36
- * Hook returning a stable `Context` handle. Identical to
37
- * `app.useContext` except `actions.dispatch` accepts the multicast
38
- * surface `MulticastActions` in addition to the local `AC` &mdash; mirroring
39
- * the way `Actions.Broadcast = BroadcastActions` already widens the
40
- * dispatch surface for broadcasts.
41
- */
42
- readonly useContext: <LocalModel extends Model | void = void, AC extends Actions | void = void, D extends Props = Props>() => AppContextHandle<LocalModel, MulticastActions extends Actions ? AC extends Actions ? AC & MulticastActions : MulticastActions : AC, D, S>;
43
- /**
44
- * Read-only Proxy over the enclosing App's Env. Identical to
45
- * `app.useEnv` &mdash; the Scope does not introduce its own Env;
46
- * scopes are about multicast routing, not ambient state.
47
- */
48
- readonly useEnv: () => Readonly<S>;
49
- /**
50
- * Resource factory bound to the enclosing App's Env. Identical to
51
- * `app.Resource`; provided on the scope handle for convenience so a
52
- * scoped feature can keep all its primitives in one place.
53
- */
54
- readonly Resource: AppResource<S>;
55
- };
56
- /**
57
- * Internal constructor for a {@link Scope} handle. Called from inside
58
- * `App<S>()` so the enclosing Env shape `S` is captured at the type
59
- * level.
60
- *
61
- * @internal
62
- */
63
- export declare function createScope<S extends object, MulticastActions>(): Scope<S, MulticastActions>;
1
+ import { ScopeHandle } from './types.js';
2
+ export type { ScopeHandle } from './types.js';
3
+ export { createScope } from './utils.js';
64
4
  /**
65
5
  * Standalone counterpart to `app.Scope<MulticastActions>()`, exported
66
6
  * as `shared.Scope` &mdash; opens a typed multicast scope without
@@ -95,4 +35,4 @@ export declare function createScope<S extends object, MulticastActions>(): Scope
95
35
  * export const scope = shared.Scope<MoodEnv, typeof MulticastActions>();
96
36
  * ```
97
37
  */
98
- export declare function Scope<E extends object, A>(): Scope<E, A>;
38
+ export declare function Scope<E extends object, A>(): ScopeHandle<E, A>;
@@ -1,11 +1,11 @@
1
- import { AppContextHandle, AppResource } from '../app/types';
2
- import { Actions, Model, Props } from '../types/index';
1
+ import { AppContextHandle, AppResource } from '../app/types.js';
2
+ import { Actions, Model, Props } from '../types/index.js';
3
3
  import type * as React from "react";
4
4
  /**
5
5
  * Handle returned by `app.Scope<MulticastActions>()`. Mirrors the
6
6
  * `App` surface (`Boundary`, `useContext`, `useEnv`, `Resource`) but
7
7
  * typed against a specific multicast action surface `MulticastActions`
8
- * and the enclosing App's Env shape `S`.
8
+ * and the enclosing App's Env shape `E`.
9
9
  *
10
10
  * Notably absent: a nested `Scope` method. Nesting scopes is supported
11
11
  * at the React-tree level &mdash; just render two `<scope.Boundary>`s
@@ -13,12 +13,12 @@ import type * as React from "react";
13
13
  * `app.Scope<MulticastActions>()` call so that its multicast surface is
14
14
  * declared up-front.
15
15
  *
16
- * @template S The enclosing App's Env shape.
16
+ * @template E The enclosing App's Env shape.
17
17
  * @template MulticastActions The multicast Actions class (or union of
18
18
  * classes) this scope's `useContext().actions.dispatch` is allowed
19
19
  * to fire.
20
20
  */
21
- export type Scope<S extends object, MulticastActions> = {
21
+ export type ScopeHandle<E extends object, MulticastActions> = {
22
22
  /**
23
23
  * Boundary component. Wrap a subtree to open a fresh multicast scope
24
24
  * &mdash; every `Distribution.Multicast` action dispatched inside this
@@ -39,17 +39,17 @@ export type Scope<S extends object, MulticastActions> = {
39
39
  * mirroring the way `Actions.Broadcast = BroadcastActions` already
40
40
  * widens the dispatch surface for broadcasts.
41
41
  */
42
- readonly useContext: <LocalModel extends Model | void = void, AC extends Actions | void = void, D extends Props = Props>() => AppContextHandle<LocalModel, MulticastActions extends Actions ? AC extends Actions ? AC & MulticastActions : MulticastActions : AC, D, S>;
42
+ readonly useContext: <LocalModel extends Model | void = void, AC extends Actions | void = void, D extends Props = Props>() => AppContextHandle<LocalModel, MulticastActions extends Actions ? AC extends Actions ? AC & MulticastActions : MulticastActions : AC, D, E>;
43
43
  /**
44
44
  * Read-only Proxy over the enclosing App's Env. Identical to
45
45
  * `app.useEnv` &mdash; the Scope does not introduce its own Env;
46
46
  * scopes are about multicast routing, not ambient state.
47
47
  */
48
- readonly useEnv: () => Readonly<S>;
48
+ readonly useEnv: () => Readonly<E>;
49
49
  /**
50
50
  * Resource factory bound to the enclosing App's Env. Identical to
51
51
  * `app.Resource`; provided on the scope handle for convenience so a
52
52
  * scoped feature can keep all its primitives in one place.
53
53
  */
54
- readonly Resource: AppResource<S>;
54
+ readonly Resource: AppResource<E>;
55
55
  };
@@ -0,0 +1,12 @@
1
+ import { Cache } from '../cache/index.js';
2
+ import { ScopeHandle } from './types.js';
3
+ /**
4
+ * Internal constructor for a {@link ScopeHandle}. Called from inside
5
+ * `App<E>()` so the enclosing Env shape `E` is captured at the type
6
+ * level. The optional `cache` is the same value `App({ cache })` was
7
+ * constructed with &mdash; resources declared via `scope.Resource`
8
+ * share that cache.
9
+ *
10
+ * @internal
11
+ */
12
+ export declare function createScope<E extends object, MulticastActions>(cache?: Cache): ScopeHandle<E, MulticastActions>;
@@ -1,24 +1,15 @@
1
+ import { AppFetcher } from '../app/types.js';
2
+ import { ResourceHandle } from '../resource/types.js';
3
+ export { useContext, useEnv } from '../app/index.js';
4
+ export { Scope } from '../scope/index.js';
1
5
  /**
2
- * `shared` namespace &mdash; standalone counterparts to the `app.X`
3
- * factories returned by `App<E>()`. Each export takes the Env shape
4
- * `E` as a mandatory first generic so reusable components can run
5
- * under more than one App without binding to a single `app` import.
6
+ * Standalone counterpart to `app.Resource`, exported as
7
+ * `shared.Resource`. Takes the **Env shape `E` as a mandatory first
8
+ * generic** so the fetcher's `context.env` is typed even when the
9
+ * resource isn't bound to a single App.
6
10
  *
7
- * Reach for `shared.X` only when a component must support more than
8
- * one App. Single-app code should keep using `app.X` &mdash; the Env
9
- * is captured from `app` automatically and the call site is one
10
- * generic shorter.
11
- *
12
- * | Bound to an App | Standalone (`shared.X`) |
13
- * | -------------------------------- | ---------------------------------------- |
14
- * | `app.useContext<M, A, D>()` | `shared.useContext<E, M, A, D>()` |
15
- * | `app.useEnv()` | `shared.useEnv<E>()` |
16
- * | `app.Resource<T, P>(...)` | `shared.Resource<E, T, P>(...)` |
17
- * | `app.Resource.Cachable(...)` | `shared.Resource.Cachable<E, T, P>(...)` |
18
- * | `app.Scope<A>()` | `shared.Scope<E, A>()` |
19
- *
20
- * @see {@link ./app/index.tsx App}
11
+ * Always uses an isolated in-memory cache &mdash; persistent caching
12
+ * is an App-level concern wired through `App({ cache })`, so reach for
13
+ * `app.Resource` when a resource needs to survive reloads.
21
14
  */
22
- export { useContext, useEnv } from '../app/index';
23
- export { Scope } from '../scope/index';
24
- export { Resource } from '../resource/index';
15
+ export declare function Resource<E extends object, T, P extends object = Record<never, never>>(fetcher: AppFetcher<E, T, P>): ResourceHandle<T, P>;