@genesislcap/foundation-react-utils 14.496.2-alpha-2c4a633.0 → 14.498.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.
@@ -263,6 +263,251 @@
263
263
  }
264
264
  }
265
265
  ]
266
+ },
267
+ {
268
+ "kind": "javascript-module",
269
+ "path": "src/router/index.ts",
270
+ "declarations": [],
271
+ "exports": [
272
+ {
273
+ "kind": "js",
274
+ "name": "ProtectedRoute",
275
+ "declaration": {
276
+ "name": "ProtectedRoute",
277
+ "module": "./protected-route"
278
+ }
279
+ },
280
+ {
281
+ "kind": "js",
282
+ "name": "createProtectedRoute",
283
+ "declaration": {
284
+ "name": "createProtectedRoute",
285
+ "module": "./protected-route"
286
+ }
287
+ },
288
+ {
289
+ "kind": "js",
290
+ "name": "ProtectedRouteProps",
291
+ "declaration": {
292
+ "name": "ProtectedRouteProps",
293
+ "module": "./protected-route"
294
+ }
295
+ },
296
+ {
297
+ "kind": "js",
298
+ "name": "BoundProtectedRouteProps",
299
+ "declaration": {
300
+ "name": "BoundProtectedRouteProps",
301
+ "module": "./protected-route"
302
+ }
303
+ },
304
+ {
305
+ "kind": "js",
306
+ "name": "CreateProtectedRouteOptions",
307
+ "declaration": {
308
+ "name": "CreateProtectedRouteOptions",
309
+ "module": "./protected-route"
310
+ }
311
+ },
312
+ {
313
+ "kind": "js",
314
+ "name": "buildPostLoginRedirect",
315
+ "declaration": {
316
+ "name": "buildPostLoginRedirect",
317
+ "module": "./post-login-redirect"
318
+ }
319
+ },
320
+ {
321
+ "kind": "js",
322
+ "name": "RedirectableLocationState",
323
+ "declaration": {
324
+ "name": "RedirectableLocationState",
325
+ "module": "./post-login-redirect"
326
+ }
327
+ },
328
+ {
329
+ "kind": "js",
330
+ "name": "readInitialParam",
331
+ "declaration": {
332
+ "name": "readInitialParam",
333
+ "module": "./single-component"
334
+ }
335
+ },
336
+ {
337
+ "kind": "js",
338
+ "name": "createComponentRegistry",
339
+ "declaration": {
340
+ "name": "createComponentRegistry",
341
+ "module": "./single-component"
342
+ }
343
+ },
344
+ {
345
+ "kind": "js",
346
+ "name": "SingleComponentOutlet",
347
+ "declaration": {
348
+ "name": "SingleComponentOutlet",
349
+ "module": "./single-component"
350
+ }
351
+ },
352
+ {
353
+ "kind": "js",
354
+ "name": "ComponentRegistry",
355
+ "declaration": {
356
+ "name": "ComponentRegistry",
357
+ "module": "./single-component"
358
+ }
359
+ },
360
+ {
361
+ "kind": "js",
362
+ "name": "SingleComponentOutletProps",
363
+ "declaration": {
364
+ "name": "SingleComponentOutletProps",
365
+ "module": "./single-component"
366
+ }
367
+ },
368
+ {
369
+ "kind": "js",
370
+ "name": "renderAppRoutes",
371
+ "declaration": {
372
+ "name": "renderAppRoutes",
373
+ "module": "./app-routes"
374
+ }
375
+ },
376
+ {
377
+ "kind": "js",
378
+ "name": "AppRouteConfig",
379
+ "declaration": {
380
+ "name": "AppRouteConfig",
381
+ "module": "./app-routes"
382
+ }
383
+ },
384
+ {
385
+ "kind": "js",
386
+ "name": "RouteRedirect",
387
+ "declaration": {
388
+ "name": "RouteRedirect",
389
+ "module": "./app-routes"
390
+ }
391
+ },
392
+ {
393
+ "kind": "js",
394
+ "name": "RenderAppRoutesOptions",
395
+ "declaration": {
396
+ "name": "RenderAppRoutesOptions",
397
+ "module": "./app-routes"
398
+ }
399
+ },
400
+ {
401
+ "kind": "js",
402
+ "name": "mergePbcRoutes",
403
+ "declaration": {
404
+ "name": "mergePbcRoutes",
405
+ "module": "./pbc-routes"
406
+ }
407
+ },
408
+ {
409
+ "kind": "js",
410
+ "name": "PbcRouteInput",
411
+ "declaration": {
412
+ "name": "PbcRouteInput",
413
+ "module": "./pbc-routes"
414
+ }
415
+ },
416
+ {
417
+ "kind": "js",
418
+ "name": "MergePbcRoutesOptions",
419
+ "declaration": {
420
+ "name": "MergePbcRoutesOptions",
421
+ "module": "./pbc-routes"
422
+ }
423
+ }
424
+ ]
425
+ },
426
+ {
427
+ "kind": "javascript-module",
428
+ "path": "src/router/pbc-routes.ts",
429
+ "declarations": [
430
+ {
431
+ "kind": "function",
432
+ "name": "mergePbcRoutes",
433
+ "return": {
434
+ "type": {
435
+ "text": "AppRouteConfig[]"
436
+ }
437
+ },
438
+ "parameters": [
439
+ {
440
+ "name": "staticRoutes",
441
+ "type": {
442
+ "text": "AppRouteConfig[]"
443
+ }
444
+ },
445
+ {
446
+ "name": "pbcRoutes",
447
+ "type": {
448
+ "text": "PbcRouteInput[]"
449
+ }
450
+ },
451
+ {
452
+ "name": "{ renderPbc }",
453
+ "type": {
454
+ "text": "MergePbcRoutesOptions"
455
+ }
456
+ }
457
+ ],
458
+ "description": "Merge a static route table with PBC routes (e.g. from `getApp().routes`) into\none AppRouteConfig array. PBC routes are guarded, in-layout, and carry\ntheir element/tag + navItems through `data` so downstream consumers (a PBC\ncontainer, nav-item derivation) can read them — matching the `data` shape the\nhand-rolled version produced."
459
+ }
460
+ ],
461
+ "exports": [
462
+ {
463
+ "kind": "js",
464
+ "name": "mergePbcRoutes",
465
+ "declaration": {
466
+ "name": "mergePbcRoutes",
467
+ "module": "src/router/pbc-routes.ts"
468
+ }
469
+ }
470
+ ]
471
+ },
472
+ {
473
+ "kind": "javascript-module",
474
+ "path": "src/router/post-login-redirect.ts",
475
+ "declarations": [
476
+ {
477
+ "kind": "function",
478
+ "name": "buildPostLoginRedirect",
479
+ "return": {
480
+ "type": {
481
+ "text": "string"
482
+ }
483
+ },
484
+ "parameters": [
485
+ {
486
+ "name": "location",
487
+ "type": {
488
+ "text": "{ state?: RedirectableLocationState | null }"
489
+ },
490
+ "description": "The current router location (needs only `state.from`)."
491
+ },
492
+ {
493
+ "name": "defaultPath",
494
+ "default": "'/'",
495
+ "description": "Where to land when nothing was stashed. Defaults to `/`."
496
+ }
497
+ ],
498
+ "description": "Build the post-login redirect target from a router location's `state.from`,\npreserving the query string and hash so deep-link params (e.g.\n`?component=<name>`) survive the login bounce. Falls back to `defaultPath`\nwhen there is no stashed origin."
499
+ }
500
+ ],
501
+ "exports": [
502
+ {
503
+ "kind": "js",
504
+ "name": "buildPostLoginRedirect",
505
+ "declaration": {
506
+ "name": "buildPostLoginRedirect",
507
+ "module": "src/router/post-login-redirect.ts"
508
+ }
509
+ }
510
+ ]
266
511
  }
