@magicdoor/magic-use-case-react 0.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,99 @@
1
+ # @magicdoor/magic-use-case-react
2
+
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - First release of `magic-use-case` — clean-architecture use cases and
8
+ presentations for front-end TypeScript, consolidating the previous
9
+ `solid-use-case`, `solid-use-case-core` and `solid-use-case-react` packages
10
+ into one project.
11
+
12
+ **Use cases and presentations.** `UseCase` holds application logic; a
13
+ `Presentation` — a pure function from state to a view model — is what a screen
14
+ renders. Both are framework-agnostic, bound to your framework by a thin
15
+ adapter: `useUseCase`, `usePresenter`, `ErrorHandler` and `Navigator`.
16
+
17
+ **A presentation is a value, not a subclass.** `usePresenter(presentTenants)`
18
+ takes the function itself, so two screens that need the same rule share it by
19
+ calling the same function, and one presentation can back several presenters.
20
+ `Presenter` is exported as infrastructure — it holds a presentation and reruns
21
+ it on every state change — not as a base class to extend. Presenters given the
22
+ same presentation share one run of it: the model is built once per state
23
+ change and handed to all of them. Sharing is by the identity of the function
24
+ object, so it survives minification, which mangles names and can leave two
25
+ unrelated functions sharing one. The model handed to a component is readonly —
26
+ `usePresenter` returns `DeepReadonly<TModel>`, enforced by the compiler and at
27
+ runtime — since one model is shared by every screen using that presentation. A
28
+ presentation is only ever called with state: `resetAppState()` empties every
29
+ model directly, rather than asking each presentation to map an absence. One that throws anyway
30
+ is reported to the console and leaves its model empty, rather than failing the
31
+ render or the emit that other screens are waiting on.
32
+
33
+ **A use case can run other use cases.** A run announces its work unless
34
+ somebody is waiting on it: a nested run stays silent and its caller announces
35
+ everything written beneath it, once. Two flows a screen started independently
36
+ announce independently, so a long initialization never holds back the fetch a
37
+ page made for itself. Navigation is not held back either, so a nested use case
38
+ still moves the screen the moment it decides to.
39
+
40
+ **Failures propagate.** `execute()` rejects, so a nested failure aborts its
41
+ caller and a sequence of steps is a sequence. The failure reaches `onError`
42
+ exactly once, from the outermost run. A caller that catches decides what the
43
+ failure means and only what it throws is reported; a caller that handles it
44
+ reports nothing, and `report()` is there for the case that recovers but still
45
+ wants the screen to know. A use case that writes state and then throws still
46
+ announces what it wrote.
47
+
48
+ **State is only mutable inside a use case.** `getState()` returns a deep proxy
49
+ that accepts writes only while a use case runs. Writing elsewhere throws rather
50
+ than silently desyncing the UI, since such a write emits no state-change event.
51
+ A presentation receives a fully readonly view and can never write.
52
+
53
+ **In-place or immutable, your choice.** Mutating state in place and replacing
54
+ branches with new frozen values are both supported, including in the same use
55
+ case. State built with `Object.freeze` reads back correctly.
56
+
57
+ **State is adopted, not borrowed.** The object returned from `initializeState()`
58
+ is deep-cloned, so the caller's reference is no longer live state. The clone
59
+ preserves prototypes, keeping class-based state class-based. `#private` fields
60
+ cannot be cloned — use TypeScript `private` or a `_` prefix.
61
+
62
+ **Bootstrapping happens exactly once**, decided by the library rather than by
63
+ each use case, so no use case can replace live state by accident.
64
+ `resetAppState()` — protected, callable only from within a running use case — is
65
+ the one way to clear state and bootstrap again.
66
+
67
+ **The state type is pinned to the application's.** An application names its
68
+ state type once by augmenting `MagicUseCaseTypes`, and `usePresenter` then
69
+ accepts only presentations written against it. Without that, a presentation's
70
+ state type is whatever the presentation claims, and one written against the
71
+ wrong shape compiles cleanly and fails on the screen.
72
+
73
+ **Server-side rendering** resolves a scope per request, so two requests never
74
+ share state, the event bus, or a presentation's model. Everything that makes
75
+ up a running application lives in one scope, and on a server that scope
76
+ belongs to the request being served. Solid needs nothing from you: its server
77
+ build reads the request from `getRequestEvent()` and keeps that request's
78
+ scope beside it. React has no request context of its own, so
79
+ `@magicdoor/magic-use-case-react/server` exports `runInRequestScope()`, which
80
+ the host opens once per request; the subpath is what keeps `node:async_hooks`
81
+ out of browser bundles. Rendering outside a request throws rather than falling
82
+ back to a shared scope, because a silent fallback is the leak this prevents.
83
+
84
+ **State can travel to the browser**, so a page rendered with data is not
85
+ fetched again while it hydrates. It is opt-in and it is one line: Solid
86
+ renders `<StateTransfer />` once in its tree, React puts
87
+ `serializedStateScript()` in its document, and the browser adopts what it
88
+ finds as the package loads. Application state has to be data — objects,
89
+ arrays, sets, maps, dates, primitives — since a class instance cannot cross
90
+ without its prototype. Two things to weigh: everything in state reaches the
91
+ page in plain text, and the payload is an inline script, so a strict
92
+ `script-src` needs a nonce.
93
+
94
+ `createScope()` hands back an opaque handle: the only thing an application can
95
+ do with a scope is give it to `setScopeResolver()`, since the bus and the
96
+ state inside it are the library's own.
97
+
98
+ `@magicdoor/magic-use-case-core` is internal and never published — it is bundled into each
99
+ adapter, so the adapters' exports are the entire supported API surface.
package/LICENSE ADDED
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright 2026 MagicDoor, Inc.
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # @magicdoor/magic-use-case-react
2
+
3
+ React ≥17 bindings for [magic-use-case](https://github.com/MagicDoorInc/magic-use-case) —
4
+ clean-architecture use cases and presentations for front-end TypeScript.
5
+
6
+ ```bash
7
+ npm install @magicdoor/magic-use-case-react
8
+ ```
9
+
10
+ ## Exports
11
+
12
+ - `UseCase` — base class for your application logic
13
+ - `Presentation` — the type of a function mapping state to a view model
14
+ - `DeepReadonly` — the type of a model as a screen sees it
15
+ - `useUseCase` — execute a use case, with loading, progress and success state
16
+ - `usePresenter` — subscribe a component to a presentation's model
17
+ - `Presenter` — the presentation holder behind `usePresenter`, for wiring of your own
18
+ - `ErrorHandler` — error boundary wired to the use-case error channel
19
+ - `Navigator` — bridges use-case navigation events to your router
20
+ - `onError` / `onNavigation` — subscribe to those two channels directly
21
+ - `createScope` / `setScopeResolver` — one scope per request, for server rendering
22
+
23
+ New to it? The [main README](https://github.com/MagicDoorInc/magic-use-case#readme)
24
+ introduces the five players, walks a complete feature in five steps, and sets out
25
+ the rules the library is built around.
26
+
27
+ ## Credits
28
+
29
+ Created by Norbert Nemes.
30
+
31
+ ## License
32
+
33
+ Apache-2.0 © MagicDoor, Inc.
@@ -0,0 +1,124 @@
1
+ /**
2
+ * The whole of what a screen needs written by hand: a pure function from
3
+ * application state to the model the view renders. It is called again on every
4
+ * state change, so it must derive its result from `state` alone.
5
+ *
6
+ * Presentations are plain functions, so two screens that need the same rule
7
+ * share it by calling the same function — no class hierarchy, and no presenter
8
+ * constructed inside another presenter. Sharing is by identity: pass the same
9
+ * function and the work is done once for all of them.
10
+ */
11
+ type Presentation<TState, TModel extends object> = (state: TState) => TModel | undefined;
12
+
13
+ declare const scopeBrand: unique symbol;
14
+ /**
15
+ * A scope, as everything outside the library sees it: a value to hand back to
16
+ * `setScopeResolver`, and nothing more. The bus, the state and the bookkeeping
17
+ * are the library's own.
18
+ */
19
+ interface ScopeHandle {
20
+ readonly [scopeBrand]?: never;
21
+ }
22
+ declare function createScope(initialState?: unknown): ScopeHandle;
23
+ /**
24
+ * Installed by the server build to resolve a scope per request. Left alone,
25
+ * every caller shares the one browser scope.
26
+ */
27
+ declare function setScopeResolver(resolver: () => ScopeHandle): void;
28
+ declare function onError(handler: (error: Error) => void): () => void;
29
+ declare function onNavigation(handler: (url: string) => void): () => void;
30
+
31
+ type UseCaseClass<T> = new (...args: any[]) => UseCase<T>;
32
+ declare abstract class UseCase<T> {
33
+ protected onProgress?: (progress: number) => void;
34
+ constructor(onProgress?: (progress: number) => void);
35
+ /** Resolved per emit, so an execution always announces on the scope it is running in. */
36
+ private get eventEmitter();
37
+ execute(params?: unknown): Promise<void>;
38
+ /**
39
+ * Starts a use case that this one does not wait for. It runs on its own: the
40
+ * caller returns and announces its own changes immediately, and the detached
41
+ * run announces its own when it lands. Nobody is waiting on it, so its failure
42
+ * is reported rather than thrown.
43
+ */
44
+ protected detach(UseCaseClass: UseCaseClass<T>, params?: unknown): void;
45
+ private run;
46
+ protected getState(): T;
47
+ /**
48
+ * Clears application state so the next execution bootstraps it again.
49
+ *
50
+ * All four pieces of module state go together: the state itself, the
51
+ * emitter's retained copy, the in-flight dedup map, and any bootstrap in
52
+ * flight. Leaving any one behind resurrects the old state.
53
+ */
54
+ protected resetAppState(): void;
55
+ protected navigate(url: string): void;
56
+ /**
57
+ * Announces a failure the screen should hear about, for a use case that
58
+ * handles the failure rather than rethrowing it. Anything rethrown is
59
+ * reported on its way out and must not be reported here as well.
60
+ */
61
+ protected report(error: Error): void;
62
+ protected abstract runLogic(params: unknown): Promise<void>;
63
+ protected abstract initializeState(): Promise<T>;
64
+ /**
65
+ * A run announces its work unless somebody is waiting on it. A nested run
66
+ * stays silent and its caller announces everything written beneath it, once.
67
+ *
68
+ * Whether a run has a caller is known when it starts, which is what keeps an
69
+ * unrelated run finishing at the same moment from being swallowed: two flows
70
+ * a screen started independently each reach it on their own.
71
+ */
72
+ private runWithUpdate;
73
+ private announceUnlessCallerWill;
74
+ /**
75
+ * Counting who is running cannot tell a caller from an unrelated run that
76
+ * happens to overlap, and guessing wrong loses the failure entirely. Whether
77
+ * this run has a caller is known when it starts, so that is what decides.
78
+ */
79
+ private reportUnlessCallerWill;
80
+ }
81
+
82
+ /**
83
+ * Infrastructure, not a place for logic: it subscribes to the model built by a
84
+ * presentation and pushes it at whoever is listening. Adapters construct it —
85
+ * `usePresenter(presentation)` — so application code writes presentations and
86
+ * never touches this class.
87
+ *
88
+ * Presenters that were given the same presentation share one run of it: the
89
+ * model is built once per state change and handed to all of them.
90
+ */
91
+ declare class Presenter<TState, TModel extends object> {
92
+ private model;
93
+ private subscribers;
94
+ private presentation;
95
+ private source;
96
+ private onModel;
97
+ private scope;
98
+ constructor(presentation: Presentation<TState, TModel>);
99
+ subscribe(listener: (model?: TModel) => void): void;
100
+ unsubscribe(listener: (model?: TModel) => void): void;
101
+ private notifySubscribers;
102
+ destroy(): void;
103
+ }
104
+
105
+ /**
106
+ * The state this request rendered from, as a script the browser runs before it
107
+ * renders. Serialized rather than JSON-encoded: application state holds sets,
108
+ * maps, dates and shared references, none of which survive JSON, and all of
109
+ * which the emitted source rebuilds.
110
+ *
111
+ * seroval writes `<` as `\x3C`, so a string in state cannot close the tag.
112
+ *
113
+ * Server-side only. The browser never needs to deserialize anything — the
114
+ * script it runs *is* the value.
115
+ */
116
+ declare function serializedStateScript(): string;
117
+
118
+ type AnyFunction = (...args: never[]) => unknown;
119
+ /** What a presentation's model looks like to the screen rendering it. */
120
+ type DeepReadonly<T> = T extends AnyFunction ? T : T extends ReadonlyMap<infer K, infer V> ? ReadonlyMap<K, DeepReadonly<V>> : T extends ReadonlySet<infer V> ? ReadonlySet<DeepReadonly<V>> : T extends ReadonlyArray<infer V> ? ReadonlyArray<DeepReadonly<V>> : T extends object ? {
121
+ readonly [K in keyof T]: DeepReadonly<T[K]>;
122
+ } : T;
123
+
124
+ export { type DeepReadonly as D, type Presentation as P, type UseCaseClass as U, Presenter as a, UseCase as b, createScope as c, onNavigation as d, serializedStateScript as e, onError as o, setScopeResolver as s };
@@ -0,0 +1,54 @@
1
+ import { U as UseCaseClass, P as Presentation, D as DeepReadonly } from './index.d-Dyx9E1u0.js';
2
+ export { a as Presenter, b as UseCase, c as createScope, o as onError, d as onNavigation, s as setScopeResolver } from './index.d-Dyx9E1u0.js';
3
+ import React, { FC } from 'react';
4
+
5
+ declare function useUseCase<T>(UseCaseClass: UseCaseClass<T>): {
6
+ execute: (params?: unknown) => Promise<void>;
7
+ isLoading: boolean;
8
+ didSucceed: boolean;
9
+ progress: number;
10
+ };
11
+
12
+ interface ErrorDialogProps {
13
+ error: Error;
14
+ onClose: () => void;
15
+ }
16
+ interface ErrorHandlerProps {
17
+ children: React.ReactNode;
18
+ onWillReportError: (error: Error) => boolean;
19
+ onDidReportError?: (error: Error) => void;
20
+ renderErrorDialog: (props: ErrorDialogProps) => React.ReactNode;
21
+ }
22
+ declare const ErrorHandler: React.FC<ErrorHandlerProps>;
23
+
24
+ interface NavigatorProps {
25
+ onNavigate?: (url: string) => void;
26
+ }
27
+ declare const Navigator: FC<NavigatorProps>;
28
+
29
+ /**
30
+ * Augmented by the application to name the type its use cases keep state in:
31
+ *
32
+ * declare module '@magicdoor/magic-use-case-react' {
33
+ * interface MagicUseCaseTypes {
34
+ * state: MyAppState;
35
+ * }
36
+ * }
37
+ *
38
+ * Until an application does, presentations go unchecked, which is how one can
39
+ * declare a state type the application never emits and fail only at runtime.
40
+ */
41
+ interface MagicUseCaseTypes {
42
+ }
43
+ type ApplicationState = MagicUseCaseTypes extends {
44
+ state: infer TState;
45
+ } ? TState : any;
46
+ /**
47
+ * Declared here rather than re-exported, so an application's augmentation of
48
+ * this module merges with the interface above.
49
+ */
50
+ declare function usePresenter<TModel extends object>(presentation: Presentation<ApplicationState, TModel>): {
51
+ model: DeepReadonly<TModel> | undefined;
52
+ };
53
+
54
+ export { DeepReadonly, ErrorHandler, type MagicUseCaseTypes, Navigator, Presentation, usePresenter, useUseCase };