mobx-mantle 0.3.7 → 0.6.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.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,51 @@
1
- import * as react from 'react';
2
- import { Ref, JSX } from 'react';
3
- import { IObservableValue } from 'mobx';
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import React, { Ref, JSX } from 'react';
3
+ import { Reaction } from 'mobx';
4
+ import { M as MaybeGetter } from './reactive-args-BBucrU54.js';
5
+ export { t as toValue } from './reactive-args-BBucrU54.js';
6
+
7
+ declare const serviceType: unique symbol;
8
+ /** Class tokens and typed symbols are also valid tsyringe injection tokens. */
9
+ type ServiceToken<T extends object> = (new (...args: any[]) => T) | (symbol & {
10
+ readonly [serviceType]: T;
11
+ });
12
+ type ServiceResolver = <T extends object>(token: ServiceToken<T>) => T;
13
+ declare function createServiceToken<T extends object>(description: string): ServiceToken<T>;
14
+ declare function ServiceProvider({ resolve, children }: {
15
+ resolve: ServiceResolver;
16
+ children?: React.ReactNode;
17
+ }): react_jsx_runtime.JSX.Element;
18
+ /** Synchronous construction scope, also useful outside React. Does not own services. */
19
+ declare function withInjectionScope<T>(resolve: ServiceResolver | undefined, construct: () => T): T;
20
+ /** Resolve during synchronous construction/onCreate, or inside withInjectionScope(). */
21
+ declare function inject<T extends object>(token: ServiceToken<T>): T;
22
+
23
+ /**
24
+ * Holder linking a component instance to its render reaction, so PropsBox
25
+ * can recognize "my own render is reading me" and skip tracking (React
26
+ * already delivers prop changes through memo — tracking would only schedule
27
+ * a redundant second render). Stable per React component instance; survives
28
+ * HMR instance swaps.
29
+ */
30
+ interface RenderReactionHolder {
31
+ current: Reaction | null;
32
+ }
33
+ /**
34
+ * Minimal observer() for plain function components — re-renders when any
35
+ * observable read during render changes. Useful together with useBehavior()
36
+ * in codebases that don't (or don't yet) use Mantle components.
37
+ *
38
+ * @example
39
+ * ```tsx
40
+ * const Toolbar = observer((props: Props) => {
41
+ * const size = useBehavior(() => withWindowSize());
42
+ * return <div>{size.width}px</div>;
43
+ * });
44
+ * ```
45
+ */
46
+ declare function observer<P extends object>(fc: ((props: P) => React.ReactElement | null) & {
47
+ displayName?: string;
48
+ }): (props: P) => React.ReactElement | null;
4
49
 
5
50
  /** Options for the watch method */