267
512
  ]
268
513
  }
@@ -0,0 +1,69 @@
1
+ import type { ComponentType, ReactElement, ReactNode } from 'react';
2
+ import type { BoundProtectedRouteProps } from './protected-route';
3
+ /**
4
+ * Declarative description of one app route. The `element` is rendered when the
5
+ * path matches; guarding, layout nesting, and permission are derived from the
6
+ * flags below so the app doesn't hand-write the `<Route>`/`<ProtectedRoute>`
7
+ * boilerplate per route.
8
+ */
9
+ export interface AppRouteConfig {
10
+ path: string;
11
+ element: ReactNode;
12
+ /** Skip the auth guard (e.g. login, not-permitted). Default `false`. */
13
+ public?: boolean;
14
+ /** Render outside the shared layout. Default `false` (guarded routes sit in the layout). */
15
+ noLayout?: boolean;
16
+ /** Permission code handed to the guard's optional permission check. */
17
+ permissionCode?: string;
18
+ /**
19
+ * Nested child routes rendered inside this route's `element` (which must
20
+ * render an `<Outlet />`). Children use paths relative to this route and
21
+ * inherit its guard — they are rendered as plain routes, not re-guarded.
22
+ */
23
+ children?: AppRouteConfig[];
24
+ /** Arbitrary metadata preserved for other consumers (nav items, PBC info, ...). */
25
+ data?: Record<string, any>;
26
+ }
27
+ /** A simple `from → to` redirect. */
28
+ export interface RouteRedirect {
29
+ from: string;
30
+ to: string;
31
+ }
32
+ /**
33
+ * Options for {@link renderAppRoutes}.
34
+ */
35
+ export interface RenderAppRoutesOptions {
36
+ /** The full route table (static + any merged PBC routes). */
37
+ routes: AppRouteConfig[];
38
+ /**
39
+ * Guard component (typically from `createProtectedRoute`) wrapping every
40
+ * non-public route; receives each route's `permissionCode`.
41
+ */
42
+ ProtectedRoute: ComponentType<BoundProtectedRouteProps>;
43
+ /**
44
+ * Layout route element (rendering an `<Outlet />`) that wraps in-layout
45
+ * routes. Omit to render every route at the top level.
46
+ */
47
+ layout?: ReactElement;
48
+ /** `from → to` redirects rendered before the routes. */
49
+ redirects?: RouteRedirect[];
50
+ /** Element for unmatched paths (`*`). */
51
+ notFound?: ReactNode;
52
+ /**
53
+ * When `name` is truthy, short-circuit to single-component mode: render only
54
+ * the public routes (so login / session-restore still work) plus a catch-all
55
+ * rendering `element` behind the guard. See the single-component deep-link.
56
+ */
57
+ singleComponent?: {
58
+ name: string | null | undefined;
59
+ element: ReactNode;
60
+ };
61
+ }
62
+ /**
63
+ * Build the app's `<Routes>` tree from a declarative route table, applying the
64
+ * auth guard, layout nesting, redirects, not-found, and the single-component
65
+ * short-circuit — so a consuming app configures routes instead of hand-writing
66
+ * repetitive `<Route element={<ProtectedRoute>…}>` markup.
67
+ */
68
+ export declare function renderAppRoutes({ routes, ProtectedRoute, layout, redirects, notFound, singleComponent, }: RenderAppRoutesOptions): ReactElement;
69
+ //# sourceMappingURL=app-routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-routes.d.ts","sourceRoot":"","sources":["../../../src/router/app-routes.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEpE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAElE;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,SAAS,CAAC;IACnB,wEAAwE;IACxE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uEAAuE;IACvE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAC;IAC5B,mFAAmF;IACnF,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC5B;AAED,qCAAqC;AACrC,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,6DAA6D;IAC7D,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB;;;OAGG;IACH,cAAc,EAAE,aAAa,CAAC,wBAAwB,CAAC,CAAC;IACxD;;;OAGG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,wDAAwD;IACxD,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC;IAC5B,yCAAyC;IACzC,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB;;;;OAIG;IACH,eAAe,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;QAAC,OAAO,EAAE,SAAS,CAAA;KAAE,CAAC;CAC3E;AAKD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,cAAc,EACd,MAAM,EACN,SAAc,EACd,QAAQ,EACR,eAAe,GAChB,EAAE,sBAAsB,GAAG,YAAY,CA6DvC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * `@genesislcap/foundation-react-utils/router`
3
+ *
4
+ * Reusable `react-router-dom` primitives for Genesis Foundation React apps, so
5
+ * the app-shell routing logic doesn't have to be reimplemented per app.
6
+ *
7
+ * Exposed as a subpath (`/router`) rather than from the package root so that
8
+ * consumers which don't route (and don't depend on `react-router-dom`) are
9
+ * unaffected.
10
+ *
11
+ * Key exports:
12
+ * - `ProtectedRoute` / `createProtectedRoute` — auth + permission gate that
13
+ * stashes the full origin location (incl. `search` + `hash`) for post-login
14
+ * restore, and redirects to a not-permitted path when authorized-but-blocked.
15
+ * - `buildPostLoginRedirect` — rebuild the return URL preserving query + hash.
16
+ * - `readInitialParam` / `createComponentRegistry` / `SingleComponentOutlet` —
17
+ * render a single registered component full-screen from a `?param=<name>`
18
+ * deep-link captured before the shell strips the query string.
19
+ * - `renderAppRoutes` — build the app's `<Routes>` from a declarative route
20
+ * table (guarding, layout nesting, redirects, not-found, single-component).
21
+ * - `mergePbcRoutes` — merge static routes with shell/PBC routes into one table.
22
+ */
23
+ export { ProtectedRoute, createProtectedRoute } from './protected-route';
24
+ export type { ProtectedRouteProps, BoundProtectedRouteProps, CreateProtectedRouteOptions, } from './protected-route';
25
+ export { buildPostLoginRedirect } from './post-login-redirect';
26
+ export type { RedirectableLocationState } from './post-login-redirect';
27
+ export { readInitialParam, createComponentRegistry, SingleComponentOutlet, } from './single-component';
28
+ export type { ComponentRegistry, SingleComponentOutletProps } from './single-component';
29
+ export { renderAppRoutes } from './app-routes';
30
+ export type { AppRouteConfig, RouteRedirect, RenderAppRoutesOptions } from './app-routes';
31
+ export { mergePbcRoutes } from './pbc-routes';
32
+ export type { PbcRouteInput, MergePbcRoutesOptions } from './pbc-routes';
33
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/router/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzE,YAAY,EACV,mBAAmB,EACnB,wBAAwB,EACxB,2BAA2B,GAC5B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,YAAY,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AACxF,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,36 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { AppRouteConfig } from './app-routes';
3
+ /**
4
+ * Minimal shape of a PBC route as provided by the shell (e.g. `getApp().routes`).
5
+ * Kept as an input type — rather than importing `foundation-shell` — so this
6
+ * helper stays dependency-free and the `/router` subpath doesn't pull the shell
7
+ * into apps that don't use PBCs.
8
+ */
9
+ export interface PbcRouteInput {
10
+ path: string;
11
+ title?: string;
12
+ /** The PBC element (module/loader) the container will mount. */
13
+ element?: unknown;
14
+ /** Explicit custom-element tag, if the container shouldn't derive one. */
15
+ elementTag?: string;
16
+ /** Nav items contributed by this PBC. */
17
+ navItems?: unknown;
18
+ /** Extra per-route settings (e.g. `permissionCode`), spread into `data`. */
19
+ settings?: Record<string, unknown>;
20
+ }
21
+ /**
22
+ * Options for {@link mergePbcRoutes}.
23
+ */
24
+ export interface MergePbcRoutesOptions {
25
+ /** Render the element for a PBC route (typically `() => <PBCContainer />`). */
26
+ renderPbc: (pbc: PbcRouteInput) => ReactNode;
27
+ }
28
+ /**
29
+ * Merge a static route table with PBC routes (e.g. from `getApp().routes`) into
30
+ * one {@link AppRouteConfig} array. PBC routes are guarded, in-layout, and carry
31
+ * their element/tag + navItems through `data` so downstream consumers (a PBC
32
+ * container, nav-item derivation) can read them — matching the `data` shape the
33
+ * hand-rolled version produced.
34
+ */
35
+ export declare function mergePbcRoutes(staticRoutes: AppRouteConfig[], pbcRoutes: PbcRouteInput[], { renderPbc }: MergePbcRoutesOptions): AppRouteConfig[];
36
+ //# sourceMappingURL=pbc-routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pbc-routes.d.ts","sourceRoot":"","sources":["../../../src/router/pbc-routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,+EAA+E;IAC/E,SAAS,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,SAAS,CAAC;CAC9C;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,YAAY,EAAE,cAAc,EAAE,EAC9B,SAAS,EAAE,aAAa,EAAE,EAC1B,EAAE,SAAS,EAAE,EAAE,qBAAqB,GACnC,cAAc,EAAE,CAclB"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Shape of the router location `state` that a route guard stashes before
3
+ * redirecting to login. Structural, so it works with `react-router-dom`'s
4
+ * `Location` without importing it here.
5
+ */
6
+ export interface RedirectableLocationState {
7
+ from?: {
8
+ pathname: string;
9
+ search?: string;
10
+ hash?: string;
11
+ };
12
+ }
13
+ /**
14
+ * Build the post-login redirect target from a router location's `state.from`,
15
+ * preserving the query string and hash so deep-link params (e.g.
16
+ * `?component=<name>`) survive the login bounce. Falls back to `defaultPath`
17
+ * when there is no stashed origin.
18
+ *
19
+ * @param location - The current router location (needs only `state.from`).
20
+ * @param defaultPath - Where to land when nothing was stashed. Defaults to `/`.
21
+ */
22
+ export declare function buildPostLoginRedirect(location: {
23
+ state?: RedirectableLocationState | null;
24
+ }, defaultPath?: string): string;
25
+ //# sourceMappingURL=post-login-redirect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"post-login-redirect.d.ts","sourceRoot":"","sources":["../../../src/router/post-login-redirect.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,yBAAyB;IACxC,IAAI,CAAC,EAAE;QACL,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE;IAAE,KAAK,CAAC,EAAE,yBAAyB,GAAG,IAAI,CAAA;CAAE,EACtD,WAAW,SAAM,GAChB,MAAM,CAGR"}
@@ -0,0 +1,80 @@
1
+ import type { ReactNode } from 'react';
2
+ /**
3
+ * Props for {@link ProtectedRoute}.
4
+ */
5
+ export interface ProtectedRouteProps {
6
+ /** Whether the current user is authenticated. */
7
+ isAuthenticated: boolean;
8
+ /**
9
+ * Whether the current user may view this route. When `false` (and
10
+ * authenticated), redirects to `notPermittedPath` instead of rendering.
11
+ * Defaults to `true`.
12
+ */
13
+ hasPermission?: boolean;
14
+ /** Path to redirect to when unauthenticated. Defaults to `/login`. */
15
+ loginPath?: string;
16
+ /** Path to redirect to when authenticated but not permitted. Defaults to `/not-permitted`. */
17
+ notPermittedPath?: string;
18
+ children: ReactNode;
19
+ }
20
+ /**
21
+ * Route guard. In order:
22
+ * - not authenticated → redirect to `loginPath`, stashing the full current
23
+ * location in `state.from` (incl. `search` + `hash`) so the login flow can
24
+ * restore the original deep-link via {@link buildPostLoginRedirect};
25
+ * - authenticated but `hasPermission === false` → redirect to `notPermittedPath`;
26
+ * - otherwise → render `children`.
27
+ *
28
+ * Deliberately decoupled from any auth package — pass the booleans in (or use
29
+ * {@link createProtectedRoute} to bind the checks once).
30
+ */
31
+ export declare function ProtectedRoute({ isAuthenticated, hasPermission, loginPath, notPermittedPath, children, }: ProtectedRouteProps): import("react/jsx-runtime").JSX.Element;
32
+ /**
33
+ * Props of the component returned by {@link createProtectedRoute}. `permissionCode`
34
+ * is forwarded to the bound `hasPermission` check (e.g. by {@link renderAppRoutes}).
35
+ */
36
+ export interface BoundProtectedRouteProps {
37
+ children: ReactNode;
38
+ /** Route permission code handed to the bound `hasPermission` check. */
39
+ permissionCode?: string;
40
+ }
41
+ /**
42
+ * Options for {@link createProtectedRoute}.
43
+ */
44
+ export interface CreateProtectedRouteOptions {
45
+ /**
46
+ * Called at render time to determine auth state. Kept as a function (not a
47
+ * boolean) so it is re-evaluated on every render and the util stays free of
48
+ * any specific auth dependency.
49
+ */
50
+ getIsAuthenticated: () => boolean;
51
+ /**
52
+ * Optional per-route permission check, called at render time with the route's
53
+ * `permissionCode`. Absent → every authenticated user is permitted.
54
+ */
55
+ hasPermission?: (permissionCode: string | undefined) => boolean;
56
+ /** Path to redirect to when unauthenticated. Defaults to `/login`. */
57
+ loginPath?: string;
58
+ /** Path to redirect to when authenticated but not permitted. Defaults to `/not-permitted`. */
59
+ notPermittedPath?: string;
60
+ }
61
+ /**
62
+ * Bind auth (and optionally permission) checks once and get an ergonomic
63
+ * `<ProtectedRoute>` that only needs `children` (plus an optional
64
+ * `permissionCode`) — ideal when a route table wraps many elements.
65
+ *
66
+ * **Call this at module scope, never inside a component's render.** It returns a
67
+ * new component type each call; invoking it during render recreates that type
68
+ * every render, forcing React to unmount and remount the entire protected
69
+ * subtree (flicker + lost state).
70
+ *
71
+ * @example
72
+ * ```tsx
73
+ * const ProtectedRoute = createProtectedRoute({
74
+ * getIsAuthenticated: () => getUser().isAuthenticated,
75
+ * hasPermission: (code) => !code || canView(getUser(), code),
76
+ * });
77
+ * ```
78
+ */
79
+ export declare function createProtectedRoute({ getIsAuthenticated, hasPermission, loginPath, notPermittedPath, }: CreateProtectedRouteOptions): ({ children, permissionCode }: BoundProtectedRouteProps) => import("react/jsx-runtime").JSX.Element;
80
+ //# sourceMappingURL=protected-route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protected-route.d.ts","sourceRoot":"","sources":["../../../src/router/protected-route.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,iDAAiD;IACjD,eAAe,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8FAA8F;IAC9F,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,EAC7B,eAAe,EACf,aAAoB,EACpB,SAAoB,EACpB,gBAAmC,EACnC,QAAQ,GACT,EAAE,mBAAmB,2CAYrB;AAED;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,SAAS,CAAC;IACpB,uEAAuE;IACvE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,kBAAkB,EAAE,MAAM,OAAO,CAAC;IAClC;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,KAAK,OAAO,CAAC;IAChE,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8FAA8F;IAC9F,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,kBAAkB,EAClB,aAAa,EACb,SAAS,EACT,gBAAgB,GACjB,EAAE,2BAA2B,IACQ,8BAA8B,wBAAwB,6CAY3F"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=router.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.test.d.ts","sourceRoot":"","sources":["../../../src/router/router.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,52 @@
1
+ import type { ComponentType, ReactNode } from 'react';
2
+ /**
3
+ * Read a query-string parameter from the current URL.
4
+ *
5
+ * Call this at **module-evaluation time** in your app (i.e. as a top-level
6
+ * `const`), before the app shell's async bootstrap rewrites the URL and drops
7
+ * the query string, and store the result. That way a deep-link param such as
8
+ * `?component=<name>` survives even though it is gone by the time the router
9
+ * mounts.
10
+ */
11
+ export declare function readInitialParam(key: string): string | null;
12
+ /**
13
+ * A name → component registry with tolerant lookup. Create one with
14
+ * {@link createComponentRegistry}.
15
+ */
16
+ export interface ComponentRegistry {
17
+ /** The underlying name → component map. */
18
+ entries: Record<string, ComponentType<any>>;
19
+ /** All registered names, for help/error output. */
20
+ available(): string[];
21
+ /** Resolve a registered component by name, tolerant of case and separators. */
22
+ resolve(name: string | null | undefined): ComponentType<any> | undefined;
23
+ }
24
+ /**
25
+ * Create a name → component registry whose `resolve` is case- and
26
+ * separator-insensitive (`LimitDistributionChart` === `limit-distribution-chart`).
27
+ */
28
+ export declare function createComponentRegistry(entries: Record<string, ComponentType<any>>): ComponentRegistry;
29
+ /**
30
+ * Props for {@link SingleComponentOutlet}.
31
+ */
32
+ export interface SingleComponentOutletProps {
33
+ /** The requested component name (typically captured via {@link readInitialParam}). */
34
+ name: string | null | undefined;
35
+ /** Registry to resolve the name against. */
36
+ registry: ComponentRegistry;
37
+ /**
38
+ * Render when no component matches. Defaults to a plain, unstyled
39
+ * "Unknown component" message; pass your own to theme it.
40
+ */
41
+ renderUnknown?: (name: string | null | undefined, available: string[]) => ReactNode;
42
+ }
43
+ /**
44
+ * Resolve `name` against `registry` and render the matching component, or a
45
+ * fallback when unmatched.
46
+ *
47
+ * Theming/provider chrome is intentionally left to the caller — wrap this outlet
48
+ * in your app's design-system provider so the standalone component is themed
49
+ * identically to how it appears in-app.
50
+ */
51
+ export declare function SingleComponentOutlet({ name, registry, renderUnknown, }: SingleComponentOutletProps): import("react/jsx-runtime").JSX.Element;
52
+ //# sourceMappingURL=single-component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"single-component.d.ts","sourceRoot":"","sources":["../../../src/router/single-component.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEtD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG3D;AAKD;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5C,mDAAmD;IACnD,SAAS,IAAI,MAAM,EAAE,CAAC;IACtB,+EAA+E;IAC/E,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;CAC1E;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,GAC1C,iBAAiB,CAiBnB;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,sFAAsF;IACtF,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAChC,4CAA4C;IAC5C,QAAQ,EAAE,iBAAiB,CAAC;IAC5B;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,SAAS,CAAC;CACrF;AAQD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,IAAI,EACJ,QAAQ,EACR,aAA8B,GAC/B,EAAE,0BAA0B,2CAG5B"}
@@ -0,0 +1,32 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Navigate, Route, Routes } from 'react-router-dom';
3
+ const resolvePermissionCode = (route) => { var _a, _b; return (_a = route.permissionCode) !== null && _a !== void 0 ? _a : (_b = route.data) === null || _b === void 0 ? void 0 : _b.permissionCode; };
4
+ /**
5
+ * Build the app's `<Routes>` tree from a declarative route table, applying the
6
+ * auth guard, layout nesting, redirects, not-found, and the single-component
7
+ * short-circuit — so a consuming app configures routes instead of hand-writing
8
+ * repetitive `<Route element={<ProtectedRoute>…}>` markup.
9
+ */
10
+ export function renderAppRoutes({ routes, ProtectedRoute, layout, redirects = [], notFound, singleComponent, }) {
11
+ const publicRoutes = routes.filter((r) => r.public);
12
+ // Single-component mode: keep public routes reachable (login / session
13
+ // restore) and gate everything else behind the guard rendering the component.
14
+ if (singleComponent === null || singleComponent === void 0 ? void 0 : singleComponent.name) {
15
+ return (_jsxs(Routes, { children: [publicRoutes.map((r) => (_jsx(Route, { path: r.path, element: r.element }, r.path))), _jsx(Route, { path: "*", element: _jsx(ProtectedRoute, { children: singleComponent.element }) })] }));
16
+ }
17
+ const guarded = routes.filter((r) => !r.public);
18
+ const inLayout = guarded.filter((r) => !r.noLayout);
19
+ const topLevelGuarded = guarded.filter((r) => r.noLayout);
20
+ // Nested children render as plain relative routes inside the parent's
21
+ // `<Outlet />`; they inherit the parent's guard, so they are not re-wrapped.
22
+ const renderChild = (c) => {
23
+ var _a;
24
+ return ((_a = c.children) === null || _a === void 0 ? void 0 : _a.length) ? (_jsx(Route, { path: c.path, element: c.element, children: c.children.map(renderChild) }, c.path)) : (_jsx(Route, { path: c.path, element: c.element }, c.path));
25
+ };
26
+ const renderGuarded = (r) => {
27
+ var _a;
28
+ const element = (_jsx(ProtectedRoute, { permissionCode: resolvePermissionCode(r), children: r.element }));
29
+ return ((_a = r.children) === null || _a === void 0 ? void 0 : _a.length) ? (_jsx(Route, { path: r.path, element: element, children: r.children.map(renderChild) }, r.path)) : (_jsx(Route, { path: r.path, element: element }, r.path));
30
+ };
31
+ return (_jsxs(Routes, { children: [redirects.map((r) => (_jsx(Route, { path: r.from, element: _jsx(Navigate, { to: r.to, replace: true }) }, `redirect:${r.from}`))), publicRoutes.map((r) => (_jsx(Route, { path: r.path, element: r.element }, r.path))), topLevelGuarded.map(renderGuarded), layout ? (_jsx(Route, { element: layout, children: inLayout.map(renderGuarded) })) : (inLayout.map(renderGuarded)), notFound ? _jsx(Route, { path: "*", element: notFound }) : null] }));
32
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * `@genesislcap/foundation-react-utils/router`
3
+ *
4
+ * Reusable `react-router-dom` primitives for Genesis Foundation React apps, so
5
+ * the app-shell routing logic doesn't have to be reimplemented per app.
6
+ *
7
+ * Exposed as a subpath (`/router`) rather than from the package root so that
8
+ * consumers which don't route (and don't depend on `react-router-dom`) are
9
+ * unaffected.
10
+ *
11
+ * Key exports:
12
+ * - `ProtectedRoute` / `createProtectedRoute` — auth + permission gate that
13
+ * stashes the full origin location (incl. `search` + `hash`) for post-login
14
+ * restore, and redirects to a not-permitted path when authorized-but-blocked.
15
+ * - `buildPostLoginRedirect` — rebuild the return URL preserving query + hash.
16
+ * - `readInitialParam` / `createComponentRegistry` / `SingleComponentOutlet` —
17
+ * render a single registered component full-screen from a `?param=<name>`
18
+ * deep-link captured before the shell strips the query string.
19
+ * - `renderAppRoutes` — build the app's `<Routes>` from a declarative route
20
+ * table (guarding, layout nesting, redirects, not-found, single-component).
21
+ * - `mergePbcRoutes` — merge static routes with shell/PBC routes into one table.
22
+ */
23
+ export { ProtectedRoute, createProtectedRoute } from './protected-route';
24
+ export { buildPostLoginRedirect } from './post-login-redirect';
25
+ export { readInitialParam, createComponentRegistry, SingleComponentOutlet, } from './single-component';
26
+ export { renderAppRoutes } from './app-routes';
27
+ export { mergePbcRoutes } from './pbc-routes';