@react-router-modules/runtime 1.0.0 → 2.0.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @react-router-modules/runtime
2
2
 
3
- Application assembly layer for the reactive framework. Takes modules and configuration, produces a running app with routing, slots, zones, navigation, and provider wiring.
3
+ Application assembly layer for the modular-react framework (React Router integration). Takes modules and configuration, produces a running app with routing, slots, zones, navigation, and provider wiring.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,6 +8,14 @@ Application assembly layer for the reactive framework. Takes modules and configu
8
8
  npm install @react-router-modules/runtime
9
9
  ```
10
10
 
11
+ ## What's included
12
+
13
+ - **Registry**: `createRegistry` — assembles modules into a running app
14
+ - **Zones**: `useZones` (reads zone components from matched route `handle`), `useActiveZones` (merges route zones with active module zones)
15
+ - **Types**: `ModuleRegistry`, `ResolveOptions`, `RegistryConfig`, `ApplicationManifest`
16
+ - **Re-exported from `@modular-react/core`**: `buildSlotsManifest`, `collectDynamicSlotFactories`, `evaluateDynamicSlots`, `buildNavigationManifest`, `validateNoDuplicateIds`, `validateDependencies`, `NavigationGroup`, `NavigationManifest`, `ModuleEntry`, `DynamicSlotFactory`, `SlotFilter`
17
+ - **Re-exported from `@modular-react/react`**: `useNavigation`, `useSlots`, `useRecalculateSlots`, `useModules`, `getModuleMeta`, `ModuleErrorBoundary`, `NavigationContext`, `SlotsContext`, `RecalculateSlotsContext`, `ModulesContext`, `DynamicSlotsProvider`, `createSlotsSignal`
18
+
11
19
  ## Usage
12
20
 
13
21
  ```typescript
