@buzola/router 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/dist/__tests__/create-page.test.d.ts +2 -0
  2. package/dist/__tests__/create-page.test.d.ts.map +1 -0
  3. package/dist/__tests__/create-page.test.js +457 -0
  4. package/dist/__tests__/create-page.test.js.map +1 -0
  5. package/dist/__tests__/define-routes.test.d.ts +2 -0
  6. package/dist/__tests__/define-routes.test.d.ts.map +1 -0
  7. package/dist/__tests__/define-routes.test.js +42 -0
  8. package/dist/__tests__/define-routes.test.js.map +1 -0
  9. package/dist/__tests__/matcher.test.d.ts +2 -0
  10. package/dist/__tests__/matcher.test.d.ts.map +1 -0
  11. package/dist/__tests__/matcher.test.js +108 -0
  12. package/dist/__tests__/matcher.test.js.map +1 -0
  13. package/dist/__tests__/router.test.d.ts +2 -0
  14. package/dist/__tests__/router.test.d.ts.map +1 -0
  15. package/dist/__tests__/router.test.js +327 -0
  16. package/dist/__tests__/router.test.js.map +1 -0
  17. package/dist/__tests__/type-safety.test.d.ts +15 -0
  18. package/dist/__tests__/type-safety.test.d.ts.map +1 -0
  19. package/dist/__tests__/type-safety.test.js +163 -0
  20. package/dist/__tests__/type-safety.test.js.map +1 -0
  21. package/dist/__tests__/types.test.d.ts +2 -0
  22. package/dist/__tests__/types.test.d.ts.map +1 -0
  23. package/dist/__tests__/types.test.js +22 -0
  24. package/dist/__tests__/types.test.js.map +1 -0
  25. package/dist/define/create-page.d.ts +87 -0
  26. package/dist/define/create-page.d.ts.map +1 -0
  27. package/dist/define/create-page.js +305 -0
  28. package/dist/define/create-page.js.map +1 -0
  29. package/dist/define/define-routes.d.ts +34 -0
  30. package/dist/define/define-routes.d.ts.map +1 -0
  31. package/dist/define/define-routes.js +53 -0
  32. package/dist/define/define-routes.js.map +1 -0
  33. package/dist/engine/matcher.d.ts +8 -0
  34. package/dist/engine/matcher.d.ts.map +1 -0
  35. package/dist/engine/matcher.js +79 -0
  36. package/dist/engine/matcher.js.map +1 -0
  37. package/dist/engine/navigation-adapter.d.ts +13 -0
  38. package/dist/engine/navigation-adapter.d.ts.map +1 -0
  39. package/dist/engine/navigation-adapter.js +156 -0
  40. package/dist/engine/navigation-adapter.js.map +1 -0
  41. package/dist/engine/route-tree.d.ts +21 -0
  42. package/dist/engine/route-tree.d.ts.map +1 -0
  43. package/dist/engine/route-tree.js +95 -0
  44. package/dist/engine/route-tree.js.map +1 -0
  45. package/dist/engine/router.d.ts +162 -0
  46. package/dist/engine/router.d.ts.map +1 -0
  47. package/dist/engine/router.js +400 -0
  48. package/dist/engine/router.js.map +1 -0
  49. package/dist/engine/schema.d.ts +46 -0
  50. package/dist/engine/schema.d.ts.map +1 -0
  51. package/dist/engine/schema.js +110 -0
  52. package/dist/engine/schema.js.map +1 -0
  53. package/dist/engine/types.d.ts +183 -0
  54. package/dist/engine/types.d.ts.map +1 -0
  55. package/dist/engine/types.js +2 -0
  56. package/dist/engine/types.js.map +1 -0
  57. package/dist/engine/utils.d.ts +7 -0
  58. package/dist/engine/utils.d.ts.map +1 -0
  59. package/dist/engine/utils.js +17 -0
  60. package/dist/engine/utils.js.map +1 -0
  61. package/dist/index.d.ts +14 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +12 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/react/context.d.ts +21 -0
  66. package/dist/react/context.d.ts.map +1 -0
  67. package/dist/react/context.js +7 -0
  68. package/dist/react/context.js.map +1 -0
  69. package/dist/react/error-boundary.d.ts +24 -0
  70. package/dist/react/error-boundary.d.ts.map +1 -0
  71. package/dist/react/error-boundary.js +37 -0
  72. package/dist/react/error-boundary.js.map +1 -0
  73. package/dist/react/hooks.d.ts +85 -0
  74. package/dist/react/hooks.d.ts.map +1 -0
  75. package/dist/react/hooks.js +161 -0
  76. package/dist/react/hooks.js.map +1 -0
  77. package/dist/react/index.d.ts +12 -0
  78. package/dist/react/index.d.ts.map +1 -0
  79. package/dist/react/index.js +7 -0
  80. package/dist/react/index.js.map +1 -0
  81. package/dist/react/link.d.ts +42 -0
  82. package/dist/react/link.d.ts.map +1 -0
  83. package/dist/react/link.js +38 -0
  84. package/dist/react/link.js.map +1 -0
  85. package/dist/react/outlet.d.ts +13 -0
  86. package/dist/react/outlet.d.ts.map +1 -0
  87. package/dist/react/outlet.js +46 -0
  88. package/dist/react/outlet.js.map +1 -0
  89. package/dist/react/provider.d.ts +22 -0
  90. package/dist/react/provider.d.ts.map +1 -0
  91. package/dist/react/provider.js +37 -0
  92. package/dist/react/provider.js.map +1 -0
  93. package/package.json +29 -0
  94. package/src/__tests__/create-page.test.ts +588 -0
  95. package/src/__tests__/matcher.test.ts +127 -0
  96. package/src/__tests__/router.test.ts +424 -0
  97. package/src/__tests__/type-safety.test.tsx +207 -0
  98. package/src/define/create-page.tsx +472 -0
  99. package/src/engine/matcher.ts +88 -0
  100. package/src/engine/navigation-adapter.ts +191 -0
  101. package/src/engine/route-tree.ts +129 -0
  102. package/src/engine/router.ts +471 -0
  103. package/src/engine/schema.ts +130 -0
  104. package/src/engine/types.ts +203 -0
  105. package/src/engine/utils.ts +16 -0
  106. package/src/env.d.ts +120 -0
  107. package/src/index.ts +61 -0
  108. package/src/react/context.ts +24 -0
  109. package/src/react/error-boundary.tsx +56 -0
  110. package/src/react/hooks.ts +211 -0
  111. package/src/react/index.ts +16 -0
  112. package/src/react/link.tsx +93 -0
  113. package/src/react/outlet.tsx +67 -0
  114. package/src/react/provider.tsx +60 -0