6
51
  interface WatchOptions {
@@ -9,32 +54,74 @@ interface WatchOptions {
9
54
  /** Run callback immediately with current value */
10
55
  fireImmediately?: boolean;
11
56
  }
57
+ /** Options for the effect method */
58
+ interface EffectOptions {
59
+ /** Debounce the effect by N milliseconds */
60
+ delay?: number;
61
+ }
12
62
  /**
13
63
  * Error context passed to the onError handler
14
64
  */
15
65
  interface MantleErrorContext {
16
66
  /** The lifecycle phase where the error occurred */
17
- phase: 'onLayoutMount' | 'onMount' | 'onUpdate' | 'onUnmount' | 'watch';
18
- /** The View or Behavior class name */
67
+ phase: 'onLayoutMount' | 'onMount' | 'onUpdate' | 'onUnmount' | 'watch' | 'effect' | 'sync';
68
+ /** The Component or Behavior class name */
19
69
  name: string;
20
- /** Whether the error came from a Behavior (true) or a View (false) */
70
+ /** Whether the error came from a Behavior (true) or a Component (false) */
21
71
  isBehavior: boolean;
22
72
  }
23
73
  /**
24
74
  * Global configuration options for mobx-mantle
25
75
  */
26
76
  interface MantleConfig {
27
- /** Whether to automatically make View/Behavior instances observable (default: true) */
77
+ /** Application service resolver, captured at construction. Explicit service scopes override it. */
78
+ resolveService?: ServiceResolver;
79
+ /** Whether to automatically make Component/Behavior instances observable (default: true) */
28
80
  autoObservable?: boolean;
81
+ /**
82
+ * Cache per-class prototype annotation data (getters, methods) so repeated
83
+ * instantiations of the same class skip the prototype walk (default: true).
84
+ * Turn off only if a class's prototype is mutated between instantiations.
85
+ */
86
+ cacheAnnotations?: boolean;
29
87
  /** Global error handler for lifecycle errors. Defaults to console.error. */
30
88
  onError?: (error: unknown, context: MantleErrorContext) => void;
89
+ /**
90
+ * Set true to opt into process-wide permissive MobX actions (default: false).
91
+ * By default Mantle preserves the host's MobX policy. Synchronous methods
92
+ * and lifecycle callbacks are actions; async continuations should use
93
+ * runInAction. Configure before the first component or behavior is created.
94
+ */
95
+ manageMobxActions?: boolean;
31
96
  }
32
97
  /**
33
98
  * Configure global defaults for mobx-mantle.
34
- * Settings can still be overridden per-view in createView options.
99
+ * Settings can still be overridden per-component in createComponent options.
35
100
  */
36
101
  declare function configure(config: MantleConfig): void;
37
102
 
103
+ /**
104
+ * A watch/effect registration. `create` builds the underlying MobX reaction
105
+ * and returns its disposer. Specs registered before mount are kept so the
106
+ * reaction can be re-created if the host unmounts and remounts with the same
107
+ * instance — React StrictMode does exactly this in development.
108
+ */
109
+ interface ReactiveSpec {
110
+ create: () => () => void;
111
+ stopped: boolean;
112
+ dispose: (() => void) | null;
113
+ }
114
+
115
+ /**
116
+ * Placeholder returned by sync() during construction. The wrapper constructor
117
+ * scans own fields right after onCreate, finds which key holds each marker,
118
+ * swaps in the initial value, and registers the hidden sync effect for that
119
+ * key. (Same post-onCreate scan window that behavior collection uses.)
120
+ */
121
+ declare class SyncMarker {
122
+ readonly source: () => unknown;
123
+ constructor(source: () => unknown);
124
+ }
38
125
  /**
39
126
  * Base class for behaviors. Provides lifecycle method signatures for IDE autocomplete.
40
127
  * Extend this class and wrap with createBehavior() to create a factory.
@@ -50,17 +137,50 @@ declare function configure(config: MantleConfig): void;
50
137
  * ```
51
138
  */
52
139
  declare class Behavior {
140
+ constructor();
141
+ inject<T extends object>(token: ServiceToken<T>): T;
53
142
  /** @internal */
54
143
  _watchDisposers: (() => void)[];
144
+ /** @internal — pre-mount watch/effect registrations, materialized at mount */
145
+ _reactiveSpecs: ReactiveSpec[];
146
+ /** @internal — child behaviors declared as fields, collected at construction */
147
+ _behaviors: BehaviorEntry[];
148
+ /** @internal — true after the parent Component first mounts */
149
+ _mounted: boolean;
150
+ /** @internal — set when effects clean up so StrictMode replay can resurrect pre-mount watchers */
151
+ _wasUnmounted: boolean;
152
+ /** @internal — sync() markers awaiting the post-onCreate field scan; null once construction completes */
153
+ _pendingSyncs: SyncMarker[] | null;
154
+ /** @internal — true while the hidden sync effect writes a mirrored field (dev write-guard) */
155
+ _syncWriting: boolean;
55
156
  onCreate?(...args: any[]): void;
56
157
  onLayoutMount?(): void | (() => void);
57
158
  onMount?(): void | (() => void);
58
159
  onUnmount?(): void;
160
+ /**
161
+ * Register a cleanup function to run automatically on unmount.
162
+ * Returns a function that can be called for early cleanup.
163
+ *
164
+ * Cleanups are one-shot: they are not re-created during React StrictMode's
165
+ * same-instance effect replay. Call this from onMount (which is replayed),
166
+ * or use effect() for a replay-safe setup/teardown pair.
167
+ */
168
+ addCleanup(cleanup: () => void): () => void;
169
+ /** @internal — addCleanup without the pre-mount dev warning (used by watch/effect) */
170
+ _addCleanup(cleanup: () => void): () => void;
59
171
  /**
60
172
  * Watch a reactive expression and run a callback when it changes.
61
- * Automatically disposed on unmount.
173
+ * Automatically disposed on unmount and re-created during React StrictMode's
174
+ * same-instance effect replay.
62
175
  *
63
- * @param expr - Reactive expression (getter) to watch
176
+ * Safe to call from onCreate: registration is recorded and comes alive
177
+ * when the parent commits (just before first paint), so the watcher
178
+ * tracks observable fields correctly and renders that React discards
179
+ * never leak reactions.
180
+ *
181
+ * @param source - Reactive expression (getter) to watch, or a MaybeGetter
182
+ * argument passed through as-is. To watch a value that is itself a
183
+ * function, wrap it: `watch(() => this.callback, …)`
64
184
  * @param callback - Called when the expression result changes
65
185
  * @param options - Optional configuration (delay, fireImmediately)
66
186
  * @returns Dispose function for early teardown
@@ -73,7 +193,52 @@ declare class Behavior {
73
193
  * }
74
194
  * ```
75
195
  */
76
- watch<T>(expr: () => T, callback: (value: T, prevValue: T | undefined) => void, options?: WatchOptions): () => void;
196
+ watch<T>(source: MaybeGetter<T>, callback: (value: T, prevValue: T | undefined) => void, options?: WatchOptions): () => void;
197
+ /**
198
+ * Run a side effect that auto-tracks reactive dependencies.
199
+ * Re-runs whenever any accessed observable changes.
200
+ * Automatically disposed on unmount.
201
+ *
202
+ * Best for simple synchronization (DOM updates, logging). For complex
203
+ * side effects with explicit triggers, prefer `watch()`.
204
+ *
205
+ * @param fn - Effect function. May return a cleanup function.
206
+ * @param options - Optional configuration (delay)
207
+ * @returns Dispose function for early teardown
208
+ *
209
+ * @example
210
+ * ```tsx
211
+ * onCreate() {
212
+ * this.effect(() => {
213
+ * console.log('Current value:', this.value);
214
+ * });
215
+ * }
216
+ * ```
217
+ */
218
+ effect(fn: () => void | (() => void), options?: EffectOptions): () => void;
219
+ /**
220
+ * Normalize a MaybeGetter argument into an ordinary field that stays
221
+ * current. Call once in onCreate (or a field initializer) and assign the
222
+ * result to a field; everywhere else, read the field like normal state:
223
+ *
224
+ * ```tsx
225
+ * url!: string;
226
+ *
227
+ * onCreate(url: MaybeGetter<string>) {
228
+ * this.url = this.sync(url); // the one convention line
229
+ * this.watch(() => this.url, (u) => this.load(u));
230
+ * }
231
+ * refresh() { this.load(this.url); } // plain read, always current
232
+ * ```
233
+ *
234
+ * - Plain value → the field just holds it (no effect registered).
235
+ * - Getter → a hidden effect keeps the field updated whenever the getter's
236
+ * observable sources change (alive from mount, like watch/effect).
237
+ *
238
+ * The sync is one-way (argument → field). Don't write to the field from
239
+ * outside: the next source change overwrites it (dev mode warns).
240
+ */
241
+ sync<T>(source: MaybeGetter<T>): T;
77
242
  /** @internal */
78
243
  _disposeWatchers(): void;
79
244
  }
@@ -122,19 +287,19 @@ type BehaviorArgs<T extends new (...args: any[]) => any> = ConstructorParams<T>
122
287
  * export const withDrag = createBehavior(DragTrait);
123
288
  * ```
124
289
  *
125
- * @example Using in a View
290
+ * @example Using in a Component
126
291
  * ```tsx
127
- * class Editor extends View<Props> {
292
+ * class Editor extends Component<Props> {
128
293
  * canvas = this.ref<HTMLCanvasElement>();
129
294
  *
130
295
  * // No `new` keyword — factory function
131
296
  * drag = withDrag(this.canvas);
132
297
  * autosave = withAutosave('/api/save', 5000);
133
298
  * }
134
- * export default createView(Editor);
299
+ * export default createComponent(Editor);
135
300
  * ```
136
301
  *
137
- * The `with` prefix convention signals that the view manages this behavior's lifecycle.
302
+ * The `with` prefix convention signals that the component manages this behavior's lifecycle.
138
303
  */
139
304
  /**
140
305
  * Type that supports both `new` and direct call syntax
@@ -147,78 +312,77 @@ declare function createBehavior<T extends new (...args: any[]) => any>(Def: T, o
147
312
  autoObservable?: boolean;
148
313
  }): BehaviorFactory<BehaviorArgs<T>, InstanceType<T>>;
149
314
 
150
- /**
151
- * Decorator context type for TC39 decorators
152
- */
153
- interface DecoratorContext {
154
- kind: 'field' | 'method' | 'getter' | 'setter' | 'accessor' | 'class';
315
+ type Context = {
316
+ kind: string;
155
317
  name: string | symbol;
156
- metadata: Record<symbol, unknown>;
157
- }
318
+ metadata?: Record<symbol, unknown>;
319
+ static?: boolean;
320
+ private?: boolean;
321
+ };
322
+ /** Plain fields in either TypeScript decorator mode; no accessor keyword needed. */
323
+ declare const observable: ((_target: any, key: string | symbol | Context, descriptor?: PropertyDescriptor) => void) & {
324
+ ref: (_target: any, key: string | symbol | Context, descriptor?: PropertyDescriptor) => void;
325
+ shallow: (_target: any, key: string | symbol | Context, descriptor?: PropertyDescriptor) => void;
326
+ deep: (_target: any, key: string | symbol | Context, descriptor?: PropertyDescriptor) => void;
327
+ struct: (_target: any, key: string | symbol | Context, descriptor?: PropertyDescriptor) => void;
328
+ };
329
+ /** Bound prototype method, or an arrow-function field (already bound); synchronous writes are batched. */
330
+ declare const action: (target: any, key: string | symbol | Context, descriptor?: PropertyDescriptor) => void;
331
+ declare const computed: (_target: any, key: string | symbol | Context, descriptor?: PropertyDescriptor) => void;
332
+
158
333
  /**
159
- * Marks a field as observable. No `accessor` keyword needed.
160
- *
161
- * @example
162
- * ```tsx
163
- * class Counter extends View {
164
- * @observable count = 0;
165
- * }
166
- * export default createView(Counter);
167
- * ```
334
+ * Props holder built on MobX's public atom API. Reads are tracked like any
335
+ * observable. Writes come in two flavors:
336
+ * - setSilently: update the value without notifying observers — safe during
337
+ * React's render phase (see Component._syncProps)
338
+ * - set: update and notify observers — called from useLayoutEffect after
339
+ * React finishes the render pass
168
340
  */
169
- declare function observable(_value: undefined, context: DecoratorContext): void;
170
- declare namespace observable {
171
- var ref: (_value: undefined, context: DecoratorContext) => void;
172
- var shallow: (_value: undefined, context: DecoratorContext) => void;
173
- var struct: (_value: undefined, context: DecoratorContext) => void;
174
- var deep: (_value: undefined, context: DecoratorContext) => void;
341
+ declare class PropsBox<P> {
342
+ private _atom;
343
+ private _value;
344
+ /**
345
+ * @internal The owning component's render reaction. Reads coming from it
346
+ * are not tracked: React already re-renders the component on prop change
347
+ * (via memo), so letting the render reaction observe the props atom would
348
+ * only schedule a redundant second render after every prop change.
349
+ * Computeds, watchers, and other components track normally.
350
+ */
351
+ _renderReaction: RenderReactionHolder | null;
352
+ constructor(value: P, name: string);
353
+ get(): P;
354
+ setSilently(value: P): void;
355
+ set(value: P): void;
175
356
  }
176
- /**
177
- * Marks a method as an action. Auto-bound by default.
178
- *
179
- * @example
180
- * ```tsx
181
- * class Counter extends View {
182
- * @observable count = 0;
183
- *
184
- * @action increment() {
185
- * this.count++;
186
- * }
187
- * }
188
- * export default createView(Counter);
189
- * ```
190
- */
191
- declare function action(_value: Function, context: DecoratorContext): void;
192
- /**
193
- * Marks a getter as computed.
194
- * Note: Getters are automatically computed in autoObservable mode,
195
- * but this decorator is useful for explicit annotation.
196
- *
197
- * @example
198
- * ```tsx
199
- * class Counter extends View {
200
- * @observable count = 0;
201
- *
202
- * @computed get doubled() {
203
- * return this.count * 2;
204
- * }
205
- * }
206
- * export default createView(Counter);
207
- * ```
208
- */
209
- declare function computed(_value: Function, context: DecoratorContext): void;
210
-
211
- declare class View<P = {}> {
357
+ type ComponentConstructor<C extends Component<any> = Component<any>> = abstract new (...args: any[]) => C;
358
+ declare class Component<P = {}> {
212
359
  /** @internal */
213
- _propsBox: IObservableValue<P>;
360
+ _propsBox: PropsBox<P>;
361
+ constructor(props?: P);
214
362
  get props(): P;
215
- /** @internal — called by createView to silently update props during render */
363
+ /** Resolve through the scope captured during construction; preserve service identity. */
364
+ inject<T extends object>(token: ServiceToken<T>): T;
365
+ /** Return the immediate ancestor in the live Mantle view tree. */
366
+ getParent(): Component<any> | undefined;
367
+ /** Find the nearest parent with the supplied logical model identity. */
368
+ findParent<C extends Component<any>>(type: ComponentConstructor<C>): C | undefined;
369
+ /** Return all Mantle parents, ordered from the immediate parent to the root. */
370
+ getParents(): readonly Component<any>[];
371
+ /** Return the highest Mantle ancestor, or this instance when already root. */
372
+ getRoot(): Component<any>;
373
+ /** @internal — called by createComponent to silently update props during render */
216
374
  _syncProps(value: P): void;
217
375
  forwardRef?: Ref<any>;
218
376
  /** @internal */
219
377
  _behaviors: BehaviorEntry[];
220
378
  /** @internal */
221
379
  _watchDisposers: (() => void)[];
380
+ /** @internal — pre-mount watch/effect registrations, materialized at mount */
381
+ _reactiveSpecs: ReactiveSpec[];
382
+ /** @internal — true after the first (layout) mount */
383
+ _mounted: boolean;
384
+ /** @internal — set when effects clean up so StrictMode replay can resurrect watchers */
385
+ _wasUnmounted: boolean;
222
386
  onCreate?(props: P): void;
223
387
  onLayoutMount?(): void | (() => void);
224
388
  onMount?(): void | (() => void);
@@ -227,18 +391,33 @@ declare class View<P = {}> {
227
391
  ref<T extends HTMLElement = HTMLElement>(): {
228
392
  current: T | null;
229
393
  };
394
+ /**
395
+ * Register a cleanup function to run automatically on unmount.
396
+ * Returns a function that can be called for early cleanup.
397
+ *
398
+ * Cleanups are one-shot: they are not re-created during React StrictMode's
399
+ * same-instance effect replay. Call this from onMount (which is replayed),
400
+ * or use effect() for a replay-safe setup/teardown pair.
401
+ */
402
+ addCleanup(cleanup: () => void): () => void;
403
+ /** @internal — addCleanup without the pre-mount dev warning (used by watch/effect) */
404
+ _addCleanup(cleanup: () => void): () => void;
230
405
  /**
231
406
  * Watch a reactive expression and run a callback when it changes.
232
- * Automatically disposed on unmount.
407
+ * Automatically disposed on unmount. Watchers declared before mount
408
+ * (field initializers, onCreate) are re-created during React StrictMode's
409
+ * same-instance effect replay.
233
410
  *
234
- * @param expr - Reactive expression (getter) to watch
411
+ * @param source - Reactive expression (getter) to watch, or a MaybeGetter
412
+ * argument passed through as-is. To watch a value that is itself a
413
+ * function, wrap it: `watch(() => this.callback, …)`
235
414
  * @param callback - Called when the expression result changes
236
415
  * @param options - Optional configuration (delay, fireImmediately)
237
416
  * @returns Dispose function for early teardown
238
417
  *
239
418
  * @example
240
419
  * ```tsx
241
- * onCreate() {
420
+ * onMount() {
242
421
  * this.watch(
243
422
  * () => this.query,
244
423
  * async (query) => {
@@ -251,7 +430,42 @@ declare class View<P = {}> {
251
430
  * }
252
431
  * ```
253
432
  */
254
- watch<T>(expr: () => T, callback: (value: T, prevValue: T | undefined) => void, options?: WatchOptions): () => void;
433
+ watch<T>(source: MaybeGetter<T>, callback: (value: T, prevValue: T | undefined) => void, options?: WatchOptions): () => void;
434
+ /**
435
+ * Run a side effect that auto-tracks reactive dependencies.
436
+ * Re-runs whenever any accessed observable changes.
437
+ * Automatically disposed on unmount. Effects declared before mount
438
+ * (field initializers, onCreate) are re-created during React StrictMode's
439
+ * same-instance effect replay.
440
+ *
441
+ * Best for simple synchronization (DOM updates, logging). For complex
442
+ * side effects with explicit triggers, prefer `watch()`.
443
+ *
444
+ * @param fn - Effect function. May return a cleanup function.
445
+ * @param options - Optional configuration (delay)
446
+ * @returns Dispose function for early teardown
447
+ *
448
+ * @example
449
+ * ```tsx
450
+ * onMount() {
451
+ * this.effect(() => {
452
+ * document.title = `${this.items.length} items`;
453
+ * });
454
+ * }
455
+ * ```
456
+ *
457
+ * @example With cleanup
458
+ * ```tsx
459
+ * onMount() {
460
+ * this.effect(() => {
461
+ * const handler = () => console.log(this.count);
462
+ * window.addEventListener('click', handler);
463
+ * return () => window.removeEventListener('click', handler);
464
+ * });
465
+ * }
466
+ * ```
467
+ */
468
+ effect(fn: () => void | (() => void), options?: EffectOptions): () => void;
255
469
  /** @internal */
256
470
  _disposeWatchers(): void;
257
471
  /** @internal - Scan own properties for behavior instances and register them */
@@ -265,9 +479,143 @@ declare class View<P = {}> {
265
479
  render?(): JSX.Element | null;
266
480
  }
267
481
 
268
- type PropsOf<V> = V extends View<infer P> ? P : object;
269
- declare function createView<V extends View<any>>(ViewClass: new () => V, templateOrOptions?: ((vm: V) => JSX.Element) | {
482
+ type PropsOf<C> = C extends Component<infer P> ? P : object;
483
+ /**
484
+ * Return type for createComponent.
485
+ *
486
+ * Simple callable signature that works with libraries expecting ComponentType<P>
487
+ * or strict function components (react-window, react-virtualized, etc.).
488
+ *
489
+ * Uses ReactElement | null rather than React.FC's looser ReactNode return type.
490
+ */
491
+ type MantleComponent<P> = (props: P) => React.ReactElement | null;
492
+ /**
493
+ * Return type for createForwardRef when you need typed ref forwarding.
494
+ *
495
+ * Use this when you need to forward refs to DOM elements or child components
496
+ * and want TypeScript to know the ref type.
497
+ */
498
+ type ForwardRefMantleComponent<P, RefType> = ((props: P) => React.ReactElement | null) & React.ForwardRefExoticComponent<React.PropsWithoutRef<P> & React.RefAttributes<RefType>>;
499
+ declare function createComponent<C extends Component<any>>(ComponentClass: new (...args: any[]) => C, templateOrOptions?: ((vm: C) => JSX.Element) | {
500
+ autoObservable?: boolean;
501
+ }): MantleComponent<PropsOf<C>>;
502
+ /**
503
+ * Creates a React component with typed ref forwarding.
504
+ *
505
+ * Use this instead of createComponent when you need to forward refs to DOM elements
506
+ * or child components and want proper TypeScript support for the ref type.
507
+ *
508
+ * @example
509
+ * ```tsx
510
+ * class FancyInput extends Component<Props> {
511
+ * render() {
512
+ * return <input ref={this.forwardRef} className="fancy" />;
513
+ * }
514
+ * }
515
+ *
516
+ * const FancyInputComponent = createForwardRef<HTMLInputElement>(FancyInput);
517
+ *
518
+ * // Parent gets typed ref:
519
+ * const inputRef = useRef<HTMLInputElement>(null);
520
+ * <FancyInputComponent ref={inputRef} />
521
+ * ```
522
+ */
523
+ declare function createForwardRef<RefType, C extends Component<any> = Component<any>>(ComponentClass: new (...args: any[]) => C, templateOrOptions?: ((vm: C) => JSX.Element) | {
270
524
  autoObservable?: boolean;
271
- }): react.ForwardRefExoticComponent<react.PropsWithoutRef<PropsOf<V>> & react.RefAttributes<unknown>>;
525
+ }): ForwardRefMantleComponent<PropsOf<C>, RefType>;
526
+
527
+ /**
528
+ * Host a behavior inside a plain React function component. The behavior gets
529
+ * the same lifecycle relay a Mantle Component would give it: onLayoutMount in
530
+ * the layout phase, onMount after paint, watchers alive from commit, full
531
+ * teardown at unmount, and StrictMode remount resurrection.
532
+ *
533
+ * The instance is created once per mount lifetime, from a factory so nothing
534
+ * is constructed on re-renders. It is returned as-is — wrap the component in
535
+ * observer() (from mobx-mantle) so renders track the behavior's observables:
536
+ *
537
+ * @example
538
+ * ```tsx
539
+ * const Toolbar = observer(() => {
540
+ * const size = useBehavior(() => withWindowSize());
541
+ * return <div>{size.width} × {size.height}</div>;
542
+ * });
543
+ * ```
544
+ *
545
+ * Note: the factory runs once, so getter arguments close over the first
546
+ * render's scope. Getters reading observables (a store, another behavior)
547
+ * stay live; getters reading a function component's props capture the first
548
+ * render's props object and go stale — pass observable sources instead.
549
+ */
550
+ declare function useBehavior<T extends object>(create: () => T): T;
551
+
552
+ interface SharedOptions {
553
+ /**
554
+ * Persist the shared fields to localStorage under the key and restore
555
+ * them at startup, so a fresh window starts where the last one left off.
556
+ * Default true.
557
+ */
558
+ persist?: boolean;
559
+ /**
560
+ * The fields to share. Defaults to the store's own observable data
561
+ * fields: not getters, not methods, not names starting with `_`.
562
+ */
563
+ fields?: readonly string[];
564
+ }
565
+ interface ShareStoresOptions extends SharedOptions {
566
+ /** Key prefix for each store; the key is `${prefix}:${name}`. Default `shared`. */
567
+ prefix?: string;
568
+ }
569
+ /**
570
+ * Share a MobX store's state across every window of one application:
571
+ * other tabs, the frames a design tool hosts, anything on the same origin
572
+ * that imports the same module. Each window keeps its own store instance;
573
+ * values are mirrored, identity is not shared, and nothing goes through a
574
+ * server or a parent document.
575
+ *
576
+ * Mechanics, the web's own answer for two windows, packaged so it is never
577
+ * written by hand:
578
+ * - restore the store's fields from localStorage at startup (`persist`);
579
+ * - publish changes through a reaction to localStorage and a
580
+ * BroadcastChannel;
581
+ * - apply incoming changes from the channel or the `storage` event inside
582
+ * an action.
583
+ * Echo loops are bounded because a window only publishes when its
584
+ * serialized state changed, and only applies what differs from it.
585
+ *
586
+ * The one authoring rule: effects must derive from state — a `reaction`,
587
+ * `autorun`, or a component's `watch`/`effect` — never run inside setters,
588
+ * because a change that arrives from another window sets fields directly.
589
+ *
590
+ * @example
591
+ * export const themeStore = shared(new ThemeStore(), 'theme');
592
+ */
593
+ declare function shared<T extends object>(store: T, key: string, options?: SharedOptions): T;
594
+ /** Stop sharing a store (tests, teardown). The store keeps its current state. */
595
+ declare function unshare(store: object): void;
596
+ /**
597
+ * Share several stores at once, keyed by name: the one line an application
598
+ * writes at its entry point (a design tool's manifest, an app's main
599
+ * module) so every window that imports it wires the same stores.
600
+ *
601
+ * @example
602
+ * shareStores({ themeStore, navPreferences });
603
+ */
604
+ declare function shareStores<T extends Record<string, object>>(stores: T, options?: ShareStoresOptions): T;
605
+
606
+ type ModelClass<C extends Component<any> = Component<any>> = new (...args: any[]) => C;
607
+ interface ModelSubstitution<C extends Component<any> = Component<any>> {
608
+ /** Runs for each instance instead of its original constructor. Supply data/callbacks only. */
609
+ state: () => object;
610
+ /** Required for integrated render() classes; optional for separate templates. */
611
+ template?: (model: C) => React.JSX.Element;
612
+ }
613
+ /** Construction policy. The mounted tree takes a snapshot; remount to change it. */
614
+ declare function ModelProvider({ models, children }: {
615
+ models: ReadonlyMap<ModelClass, ModelSubstitution>;
616
+ children?: React.ReactNode;
617
+ }): react_jsx_runtime.JSX.Element;
618
+ /** Logical identity works for real and substituted models. It does not fake instanceof. */
619
+ declare function isModel<C extends Component<any>>(model: object, type: abstract new (...args: any[]) => C): model is C;
272
620
 
273
- export { Behavior, type MantleConfig, type MantleErrorContext, View, View as ViewModel, type WatchOptions, action, computed, configure, createBehavior, createView, observable };
621
+ export { Behavior, Component, type ComponentConstructor, type ForwardRefMantleComponent, type MantleComponent, type MantleConfig, type MantleErrorContext, MaybeGetter, type ModelClass, ModelProvider, type ModelSubstitution, ServiceProvider, type ServiceResolver, type ServiceToken, type ShareStoresOptions, type SharedOptions, Component as ViewModel, type WatchOptions, action, computed, configure, createBehavior, createComponent, createForwardRef, createServiceToken, inject, isModel, observable, observer, shareStores, shared, unshare, useBehavior, withInjectionScope };