@@ -22,10 +30,19 @@ const registry = createRegistry<AppDependencies, AppSlots>({
22
30
 
23
31
  registry.register(billingModule);
24
32
 
25
- const { App } = registry.resolve({
33
+ const { App, recalculateSlots } = registry.resolve({
26
34
  rootComponent: Layout,
27
35
  indexComponent: HomePage,
28
36
  });
37
+
38
+ // Re-evaluate dynamic slots after auth state changes
39
+ authStore.subscribe((state, prev) => {
40
+ if (state.isAuthenticated !== prev.isAuthenticated) {
41
+ recalculateSlots();
42
+ }
43
+ });
29
44
  ```
30
45
 
31
- See the [main documentation](https://github.com/kibertoad/reactive#readme) for the full guide.
46
+ Modules can contribute conditional slot entries via `dynamicSlots` and trigger re-evaluation from components via `useRecalculateSlots()`. The shell can apply cross-cutting filters via `slotFilter` on `resolve()`.
47
+
48
+ See the [main documentation](https://github.com/kibertoad/modular-react#readme) for the full guide.
package/dist/index.d.ts CHANGED
@@ -1,124 +1,106 @@
1
- import { Component } from 'react';
2
- import { Context } from 'react';
1
+ import { buildNavigationManifest } from '@modular-react/core';
2
+ import { buildSlotsManifest } from '@modular-react/core';
3
+ import { collectDynamicSlotFactories } from '@modular-react/core';
4
+ import { createSlotsSignal } from '@modular-react/react';
3
5
  import { DataRouter } from 'react-router';
4
- import { ErrorInfo } from 'react';
5
- import { JSX } from 'react/jsx-runtime';
6
- import { JSXElementConstructor } from 'react';
6
+ import { DynamicSlotFactory } from '@modular-react/core';
7
+ import { DynamicSlotsProvider } from '@modular-react/react';
8
+ import { evaluateDynamicSlots } from '@modular-react/core';
9
+ import { getModuleMeta } from '@modular-react/react';
7
10
  import { LazyModuleDescriptor } from '@react-router-modules/core';
8
- import { NavigationItem } from '@react-router-modules/core';
9
- import { ReactElement } from 'react';
10
- import { ReactiveModuleDescriptor } from '@react-router-modules/core';
11
- import { ReactiveService } from '@react-router-modules/core';
12
- import { ReactNode } from 'react';
13
- import { ReactPortal } from 'react';
11
+ import { ModuleDescriptor } from '@react-router-modules/core';
12
+ import { ModuleEntry } from '@modular-react/core';
13
+ import { ModuleErrorBoundary } from '@modular-react/react';
14
+ import { ModulesContext } from '@modular-react/react';
15
+ import { NavigationContext } from '@modular-react/react';
16
+ import { NavigationGroup } from '@modular-react/core';
17
+ import { NavigationManifest } from '@modular-react/core';
18
+ import { ReactiveService } from '@modular-react/core';
19
+ import { RecalculateSlotsContext } from '@modular-react/react';
14
20
  import { RouteObject } from 'react-router';
21
+ import { SlotFilter } from '@modular-react/core';
15
22
  import { SlotMap } from '@react-router-modules/core';
23
+ import { SlotMap as SlotMap_2 } from '@modular-react/core';
16
24
  import { SlotMapOf } from '@react-router-modules/core';
25
+ import { SlotMapOf as SlotMapOf_2 } from '@modular-react/core';
26
+ import { SlotsContext } from '@modular-react/react';
27
+ import { SlotsSignal } from '@modular-react/react';
17
28
  import { StoreApi } from 'zustand';
29
+ import { useModules } from '@modular-react/react';
30
+ import { useNavigation } from '@modular-react/react';
31
+ import { useRecalculateSlots } from '@modular-react/react';
32
+ import { useSlots } from '@modular-react/react';
33
+ import { validateDependencies } from '@modular-react/core';
34
+ import { validateNoDuplicateIds } from '@modular-react/core';
18
35
  import { ZoneMapOf } from '@react-router-modules/core';
19
36
 
20
- export declare interface ApplicationManifest<TSlots extends SlotMapOf<TSlots> = SlotMap> {
37
+ export declare interface ApplicationManifest<TSlots extends SlotMapOf_2<TSlots> = SlotMap_2> {
21
38
  /** The root React component with all providers wired */
22
39
  readonly App: React.ComponentType;
23
40
  /** The React Router instance — pass to <RouterProvider /> if needed */
24
41
  readonly router: DataRouter;
25
42
  /** Auto-generated navigation manifest from all modules */
26
43
  readonly navigation: NavigationManifest;
27
- /** Collected slot contributions from all modules */
44
+ /** Collected slot contributions from all modules (static base — does not include dynamic) */
28
45
  readonly slots: TSlots;
29
46
  /** Registered module summaries — use useModules() to access in components */
30
47
  readonly modules: readonly ModuleEntry[];
48
+ /**
49
+ * Trigger re-evaluation of dynamic slots.
50
+ *
51
+ * Call this after a state change that affects `dynamicSlots` or `slotFilter`
52
+ * results — for example after login, role change, or feature flag update.
53
+ * Components consuming `useSlots()` will re-render with the new values.
54
+ *
55
+ * No-op when no module uses `dynamicSlots` and no `slotFilter` is configured.
56
+ */
57
+ readonly recalculateSlots: () => void;
31
58
  }
32
59
 
33
- /**
34
- * Collects slot contributions from all registered modules.
35
- * Arrays are concatenated per slot key across modules.
36
- *
37
- * When defaults are provided, every key in defaults is guaranteed to exist
38
- * in the result — even if no module contributes to it.
39
- */
40
- export declare function buildSlotsManifest<TSlots extends {
41
- [K in keyof TSlots]: readonly unknown[];
42
- }>(modules: readonly ReactiveModuleDescriptor<any, TSlots>[], defaults?: Partial<{
43
- [K in keyof TSlots]: TSlots[K];
44
- }>): TSlots;
60
+ export { buildNavigationManifest }
45
61
 
46
- export declare function createRegistry<TSharedDependencies extends Record<string, any>, TSlots extends SlotMapOf<TSlots> = SlotMap>(config: RegistryConfig<TSharedDependencies, TSlots>): ReactiveRegistry<TSharedDependencies, TSlots>;
62
+ export { buildSlotsManifest }
47
63
 
48
- /**
49
- * Type-safe accessor for module metadata.
50
- * Use this when the shell defines a known meta shape and wants to read it
51
- * without casting every field.
52
- *
53
- * Returns undefined if the module has no meta.
54
- *
55
- * @example
56
- * interface JourneyMeta { name: string; category: string; icon: string }
57
- * const meta = getModuleMeta<JourneyMeta>(mod)
58
- * if (meta) console.log(meta.name) // typed, no cast
59
- */
60
- export declare function getModuleMeta<TMeta extends {
61
- [K in keyof TMeta]: unknown;
62
- }>(entry: ModuleEntry): Readonly<TMeta> | undefined;
64
+ export { collectDynamicSlotFactories }
63
65
 
64
- /**
65
- * A summary of a registered module exposed to the shell.
66
- * Includes the module's identity, metadata, and optional component.
67
- */
68
- export declare interface ModuleEntry {
69
- /** Unique module identifier */
70
- readonly id: string;
71
- /** SemVer version string */
72
- readonly version: string;
73
- /** Catalog metadata (description, icon, category, etc.) */
74
- readonly meta?: Readonly<Record<string, unknown>>;
75
- /** A React component the shell can render outside of routes */
76
- readonly component?: React.ComponentType<any>;
77
- /** Zone components contributed when this module is active in a workspace tab */
78
- readonly zones?: Readonly<Record<string, React.ComponentType<any>>>;
79
- }
66
+ export declare function createRegistry<TSharedDependencies extends Record<string, any>, TSlots extends SlotMapOf<TSlots> = SlotMap>(config: RegistryConfig<TSharedDependencies, TSlots>): ModuleRegistry<TSharedDependencies, TSlots>;
80
67
 
81
- export declare class ModuleErrorBoundary extends Component<Props, State> {
82
- state: State;
83
- static getDerivedStateFromError(error: Error): State;
84
- componentDidCatch(error: Error, info: ErrorInfo): void;
85
- render(): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | ReactPortal | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | JSX.Element | null | undefined;
86
- }
68
+ export { createSlotsSignal }
87
69
 
88
- export declare const ModulesContext: Context<readonly ModuleEntry[] | null>;
70
+ export { DynamicSlotFactory }
89
71
 
90
- export declare interface NavigationGroup {
91
- readonly group: string;
92
- readonly items: readonly NavigationItem[];
93
- }
72
+ export { DynamicSlotsProvider }
94
73
 
95
- export declare interface NavigationManifest {
96
- /** All navigation items flat */
97
- readonly items: readonly NavigationItem[];
98
- /** Items grouped by their group key, sorted by order within each group */
99
- readonly groups: readonly NavigationGroup[];
100
- /** Ungrouped items (no group key) */
101
- readonly ungrouped: readonly NavigationItem[];
102
- }
74
+ export { evaluateDynamicSlots }
103
75
 
104
- declare interface Props {
105
- moduleId: string;
106
- fallback?: ReactNode;
107
- children: ReactNode;
108
- }
76
+ export { getModuleMeta }
77
+
78
+ export { ModuleEntry }
109
79
 
110
- export declare interface ReactiveRegistry<TSharedDependencies extends Record<string, any>, TSlots extends SlotMapOf<TSlots> = SlotMap> {
80
+ export { ModuleErrorBoundary }
81
+
82
+ export declare interface ModuleRegistry<TSharedDependencies extends Record<string, any>, TSlots extends SlotMapOf<TSlots> = SlotMap> {
111
83
  /** Register an eager module */
112
- register(module: ReactiveModuleDescriptor<TSharedDependencies, TSlots>): void;
84
+ register(module: ModuleDescriptor<TSharedDependencies, TSlots>): void;
113
85
  /** Register a lazily-loaded module */
114
86
  registerLazy(descriptor: LazyModuleDescriptor<TSharedDependencies, TSlots>): void;
115
87
  /**
116
88
  * Resolve all modules and produce the application manifest.
117
89
  * Validates dependencies and builds the route tree.
118
90
  */
119
- resolve(options?: ResolveOptions): ApplicationManifest<TSlots>;
91
+ resolve(options?: ResolveOptions<TSharedDependencies, TSlots>): ApplicationManifest<TSlots>;
120
92
  }
121
93
 
94
+ export { ModulesContext }
95
+
96
+ export { NavigationContext }
97
+
98
+ export { NavigationGroup }
99
+
100
+ export { NavigationManifest }
101
+
102
+ export { RecalculateSlotsContext }
103
+
122
104
  /**
123
105
  * Configuration for creating a registry.
124
106
  *
@@ -127,7 +109,7 @@ export declare interface ReactiveRegistry<TSharedDependencies extends Record<str
127
109
  * - **services** — plain objects (non-reactive, static references)
128
110
  * - **reactiveServices** — external sources with subscribe/getSnapshot (reactive via useSyncExternalStore)
129
111
  */
130
- export declare interface RegistryConfig<TSharedDependencies extends Record<string, any>, TSlots extends SlotMapOf<TSlots> = SlotMap> {
112
+ export declare interface RegistryConfig<TSharedDependencies extends Record<string, any>, TSlots extends SlotMapOf_2<TSlots> = SlotMap_2> {
131
113
  /** Zustand stores — state you own and mutate */
132
114
  stores?: {
133
115
  [K in keyof TSharedDependencies]?: StoreApi<TSharedDependencies[K]>;
@@ -150,13 +132,11 @@ export declare interface RegistryConfig<TSharedDependencies extends Record<strin
150
132
  };
151
133
  }
152
134
 
153
- export declare interface ResolveOptions {
135
+ export declare interface ResolveOptions<TSharedDependencies extends Record<string, any> = Record<string, any>, TSlots extends SlotMapOf<TSlots> = SlotMap> {
154
136
  /** Root layout component (renders <Outlet /> for child routes) */
155
137
  rootComponent?: () => React.JSX.Element;
156
138
  /**
157
139
  * Pre-built root route — if provided, used instead of auto-creating one.
158
- * Use this when you need full control over the root route config
159
- * (loader, errorElement, etc.).
160
140
  * Mutually exclusive with rootComponent/notFoundComponent/loader.
161
141
  */
162
142
  rootRoute?: RouteObject;
@@ -167,11 +147,7 @@ export declare interface ResolveOptions {
167
147
  /**
168
148
  * Called before every route loads — for observability, analytics, feature flags.
169
149
  * Runs for ALL routes including public ones like /login.
170
- * Throw a `redirect()` from react-router to redirect.
171
- * Ignored if rootRoute is provided (configure loader on your root route instead).
172
- *
173
- * For auth guards, use `authenticatedRoute` instead — it creates a layout route
174
- * boundary that only wraps protected routes.
150
+ * Ignored if rootRoute is provided.
175
151
  */
176
152
  loader?: (args: {
177
153
  request: Request;
@@ -179,34 +155,7 @@ export declare interface ResolveOptions {
179
155
  }) => any;
180
156
  /**
181
157
  * Auth boundary — a pathless layout route that guards module routes and
182
- * the index route. Shell routes (login, error pages) sit outside this
183
- * boundary and are NOT guarded.
184
- *
185
- * Follows React Router's recommended layout route pattern:
186
- * ```
187
- * Root (loader runs for ALL routes — observability, etc.)
188
- * ├── shellRoutes (public — /login, /signup)
189
- * └── _authenticated (layout — auth guard)
190
- * ├── / (indexComponent)
191
- * └── module routes
192
- * ```
193
- *
194
- * @example
195
- * ```ts
196
- * registry.resolve({
197
- * authenticatedRoute: {
198
- * loader: async () => {
199
- * const res = await fetch('/api/auth/session')
200
- * if (!res.ok) throw redirect('/login')
201
- * return null
202
- * },
203
- * Component: ShellLayout,
204
- * },
205
- * shellRoutes: () => [
206
- * { path: '/login', Component: LoginPage },
207
- * ],
208
- * })
209
- * ```
158
+ * the index route. Shell routes sit outside this boundary.
210
159
  */
211
160
  authenticatedRoute?: {
212
161
  /** Auth guard — throw redirect() to deny access */
@@ -217,50 +166,27 @@ export declare interface ResolveOptions {
217
166
  /** Layout component for authenticated pages. Defaults to <Outlet />. */
218
167
  Component?: () => React.JSX.Element;
219
168
  };
220
- /**
221
- * Additional routes owned by the shell (login, error pages, onboarding, etc.)
222
- * that sit alongside module routes at the root level.
223
- *
224
- * When `authenticatedRoute` is used, shell routes are NOT guarded — they
225
- * are siblings of the auth layout, not children. This is the natural place
226
- * for public pages like /login.
227
- */
169
+ /** Additional routes owned by the shell (login, error pages, etc.) */
228
170
  shellRoutes?: () => RouteObject[];
229
171
  /**
230
172
  * Additional React providers to wrap around the app tree.
231
- *
232
- * **Nesting order:** First element is outermost. `[A, B, C]` produces:
233
- * ```tsx
234
- * <A>
235
- * <B>
236
- * <C>
237
- * ...app...
238
- * </C>
239
- * </B>
240
- * </A>
241
- * ```
242
- *
243
- * Place providers that other providers depend on **first** in the array.
244
- * For example, if your data-fetching provider reads from a theme context,
245
- * list the theme provider before the data-fetching provider.
246
- *
247
- * @example
248
- * ```ts
249
- * providers: [SWRConfigProvider, ThemeProvider, TooltipProvider]
250
- * // Produces: <SWRConfigProvider><ThemeProvider><TooltipProvider>...app...</TooltipProvider></ThemeProvider></SWRConfigProvider>
251
- * ```
173
+ * First element is outermost.
252
174
  */
253
175
  providers?: React.ComponentType<{
254
176
  children: React.ReactNode;
255
177
  }>[];
178
+ /**
179
+ * Global filter applied to the fully resolved slot manifest (static + dynamic)
180
+ * on every `recalculateSlots()` call.
181
+ */
182
+ slotFilter?: (slots: TSlots, deps: TSharedDependencies) => TSlots;
256
183
  }
257
184
 
258
- export declare const SlotsContext: Context<object | null>;
185
+ export { SlotFilter }
259
186
 
260
- declare interface State {
261
- hasError: boolean;
262
- error: Error | null;
263
- }
187
+ export { SlotsContext }
188
+
189
+ export { SlotsSignal }
264
190
 
265
191
  /**
266
192
  * Read zone components from both the matched route hierarchy AND the
@@ -271,60 +197,17 @@ declare interface State {
271
197
  * - **Tab-based modules** contribute zones via the `zones` field on their descriptor
272
198
  *
273
199
  * When both sources provide a value for the same zone key, the module's
274
- * contribution wins — the assumption is that the active tab's content is more
275
- * relevant than the underlying route's.
276
- *
277
- * @param activeModuleId - The id of the module that is currently active in the
278
- * workspace (e.g. the journey tab's moduleId). Pass `null` or `undefined`
279
- * when no module tab is active — only route zones are returned.
280
- *
281
- * @example
282
- * // In the shell layout:
283
- * const activeTab = getActiveTab(interactionId)
284
- * const moduleId = activeTab?.type === 'native-journey' ? activeTab.journeyId : null
285
- * const zones = useActiveZones<AppZones>(moduleId)
286
- * const Panel = zones.contextualPanel
287
- *
288
- * return (
289
- * <aside>{Panel ? <Panel /> : <DefaultPanel />}</aside>
290
- * )
200
+ * contribution wins.
291
201
  */
292
202
  export declare function useActiveZones<TZones extends ZoneMapOf<TZones>>(activeModuleId?: string | null): Partial<TZones>;
293
203
 
294
- /**
295
- * Access the list of registered modules with their metadata and components.
296
- * Must be used within a <ReactiveApp /> provider tree.
297
- *
298
- * Use this to build discovery UIs (directory pages, search, catalogs)
299
- * and to render module components in workspace tabs or panels.
300
- *
301
- * @example
302
- * const modules = useModules()
303
- * const journeys = modules.filter(m => m.meta?.category === 'payments')
304
- *
305
- * @example
306
- * const mod = modules.find(m => m.id === activeTab.moduleId)
307
- * if (mod?.component) return <mod.component {...props} />
308
- */
309
- export declare function useModules(): readonly ModuleEntry[];
204
+ export { useModules }
310
205
 
311
- /**
312
- * Access the auto-generated navigation manifest from registered modules.
313
- * Use this in layout components to render sidebar/nav items.
314
- */
315
- export declare function useNavigation(): NavigationManifest;
206
+ export { useNavigation }
316
207
 
317
- /**
318
- * Access the collected slot contributions from all registered modules.
319
- * Must be used within a <ReactiveApp /> provider tree.
320
- *
321
- * @example
322
- * const slots = useSlots<AppSlots>()
323
- * const commands = slots.commands // CommandDefinition[] from all modules
324
- */
325
- export declare function useSlots<TSlots extends {
326
- [K in keyof TSlots]: readonly unknown[];
327
- }>(): TSlots;
208
+ export { useRecalculateSlots }
209
+
210
+ export { useSlots }
328
211
 
329
212
  /**
330
213
  * Read zone components contributed by the currently matched route hierarchy.
@@ -357,4 +240,8 @@ export declare function useSlots<TSlots extends {
357
240
  */
358
241
  export declare function useZones<TZones extends ZoneMapOf<TZones>>(): Partial<TZones>;
359
242
 
243
+ export { validateDependencies }
244
+
245
+ export { validateNoDuplicateIds }
246
+
360
247
  export { }
package/dist/index.js CHANGED
@@ -1,64 +1,11 @@
1
1
  import { Outlet as e, RouterProvider as t, createBrowserRouter as n, createMemoryRouter as r, useMatches as i, useRoutes as a } from "react-router";
2
- import { jsx as o, jsxs as s } from "react/jsx-runtime";
3
- import { Component as c, createContext as l, useContext as u, useMemo as d } from "react";
4
- import { SharedDependenciesContext as f } from "@react-router-modules/core";
5
- //#region src/validation.ts
6
- function p(e, t) {
7
- let n = /* @__PURE__ */ new Set();
8
- for (let t of e) {
9
- if (n.has(t.id)) throw Error(`[@react-router-modules/runtime] Duplicate module ID "${t.id}". Each module must have a unique ID.`);
10
- n.add(t.id);
11
- }
12
- for (let e of t) {
13
- if (n.has(e.id)) throw Error(`[@react-router-modules/runtime] Duplicate module ID "${e.id}". Each module must have a unique ID.`);
14
- n.add(e.id);
15
- }
16
- }
17
- function m(e, t) {
18
- for (let n of e) {
19
- if (n.requires) {
20
- let e = n.requires.filter((e) => !t.has(e));
21
- if (e.length > 0) throw Error(`[@react-router-modules/runtime] Module "${n.id}" requires dependencies not provided by the registry: ${e.map(String).join(", ")}. Available: ${[...t].join(", ") || "(none)"}`);
22
- }
23
- if (n.optionalRequires) {
24
- let e = n.optionalRequires.filter((e) => !t.has(e));
25
- e.length > 0 && console.warn(`[@react-router-modules/runtime] Module "${n.id}" has optional dependencies not provided: ${e.map(String).join(", ")}. The module will still load but may have reduced functionality.`);
26
- }
27
- }
28
- }
29
- //#endregion
30
- //#region src/navigation.ts
31
- function h(e) {
32
- let t = [];
33
- for (let n of e) n.navigation && t.push(...n.navigation);
34
- let n = [...t].sort((e, t) => {
35
- let n = (e.order ?? 999) - (t.order ?? 999);
36
- return n === 0 ? e.label.localeCompare(t.label) : n;
37
- }), r = /* @__PURE__ */ new Map(), i = [];
38
- for (let e of n) if (e.group) {
39
- let t = r.get(e.group);
40
- t || (t = [], r.set(e.group, t)), t.push(e);
41
- } else i.push(e);
42
- return {
43
- items: n,
44
- groups: [...r.entries()].map(([e, t]) => ({
45
- group: e,
46
- items: t
47
- })),
48
- ungrouped: i
49
- };
50
- }
51
- //#endregion
52
- //#region src/slots.ts
53
- function g(e, t) {
54
- let n = {};
55
- if (t) for (let [e, r] of Object.entries(t)) n[e] = Array.isArray(r) ? [...r] : [];
56
- for (let t of e) if (t.slots) for (let [e, r] of Object.entries(t.slots)) n[e] || (n[e] = []), Array.isArray(r) && n[e].push(...r);
57
- return n;
58
- }
59
- //#endregion
2
+ import { buildNavigationManifest as o, buildNavigationManifest as s, buildSlotsManifest as c, buildSlotsManifest as l, collectDynamicSlotFactories as u, collectDynamicSlotFactories as d, evaluateDynamicSlots as f, validateDependencies as p, validateDependencies as m, validateNoDuplicateIds as h, validateNoDuplicateIds as g } from "@modular-react/core";
3
+ import { DynamicSlotsProvider as _, DynamicSlotsProvider as v, ModuleErrorBoundary as y, ModulesContext as b, ModulesContext as x, NavigationContext as S, NavigationContext as C, RecalculateSlotsContext as w, RecalculateSlotsContext as T, SlotsContext as E, SlotsContext as D, createSlotsSignal as O, createSlotsSignal as k, getModuleMeta as A, useModules as j, useModules as M, useNavigation as N, useRecalculateSlots as P, useSlots as F } from "@modular-react/react";
4
+ import { jsx as I } from "react/jsx-runtime";
5
+ import { useMemo as L } from "react";
6
+ import { SharedDependenciesContext as R } from "@react-router-modules/core";
60
7
  //#region src/route-builder.tsx
61
- function _(e, t, n) {
8
+ function z(e, t, n) {
62
9
  if (n?.rootRoute) {
63
10
  let r = [...n.rootRoute.children ?? []];
64
11
  n?.shellRoutes && r.push(...n.shellRoutes());
@@ -73,8 +20,8 @@ function _(e, t, n) {
73
20
  if (!e) throw Error(`[@react-router-modules/runtime] Module "${t.id}" createRoutes() returned a falsy value.`);
74
21
  i.push(...Array.isArray(e) ? e : [e]);
75
22
  }
76
- for (let e of t) i.push(y(e));
77
- return n?.authenticatedRoute ? r.push(v(n.authenticatedRoute, i)) : r.push(...i), n.rootRoute.children = r, [n.rootRoute];
23
+ for (let e of t) i.push(V(e));
24
+ return n?.authenticatedRoute ? r.push(B(n.authenticatedRoute, i)) : r.push(...i), n.rootRoute.children = r, [n.rootRoute];
78
25
  }
79
26
  let r = [];
80
27
  n?.shellRoutes && r.push(...n.shellRoutes());
@@ -89,8 +36,8 @@ function _(e, t, n) {
89
36
  if (!e) throw Error(`[@react-router-modules/runtime] Module "${t.id}" createRoutes() returned a falsy value.`);
90
37
  i.push(...Array.isArray(e) ? e : [e]);
91
38
  }
92
- for (let e of t) i.push(y(e));
93
- return n?.authenticatedRoute ? r.push(v(n.authenticatedRoute, i)) : r.push(...i), n?.notFoundComponent && r.push({
39
+ for (let e of t) i.push(V(e));
40
+ return n?.authenticatedRoute ? r.push(B(n.authenticatedRoute, i)) : r.push(...i), n?.notFoundComponent && r.push({
94
41
  path: "*",
95
42
  Component: n.notFoundComponent
96
43
  }), [{
@@ -100,15 +47,15 @@ function _(e, t, n) {
100
47
  children: r
101
48
  }];
102
49
  }
103
- function v(t, n) {
50
+ function B(t, n) {
104
51
  return {
105
52
  id: "_authenticated",
106
- Component: t.Component ?? (() => /* @__PURE__ */ o(e, {})),
53
+ Component: t.Component ?? (() => /* @__PURE__ */ I(e, {})),
107
54
  loader: t.loader,
108
55
  children: n
109
56
  };
110
57
  }
111
- function y(e) {
58
+ function V(e) {
112
59
  let t = null;
113
60
  return {
114
61
  path: e.basePath.replace(/^\//, "") + "/*",
@@ -128,64 +75,52 @@ function y(e) {
128
75
  };
129
76
  }
130
77
  //#endregion
131
- //#region src/navigation-context.tsx
132
- var b = l(null);
133
- function x() {
134
- let e = u(b);
135
- if (!e) throw Error("[@react-router-modules/runtime] useNavigation must be used within a <ReactiveApp />.");
136
- return e;
137
- }
138
- //#endregion
139
- //#region src/slots-context.tsx
140
- var S = l(null);
141
- function C() {
142
- let e = u(S);
143
- if (!e) throw Error("[@react-router-modules/runtime] useSlots must be used within a <ReactiveApp />.");
144
- return e;
145
- }
146
- //#endregion
147
- //#region src/modules-context.ts
148
- var w = l(null);
149
- function T() {
150
- let e = u(w);
151
- if (!e) throw Error("[@react-router-modules/runtime] useModules must be used within a <ReactiveApp />.");
152
- return e;
153
- }
154
- function E(e) {
155
- return e.meta;
156
- }
157
- //#endregion
158
78
  //#region src/app.tsx
159
- function D({ router: e, stores: n, services: r, reactiveServices: i, navigation: a, slots: s, modules: c, providers: l }) {
160
- let u = {
79
+ function H({ router: e, stores: n, services: r, reactiveServices: i, navigation: a, slots: o, modules: s, providers: c, dynamicSlotFactories: l, slotFilter: u, slotsSignal: d, recalculateSlots: f }) {
80
+ let p = {
161
81
  stores: n,
162
82
  services: r,
163
83
  reactiveServices: i
164
- };
165
- function p() {
166
- return d(() => {
167
- let n = /* @__PURE__ */ o(f, {
168
- value: u,
169
- children: /* @__PURE__ */ o(b, {
84
+ }, m = l.length > 0 || u != null;
85
+ function h() {
86
+ return L(() => {
87
+ let h = /* @__PURE__ */ I(R, {
88
+ value: p,
89
+ children: /* @__PURE__ */ I(C, {
170
90
  value: a,
171
- children: /* @__PURE__ */ o(S, {
172
- value: s,
173
- children: /* @__PURE__ */ o(w, {
174
- value: c,
175
- children: /* @__PURE__ */ o(t, { router: e })
91
+ children: /* @__PURE__ */ I(T, {
92
+ value: f,
93
+ children: m ? /* @__PURE__ */ I(v, {
94
+ baseSlots: o,
95
+ factories: l,
96
+ filter: u,
97
+ stores: n,
98
+ services: r,
99
+ reactiveServices: i,
100
+ signal: d,
101
+ children: /* @__PURE__ */ I(x, {
102
+ value: s,
103
+ children: /* @__PURE__ */ I(t, { router: e })
104
+ })
105
+ }) : /* @__PURE__ */ I(D, {
106
+ value: o,
107
+ children: /* @__PURE__ */ I(x, {
108
+ value: s,
109
+ children: /* @__PURE__ */ I(t, { router: e })
110
+ })
176
111
  })
177
112
  })
178
113
  })
179
114
  });
180
- if (l) for (let e of [...l].reverse()) n = /* @__PURE__ */ o(e, { children: n });
181
- return n;
115
+ if (c) for (let e of [...c].reverse()) h = /* @__PURE__ */ I(e, { children: h });
116
+ return h;
182
117
  }, []);
183
118
  }
184
- return p.displayName = "ReactiveApp", p;
119
+ return h.displayName = "ModularApp", h;
185
120
  }
186
121
  //#endregion
187
122
  //#region src/registry.ts
188
- function O(e) {
123
+ function U(e) {
189
124
  let t = [], i = [], a = !1, o = new Set([
190
125
  ...Object.keys(e.stores ?? {}),
191
126
  ...Object.keys(e.services ?? {}),
@@ -200,53 +135,59 @@ function O(e) {
200
135
  if (a) throw Error("[@react-router-modules/runtime] Cannot register modules after resolve() has been called.");
201
136
  i.push(e);
202
137
  },
203
- resolve(s) {
138
+ resolve(c) {
204
139
  if (a) throw Error("[@react-router-modules/runtime] resolve() can only be called once.");
205
- a = !0, p(t, i), m(t, o);
206
- let c = k(e);
140
+ a = !0, g(t, i), m(t, o);
141
+ let u = W(e);
207
142
  for (let e of t) try {
208
- e.lifecycle?.onRegister?.(c);
143
+ e.lifecycle?.onRegister?.(u);
209
144
  } catch (t) {
210
145
  throw Error(`[@react-router-modules/runtime] Module "${e.id}" lifecycle.onRegister() failed: ${t instanceof Error ? t.message : String(t)}`, { cause: t });
211
146
  }
212
- let l = _(t, i, {
213
- rootRoute: s?.rootRoute,
214
- rootComponent: s?.rootComponent,
215
- indexComponent: s?.indexComponent,
216
- notFoundComponent: s?.notFoundComponent,
217
- loader: s?.loader,
218
- authenticatedRoute: s?.authenticatedRoute,
219
- shellRoutes: s?.shellRoutes
220
- }), u = typeof document < "u" ? n(l) : r(l), d = h(t), f = g(t, e.slots), v = t.map((e) => ({
147
+ let f = z(t, i, {
148
+ rootRoute: c?.rootRoute,
149
+ rootComponent: c?.rootComponent,
150
+ indexComponent: c?.indexComponent,
151
+ notFoundComponent: c?.notFoundComponent,
152
+ loader: c?.loader,
153
+ authenticatedRoute: c?.authenticatedRoute,
154
+ shellRoutes: c?.shellRoutes
155
+ }), p = typeof document < "u" ? n(f) : r(f), h = s(t), _ = l(t, e.slots), v = d(t), y = c?.slotFilter, b = t.map((e) => ({
221
156
  id: e.id,
222
157
  version: e.version,
223
158
  meta: e.meta,
224
159
  component: e.component,
225
160
  zones: e.zones
226
- })), y = {}, b = {}, x = {};
227
- if (e.stores) for (let [t, n] of Object.entries(e.stores)) n && (y[t] = n);
228
- if (e.services) for (let [t, n] of Object.entries(e.services)) n !== void 0 && (b[t] = n);
229
- if (e.reactiveServices) for (let [t, n] of Object.entries(e.reactiveServices)) n && (x[t] = n);
161
+ })), x = {}, S = {}, C = {};
162
+ if (e.stores) for (let [t, n] of Object.entries(e.stores)) n && (x[t] = n);
163
+ if (e.services) for (let [t, n] of Object.entries(e.services)) n !== void 0 && (S[t] = n);
164
+ if (e.reactiveServices) for (let [t, n] of Object.entries(e.reactiveServices)) n && (C[t] = n);
165
+ let w = k(), T = v.length > 0 || y != null ? () => w.notify() : () => {};
230
166
  return {
231
- App: D({
232
- router: u,
233
- stores: y,
234
- services: b,
235
- reactiveServices: x,
236
- navigation: d,
237
- slots: f,
238
- modules: v,
239
- providers: s?.providers
167
+ App: H({
168
+ router: p,
169
+ stores: x,
170
+ services: S,
171
+ reactiveServices: C,
172
+ navigation: h,
173
+ slots: _,
174
+ modules: b,
175
+ providers: c?.providers,
176
+ dynamicSlotFactories: v,
177
+ slotFilter: y,
178
+ slotsSignal: w,
179
+ recalculateSlots: T
240
180
  }),
241
- router: u,
242
- navigation: d,
243
- slots: f,
244
- modules: v
181
+ router: p,
182
+ navigation: h,
183
+ slots: _,
184
+ modules: b,
185
+ recalculateSlots: T
245
186
  };
246
187
  }
247
188
  };
248
189
  }
249
- function k(e) {
190
+ function W(e) {
250
191
  let t = {};
251
192
  if (e.stores) for (let [n, r] of Object.entries(e.stores)) r && (t[n] = r.getState());
252
193
  if (e.services) for (let [n, r] of Object.entries(e.services)) r !== void 0 && (t[n] = r);
@@ -255,7 +196,7 @@ function k(e) {
255
196
  }
256
197
  //#endregion
257
198
  //#region src/zones.ts
258
- function A() {
199
+ function G() {
259
200
  let e = i(), t = {};
260
201
  for (let n of e) {
261
202
  let e = n.handle;
@@ -265,8 +206,8 @@ function A() {
265
206
  }
266
207
  //#endregion
267
208
  //#region src/active-zones.ts
268
- function j(e) {
269
- let t = A(), n = T();
209
+ function K(e) {
210
+ let t = G(), n = M();
270
211
  if (!e) return t;
271
212
  let r = n.find((t) => t.id === e);
272
213
  return r?.zones ? {
@@ -275,51 +216,6 @@ function j(e) {
275
216
  } : t;
276
217
  }
277
218
  //#endregion
278
- //#region src/error-boundary.tsx
279
- var M = class extends c {
280
- state = {
281
- hasError: !1,
282
- error: null
283
- };
284
- static getDerivedStateFromError(e) {
285
- return {
286
- hasError: !0,
287
- error: e
288
- };
289
- }
290
- componentDidCatch(e, t) {
291
- console.error(`[@react-router-modules/runtime] Module "${this.props.moduleId}" encountered an error:`, e, t);
292
- }
293
- render() {
294
- return this.state.hasError ? this.props.fallback ? this.props.fallback : /* @__PURE__ */ s("div", {
295
- style: {
296
- padding: "1rem",
297
- border: "1px solid #e53e3e",
298
- borderRadius: "0.5rem",
299
- margin: "1rem"
300
- },
301
- children: [/* @__PURE__ */ s("h3", {
302
- style: {
303
- color: "#e53e3e",
304
- margin: "0 0 0.5rem 0"
305
- },
306
- children: [
307
- "Module \"",
308
- this.props.moduleId,
309
- "\" encountered an error"
310
- ]
311
- }), /* @__PURE__ */ o("pre", {
312
- style: {
313
- fontSize: "0.875rem",
314
- color: "#718096",
315
- whiteSpace: "pre-wrap"
316
- },
317
- children: this.state.error?.message
318
- })]
319
- }) : this.props.children;
320
- }
321
- };
322
- //#endregion
323
- export { M as ModuleErrorBoundary, w as ModulesContext, S as SlotsContext, g as buildSlotsManifest, O as createRegistry, E as getModuleMeta, j as useActiveZones, T as useModules, x as useNavigation, C as useSlots, A as useZones };
219
+ export { _ as DynamicSlotsProvider, y as ModuleErrorBoundary, b as ModulesContext, S as NavigationContext, w as RecalculateSlotsContext, E as SlotsContext, o as buildNavigationManifest, c as buildSlotsManifest, u as collectDynamicSlotFactories, U as createRegistry, O as createSlotsSignal, f as evaluateDynamicSlots, A as getModuleMeta, K as useActiveZones, j as useModules, N as useNavigation, P as useRecalculateSlots, F as useSlots, G as useZones, p as validateDependencies, h as validateNoDuplicateIds };
324
220
 
325
221
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/validation.ts","../src/navigation.ts","../src/slots.ts","../src/route-builder.tsx","../src/navigation-context.tsx","../src/slots-context.tsx","../src/modules-context.ts","../src/app.tsx","../src/registry.ts","../src/zones.ts","../src/active-zones.ts","../src/error-boundary.tsx"],"sourcesContent":["import type { ReactiveModuleDescriptor, LazyModuleDescriptor } from \"@react-router-modules/core\";\r\n\r\nexport function validateNoDuplicateIds(\r\n modules: ReactiveModuleDescriptor[],\r\n lazyModules: LazyModuleDescriptor[],\r\n): void {\r\n const ids = new Set<string>();\r\n for (const mod of modules) {\r\n if (ids.has(mod.id)) {\r\n throw new Error(\r\n `[@react-router-modules/runtime] Duplicate module ID \"${mod.id}\". Each module must have a unique ID.`,\r\n );\r\n }\r\n ids.add(mod.id);\r\n }\r\n for (const mod of lazyModules) {\r\n if (ids.has(mod.id)) {\r\n throw new Error(\r\n `[@react-router-modules/runtime] Duplicate module ID \"${mod.id}\". Each module must have a unique ID.`,\r\n );\r\n }\r\n ids.add(mod.id);\r\n }\r\n}\r\n\r\nexport function validateDependencies(\r\n modules: ReactiveModuleDescriptor[],\r\n availableKeys: Set<string>,\r\n): void {\r\n for (const mod of modules) {\r\n if (mod.requires) {\r\n const missing = mod.requires.filter((key) => !availableKeys.has(key as string));\r\n if (missing.length > 0) {\r\n throw new Error(\r\n `[@react-router-modules/runtime] Module \"${mod.id}\" requires dependencies not provided by the registry: ` +\r\n `${missing.map(String).join(\", \")}. ` +\r\n `Available: ${[...availableKeys].join(\", \") || \"(none)\"}`,\r\n );\r\n }\r\n }\r\n\r\n if (mod.optionalRequires) {\r\n const missing = mod.optionalRequires.filter((key) => !availableKeys.has(key as string));\r\n if (missing.length > 0) {\r\n console.warn(\r\n `[@react-router-modules/runtime] Module \"${mod.id}\" has optional dependencies not provided: ` +\r\n `${missing.map(String).join(\", \")}. The module will still load but may have reduced functionality.`,\r\n );\r\n }\r\n }\r\n }\r\n}\r\n","import type { ReactiveModuleDescriptor, NavigationItem } from \"@react-router-modules/core\";\r\nimport type { NavigationManifest, NavigationGroup } from \"./types.js\";\r\n\r\nexport function buildNavigationManifest(modules: ReactiveModuleDescriptor[]): NavigationManifest {\r\n const allItems: NavigationItem[] = [];\r\n\r\n for (const mod of modules) {\r\n if (mod.navigation) {\r\n allItems.push(...mod.navigation);\r\n }\r\n }\r\n\r\n // Sort by order (lower first), then by label alphabetically\r\n const sorted = [...allItems].sort((a, b) => {\r\n const orderDiff = (a.order ?? 999) - (b.order ?? 999);\r\n if (orderDiff !== 0) return orderDiff;\r\n return a.label.localeCompare(b.label);\r\n });\r\n\r\n // Group items\r\n const groupMap = new Map<string, NavigationItem[]>();\r\n const ungrouped: NavigationItem[] = [];\r\n\r\n for (const item of sorted) {\r\n if (item.group) {\r\n let group = groupMap.get(item.group);\r\n if (!group) {\r\n group = [];\r\n groupMap.set(item.group, group);\r\n }\r\n group.push(item);\r\n } else {\r\n ungrouped.push(item);\r\n }\r\n }\r\n\r\n const groups: NavigationGroup[] = [...groupMap.entries()].map(([group, items]) => ({\r\n group,\r\n items,\r\n }));\r\n\r\n return { items: sorted, groups, ungrouped };\r\n}\r\n","import type { ReactiveModuleDescriptor } from \"@react-router-modules/core\";\r\n\r\n/**\r\n * Collects slot contributions from all registered modules.\r\n * Arrays are concatenated per slot key across modules.\r\n *\r\n * When defaults are provided, every key in defaults is guaranteed to exist\r\n * in the result — even if no module contributes to it.\r\n */\r\nexport function buildSlotsManifest<TSlots extends { [K in keyof TSlots]: readonly unknown[] }>(\r\n modules: readonly ReactiveModuleDescriptor<any, TSlots>[],\r\n defaults?: Partial<{ [K in keyof TSlots]: TSlots[K] }>,\r\n): TSlots {\r\n const result: Record<string, unknown[]> = {};\r\n\r\n // Initialize from defaults so every declared key exists\r\n if (defaults) {\r\n for (const [key, items] of Object.entries(defaults)) {\r\n result[key] = Array.isArray(items) ? [...items] : [];\r\n }\r\n }\r\n\r\n for (const mod of modules) {\r\n if (!mod.slots) continue;\r\n for (const [key, items] of Object.entries(mod.slots)) {\r\n if (!result[key]) result[key] = [];\r\n if (Array.isArray(items)) {\r\n result[key].push(...items);\r\n }\r\n }\r\n }\r\n\r\n return result as unknown as TSlots;\r\n}\r\n","import { Outlet, useRoutes } from \"react-router\";\r\nimport type { RouteObject } from \"react-router\";\r\nimport type { ReactiveModuleDescriptor, LazyModuleDescriptor } from \"@react-router-modules/core\";\r\n\r\nexport interface RouteBuilderOptions {\r\n /**\r\n * Pre-built root route. If provided, rootComponent/notFoundComponent/loader\r\n * are ignored — configure them directly on this route instead.\r\n */\r\n rootRoute?: RouteObject;\r\n /** Component for the root layout (renders <Outlet /> for child routes) */\r\n rootComponent?: () => React.JSX.Element;\r\n /** Component for the index route (/) */\r\n indexComponent?: () => React.JSX.Element;\r\n /** Component for the 404 / not-found route */\r\n notFoundComponent?: () => React.JSX.Element;\r\n /**\r\n * Called before every route loads — for observability, feature flags, etc.\r\n * Runs for ALL routes including public ones.\r\n * Ignored if rootRoute is provided.\r\n */\r\n loader?: (args: { request: Request; params: Record<string, string | undefined> }) => any;\r\n /**\r\n * Auth boundary — a pathless layout route that wraps module routes and\r\n * the index route. Shell routes (login, error pages) sit outside this\r\n * boundary and are NOT guarded.\r\n *\r\n * Follows React Router's recommended layout route pattern.\r\n *\r\n * When provided, the route tree becomes:\r\n * ```\r\n * Root (loader runs for ALL routes)\r\n * ├── shellRoutes (public — /login, /signup, etc.)\r\n * └── _authenticated (layout — loader guards children)\r\n * ├── / (indexComponent)\r\n * └── module routes\r\n * ```\r\n *\r\n * When omitted, all routes are direct children of root (no auth boundary).\r\n */\r\n authenticatedRoute?: {\r\n /** Auth guard — throw redirect() to deny access */\r\n loader: (args: { request: Request; params: Record<string, string | undefined> }) => any;\r\n /** Layout component for authenticated pages. Defaults to <Outlet />. */\r\n Component?: () => React.JSX.Element;\r\n };\r\n /** Additional routes owned by the shell (login, error pages, etc.) */\r\n shellRoutes?: () => RouteObject[];\r\n}\r\n\r\n/**\r\n * Composes all module route subtrees into a React Router route tree.\r\n * Modules without createRoutes are skipped (headless modules).\r\n */\r\nexport function buildRouteTree(\r\n modules: ReactiveModuleDescriptor[],\r\n lazyModules: LazyModuleDescriptor[],\r\n options?: RouteBuilderOptions,\r\n): RouteObject[] {\r\n // If a custom root route is provided, use it as the base\r\n if (options?.rootRoute) {\r\n const rootChildren: RouteObject[] = [...(options.rootRoute.children ?? [])];\r\n\r\n // Shell-owned routes (login, error pages) — always direct children of root\r\n if (options?.shellRoutes) {\r\n rootChildren.push(...options.shellRoutes());\r\n }\r\n\r\n const protectedChildren: RouteObject[] = [];\r\n\r\n // Add index route if provided\r\n if (options?.indexComponent) {\r\n protectedChildren.push({\r\n index: true,\r\n Component: options.indexComponent,\r\n });\r\n }\r\n\r\n // Eager modules\r\n for (const mod of modules) {\r\n if (!mod.createRoutes) continue;\r\n const routes = mod.createRoutes();\r\n if (!routes) {\r\n throw new Error(\r\n `[@react-router-modules/runtime] Module \"${mod.id}\" createRoutes() returned a falsy value.`,\r\n );\r\n }\r\n protectedChildren.push(...(Array.isArray(routes) ? routes : [routes]));\r\n }\r\n\r\n // Lazy modules\r\n for (const lazyMod of lazyModules) {\r\n protectedChildren.push(createLazyModuleRoute(lazyMod));\r\n }\r\n\r\n if (options?.authenticatedRoute) {\r\n rootChildren.push(\r\n createAuthenticatedLayoutRoute(options.authenticatedRoute, protectedChildren),\r\n );\r\n } else {\r\n rootChildren.push(...protectedChildren);\r\n }\r\n\r\n options.rootRoute.children = rootChildren;\r\n return [options.rootRoute];\r\n }\r\n\r\n // Build root route from options\r\n const rootChildren: RouteObject[] = [];\r\n\r\n // Shell-owned routes (login, error pages) — always direct children of root\r\n if (options?.shellRoutes) {\r\n rootChildren.push(...options.shellRoutes());\r\n }\r\n\r\n const protectedChildren: RouteObject[] = [];\r\n\r\n // Add index route if provided\r\n if (options?.indexComponent) {\r\n protectedChildren.push({\r\n index: true,\r\n Component: options.indexComponent,\r\n });\r\n }\r\n\r\n // Eager modules: call createRoutes\r\n for (const mod of modules) {\r\n if (!mod.createRoutes) continue;\r\n const routes = mod.createRoutes();\r\n if (!routes) {\r\n throw new Error(\r\n `[@react-router-modules/runtime] Module \"${mod.id}\" createRoutes() returned a falsy value.`,\r\n );\r\n }\r\n protectedChildren.push(...(Array.isArray(routes) ? routes : [routes]));\r\n }\r\n\r\n // Lazy modules\r\n for (const lazyMod of lazyModules) {\r\n protectedChildren.push(createLazyModuleRoute(lazyMod));\r\n }\r\n\r\n if (options?.authenticatedRoute) {\r\n rootChildren.push(\r\n createAuthenticatedLayoutRoute(options.authenticatedRoute, protectedChildren),\r\n );\r\n } else {\r\n rootChildren.push(...protectedChildren);\r\n }\r\n\r\n // Not-found catch-all\r\n if (options?.notFoundComponent) {\r\n rootChildren.push({\r\n path: \"*\",\r\n Component: options.notFoundComponent,\r\n });\r\n }\r\n\r\n const rootRoute: RouteObject = {\r\n path: \"/\",\r\n Component: options?.rootComponent,\r\n loader: options?.loader,\r\n children: rootChildren,\r\n };\r\n\r\n return [rootRoute];\r\n}\r\n\r\nfunction createAuthenticatedLayoutRoute(\r\n auth: NonNullable<RouteBuilderOptions[\"authenticatedRoute\"]>,\r\n children: RouteObject[],\r\n): RouteObject {\r\n return {\r\n id: \"_authenticated\",\r\n Component: auth.Component ?? (() => <Outlet />),\r\n loader: auth.loader,\r\n children,\r\n };\r\n}\r\n\r\n/**\r\n * Creates a catch-all route for a lazily-loaded module.\r\n * On first navigation, the module descriptor is loaded and its routes\r\n * are rendered as descendant routes via useRoutes().\r\n */\r\nfunction createLazyModuleRoute(lazyMod: LazyModuleDescriptor): RouteObject {\r\n // Capture the loaded routes so they're resolved only once\r\n let cachedRoutes: RouteObject[] | null = null;\r\n\r\n return {\r\n path: lazyMod.basePath.replace(/^\\//, \"\") + \"/*\",\r\n lazy: async () => {\r\n if (!cachedRoutes) {\r\n const { default: descriptor } = await lazyMod.load();\r\n if (descriptor.createRoutes) {\r\n const routes = descriptor.createRoutes();\r\n cachedRoutes = Array.isArray(routes) ? routes : [routes];\r\n } else {\r\n cachedRoutes = [];\r\n }\r\n }\r\n const routes = cachedRoutes;\r\n return {\r\n Component: function LazyModule() {\r\n return useRoutes(routes);\r\n },\r\n };\r\n },\r\n };\r\n}\r\n","import { createContext, useContext } from \"react\";\r\nimport type { NavigationManifest } from \"./types.js\";\r\n\r\nexport const NavigationContext = createContext<NavigationManifest | null>(null);\r\n\r\n/**\r\n * Access the auto-generated navigation manifest from registered modules.\r\n * Use this in layout components to render sidebar/nav items.\r\n */\r\nexport function useNavigation(): NavigationManifest {\r\n const nav = useContext(NavigationContext);\r\n if (!nav) {\r\n throw new Error(\r\n \"[@react-router-modules/runtime] useNavigation must be used within a <ReactiveApp />.\",\r\n );\r\n }\r\n return nav;\r\n}\r\n","import { createContext, useContext } from \"react\";\r\n\r\nexport const SlotsContext = createContext<object | null>(null);\r\n\r\n/**\r\n * Access the collected slot contributions from all registered modules.\r\n * Must be used within a <ReactiveApp /> provider tree.\r\n *\r\n * @example\r\n * const slots = useSlots<AppSlots>()\r\n * const commands = slots.commands // CommandDefinition[] from all modules\r\n */\r\nexport function useSlots<TSlots extends { [K in keyof TSlots]: readonly unknown[] }>(): TSlots {\r\n const slots = useContext(SlotsContext);\r\n if (!slots) {\r\n throw new Error(\r\n \"[@react-router-modules/runtime] useSlots must be used within a <ReactiveApp />.\",\r\n );\r\n }\r\n return slots as TSlots;\r\n}\r\n","import { createContext, useContext } from \"react\";\r\nimport type { ModuleEntry } from \"./types.js\";\r\n\r\nexport const ModulesContext = createContext<readonly ModuleEntry[] | null>(null);\r\n\r\n/**\r\n * Access the list of registered modules with their metadata and components.\r\n * Must be used within a <ReactiveApp /> provider tree.\r\n *\r\n * Use this to build discovery UIs (directory pages, search, catalogs)\r\n * and to render module components in workspace tabs or panels.\r\n *\r\n * @example\r\n * const modules = useModules()\r\n * const journeys = modules.filter(m => m.meta?.category === 'payments')\r\n *\r\n * @example\r\n * const mod = modules.find(m => m.id === activeTab.moduleId)\r\n * if (mod?.component) return <mod.component {...props} />\r\n */\r\nexport function useModules(): readonly ModuleEntry[] {\r\n const modules = useContext(ModulesContext);\r\n if (!modules) {\r\n throw new Error(\r\n \"[@react-router-modules/runtime] useModules must be used within a <ReactiveApp />.\",\r\n );\r\n }\r\n return modules;\r\n}\r\n\r\n/**\r\n * Type-safe accessor for module metadata.\r\n * Use this when the shell defines a known meta shape and wants to read it\r\n * without casting every field.\r\n *\r\n * Returns undefined if the module has no meta.\r\n *\r\n * @example\r\n * interface JourneyMeta { name: string; category: string; icon: string }\r\n * const meta = getModuleMeta<JourneyMeta>(mod)\r\n * if (meta) console.log(meta.name) // typed, no cast\r\n */\r\nexport function getModuleMeta<TMeta extends { [K in keyof TMeta]: unknown }>(\r\n entry: ModuleEntry,\r\n): Readonly<TMeta> | undefined {\r\n return entry.meta as Readonly<TMeta> | undefined;\r\n}\r\n","import { useMemo } from \"react\";\r\nimport { RouterProvider } from \"react-router\";\r\nimport type { DataRouter } from \"react-router\";\r\nimport type { StoreApi } from \"zustand\";\r\nimport type { ReactiveService } from \"@react-router-modules/core\";\r\nimport { SharedDependenciesContext } from \"@react-router-modules/core\";\r\nimport { NavigationContext } from \"./navigation-context.js\";\r\nimport { SlotsContext } from \"./slots-context.js\";\r\nimport { ModulesContext } from \"./modules-context.js\";\r\nimport type { NavigationManifest, ModuleEntry } from \"./types.js\";\r\n\r\ninterface AppProps {\r\n router: DataRouter;\r\n stores: Record<string, StoreApi<unknown>>;\r\n services: Record<string, unknown>;\r\n reactiveServices: Record<string, ReactiveService<unknown>>;\r\n navigation: NavigationManifest;\r\n slots: object;\r\n modules: readonly ModuleEntry[];\r\n providers?: React.ComponentType<{ children: React.ReactNode }>[];\r\n}\r\n\r\nexport function createAppComponent({\r\n router,\r\n stores,\r\n services,\r\n reactiveServices,\r\n navigation,\r\n slots,\r\n modules,\r\n providers,\r\n}: AppProps) {\r\n // All values captured in closure are stable references created once at resolve() time.\r\n // Wrap in a stable object so context consumers don't re-render on parent renders.\r\n const depsValue = { stores, services, reactiveServices };\r\n\r\n function App() {\r\n const tree = useMemo(() => {\r\n let node: React.ReactNode = (\r\n <SharedDependenciesContext value={depsValue}>\r\n <NavigationContext value={navigation}>\r\n <SlotsContext value={slots}>\r\n <ModulesContext value={modules}>\r\n <RouterProvider router={router} />\r\n </ModulesContext>\r\n </SlotsContext>\r\n </NavigationContext>\r\n </SharedDependenciesContext>\r\n );\r\n\r\n // Wrap with user-supplied providers (first element = outermost wrapper)\r\n if (providers) {\r\n for (const Provider of [...providers].reverse()) {\r\n node = <Provider>{node}</Provider>;\r\n }\r\n }\r\n\r\n return node;\r\n }, []);\r\n\r\n return tree;\r\n }\r\n\r\n App.displayName = \"ReactiveApp\";\r\n return App;\r\n}\r\n","import { createBrowserRouter, createMemoryRouter } from \"react-router\";\r\nimport type { RouteObject } from \"react-router\";\r\nimport type { StoreApi } from \"zustand\";\r\nimport type {\r\n ReactiveModuleDescriptor,\r\n LazyModuleDescriptor,\r\n ReactiveService,\r\n SlotMap,\r\n SlotMapOf,\r\n} from \"@react-router-modules/core\";\r\n\r\nimport type {\r\n RegistryConfig,\r\n ApplicationManifest,\r\n NavigationManifest,\r\n ModuleEntry,\r\n} from \"./types.js\";\r\nimport { validateNoDuplicateIds, validateDependencies } from \"./validation.js\";\r\nimport { buildNavigationManifest } from \"./navigation.js\";\r\nimport { buildSlotsManifest } from \"./slots.js\";\r\nimport { buildRouteTree, type RouteBuilderOptions } from \"./route-builder.js\";\r\nimport { createAppComponent } from \"./app.js\";\r\n\r\nexport interface ReactiveRegistry<\r\n TSharedDependencies extends Record<string, any>,\r\n TSlots extends SlotMapOf<TSlots> = SlotMap,\r\n> {\r\n /** Register an eager module */\r\n register(module: ReactiveModuleDescriptor<TSharedDependencies, TSlots>): void;\r\n\r\n /** Register a lazily-loaded module */\r\n registerLazy(descriptor: LazyModuleDescriptor<TSharedDependencies, TSlots>): void;\r\n\r\n /**\r\n * Resolve all modules and produce the application manifest.\r\n * Validates dependencies and builds the route tree.\r\n */\r\n resolve(options?: ResolveOptions): ApplicationManifest<TSlots>;\r\n}\r\n\r\nexport interface ResolveOptions {\r\n /** Root layout component (renders <Outlet /> for child routes) */\r\n rootComponent?: () => React.JSX.Element;\r\n\r\n /**\r\n * Pre-built root route — if provided, used instead of auto-creating one.\r\n * Use this when you need full control over the root route config\r\n * (loader, errorElement, etc.).\r\n * Mutually exclusive with rootComponent/notFoundComponent/loader.\r\n */\r\n rootRoute?: RouteObject;\r\n\r\n /** Component for the index route (/) */\r\n indexComponent?: () => React.JSX.Element;\r\n\r\n /** Component for 404 / not-found */\r\n notFoundComponent?: () => React.JSX.Element;\r\n\r\n /**\r\n * Called before every route loads — for observability, analytics, feature flags.\r\n * Runs for ALL routes including public ones like /login.\r\n * Throw a `redirect()` from react-router to redirect.\r\n * Ignored if rootRoute is provided (configure loader on your root route instead).\r\n *\r\n * For auth guards, use `authenticatedRoute` instead — it creates a layout route\r\n * boundary that only wraps protected routes.\r\n */\r\n loader?: (args: { request: Request; params: Record<string, string | undefined> }) => any;\r\n\r\n /**\r\n * Auth boundary — a pathless layout route that guards module routes and\r\n * the index route. Shell routes (login, error pages) sit outside this\r\n * boundary and are NOT guarded.\r\n *\r\n * Follows React Router's recommended layout route pattern:\r\n * ```\r\n * Root (loader runs for ALL routes — observability, etc.)\r\n * ├── shellRoutes (public — /login, /signup)\r\n * └── _authenticated (layout — auth guard)\r\n * ├── / (indexComponent)\r\n * └── module routes\r\n * ```\r\n *\r\n * @example\r\n * ```ts\r\n * registry.resolve({\r\n * authenticatedRoute: {\r\n * loader: async () => {\r\n * const res = await fetch('/api/auth/session')\r\n * if (!res.ok) throw redirect('/login')\r\n * return null\r\n * },\r\n * Component: ShellLayout,\r\n * },\r\n * shellRoutes: () => [\r\n * { path: '/login', Component: LoginPage },\r\n * ],\r\n * })\r\n * ```\r\n */\r\n authenticatedRoute?: {\r\n /** Auth guard — throw redirect() to deny access */\r\n loader: (args: { request: Request; params: Record<string, string | undefined> }) => any;\r\n /** Layout component for authenticated pages. Defaults to <Outlet />. */\r\n Component?: () => React.JSX.Element;\r\n };\r\n\r\n /**\r\n * Additional routes owned by the shell (login, error pages, onboarding, etc.)\r\n * that sit alongside module routes at the root level.\r\n *\r\n * When `authenticatedRoute` is used, shell routes are NOT guarded — they\r\n * are siblings of the auth layout, not children. This is the natural place\r\n * for public pages like /login.\r\n */\r\n shellRoutes?: () => RouteObject[];\r\n\r\n /**\r\n * Additional React providers to wrap around the app tree.\r\n *\r\n * **Nesting order:** First element is outermost. `[A, B, C]` produces:\r\n * ```tsx\r\n * <A>\r\n * <B>\r\n * <C>\r\n * ...app...\r\n * </C>\r\n * </B>\r\n * </A>\r\n * ```\r\n *\r\n * Place providers that other providers depend on **first** in the array.\r\n * For example, if your data-fetching provider reads from a theme context,\r\n * list the theme provider before the data-fetching provider.\r\n *\r\n * @example\r\n * ```ts\r\n * providers: [SWRConfigProvider, ThemeProvider, TooltipProvider]\r\n * // Produces: <SWRConfigProvider><ThemeProvider><TooltipProvider>...app...</TooltipProvider></ThemeProvider></SWRConfigProvider>\r\n * ```\r\n */\r\n providers?: React.ComponentType<{ children: React.ReactNode }>[];\r\n}\r\n\r\nexport function createRegistry<\r\n TSharedDependencies extends Record<string, any>,\r\n TSlots extends SlotMapOf<TSlots> = SlotMap,\r\n>(\r\n config: RegistryConfig<TSharedDependencies, TSlots>,\r\n): ReactiveRegistry<TSharedDependencies, TSlots> {\r\n const modules: ReactiveModuleDescriptor<TSharedDependencies, TSlots>[] = [];\r\n const lazyModules: LazyModuleDescriptor<TSharedDependencies, TSlots>[] = [];\r\n let resolved = false;\r\n\r\n // Collect all available dependency keys from all three buckets\r\n const availableKeys = new Set<string>([\r\n ...Object.keys(config.stores ?? {}),\r\n ...Object.keys(config.services ?? {}),\r\n ...Object.keys(config.reactiveServices ?? {}),\r\n ]);\r\n\r\n return {\r\n register(module) {\r\n if (resolved) {\r\n throw new Error(\r\n \"[@react-router-modules/runtime] Cannot register modules after resolve() has been called.\",\r\n );\r\n }\r\n modules.push(module);\r\n },\r\n\r\n registerLazy(descriptor) {\r\n if (resolved) {\r\n throw new Error(\r\n \"[@react-router-modules/runtime] Cannot register modules after resolve() has been called.\",\r\n );\r\n }\r\n lazyModules.push(descriptor);\r\n },\r\n\r\n resolve(options?: ResolveOptions) {\r\n if (resolved) {\r\n throw new Error(\"[@react-router-modules/runtime] resolve() can only be called once.\");\r\n }\r\n resolved = true;\r\n\r\n // Validate — cast is safe since validation only reads structural properties (id, requires)\r\n validateNoDuplicateIds(\r\n modules as ReactiveModuleDescriptor[],\r\n lazyModules as LazyModuleDescriptor[],\r\n );\r\n validateDependencies(modules as ReactiveModuleDescriptor[], availableKeys);\r\n\r\n // Run onRegister lifecycle hooks\r\n const deps = buildDepsObject<TSharedDependencies>(config);\r\n for (const mod of modules) {\r\n try {\r\n mod.lifecycle?.onRegister?.(deps);\r\n } catch (err) {\r\n throw new Error(\r\n `[@react-router-modules/runtime] Module \"${mod.id}\" lifecycle.onRegister() failed: ${err instanceof Error ? err.message : String(err)}`,\r\n { cause: err },\r\n );\r\n }\r\n }\r\n\r\n // Build route tree\r\n const routeBuilderOptions: RouteBuilderOptions = {\r\n rootRoute: options?.rootRoute,\r\n rootComponent: options?.rootComponent,\r\n indexComponent: options?.indexComponent,\r\n notFoundComponent: options?.notFoundComponent,\r\n loader: options?.loader,\r\n authenticatedRoute: options?.authenticatedRoute,\r\n shellRoutes: options?.shellRoutes,\r\n };\r\n const routes = buildRouteTree(\r\n modules as ReactiveModuleDescriptor[],\r\n lazyModules as LazyModuleDescriptor[],\r\n routeBuilderOptions,\r\n );\r\n\r\n // Create React Router instance (use memory router when DOM is unavailable, e.g. tests)\r\n const router =\r\n typeof document !== \"undefined\" ? createBrowserRouter(routes) : createMemoryRouter(routes);\r\n\r\n // Build navigation, slots, and module entries\r\n const navigation: NavigationManifest = buildNavigationManifest(\r\n modules as ReactiveModuleDescriptor[],\r\n );\r\n const slots = buildSlotsManifest<TSlots>(modules, config.slots);\r\n const moduleEntries: ModuleEntry[] = modules.map((mod) => ({\r\n id: mod.id,\r\n version: mod.version,\r\n meta: mod.meta,\r\n component: mod.component,\r\n zones: mod.zones,\r\n }));\r\n\r\n // Build stores, services, and reactive services maps for the context\r\n const stores: Record<string, StoreApi<unknown>> = {};\r\n const services: Record<string, unknown> = {};\r\n const reactiveServices: Record<string, ReactiveService<unknown>> = {};\r\n\r\n if (config.stores) {\r\n for (const [key, store] of Object.entries(config.stores)) {\r\n if (store) stores[key] = store as StoreApi<unknown>;\r\n }\r\n }\r\n if (config.services) {\r\n for (const [key, service] of Object.entries(config.services)) {\r\n if (service !== undefined) services[key] = service;\r\n }\r\n }\r\n if (config.reactiveServices) {\r\n for (const [key, rs] of Object.entries(config.reactiveServices)) {\r\n if (rs) reactiveServices[key] = rs as ReactiveService<unknown>;\r\n }\r\n }\r\n\r\n // Create App component\r\n const App = createAppComponent({\r\n router,\r\n stores,\r\n services,\r\n reactiveServices,\r\n navigation,\r\n slots,\r\n modules: moduleEntries,\r\n providers: options?.providers,\r\n });\r\n\r\n return { App, router, navigation, slots, modules: moduleEntries };\r\n },\r\n };\r\n}\r\n\r\nfunction buildDepsObject<TSharedDependencies extends Record<string, any>>(\r\n config: RegistryConfig<TSharedDependencies, any>,\r\n): TSharedDependencies {\r\n const deps: Record<string, unknown> = {};\r\n\r\n // For stores, get current state as the deps value\r\n // (lifecycle hooks get a snapshot, components use useStore for reactivity)\r\n if (config.stores) {\r\n for (const [key, store] of Object.entries(config.stores)) {\r\n if (store) {\r\n deps[key] = (store as StoreApi<unknown>).getState();\r\n }\r\n }\r\n }\r\n if (config.services) {\r\n for (const [key, service] of Object.entries(config.services)) {\r\n if (service !== undefined) deps[key] = service;\r\n }\r\n }\r\n // For reactive services, get current snapshot\r\n if (config.reactiveServices) {\r\n for (const [key, rs] of Object.entries(config.reactiveServices)) {\r\n if (rs) {\r\n deps[key] = (rs as ReactiveService<unknown>).getSnapshot();\r\n }\r\n }\r\n }\r\n\r\n return deps as TSharedDependencies;\r\n}\r\n","import { useMatches } from \"react-router\";\r\nimport type { ZoneMapOf } from \"@react-router-modules/core\";\r\n\r\n/**\r\n * Read zone components contributed by the currently matched route hierarchy.\r\n *\r\n * Zones are set via React Router's `handle` on individual routes.\r\n * This hook walks all matched routes from root to leaf and returns a merged\r\n * map where the deepest match wins for each zone key.\r\n *\r\n * @example\r\n * // In the shell layout:\r\n * const zones = useZones<AppZones>()\r\n * const DetailPanel = zones.detailPanel\r\n *\r\n * return (\r\n * <div className=\"grid\">\r\n * <main><Outlet /></main>\r\n * <aside>{DetailPanel && <DetailPanel />}</aside>\r\n * </div>\r\n * )\r\n *\r\n * @example\r\n * // In a module's route definition:\r\n * {\r\n * path: ':userId',\r\n * Component: UserDetailPage,\r\n * handle: {\r\n * detailPanel: UserDetailSidebar,\r\n * },\r\n * }\r\n */\r\nexport function useZones<TZones extends ZoneMapOf<TZones>>(): Partial<TZones> {\r\n const matches = useMatches();\r\n const merged: Record<string, unknown> = {};\r\n for (const match of matches) {\r\n const data = (match as any).handle;\r\n if (data && typeof data === \"object\") {\r\n for (const [key, value] of Object.entries(data as Record<string, unknown>)) {\r\n if (value !== undefined) {\r\n merged[key] = value;\r\n }\r\n }\r\n }\r\n }\r\n return merged as Partial<TZones>;\r\n}\r\n","import type { ZoneMapOf } from \"@react-router-modules/core\";\r\nimport { useZones } from \"./zones.js\";\r\nimport { useModules } from \"./modules-context.js\";\r\n\r\n/**\r\n * Read zone components from both the matched route hierarchy AND the\r\n * currently active module (identified by `activeModuleId`).\r\n *\r\n * This unifies two zone contribution patterns:\r\n * - **Route-based modules** contribute zones via React Router's `handle`\r\n * - **Tab-based modules** contribute zones via the `zones` field on their descriptor\r\n *\r\n * When both sources provide a value for the same zone key, the module's\r\n * contribution wins — the assumption is that the active tab's content is more\r\n * relevant than the underlying route's.\r\n *\r\n * @param activeModuleId - The id of the module that is currently active in the\r\n * workspace (e.g. the journey tab's moduleId). Pass `null` or `undefined`\r\n * when no module tab is active — only route zones are returned.\r\n *\r\n * @example\r\n * // In the shell layout:\r\n * const activeTab = getActiveTab(interactionId)\r\n * const moduleId = activeTab?.type === 'native-journey' ? activeTab.journeyId : null\r\n * const zones = useActiveZones<AppZones>(moduleId)\r\n * const Panel = zones.contextualPanel\r\n *\r\n * return (\r\n * <aside>{Panel ? <Panel /> : <DefaultPanel />}</aside>\r\n * )\r\n */\r\nexport function useActiveZones<TZones extends ZoneMapOf<TZones>>(\r\n activeModuleId?: string | null,\r\n): Partial<TZones> {\r\n const routeZones = useZones<TZones>();\r\n const modules = useModules();\r\n\r\n if (!activeModuleId) {\r\n return routeZones;\r\n }\r\n\r\n const activeMod = modules.find((m) => m.id === activeModuleId);\r\n if (!activeMod?.zones) {\r\n return routeZones;\r\n }\r\n\r\n // Module zones override route zones for the same key\r\n return { ...routeZones, ...activeMod.zones } as Partial<TZones>;\r\n}\r\n","import { Component } from \"react\";\r\nimport type { ErrorInfo, ReactNode } from \"react\";\r\n\r\ninterface Props {\r\n moduleId: string;\r\n fallback?: ReactNode;\r\n children: ReactNode;\r\n}\r\n\r\ninterface State {\r\n hasError: boolean;\r\n error: Error | null;\r\n}\r\n\r\nexport class ModuleErrorBoundary extends Component<Props, State> {\r\n override state: State = { hasError: false, error: null };\r\n\r\n static getDerivedStateFromError(error: Error): State {\r\n return { hasError: true, error };\r\n }\r\n\r\n override componentDidCatch(error: Error, info: ErrorInfo) {\r\n console.error(\r\n `[@react-router-modules/runtime] Module \"${this.props.moduleId}\" encountered an error:`,\r\n error,\r\n info,\r\n );\r\n }\r\n\r\n override render() {\r\n if (this.state.hasError) {\r\n if (this.props.fallback) {\r\n return this.props.fallback;\r\n }\r\n return (\r\n <div\r\n style={{\r\n padding: \"1rem\",\r\n border: \"1px solid #e53e3e\",\r\n borderRadius: \"0.5rem\",\r\n margin: \"1rem\",\r\n }}\r\n >\r\n <h3 style={{ color: \"#e53e3e\", margin: \"0 0 0.5rem 0\" }}>\r\n Module &quot;{this.props.moduleId}&quot; encountered an error\r\n </h3>\r\n <pre style={{ fontSize: \"0.875rem\", color: \"#718096\", whiteSpace: \"pre-wrap\" }}>\r\n {this.state.error?.message}\r\n </pre>\r\n </div>\r\n );\r\n }\r\n return this.props.children;\r\n }\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/route-builder.tsx","../src/app.tsx","../src/registry.ts","../src/zones.ts","../src/active-zones.ts"],"sourcesContent":["import { Outlet, useRoutes } from \"react-router\";\nimport type { RouteObject } from \"react-router\";\nimport type { ModuleDescriptor, LazyModuleDescriptor } from \"@react-router-modules/core\";\n\nexport interface RouteBuilderOptions {\n /**\n * Pre-built root route. If provided, rootComponent/notFoundComponent/loader\n * are ignored — configure them directly on this route instead.\n */\n rootRoute?: RouteObject;\n /** Component for the root layout (renders <Outlet /> for child routes) */\n rootComponent?: () => React.JSX.Element;\n /** Component for the index route (/) */\n indexComponent?: () => React.JSX.Element;\n /** Component for the 404 / not-found route */\n notFoundComponent?: () => React.JSX.Element;\n /**\n * Called before every route loads — for observability, feature flags, etc.\n * Runs for ALL routes including public ones.\n * Ignored if rootRoute is provided.\n */\n loader?: (args: { request: Request; params: Record<string, string | undefined> }) => any;\n /**\n * Auth boundary — a pathless layout route that wraps module routes and\n * the index route. Shell routes (login, error pages) sit outside this\n * boundary and are NOT guarded.\n *\n * Follows React Router's recommended layout route pattern.\n *\n * When provided, the route tree becomes:\n * ```\n * Root (loader runs for ALL routes)\n * ├── shellRoutes (public — /login, /signup, etc.)\n * └── _authenticated (layout — loader guards children)\n * ├── / (indexComponent)\n * └── module routes\n * ```\n *\n * When omitted, all routes are direct children of root (no auth boundary).\n */\n authenticatedRoute?: {\n /** Auth guard — throw redirect() to deny access */\n loader: (args: { request: Request; params: Record<string, string | undefined> }) => any;\n /** Layout component for authenticated pages. Defaults to <Outlet />. */\n Component?: () => React.JSX.Element;\n };\n /** Additional routes owned by the shell (login, error pages, etc.) */\n shellRoutes?: () => RouteObject[];\n}\n\n/**\n * Composes all module route subtrees into a React Router route tree.\n * Modules without createRoutes are skipped (headless modules).\n */\nexport function buildRouteTree(\n modules: ModuleDescriptor[],\n lazyModules: LazyModuleDescriptor[],\n options?: RouteBuilderOptions,\n): RouteObject[] {\n // If a custom root route is provided, use it as the base\n if (options?.rootRoute) {\n const rootChildren: RouteObject[] = [...(options.rootRoute.children ?? [])];\n\n // Shell-owned routes (login, error pages) — always direct children of root\n if (options?.shellRoutes) {\n rootChildren.push(...options.shellRoutes());\n }\n\n const protectedChildren: RouteObject[] = [];\n\n // Add index route if provided\n if (options?.indexComponent) {\n protectedChildren.push({\n index: true,\n Component: options.indexComponent,\n });\n }\n\n // Eager modules\n for (const mod of modules) {\n if (!mod.createRoutes) continue;\n const routes = mod.createRoutes();\n if (!routes) {\n throw new Error(\n `[@react-router-modules/runtime] Module \"${mod.id}\" createRoutes() returned a falsy value.`,\n );\n }\n protectedChildren.push(...(Array.isArray(routes) ? routes : [routes]));\n }\n\n // Lazy modules\n for (const lazyMod of lazyModules) {\n protectedChildren.push(createLazyModuleRoute(lazyMod));\n }\n\n if (options?.authenticatedRoute) {\n rootChildren.push(\n createAuthenticatedLayoutRoute(options.authenticatedRoute, protectedChildren),\n );\n } else {\n rootChildren.push(...protectedChildren);\n }\n\n options.rootRoute.children = rootChildren;\n return [options.rootRoute];\n }\n\n // Build root route from options\n const rootChildren: RouteObject[] = [];\n\n // Shell-owned routes (login, error pages) — always direct children of root\n if (options?.shellRoutes) {\n rootChildren.push(...options.shellRoutes());\n }\n\n const protectedChildren: RouteObject[] = [];\n\n // Add index route if provided\n if (options?.indexComponent) {\n protectedChildren.push({\n index: true,\n Component: options.indexComponent,\n });\n }\n\n // Eager modules: call createRoutes\n for (const mod of modules) {\n if (!mod.createRoutes) continue;\n const routes = mod.createRoutes();\n if (!routes) {\n throw new Error(\n `[@react-router-modules/runtime] Module \"${mod.id}\" createRoutes() returned a falsy value.`,\n );\n }\n protectedChildren.push(...(Array.isArray(routes) ? routes : [routes]));\n }\n\n // Lazy modules\n for (const lazyMod of lazyModules) {\n protectedChildren.push(createLazyModuleRoute(lazyMod));\n }\n\n if (options?.authenticatedRoute) {\n rootChildren.push(\n createAuthenticatedLayoutRoute(options.authenticatedRoute, protectedChildren),\n );\n } else {\n rootChildren.push(...protectedChildren);\n }\n\n // Not-found catch-all\n if (options?.notFoundComponent) {\n rootChildren.push({\n path: \"*\",\n Component: options.notFoundComponent,\n });\n }\n\n const rootRoute: RouteObject = {\n path: \"/\",\n Component: options?.rootComponent,\n loader: options?.loader,\n children: rootChildren,\n };\n\n return [rootRoute];\n}\n\nfunction createAuthenticatedLayoutRoute(\n auth: NonNullable<RouteBuilderOptions[\"authenticatedRoute\"]>,\n children: RouteObject[],\n): RouteObject {\n return {\n id: \"_authenticated\",\n Component: auth.Component ?? (() => <Outlet />),\n loader: auth.loader,\n children,\n };\n}\n\n/**\n * Creates a catch-all route for a lazily-loaded module.\n * On first navigation, the module descriptor is loaded and its routes\n * are rendered as descendant routes via useRoutes().\n */\nfunction createLazyModuleRoute(lazyMod: LazyModuleDescriptor): RouteObject {\n // Capture the loaded routes so they're resolved only once\n let cachedRoutes: RouteObject[] | null = null;\n\n return {\n path: lazyMod.basePath.replace(/^\\//, \"\") + \"/*\",\n lazy: async () => {\n if (!cachedRoutes) {\n const { default: descriptor } = await lazyMod.load();\n if (descriptor.createRoutes) {\n const routes = descriptor.createRoutes();\n cachedRoutes = Array.isArray(routes) ? routes : [routes];\n } else {\n cachedRoutes = [];\n }\n }\n const routes = cachedRoutes;\n return {\n Component: function LazyModule() {\n return useRoutes(routes);\n },\n };\n },\n };\n}\n","import { useMemo } from \"react\";\nimport { RouterProvider } from \"react-router\";\nimport type { DataRouter } from \"react-router\";\nimport type { StoreApi } from \"zustand\";\nimport type { ReactiveService } from \"@react-router-modules/core\";\nimport { SharedDependenciesContext } from \"@react-router-modules/core\";\nimport type {\n DynamicSlotFactory,\n SlotFilter,\n NavigationManifest,\n ModuleEntry,\n} from \"@modular-react/core\";\nimport {\n NavigationContext,\n SlotsContext,\n RecalculateSlotsContext,\n ModulesContext,\n DynamicSlotsProvider,\n} from \"@modular-react/react\";\nimport type { SlotsSignal } from \"@modular-react/react\";\n\ninterface AppProps {\n router: DataRouter;\n stores: Record<string, StoreApi<unknown>>;\n services: Record<string, unknown>;\n reactiveServices: Record<string, ReactiveService<unknown>>;\n navigation: NavigationManifest;\n slots: object;\n modules: readonly ModuleEntry[];\n providers?: React.ComponentType<{ children: React.ReactNode }>[];\n dynamicSlotFactories: DynamicSlotFactory[];\n slotFilter?: SlotFilter;\n slotsSignal: SlotsSignal;\n recalculateSlots: () => void;\n}\n\nexport function createAppComponent({\n router,\n stores,\n services,\n reactiveServices,\n navigation,\n slots,\n modules,\n providers,\n dynamicSlotFactories,\n slotFilter,\n slotsSignal,\n recalculateSlots,\n}: AppProps) {\n // All values captured in closure are stable references created once at resolve() time.\n // Wrap in a stable object so context consumers don't re-render on parent renders.\n const depsValue = { stores, services, reactiveServices };\n const hasDynamicSlots = dynamicSlotFactories.length > 0 || slotFilter != null;\n\n function App() {\n const tree = useMemo(() => {\n // When dynamic slots exist, use a provider that re-evaluates\n // on recalculateSlots(). Otherwise, use static context for zero overhead.\n const slotsProvider = hasDynamicSlots ? (\n <DynamicSlotsProvider\n baseSlots={slots}\n factories={dynamicSlotFactories}\n filter={slotFilter}\n stores={stores}\n services={services}\n reactiveServices={reactiveServices}\n signal={slotsSignal}\n >\n <ModulesContext value={modules}>\n <RouterProvider router={router} />\n </ModulesContext>\n </DynamicSlotsProvider>\n ) : (\n <SlotsContext value={slots}>\n <ModulesContext value={modules}>\n <RouterProvider router={router} />\n </ModulesContext>\n </SlotsContext>\n );\n\n let node: React.ReactNode = (\n <SharedDependenciesContext value={depsValue}>\n <NavigationContext value={navigation}>\n <RecalculateSlotsContext value={recalculateSlots}>\n {slotsProvider}\n </RecalculateSlotsContext>\n </NavigationContext>\n </SharedDependenciesContext>\n );\n\n // Wrap with user-supplied providers (first element = outermost wrapper)\n if (providers) {\n for (const Provider of [...providers].reverse()) {\n node = <Provider>{node}</Provider>;\n }\n }\n\n return node;\n }, []);\n\n return tree;\n }\n\n App.displayName = \"ModularApp\";\n return App;\n}\n","import { createBrowserRouter, createMemoryRouter } from \"react-router\";\nimport type { RouteObject } from \"react-router\";\nimport type { StoreApi } from \"zustand\";\nimport type {\n ModuleDescriptor,\n LazyModuleDescriptor,\n ReactiveService,\n SlotMap,\n SlotMapOf,\n} from \"@react-router-modules/core\";\nimport {\n buildNavigationManifest,\n buildSlotsManifest,\n collectDynamicSlotFactories,\n validateNoDuplicateIds,\n validateDependencies,\n} from \"@modular-react/core\";\nimport type { SlotFilter, NavigationManifest, ModuleEntry } from \"@modular-react/core\";\nimport { createSlotsSignal } from \"@modular-react/react\";\n\nimport type { RegistryConfig, ApplicationManifest } from \"./types.js\";\nimport { buildRouteTree, type RouteBuilderOptions } from \"./route-builder.js\";\nimport { createAppComponent } from \"./app.js\";\n\nexport interface ModuleRegistry<\n TSharedDependencies extends Record<string, any>,\n TSlots extends SlotMapOf<TSlots> = SlotMap,\n> {\n /** Register an eager module */\n register(module: ModuleDescriptor<TSharedDependencies, TSlots>): void;\n\n /** Register a lazily-loaded module */\n registerLazy(descriptor: LazyModuleDescriptor<TSharedDependencies, TSlots>): void;\n\n /**\n * Resolve all modules and produce the application manifest.\n * Validates dependencies and builds the route tree.\n */\n resolve(options?: ResolveOptions<TSharedDependencies, TSlots>): ApplicationManifest<TSlots>;\n}\n\nexport interface ResolveOptions<\n TSharedDependencies extends Record<string, any> = Record<string, any>,\n TSlots extends SlotMapOf<TSlots> = SlotMap,\n> {\n /** Root layout component (renders <Outlet /> for child routes) */\n rootComponent?: () => React.JSX.Element;\n\n /**\n * Pre-built root route — if provided, used instead of auto-creating one.\n * Mutually exclusive with rootComponent/notFoundComponent/loader.\n */\n rootRoute?: RouteObject;\n\n /** Component for the index route (/) */\n indexComponent?: () => React.JSX.Element;\n\n /** Component for 404 / not-found */\n notFoundComponent?: () => React.JSX.Element;\n\n /**\n * Called before every route loads — for observability, analytics, feature flags.\n * Runs for ALL routes including public ones like /login.\n * Ignored if rootRoute is provided.\n */\n loader?: (args: { request: Request; params: Record<string, string | undefined> }) => any;\n\n /**\n * Auth boundary — a pathless layout route that guards module routes and\n * the index route. Shell routes sit outside this boundary.\n */\n authenticatedRoute?: {\n /** Auth guard — throw redirect() to deny access */\n loader: (args: { request: Request; params: Record<string, string | undefined> }) => any;\n /** Layout component for authenticated pages. Defaults to <Outlet />. */\n Component?: () => React.JSX.Element;\n };\n\n /** Additional routes owned by the shell (login, error pages, etc.) */\n shellRoutes?: () => RouteObject[];\n\n /**\n * Additional React providers to wrap around the app tree.\n * First element is outermost.\n */\n providers?: React.ComponentType<{ children: React.ReactNode }>[];\n\n /**\n * Global filter applied to the fully resolved slot manifest (static + dynamic)\n * on every `recalculateSlots()` call.\n */\n slotFilter?: (slots: TSlots, deps: TSharedDependencies) => TSlots;\n}\n\nexport function createRegistry<\n TSharedDependencies extends Record<string, any>,\n TSlots extends SlotMapOf<TSlots> = SlotMap,\n>(\n config: RegistryConfig<TSharedDependencies, TSlots>,\n): ModuleRegistry<TSharedDependencies, TSlots> {\n const modules: ModuleDescriptor<TSharedDependencies, TSlots>[] = [];\n const lazyModules: LazyModuleDescriptor<TSharedDependencies, TSlots>[] = [];\n let resolved = false;\n\n // Collect all available dependency keys from all three buckets\n const availableKeys = new Set<string>([\n ...Object.keys(config.stores ?? {}),\n ...Object.keys(config.services ?? {}),\n ...Object.keys(config.reactiveServices ?? {}),\n ]);\n\n return {\n register(module) {\n if (resolved) {\n throw new Error(\n \"[@react-router-modules/runtime] Cannot register modules after resolve() has been called.\",\n );\n }\n modules.push(module);\n },\n\n registerLazy(descriptor) {\n if (resolved) {\n throw new Error(\n \"[@react-router-modules/runtime] Cannot register modules after resolve() has been called.\",\n );\n }\n lazyModules.push(descriptor);\n },\n\n resolve(options?: ResolveOptions<TSharedDependencies, TSlots>) {\n if (resolved) {\n throw new Error(\"[@react-router-modules/runtime] resolve() can only be called once.\");\n }\n resolved = true;\n\n // Validate — cast is safe since validation only reads structural properties (id, requires)\n validateNoDuplicateIds(modules as ModuleDescriptor[], lazyModules as LazyModuleDescriptor[]);\n validateDependencies(modules as ModuleDescriptor[], availableKeys);\n\n // Run onRegister lifecycle hooks\n const deps = buildDepsObject<TSharedDependencies>(config);\n for (const mod of modules) {\n try {\n mod.lifecycle?.onRegister?.(deps);\n } catch (err) {\n throw new Error(\n `[@react-router-modules/runtime] Module \"${mod.id}\" lifecycle.onRegister() failed: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n );\n }\n }\n\n // Build route tree\n const routeBuilderOptions: RouteBuilderOptions = {\n rootRoute: options?.rootRoute,\n rootComponent: options?.rootComponent,\n indexComponent: options?.indexComponent,\n notFoundComponent: options?.notFoundComponent,\n loader: options?.loader,\n authenticatedRoute: options?.authenticatedRoute,\n shellRoutes: options?.shellRoutes,\n };\n const routes = buildRouteTree(\n modules as ModuleDescriptor[],\n lazyModules as LazyModuleDescriptor[],\n routeBuilderOptions,\n );\n\n // Create React Router instance (use memory router when DOM is unavailable, e.g. tests)\n const router =\n typeof document !== \"undefined\" ? createBrowserRouter(routes) : createMemoryRouter(routes);\n\n // Build navigation, slots, and module entries\n const navigation: NavigationManifest = buildNavigationManifest(modules as ModuleDescriptor[]);\n const slots = buildSlotsManifest<TSlots>(modules, config.slots);\n const dynamicSlotFactories = collectDynamicSlotFactories(modules as ModuleDescriptor[]);\n const slotFilter = options?.slotFilter as SlotFilter | undefined;\n const moduleEntries: ModuleEntry[] = modules.map((mod) => ({\n id: mod.id,\n version: mod.version,\n meta: mod.meta,\n component: mod.component,\n zones: mod.zones,\n }));\n\n // Build stores, services, and reactive services maps for the context\n const stores: Record<string, StoreApi<unknown>> = {};\n const services: Record<string, unknown> = {};\n const reactiveServices: Record<string, ReactiveService<unknown>> = {};\n\n if (config.stores) {\n for (const [key, store] of Object.entries(config.stores)) {\n if (store) stores[key] = store as StoreApi<unknown>;\n }\n }\n if (config.services) {\n for (const [key, service] of Object.entries(config.services)) {\n if (service !== undefined) services[key] = service;\n }\n }\n if (config.reactiveServices) {\n for (const [key, rs] of Object.entries(config.reactiveServices)) {\n if (rs) reactiveServices[key] = rs as ReactiveService<unknown>;\n }\n }\n\n // Create signal for imperative recalculation of dynamic slots\n const slotsSignal = createSlotsSignal();\n const hasDynamicSlots = dynamicSlotFactories.length > 0 || slotFilter != null;\n const recalculateSlots = hasDynamicSlots ? () => slotsSignal.notify() : () => {};\n\n // Create App component\n const App = createAppComponent({\n router,\n stores,\n services,\n reactiveServices,\n navigation,\n slots,\n modules: moduleEntries,\n providers: options?.providers,\n dynamicSlotFactories,\n slotFilter,\n slotsSignal,\n recalculateSlots,\n });\n\n return { App, router, navigation, slots, modules: moduleEntries, recalculateSlots };\n },\n };\n}\n\nfunction buildDepsObject<TSharedDependencies extends Record<string, any>>(\n config: RegistryConfig<TSharedDependencies, any>,\n): TSharedDependencies {\n const deps: Record<string, unknown> = {};\n\n if (config.stores) {\n for (const [key, store] of Object.entries(config.stores)) {\n if (store) {\n deps[key] = (store as StoreApi<unknown>).getState();\n }\n }\n }\n if (config.services) {\n for (const [key, service] of Object.entries(config.services)) {\n if (service !== undefined) deps[key] = service;\n }\n }\n if (config.reactiveServices) {\n for (const [key, rs] of Object.entries(config.reactiveServices)) {\n if (rs) {\n deps[key] = (rs as ReactiveService<unknown>).getSnapshot();\n }\n }\n }\n\n return deps as TSharedDependencies;\n}\n","import { useMatches } from \"react-router\";\nimport type { ZoneMapOf } from \"@react-router-modules/core\";\n\n/**\n * Read zone components contributed by the currently matched route hierarchy.\n *\n * Zones are set via React Router's `handle` on individual routes.\n * This hook walks all matched routes from root to leaf and returns a merged\n * map where the deepest match wins for each zone key.\n *\n * @example\n * // In the shell layout:\n * const zones = useZones<AppZones>()\n * const DetailPanel = zones.detailPanel\n *\n * return (\n * <div className=\"grid\">\n * <main><Outlet /></main>\n * <aside>{DetailPanel && <DetailPanel />}</aside>\n * </div>\n * )\n *\n * @example\n * // In a module's route definition:\n * {\n * path: ':userId',\n * Component: UserDetailPage,\n * handle: {\n * detailPanel: UserDetailSidebar,\n * },\n * }\n */\nexport function useZones<TZones extends ZoneMapOf<TZones>>(): Partial<TZones> {\n const matches = useMatches();\n const merged: Record<string, unknown> = {};\n for (const match of matches) {\n const data = (match as any).handle;\n if (data && typeof data === \"object\") {\n for (const [key, value] of Object.entries(data as Record<string, unknown>)) {\n if (value !== undefined) {\n merged[key] = value;\n }\n }\n }\n }\n return merged as Partial<TZones>;\n}\n","import type { ZoneMapOf } from \"@react-router-modules/core\";\nimport { useModules } from \"@modular-react/react\";\nimport { useZones } from \"./zones.js\";\n\n/**\n * Read zone components from both the matched route hierarchy AND the\n * currently active module (identified by `activeModuleId`).\n *\n * This unifies two zone contribution patterns:\n * - **Route-based modules** contribute zones via React Router's `handle`\n * - **Tab-based modules** contribute zones via the `zones` field on their descriptor\n *\n * When both sources provide a value for the same zone key, the module's\n * contribution wins.\n */\nexport function useActiveZones<TZones extends ZoneMapOf<TZones>>(\n activeModuleId?: string | null,\n): Partial<TZones> {\n const routeZones = useZones<TZones>();\n const modules = useModules();\n\n if (!activeModuleId) {\n return routeZones;\n }\n\n const activeMod = modules.find((m) => m.id === activeModuleId);\n if (!activeMod?.zones) {\n return routeZones;\n }\n\n // Module zones override route zones for the same key\n return { ...routeZones, ...activeMod.zones } as Partial<TZones>;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@react-router-modules/runtime",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "repository": {
5
5
  "type": "git",
6
- "url": "git+https://github.com/kibertoad/tanstack-react-modules.git",
7
- "directory": "packages/runtime"
6
+ "url": "git+https://github.com/kibertoad/modular-react.git",
7
+ "directory": "packages/react-router-runtime"
8
8
  },
9
9
  "files": [
10
10
  "dist"
@@ -24,10 +24,14 @@
24
24
  "scripts": {
25
25
  "build": "vite build",
26
26
  "dev": "vite build --watch",
27
- "prepublishOnly": "pnpm build"
27
+ "test": "vitest run",
28
+ "prepublishOnly": "pnpm build",
29
+ "typecheck": "tsc --noEmit"
28
30
  },
29
31
  "dependencies": {
30
- "@react-router-modules/core": "^1.0.1"
32
+ "@modular-react/core": "workspace:*",
33
+ "@modular-react/react": "workspace:*",
34
+ "@react-router-modules/core": "workspace:*"
31
35
  },
32
36
  "devDependencies": {
33
37
  "@types/react": "^19.0.0",
@@ -38,6 +42,7 @@
38
42
  "typescript": "^6.0.2",
39
43
  "vite": "^8.0.3",
40
44
  "vite-plugin-dts": "^4.5.0",
45
+ "vitest": "^4.1.0",
41
46
  "zustand": "^5.0.0"
42
47
  },
43
48
  "peerDependencies": {