@@ -0,0 +1,183 @@
1
+ import type { ComponentType, LazyExoticComponent } from 'react';
2
+ /**
3
+ * Module augmentation point for type-safe page-centric routing.
4
+ * Vite plugin generates declarations that extend this interface.
5
+ * Maps page IDs to their params types.
6
+ */
7
+ export interface BuzolaPageMap {
8
+ }
9
+ /**
10
+ * Module augmentation point for persistent parameters.
11
+ * Vite plugin generates declarations that extend this interface.
12
+ * Keys listed here are automatically carried over between navigations.
13
+ */
14
+ export interface BuzolaPersistentParams {
15
+ }
16
+ /**
17
+ * Registered page IDs from BuzolaPageMap.
18
+ */
19
+ export type RegisteredPage = keyof BuzolaPageMap & string;
20
+ /**
21
+ * Get the params type for a registered page.
22
+ */
23
+ export type PageParams<P extends keyof BuzolaPageMap> = BuzolaPageMap[P];
24
+ /**
25
+ * Effective params for a page — persistent params become optional.
26
+ * When BuzolaPersistentParams is empty (default), collapses to BuzolaPageMap[P].
27
+ */
28
+ export type EffectivePageParams<P extends keyof BuzolaPageMap> = Omit<BuzolaPageMap[P], keyof BuzolaPersistentParams & string> & Partial<Pick<BuzolaPageMap[P], keyof BuzolaPersistentParams & string & keyof BuzolaPageMap[P]>>;
29
+ /**
30
+ * Standard Schema compatible interface (Zod 4, Valibot, ArkType).
31
+ * See: https://github.com/standard-schema/standard-schema
32
+ */
33
+ export interface StandardSchema<T = unknown> {
34
+ '~standard': {
35
+ version: 1;
36
+ vendor: string;
37
+ validate: (value: unknown) => {
38
+ value: T;
39
+ } | {
40
+ issues: readonly {
41
+ message: string;
42
+ }[];
43
+ };
44
+ };
45
+ }
46
+ /** Result returned by a route guard to redirect navigation. */
47
+ export interface GuardRedirect {
48
+ redirect: string;
49
+ }
50
+ /** Context passed to route guards. */
51
+ export interface GuardContext {
52
+ signal: AbortSignal;
53
+ }
54
+ /** Guard function — return false to block, a GuardRedirect to redirect, or true/void to allow. */
55
+ export type RouteGuardResult = boolean | void | GuardRedirect;
56
+ export type RouteGuard = (match: RouteMatch, context: GuardContext) => RouteGuardResult | Promise<RouteGuardResult>;
57
+ /** Route component — either a regular component or a lazy-loaded one. */
58
+ export type RouteComponent = ComponentType<Record<string, never>> | LazyExoticComponent<ComponentType<Record<string, never>>>;
59
+ /** Configuration for a single route. */
60
+ export interface RouteConfig {
61
+ /** The path segment for this route. */
62
+ path: string;
63
+ /** Component to render for this route. */
64
+ component?: RouteComponent;
65
+ /** Schema for validating search params (Standard Schema compatible). */
66
+ searchSchema?: StandardSchema;
67
+ /** Guard function called before entering this route. */
68
+ beforeEnter?: RouteGuard;
69
+ /** Child routes. */
70
+ children?: RouteConfig[];
71
+ /** Whether this is a layout route (has outlet but no own path segment). */
72
+ isLayout?: boolean;
73
+ /** Whether this is an index route (renders at parent's exact path). */
74
+ isIndex?: boolean;
75
+ /** Whether this is a pathless group (e.g., (auth) — no URL segment). */
76
+ isPathlessGroup?: boolean;
77
+ }
78
+ /** A matched route with extracted params. */
79
+ export interface RouteMatch {
80
+ /** The route node that matched. */
81
+ node: RouteNode;
82
+ /** Extracted path parameters. */
83
+ params: Record<string, string>;
84
+ /** The full URL pathname that was matched. */
85
+ pathname: string;
86
+ }
87
+ /** A node in the route tree. */
88
+ export interface RouteNode {
89
+ /** Unique ID for this node. */
90
+ id: string;
91
+ /** The path segment pattern (e.g., "/users/:userId"). */
92
+ path: string;
93
+ /** Full path from root (e.g., "/users/:userId"). */
94
+ fullPath: string;
95
+ /** Compiled URLPattern for matching (leaf/index routes). */
96
+ pattern?: URLPattern;
97
+ /** Compiled prefix URLPattern for param extraction (layout routes). */
98
+ prefixPattern?: URLPattern;
99
+ /** Component to render. */
100
+ component?: RouteComponent;
101
+ /** Search params schema. */
102
+ searchSchema?: StandardSchema;
103
+ /** Route guard. */
104
+ beforeEnter?: RouteGuard;
105
+ /** Child nodes. */
106
+ children: RouteNode[];
107
+ /** Parent node reference. */
108
+ parent?: RouteNode;
109
+ /** Whether this is a layout node (renders children via Outlet). */
110
+ isLayout: boolean;
111
+ /** Whether this is an index route. */
112
+ isIndex: boolean;
113
+ }
114
+ /** The current state of the router. */
115
+ export interface RouterState {
116
+ /** Current URL location. */
117
+ location: URL;
118
+ /** Array of matched routes from root to leaf. */
119
+ matches: RouteMatch[];
120
+ /** Whether a navigation is in progress. */
121
+ isPending: boolean;
122
+ /** Navigation state from Navigation API. */
123
+ navigationState?: unknown;
124
+ }
125
+ /** Options for programmatic navigation. */
126
+ export interface NavigateOptions {
127
+ /** Replace the current history entry instead of pushing. */
128
+ replace?: boolean;
129
+ /** State to associate with the navigation entry. */
130
+ state?: unknown;
131
+ /** Use View Transitions API for the navigation. */
132
+ viewTransition?: boolean;
133
+ /**
134
+ * Whether to reset scroll position after navigation.
135
+ * Default is `true` (browser handles it: scroll to top on push, restore on traverse).
136
+ * Set to `false` to preserve current scroll position.
137
+ */
138
+ resetScroll?: boolean;
139
+ }
140
+ /**
141
+ * Blocker function registered with the router.
142
+ * Called when a navigation is about to happen.
143
+ * Must return a Promise that resolves to `true` to allow navigation, `false` to block.
144
+ */
145
+ export type BlockerFn = () => Promise<boolean>;
146
+ /** Navigate event from the Navigation API (simplified). */
147
+ export interface BuzolaNavigateEvent {
148
+ /** The destination URL. */
149
+ destination: {
150
+ url: string;
151
+ };
152
+ /** Whether the navigation can be intercepted. */
153
+ canIntercept: boolean;
154
+ /** The type of navigation. */
155
+ navigationType: 'push' | 'replace' | 'reload' | 'traverse';
156
+ /** Whether the user initiated the navigation (e.g., clicking a link). */
157
+ userInitiated: boolean;
158
+ /** Whether this navigation should use View Transitions API. */
159
+ viewTransition?: boolean;
160
+ /** Intercept the navigation and handle it client-side. */
161
+ intercept(options: {
162
+ handler: () => Promise<void>;
163
+ scroll?: 'after-transition' | 'manual';
164
+ }): void;
165
+ }
166
+ /** Abstraction over the Navigation API for testability. */
167
+ export interface NavigationAdapter {
168
+ /** Get the current URL. */
169
+ getCurrentURL(): URL;
170
+ /** Navigate to a URL. */
171
+ navigate(url: string, options?: NavigateOptions): void;
172
+ /** Go back. */
173
+ back(): void;
174
+ /** Go forward. */
175
+ forward(): void;
176
+ /** Add a navigate event listener. */
177
+ addEventListener(type: 'navigate', handler: (event: BuzolaNavigateEvent) => void): void;
178
+ /** Remove a navigate event listener. */
179
+ removeEventListener(type: 'navigate', handler: (event: BuzolaNavigateEvent) => void): void;
180
+ /** Get the current navigation state. */
181
+ getState(): unknown;
182
+ }
183
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/engine/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/engine/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Extract parameter names from a route pattern.
3
+ * "/users/:userId/posts/:postId" → ["userId", "postId"]
4
+ * "/:lang/users/:userId+" → ["lang", "userId"]
5
+ */
6
+ export declare function extractParamNames(pattern: string): string[];
7
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/engine/utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAU3D"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Extract parameter names from a route pattern.
3
+ * "/users/:userId/posts/:postId" → ["userId", "postId"]
4
+ * "/:lang/users/:userId+" → ["lang", "userId"]
5
+ */
6
+ export function extractParamNames(pattern) {
7
+ const names = [];
8
+ for (const segment of pattern.split('/')) {
9
+ if (segment.startsWith(':')) {
10
+ // Strip catch-all suffix (+)
11
+ const name = segment.endsWith('+') ? segment.slice(1, -1) : segment.slice(1);
12
+ names.push(name);
13
+ }
14
+ }
15
+ return names;
16
+ }
17
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/engine/utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAChD,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1C,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7B,6BAA6B;YAC7B,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;YAC5E,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACjB,CAAC;IACF,CAAC;IACD,OAAO,KAAK,CAAA;AACb,CAAC"}
@@ -0,0 +1,14 @@
1
+ export { NavigationAbortedError, Router } from './engine/router';
2
+ export type { RouterOptions, RouterSubscriber } from './engine/router';
3
+ export { buildRouteTree, createRouteNode } from './engine/route-tree';
4
+ export type { CreateRouteNodeOptions } from './engine/route-tree';
5
+ export { matchRoutes } from './engine/matcher';
6
+ export { createBrowserNavigationAdapter, createMemoryNavigationAdapter } from './engine/navigation-adapter';
7
+ export type { MemoryNavigationAdapterOptions } from './engine/navigation-adapter';
8
+ export type { BlockerFn, BuzolaNavigateEvent, BuzolaPageMap, BuzolaPersistentParams, EffectivePageParams, GuardContext, GuardRedirect, NavigateOptions, NavigationAdapter, PageParams, RegisteredPage, RouteComponent, RouteConfig, RouteGuard, RouteGuardResult, RouteMatch, RouteNode, RouterState, StandardSchema, } from './engine/types';
9
+ export { s } from './engine/schema';
10
+ export { createPage } from './define/create-page';
11
+ export type { CatchContext, PageDefinition, PageProps } from './define/create-page';
12
+ export { BuzolaProvider, ErrorBoundary, Link, Outlet, RouteContext, RouterContext, useBlocker, useInvalidate, useNavigate, useNavigationState, useParams, useRoute, useRouter, useRouterState, useSearchParams, } from './react/index';
13
+ export type { BlockerState, BuzolaProviderProps, LinkProps, OutletProps, RouteContextValue } from './react/index';
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAChE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAEtE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACrE,YAAY,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAEjE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAE9C,OAAO,EAAE,8BAA8B,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAA;AAC3G,YAAY,EAAE,8BAA8B,EAAE,MAAM,6BAA6B,CAAA;AAGjF,YAAY,EACX,SAAS,EACT,mBAAmB,EACnB,aAAa,EACb,sBAAsB,EACtB,mBAAmB,EACnB,YAAY,EACZ,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,UAAU,EACV,cAAc,EACd,cAAc,EACd,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,SAAS,EACT,WAAW,EACX,cAAc,GACd,MAAM,gBAAgB,CAAA;AAGvB,OAAO,EAAE,CAAC,EAAE,MAAM,iBAAiB,CAAA;AAGnC,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACjD,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAGnF,OAAO,EACN,cAAc,EACd,aAAa,EACb,IAAI,EACJ,MAAM,EACN,YAAY,EACZ,aAAa,EACb,UAAU,EACV,aAAa,EACb,WAAW,EACX,kBAAkB,EAClB,SAAS,EACT,QAAQ,EACR,SAAS,EACT,cAAc,EACd,eAAe,GACf,MAAM,eAAe,CAAA;AACtB,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,SAAS,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ // ─── Engine ─────────────────────────────────────────────────────────────────
2
+ export { NavigationAbortedError, Router } from './engine/router';
3
+ export { buildRouteTree, createRouteNode } from './engine/route-tree';
4
+ export { matchRoutes } from './engine/matcher';
5
+ export { createBrowserNavigationAdapter, createMemoryNavigationAdapter } from './engine/navigation-adapter';
6
+ // ─── Schema ──────────────────────────────────────────────────────────────────
7
+ export { s } from './engine/schema';
8
+ // ─── Route definition ───────────────────────────────────────────────────────
9
+ export { createPage } from './define/create-page';
10
+ // ─── React (re-export from subpath) ─────────────────────────────────────────
11
+ export { BuzolaProvider, ErrorBoundary, Link, Outlet, RouteContext, RouterContext, useBlocker, useInvalidate, useNavigate, useNavigationState, useParams, useRoute, useRouter, useRouterState, useSearchParams, } from './react/index';
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAGhE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAGrE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAE9C,OAAO,EAAE,8BAA8B,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAA;AA0B3G,gFAAgF;AAChF,OAAO,EAAE,CAAC,EAAE,MAAM,iBAAiB,CAAA;AAEnC,+EAA+E;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAGjD,+EAA+E;AAC/E,OAAO,EACN,cAAc,EACd,aAAa,EACb,IAAI,EACJ,MAAM,EACN,YAAY,EACZ,aAAa,EACb,UAAU,EACV,aAAa,EACb,WAAW,EACX,kBAAkB,EAClB,SAAS,EACT,QAAQ,EACR,SAAS,EACT,cAAc,EACd,eAAe,GACf,MAAM,eAAe,CAAA"}
@@ -0,0 +1,21 @@
1
+ import type { Router } from '../engine/router';
2
+ import type { RouteMatch, RouterState } from '../engine/types';
3
+ /**
4
+ * Context providing the Router instance.
5
+ */
6
+ export declare const RouterContext: import("react").Context<Router | null>;
7
+ /**
8
+ * Context for the current route matching state.
9
+ */
10
+ export interface RouteContextValue {
11
+ /** Current router state. */
12
+ state: RouterState;
13
+ /** Current depth in the route match chain. */
14
+ depth: number;
15
+ /** All matches from root to leaf. */
16
+ matches: RouteMatch[];
17
+ /** Merged params from root to current depth. */
18
+ params: Record<string, string>;
19
+ }
20
+ export declare const RouteContext: import("react").Context<RouteContextValue | null>;
21
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/react/context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAE9D;;GAEG;AACH,eAAO,MAAM,aAAa,wCAAqC,CAAA;AAE/D;;GAEG;AACH,MAAM,WAAW,iBAAiB;IACjC,4BAA4B;IAC5B,KAAK,EAAE,WAAW,CAAA;IAClB,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAA;IACb,qCAAqC;IACrC,OAAO,EAAE,UAAU,EAAE,CAAA;IACrB,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC9B;AAED,eAAO,MAAM,YAAY,mDAAgD,CAAA"}
@@ -0,0 +1,7 @@
1
+ import { createContext } from 'react';
2
+ /**
3
+ * Context providing the Router instance.
4
+ */
5
+ export const RouterContext = createContext(null);
6
+ export const RouteContext = createContext(null);
7
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/react/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAIrC;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAgB,IAAI,CAAC,CAAA;AAgB/D,MAAM,CAAC,MAAM,YAAY,GAAG,aAAa,CAA2B,IAAI,CAAC,CAAA"}
@@ -0,0 +1,24 @@
1
+ import React, { Component } from 'react';
2
+ interface ErrorBoundaryProps {
3
+ fallback?: React.ReactNode | ((error: Error) => React.ReactNode);
4
+ /** When this key changes, the error state is reset. */
5
+ resetKey?: string;
6
+ children: React.ReactNode;
7
+ }
8
+ interface ErrorBoundaryState {
9
+ error: Error | null;
10
+ prevResetKey: string | undefined;
11
+ }
12
+ /**
13
+ * Error boundary for catching rendering errors in route components.
14
+ * Resets automatically when `resetKey` changes (e.g., on navigation to a different route).
15
+ */
16
+ export declare class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
17
+ state: ErrorBoundaryState;
18
+ static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState>;
19
+ static getDerivedStateFromProps(props: ErrorBoundaryProps, state: ErrorBoundaryState): Partial<ErrorBoundaryState> | null;
20
+ componentDidCatch(error: Error, info: React.ErrorInfo): void;
21
+ render(): React.ReactNode;
22
+ }
23
+ export {};
24
+ //# sourceMappingURL=error-boundary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-boundary.d.ts","sourceRoot":"","sources":["../../src/react/error-boundary.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAExC,UAAU,kBAAkB;IAC3B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,KAAK,CAAC,SAAS,CAAC,CAAA;IAChE,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CACzB;AAED,UAAU,kBAAkB;IAC3B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;IACnB,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;CAChC;AAED;;;GAGG;AACH,qBAAa,aAAc,SAAQ,SAAS,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;IACnF,KAAK,EAAE,kBAAkB,CAA2C;IAEpE,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAI1E,MAAM,CAAC,wBAAwB,CAC9B,KAAK,EAAE,kBAAkB,EACzB,KAAK,EAAE,kBAAkB,GACvB,OAAO,CAAC,kBAAkB,CAAC,GAAG,IAAI;IAWrC,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,SAAS,GAAG,IAAI;IAI5D,MAAM,IAAI,KAAK,CAAC,SAAS;CAYzB"}
@@ -0,0 +1,37 @@
1
+ import React, { Component } from 'react';
2
+ /**
3
+ * Error boundary for catching rendering errors in route components.
4
+ * Resets automatically when `resetKey` changes (e.g., on navigation to a different route).
5
+ */
6
+ export class ErrorBoundary extends Component {
7
+ state = { error: null, prevResetKey: undefined };
8
+ static getDerivedStateFromError(error) {
9
+ return { error };
10
+ }
11
+ static getDerivedStateFromProps(props, state) {
12
+ // Reset error state when resetKey changes (i.e., route changed)
13
+ if (state.error && props.resetKey !== state.prevResetKey) {
14
+ return { error: null, prevResetKey: props.resetKey };
15
+ }
16
+ if (props.resetKey !== state.prevResetKey) {
17
+ return { prevResetKey: props.resetKey };
18
+ }
19
+ return null;
20
+ }
21
+ componentDidCatch(error, info) {
22
+ console.error('Buzola route error:', error, info);
23
+ }
24
+ render() {
25
+ if (this.state.error) {
26
+ if (typeof this.props.fallback === 'function') {
27
+ return this.props.fallback(this.state.error);
28
+ }
29
+ if (this.props.fallback) {
30
+ return this.props.fallback;
31
+ }
32
+ return React.createElement('div', { style: { color: 'red' } }, `Route error: ${this.state.error.message}`);
33
+ }
34
+ return this.props.children;
35
+ }
36
+ }
37
+ //# sourceMappingURL=error-boundary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-boundary.js","sourceRoot":"","sources":["../../src/react/error-boundary.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAcxC;;;GAGG;AACH,MAAM,OAAO,aAAc,SAAQ,SAAiD;IACnF,KAAK,GAAuB,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,CAAA;IAEpE,MAAM,CAAC,wBAAwB,CAAC,KAAY;QAC3C,OAAO,EAAE,KAAK,EAAE,CAAA;IACjB,CAAC;IAED,MAAM,CAAC,wBAAwB,CAC9B,KAAyB,EACzB,KAAyB;QAEzB,gEAAgE;QAChE,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,QAAQ,KAAK,KAAK,CAAC,YAAY,EAAE,CAAC;YAC1D,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAA;QACrD,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,KAAK,KAAK,CAAC,YAAY,EAAE,CAAC;YAC3C,OAAO,EAAE,YAAY,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAA;QACxC,CAAC;QACD,OAAO,IAAI,CAAA;IACZ,CAAC;IAED,iBAAiB,CAAC,KAAY,EAAE,IAAqB;QACpD,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,KAAK,EAAE,IAAI,CAAC,CAAA;IAClD,CAAC;IAED,MAAM;QACL,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACtB,IAAI,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;gBAC/C,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;YAC7C,CAAC;YACD,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACzB,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAA;YAC3B,CAAC;YACD,OAAO,KAAK,CAAC,aAAa,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,gBAAgB,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;QAC3G,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAA;IAC3B,CAAC;CACD"}
@@ -0,0 +1,85 @@
1
+ import type { Router } from '../engine/router';
2
+ import type { EffectivePageParams, NavigateOptions, RegisteredPage, RouterState, StandardSchema } from '../engine/types';
3
+ /**
4
+ * Get the Router instance.
5
+ */
6
+ export declare function useRouter(): Router;
7
+ /**
8
+ * Type-safe navigation function.
9
+ * Uses page IDs instead of URL patterns.
10
+ * Persistent params are optional — resolved from current URL or fallback.
11
+ */
12
+ type NavigateFn = <P extends RegisteredPage>(to: P, ...args: [keyof EffectivePageParams<P>] extends [never] ? [options?: NavigateOptions] : {} extends EffectivePageParams<P> ? [options?: NavigateOptions & {
13
+ params?: EffectivePageParams<P>;
14
+ }] : [options: NavigateOptions & {
15
+ params: EffectivePageParams<P>;
16
+ }]) => void;
17
+ /**
18
+ * Hook to get a type-safe navigate function.
19
+ * Navigates using page IDs — the router looks up the route pattern from the page registry.
20
+ */
21
+ export declare function useNavigate(): NavigateFn;
22
+ /**
23
+ * Hook to get typed route params.
24
+ * Params are merged from all matched routes (root to current depth).
25
+ */
26
+ export declare function useParams<T extends Record<string, string> = Record<string, string>>(): T;
27
+ /**
28
+ * Hook to get and validate search params.
29
+ * Optionally validates with a Standard Schema compatible schema.
30
+ */
31
+ export declare function useSearchParams<T = Record<string, string>>(schema?: StandardSchema<T>): T;
32
+ /**
33
+ * Hook to get information about the current route.
34
+ */
35
+ export declare function useRoute(): {
36
+ /** Current route's params. */
37
+ params: Record<string, string>;
38
+ /** All route matches from root to leaf. */
39
+ matches: import("..").RouteMatch[];
40
+ /** Current match depth. */
41
+ depth: number;
42
+ /** Whether a navigation is pending. */
43
+ isPending: boolean;
44
+ /** Current pathname (with base path stripped). */
45
+ pathname: string;
46
+ };
47
+ /**
48
+ * Hook to get the Navigation API state for the current entry.
49
+ */
50
+ export declare function useNavigationState<T = unknown>(): T | undefined;
51
+ /**
52
+ * Hook to get the current RouterState.
53
+ */
54
+ export declare function useRouterState(): RouterState;
55
+ /**
56
+ * Hook to get a function that invalidates all page loaders.
57
+ * Loaders re-run in the background while stale data is still shown.
58
+ */
59
+ export declare function useInvalidate(): () => void;
60
+ export interface BlockerState {
61
+ /** Whether a navigation is currently blocked. */
62
+ state: 'idle' | 'blocked';
63
+ /** Allow the blocked navigation to proceed. */
64
+ proceed: () => void;
65
+ /** Cancel the blocked navigation and stay on the current page. */
66
+ reset: () => void;
67
+ }
68
+ /**
69
+ * Hook to block navigations (e.g., to warn about unsaved changes).
70
+ *
71
+ * @param when - Whether blocking is active. Pass `true` to block, `false` to allow.
72
+ * @returns An object with the blocker state and proceed/reset functions.
73
+ *
74
+ * @example
75
+ * ```tsx
76
+ * const blocker = useBlocker(formIsDirty);
77
+ *
78
+ * if (blocker.state === 'blocked') {
79
+ * return <Dialog onConfirm={blocker.proceed} onCancel={blocker.reset} />;
80
+ * }
81
+ * ```
82
+ */
83
+ export declare function useBlocker(when: boolean): BlockerState;
84
+ export {};
85
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/react/hooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,eAAe,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAGxH;;GAEG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAMlC;AAaD;;;;GAIG;AACH,KAAK,UAAU,GAAG,CAAC,CAAC,SAAS,cAAc,EAC1C,EAAE,EAAE,CAAC,EACL,GAAG,IAAI,EAAE,CAAC,MAAM,mBAAmB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,eAAe,CAAC,GAElF,EAAE,SAAS,mBAAmB,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG;IAAE,MAAM,CAAC,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAA;CAAE,CAAC,GACrG,CAAC,OAAO,EAAE,eAAe,GAAG;IAAE,MAAM,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAA;CAAE,CAAC,KAC9D,IAAI,CAAA;AAET;;;GAGG;AACH,wBAAgB,WAAW,IAAI,UAAU,CASxC;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAGxF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACzD,MAAM,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,GACxB,CAAC,CA2BH;AAED;;GAEG;AACH,wBAAgB,QAAQ;IAMtB,8BAA8B;;IAE9B,2CAA2C;;IAE3C,2BAA2B;;IAE3B,uCAAuC;;IAEvC,kDAAkD;;EAGnD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,OAAO,KAAK,CAAC,GAAG,SAAS,CAG/D;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAG5C;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,MAAM,IAAI,CAG1C;AAID,MAAM,WAAW,YAAY;IAC5B,iDAAiD;IACjD,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,+CAA+C;IAC/C,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,kEAAkE;IAClE,KAAK,EAAE,MAAM,IAAI,CAAA;CACjB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,CAsCtD"}
@@ -0,0 +1,161 @@
1
+ import { use, useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { RouteContext, RouterContext } from './context';
3
+ /**
4
+ * Get the Router instance.
5
+ */
6
+ export function useRouter() {
7
+ const router = use(RouterContext);
8
+ if (!router) {
9
+ throw new Error('useRouter must be used within a <BuzolaProvider>');
10
+ }
11
+ return router;
12
+ }
13
+ /**
14
+ * Get the current route context.
15
+ */
16
+ function useRouteContext() {
17
+ const context = use(RouteContext);
18
+ if (!context) {
19
+ throw new Error('Route hooks must be used within a <BuzolaProvider>');
20
+ }
21
+ return context;
22
+ }
23
+ /**
24
+ * Hook to get a type-safe navigate function.
25
+ * Navigates using page IDs — the router looks up the route pattern from the page registry.
26
+ */
27
+ export function useNavigate() {
28
+ const router = useRouter();
29
+ return useCallback((to, options) => {
30
+ router.navigateToPage(to, options?.params, options);
31
+ }, [router]);
32
+ }
33
+ /**
34
+ * Hook to get typed route params.
35
+ * Params are merged from all matched routes (root to current depth).
36
+ */
37
+ export function useParams() {
38
+ const context = useRouteContext();
39
+ return context.params;
40
+ }
41
+ /**
42
+ * Hook to get and validate search params.
43
+ * Optionally validates with a Standard Schema compatible schema.
44
+ */
45
+ export function useSearchParams(schema) {
46
+ const context = useRouteContext();
47
+ return useMemo(() => {
48
+ const searchParams = context.state.location.searchParams;
49
+ const raw = {};
50
+ searchParams.forEach((value, key) => {
51
+ const existing = raw[key];
52
+ if (existing) {
53
+ existing.push(value);
54
+ }
55
+ else {
56
+ raw[key] = [value];
57
+ }
58
+ });
59
+ if (schema) {
60
+ const result = schema['~standard'].validate(raw);
61
+ if ('issues' in result) {
62
+ throw new Error(`Search params validation failed: ${result.issues.map(i => i.message).join(', ')}`);
63
+ }
64
+ return result.value;
65
+ }
66
+ return raw;
67
+ }, [context.state.location.searchParams, schema]);
68
+ }
69
+ /**
70
+ * Hook to get information about the current route.
71
+ */
72
+ export function useRoute() {
73
+ const context = useRouteContext();
74
+ const router = useRouter();
75
+ const { state, matches, params, depth } = context;
76
+ return useMemo(() => ({
77
+ /** Current route's params. */
78
+ params,
79
+ /** All route matches from root to leaf. */
80
+ matches,
81
+ /** Current match depth. */
82
+ depth,
83
+ /** Whether a navigation is pending. */
84
+ isPending: state.isPending,
85
+ /** Current pathname (with base path stripped). */
86
+ pathname: router.stripBasePath(state.location.pathname),
87
+ }), [params, matches, depth, state.isPending, state.location.pathname, router]);
88
+ }
89
+ /**
90
+ * Hook to get the Navigation API state for the current entry.
91
+ */
92
+ export function useNavigationState() {
93
+ const context = useRouteContext();
94
+ return context.state.navigationState;
95
+ }
96
+ /**
97
+ * Hook to get the current RouterState.
98
+ */
99
+ export function useRouterState() {
100
+ const context = useRouteContext();
101
+ return context.state;
102
+ }
103
+ /**
104
+ * Hook to get a function that invalidates all page loaders.
105
+ * Loaders re-run in the background while stale data is still shown.
106
+ */
107
+ export function useInvalidate() {
108
+ const router = useRouter();
109
+ return useCallback(() => router.invalidateAll(), [router]);
110
+ }
111
+ /**
112
+ * Hook to block navigations (e.g., to warn about unsaved changes).
113
+ *
114
+ * @param when - Whether blocking is active. Pass `true` to block, `false` to allow.
115
+ * @returns An object with the blocker state and proceed/reset functions.
116
+ *
117
+ * @example
118
+ * ```tsx
119
+ * const blocker = useBlocker(formIsDirty);
120
+ *
121
+ * if (blocker.state === 'blocked') {
122
+ * return <Dialog onConfirm={blocker.proceed} onCancel={blocker.reset} />;
123
+ * }
124
+ * ```
125
+ */
126
+ export function useBlocker(when) {
127
+ const router = useRouter();
128
+ const [state, setState] = useState('idle');
129
+ const resolverRef = useRef(null);
130
+ const blockerFn = useCallback(() => {
131
+ return new Promise((resolve) => {
132
+ resolverRef.current = resolve;
133
+ setState('blocked');
134
+ });
135
+ }, []);
136
+ useEffect(() => {
137
+ if (!when)
138
+ return;
139
+ const cleanup = router.addBlocker(blockerFn);
140
+ return () => {
141
+ cleanup();
142
+ // If there's a pending blocker when unmounting, auto-proceed
143
+ if (resolverRef.current) {
144
+ resolverRef.current(true);
145
+ resolverRef.current = null;
146
+ }
147
+ };
148
+ }, [router, when, blockerFn]);
149
+ const proceed = useCallback(() => {
150
+ resolverRef.current?.(true);
151
+ resolverRef.current = null;
152
+ setState('idle');
153
+ }, []);
154
+ const reset = useCallback(() => {
155
+ resolverRef.current?.(false);
156
+ resolverRef.current = null;
157
+ setState('idle');
158
+ }, []);
159
+ return { state, proceed, reset };
160
+ }
161
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/react/hooks.ts"],"names":[],"mappings":"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"}