fibrae 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components.d.ts +40 -0
- package/dist/components.js +63 -0
- package/dist/components.js.map +1 -0
- package/dist/core.d.ts +25 -0
- package/dist/core.js +46 -0
- package/dist/core.js.map +1 -0
- package/dist/dom.d.ts +16 -0
- package/dist/dom.js +67 -0
- package/dist/dom.js.map +1 -0
- package/dist/fiber-render.d.ts +33 -0
- package/dist/fiber-render.js +1069 -0
- package/dist/fiber-render.js.map +1 -0
- package/dist/h.d.ts +19 -0
- package/dist/h.js +26 -0
- package/dist/h.js.map +1 -0
- package/dist/hydration.d.ts +30 -0
- package/dist/hydration.js +375 -0
- package/dist/hydration.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/jsx-runtime/index.d.ts +29 -0
- package/dist/jsx-runtime/index.js +61 -0
- package/dist/jsx-runtime/index.js.map +1 -0
- package/dist/render.d.ts +19 -0
- package/dist/render.js +325 -0
- package/dist/render.js.map +1 -0
- package/dist/router/History.d.ts +129 -0
- package/dist/router/History.js +241 -0
- package/dist/router/History.js.map +1 -0
- package/dist/router/Link.d.ts +52 -0
- package/dist/router/Link.js +131 -0
- package/dist/router/Link.js.map +1 -0
- package/dist/router/Navigator.d.ts +108 -0
- package/dist/router/Navigator.js +225 -0
- package/dist/router/Navigator.js.map +1 -0
- package/dist/router/Route.d.ts +65 -0
- package/dist/router/Route.js +143 -0
- package/dist/router/Route.js.map +1 -0
- package/dist/router/Router.d.ts +167 -0
- package/dist/router/Router.js +328 -0
- package/dist/router/Router.js.map +1 -0
- package/dist/router/RouterBuilder.d.ts +128 -0
- package/dist/router/RouterBuilder.js +112 -0
- package/dist/router/RouterBuilder.js.map +1 -0
- package/dist/router/RouterOutlet.d.ts +57 -0
- package/dist/router/RouterOutlet.js +132 -0
- package/dist/router/RouterOutlet.js.map +1 -0
- package/dist/router/RouterState.d.ts +102 -0
- package/dist/router/RouterState.js +94 -0
- package/dist/router/RouterState.js.map +1 -0
- package/dist/router/index.d.ts +28 -0
- package/dist/router/index.js +31 -0
- package/dist/router/index.js.map +1 -0
- package/dist/runtime.d.ts +55 -0
- package/dist/runtime.js +68 -0
- package/dist/runtime.js.map +1 -0
- package/dist/scope-utils.d.ts +14 -0
- package/dist/scope-utils.js +29 -0
- package/dist/scope-utils.js.map +1 -0
- package/dist/server.d.ts +112 -0
- package/dist/server.js +313 -0
- package/dist/server.js.map +1 -0
- package/dist/shared.d.ts +136 -0
- package/dist/shared.js +53 -0
- package/dist/shared.js.map +1 -0
- package/dist/tracking.d.ts +23 -0
- package/dist/tracking.js +53 -0
- package/dist/tracking.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* History service - track and manage browser navigation history.
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Current location (pathname, search, hash, state)
|
|
6
|
+
* - Navigation methods (push, replace, back, forward, go)
|
|
7
|
+
* - BrowserHistoryLive - real browser history with popstate handling
|
|
8
|
+
* - MemoryHistoryLive - in-memory history for testing/SSR
|
|
9
|
+
* - Cleanly manages event listeners with Effect finalizers
|
|
10
|
+
*
|
|
11
|
+
* Design:
|
|
12
|
+
* - Uses Atom for reactive location updates
|
|
13
|
+
* - Browser history: listens to window.popstate for back/forward
|
|
14
|
+
* - Memory history: in-memory stack, useful for SSR/testing
|
|
15
|
+
* - All navigation returns Effects, integrates with Effect runtime
|
|
16
|
+
*/
|
|
17
|
+
import * as Effect from "effect/Effect";
|
|
18
|
+
import * as Context from "effect/Context";
|
|
19
|
+
import * as Layer from "effect/Layer";
|
|
20
|
+
import { Atom, Registry as AtomRegistry } from "@effect-atom/atom";
|
|
21
|
+
// =============================================================================
|
|
22
|
+
// Service Tag
|
|
23
|
+
// =============================================================================
|
|
24
|
+
/**
|
|
25
|
+
* History service tag for Effect dependency injection.
|
|
26
|
+
*/
|
|
27
|
+
export class History extends Context.Tag("fibrae/History")() {
|
|
28
|
+
}
|
|
29
|
+
// =============================================================================
|
|
30
|
+
// Browser History Implementation
|
|
31
|
+
// =============================================================================
|
|
32
|
+
/**
|
|
33
|
+
* Get current browser location.
|
|
34
|
+
*/
|
|
35
|
+
function getBrowserLocation() {
|
|
36
|
+
return {
|
|
37
|
+
pathname: window.location.pathname,
|
|
38
|
+
search: window.location.search,
|
|
39
|
+
hash: window.location.hash,
|
|
40
|
+
state: window.history.state,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Parse path into location object.
|
|
45
|
+
*/
|
|
46
|
+
function parseLocation(path, state) {
|
|
47
|
+
try {
|
|
48
|
+
const url = new URL(path, window.location.origin);
|
|
49
|
+
return {
|
|
50
|
+
pathname: url.pathname,
|
|
51
|
+
search: url.search,
|
|
52
|
+
hash: url.hash,
|
|
53
|
+
state,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return {
|
|
58
|
+
pathname: path,
|
|
59
|
+
search: "",
|
|
60
|
+
hash: "",
|
|
61
|
+
state,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Browser history layer - real browser history with popstate handling.
|
|
67
|
+
*
|
|
68
|
+
* Features:
|
|
69
|
+
* - Tracks current location in an Atom
|
|
70
|
+
* - Listens to popstate for back/forward
|
|
71
|
+
* - Provides push/replace/go navigation methods
|
|
72
|
+
* - Cleanly removes event listener on scope close
|
|
73
|
+
* - Properly cleans up event listeners on scope close
|
|
74
|
+
*/
|
|
75
|
+
/* is-tree-shakable-suppress */
|
|
76
|
+
export const BrowserHistoryLive = Layer.scoped(History, Effect.gen(function* () {
|
|
77
|
+
const registry = yield* AtomRegistry.AtomRegistry;
|
|
78
|
+
// Create location atom with initial browser location
|
|
79
|
+
const locationAtom = Atom.make(getBrowserLocation());
|
|
80
|
+
// Subscribe to popstate for browser back/forward
|
|
81
|
+
const handlePopState = () => {
|
|
82
|
+
registry.set(locationAtom, getBrowserLocation());
|
|
83
|
+
};
|
|
84
|
+
window.addEventListener("popstate", handlePopState);
|
|
85
|
+
// Cleanup on scope close
|
|
86
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => {
|
|
87
|
+
window.removeEventListener("popstate", handlePopState);
|
|
88
|
+
}));
|
|
89
|
+
// Track history index for canGoBack (simplified approach)
|
|
90
|
+
let historyIndex = 0;
|
|
91
|
+
const service = {
|
|
92
|
+
location: locationAtom,
|
|
93
|
+
push: (path, state) => Effect.sync(() => {
|
|
94
|
+
const location = parseLocation(path, state);
|
|
95
|
+
const href = `${location.pathname}${location.search}${location.hash}`;
|
|
96
|
+
window.history.pushState(state, "", href);
|
|
97
|
+
historyIndex++;
|
|
98
|
+
registry.set(locationAtom, {
|
|
99
|
+
...location,
|
|
100
|
+
state,
|
|
101
|
+
});
|
|
102
|
+
}),
|
|
103
|
+
replace: (path, state) => Effect.sync(() => {
|
|
104
|
+
const location = parseLocation(path, state);
|
|
105
|
+
const href = `${location.pathname}${location.search}${location.hash}`;
|
|
106
|
+
window.history.replaceState(state, "", href);
|
|
107
|
+
registry.set(locationAtom, {
|
|
108
|
+
...location,
|
|
109
|
+
state,
|
|
110
|
+
});
|
|
111
|
+
}),
|
|
112
|
+
back: Effect.sync(() => {
|
|
113
|
+
window.history.back();
|
|
114
|
+
// Note: popstate handler will update location
|
|
115
|
+
}),
|
|
116
|
+
forward: Effect.sync(() => {
|
|
117
|
+
window.history.forward();
|
|
118
|
+
// Note: popstate handler will update location
|
|
119
|
+
}),
|
|
120
|
+
go: (n) => Effect.sync(() => {
|
|
121
|
+
window.history.go(n);
|
|
122
|
+
// Note: popstate handler will update location
|
|
123
|
+
}),
|
|
124
|
+
canGoBack: Effect.sync(() => historyIndex > 0),
|
|
125
|
+
};
|
|
126
|
+
return service;
|
|
127
|
+
}));
|
|
128
|
+
/**
|
|
129
|
+
* Create a memory history layer - useful for testing/SSR.
|
|
130
|
+
*
|
|
131
|
+
* Features:
|
|
132
|
+
* - In-memory navigation stack
|
|
133
|
+
* - Tracks current location in an Atom
|
|
134
|
+
* - No browser API usage (safe for SSR/testing)
|
|
135
|
+
* - Supports push/replace/back/forward/go navigation
|
|
136
|
+
* - Optional initial location/state configuration
|
|
137
|
+
*/
|
|
138
|
+
export function MemoryHistoryLive(options = {}) {
|
|
139
|
+
return Layer.scoped(History, Effect.gen(function* () {
|
|
140
|
+
const registry = yield* AtomRegistry.AtomRegistry;
|
|
141
|
+
// Create location atom with initial location
|
|
142
|
+
const initialLocation = {
|
|
143
|
+
pathname: options.initialPathname ?? "/",
|
|
144
|
+
search: options.initialSearch ?? "",
|
|
145
|
+
hash: options.initialHash ?? "",
|
|
146
|
+
state: options.initialState,
|
|
147
|
+
};
|
|
148
|
+
const locationAtom = Atom.make(initialLocation);
|
|
149
|
+
// Track history stack for back/forward
|
|
150
|
+
const historyStack = [initialLocation];
|
|
151
|
+
let historyIndex = 0;
|
|
152
|
+
const service = {
|
|
153
|
+
location: locationAtom,
|
|
154
|
+
push: (path, state) => Effect.sync(() => {
|
|
155
|
+
const location = parseLocation(path, state);
|
|
156
|
+
// Remove entries after current index
|
|
157
|
+
historyStack.splice(historyIndex + 1);
|
|
158
|
+
// Add new entry
|
|
159
|
+
historyStack.push(location);
|
|
160
|
+
historyIndex = historyStack.length - 1;
|
|
161
|
+
registry.set(locationAtom, location);
|
|
162
|
+
}),
|
|
163
|
+
replace: (path, state) => Effect.sync(() => {
|
|
164
|
+
const location = parseLocation(path, state);
|
|
165
|
+
// Replace current entry
|
|
166
|
+
historyStack[historyIndex] = location;
|
|
167
|
+
registry.set(locationAtom, location);
|
|
168
|
+
}),
|
|
169
|
+
back: Effect.sync(() => {
|
|
170
|
+
if (historyIndex > 0) {
|
|
171
|
+
historyIndex--;
|
|
172
|
+
registry.set(locationAtom, historyStack[historyIndex]);
|
|
173
|
+
}
|
|
174
|
+
}),
|
|
175
|
+
forward: Effect.sync(() => {
|
|
176
|
+
if (historyIndex < historyStack.length - 1) {
|
|
177
|
+
historyIndex++;
|
|
178
|
+
registry.set(locationAtom, historyStack[historyIndex]);
|
|
179
|
+
}
|
|
180
|
+
}),
|
|
181
|
+
go: (n) => Effect.sync(() => {
|
|
182
|
+
const newIndex = historyIndex + n;
|
|
183
|
+
if (newIndex >= 0 && newIndex < historyStack.length) {
|
|
184
|
+
historyIndex = newIndex;
|
|
185
|
+
registry.set(locationAtom, historyStack[historyIndex]);
|
|
186
|
+
}
|
|
187
|
+
}),
|
|
188
|
+
canGoBack: Effect.sync(() => historyIndex > 0),
|
|
189
|
+
};
|
|
190
|
+
return service;
|
|
191
|
+
}));
|
|
192
|
+
}
|
|
193
|
+
// =============================================================================
|
|
194
|
+
// Convenience Accessors
|
|
195
|
+
// =============================================================================
|
|
196
|
+
/**
|
|
197
|
+
* Get current location.
|
|
198
|
+
*/
|
|
199
|
+
/* is-tree-shakable-suppress */
|
|
200
|
+
export const getLocation = Effect.gen(function* () {
|
|
201
|
+
const history = yield* History;
|
|
202
|
+
return yield* Atom.get(history.location);
|
|
203
|
+
});
|
|
204
|
+
/**
|
|
205
|
+
* Push a new location.
|
|
206
|
+
*/
|
|
207
|
+
export const push = (path, state) => Effect.gen(function* () {
|
|
208
|
+
const history = yield* History;
|
|
209
|
+
yield* history.push(path, state);
|
|
210
|
+
});
|
|
211
|
+
/**
|
|
212
|
+
* Replace current location.
|
|
213
|
+
*/
|
|
214
|
+
export const replace = (path, state) => Effect.gen(function* () {
|
|
215
|
+
const history = yield* History;
|
|
216
|
+
yield* history.replace(path, state);
|
|
217
|
+
});
|
|
218
|
+
/**
|
|
219
|
+
* Go back in history.
|
|
220
|
+
*/
|
|
221
|
+
/* is-tree-shakable-suppress */
|
|
222
|
+
export const back = Effect.gen(function* () {
|
|
223
|
+
const history = yield* History;
|
|
224
|
+
yield* history.back;
|
|
225
|
+
});
|
|
226
|
+
/**
|
|
227
|
+
* Go forward in history.
|
|
228
|
+
*/
|
|
229
|
+
/* is-tree-shakable-suppress */
|
|
230
|
+
export const forward = Effect.gen(function* () {
|
|
231
|
+
const history = yield* History;
|
|
232
|
+
yield* history.forward;
|
|
233
|
+
});
|
|
234
|
+
/**
|
|
235
|
+
* Go n entries in history.
|
|
236
|
+
*/
|
|
237
|
+
export const go = (n) => Effect.gen(function* () {
|
|
238
|
+
const history = yield* History;
|
|
239
|
+
yield* history.go(n);
|
|
240
|
+
});
|
|
241
|
+
//# sourceMappingURL=History.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"History.js","sourceRoot":"","sources":["../../src/router/History.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Link component - declarative navigation.
|
|
3
|
+
*
|
|
4
|
+
* Renders an <a> element with correct href and handles click for SPA navigation.
|
|
5
|
+
* - <Link to="routeName">text</Link>
|
|
6
|
+
* - <Link to="routeName" params={{ id: 123 }}>text</Link>
|
|
7
|
+
* - <Link to="routeName" search={{ sort: "date" }}>text</Link>
|
|
8
|
+
* - <Link to="routeName" replace>text</Link>
|
|
9
|
+
*
|
|
10
|
+
* Design: href is pre-computed for SSR/accessibility. onClick prevents default
|
|
11
|
+
* and uses Navigator for SPA navigation.
|
|
12
|
+
*/
|
|
13
|
+
import * as Effect from "effect/Effect";
|
|
14
|
+
import { Registry as AtomRegistry } from "@effect-atom/atom";
|
|
15
|
+
import { Navigator } from "./Navigator.js";
|
|
16
|
+
import type { Router } from "./Router.js";
|
|
17
|
+
import type { VElement } from "../shared.js";
|
|
18
|
+
/**
|
|
19
|
+
* Props for the Link component.
|
|
20
|
+
*/
|
|
21
|
+
export interface LinkProps {
|
|
22
|
+
/** Route name to navigate to */
|
|
23
|
+
readonly to: string;
|
|
24
|
+
/** Path parameters for the route */
|
|
25
|
+
readonly params?: Record<string, unknown>;
|
|
26
|
+
/** Search/query parameters */
|
|
27
|
+
readonly search?: Record<string, unknown>;
|
|
28
|
+
/** Use history.replace instead of push */
|
|
29
|
+
readonly replace?: boolean;
|
|
30
|
+
/** Additional CSS class names */
|
|
31
|
+
readonly class?: string;
|
|
32
|
+
/** Active class name (default: "active") */
|
|
33
|
+
readonly activeClass?: string;
|
|
34
|
+
/** Data attributes for testing */
|
|
35
|
+
readonly "data-cy"?: string;
|
|
36
|
+
/** Children to render inside the anchor (already normalized by JSX runtime) */
|
|
37
|
+
readonly children?: ReadonlyArray<VElement>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Create a Link component bound to a router.
|
|
41
|
+
*
|
|
42
|
+
* The Link component must know which router to use for:
|
|
43
|
+
* - Building hrefs from route names
|
|
44
|
+
* - Checking active state
|
|
45
|
+
*
|
|
46
|
+
* Usage:
|
|
47
|
+
* ```typescript
|
|
48
|
+
* const Link = createLink(appRouter);
|
|
49
|
+
* <Link to="posts" params={{ id: 123 }}>View Post</Link>
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export declare function createLink(router: Router): (props: LinkProps) => Effect.Effect<VElement, never, Navigator | AtomRegistry.AtomRegistry>;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Link component - declarative navigation.
|
|
3
|
+
*
|
|
4
|
+
* Renders an <a> element with correct href and handles click for SPA navigation.
|
|
5
|
+
* - <Link to="routeName">text</Link>
|
|
6
|
+
* - <Link to="routeName" params={{ id: 123 }}>text</Link>
|
|
7
|
+
* - <Link to="routeName" search={{ sort: "date" }}>text</Link>
|
|
8
|
+
* - <Link to="routeName" replace>text</Link>
|
|
9
|
+
*
|
|
10
|
+
* Design: href is pre-computed for SSR/accessibility. onClick prevents default
|
|
11
|
+
* and uses Navigator for SPA navigation.
|
|
12
|
+
*/
|
|
13
|
+
import * as Effect from "effect/Effect";
|
|
14
|
+
import * as Option from "effect/Option";
|
|
15
|
+
import { Atom, Registry as AtomRegistry } from "@effect-atom/atom";
|
|
16
|
+
import { Navigator } from "./Navigator.js";
|
|
17
|
+
// =============================================================================
|
|
18
|
+
// Helpers
|
|
19
|
+
// =============================================================================
|
|
20
|
+
/**
|
|
21
|
+
* Find a route by name in the router.
|
|
22
|
+
*/
|
|
23
|
+
function findRouteByName(router, name) {
|
|
24
|
+
for (const group of router.groups) {
|
|
25
|
+
for (const route of group.routes) {
|
|
26
|
+
if (route.name === name) {
|
|
27
|
+
return Option.some(route);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return Option.none();
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Build search string from params object.
|
|
35
|
+
*/
|
|
36
|
+
function buildSearchString(params) {
|
|
37
|
+
const searchParams = new URLSearchParams();
|
|
38
|
+
for (const [key, value] of Object.entries(params)) {
|
|
39
|
+
if (value !== undefined && value !== null) {
|
|
40
|
+
searchParams.set(key, String(value));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const str = searchParams.toString();
|
|
44
|
+
return str ? `?${str}` : "";
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Build the href for a route with params.
|
|
48
|
+
*/
|
|
49
|
+
function buildHref(router, routeName, params, search, basePath = "") {
|
|
50
|
+
const route = findRouteByName(router, routeName);
|
|
51
|
+
if (Option.isNone(route)) {
|
|
52
|
+
return "#";
|
|
53
|
+
}
|
|
54
|
+
const pathname = route.value.interpolate(params ?? {});
|
|
55
|
+
const searchString = search ? buildSearchString(search) : "";
|
|
56
|
+
return `${basePath}${pathname}${searchString}`;
|
|
57
|
+
}
|
|
58
|
+
// =============================================================================
|
|
59
|
+
// Link Component Factory
|
|
60
|
+
// =============================================================================
|
|
61
|
+
/**
|
|
62
|
+
* Create a Link component bound to a router.
|
|
63
|
+
*
|
|
64
|
+
* The Link component must know which router to use for:
|
|
65
|
+
* - Building hrefs from route names
|
|
66
|
+
* - Checking active state
|
|
67
|
+
*
|
|
68
|
+
* Usage:
|
|
69
|
+
* ```typescript
|
|
70
|
+
* const Link = createLink(appRouter);
|
|
71
|
+
* <Link to="posts" params={{ id: 123 }}>View Post</Link>
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export function createLink(router) {
|
|
75
|
+
return function Link(props) {
|
|
76
|
+
return Effect.gen(function* () {
|
|
77
|
+
const navigator = yield* Navigator;
|
|
78
|
+
const currentRoute = yield* Atom.get(navigator.currentRoute);
|
|
79
|
+
// Build href for SSR/accessibility (includes basePath)
|
|
80
|
+
const href = buildHref(router, props.to, props.params, props.search, navigator.basePath);
|
|
81
|
+
// Check if this link is active
|
|
82
|
+
const isActive = Option.match(currentRoute, {
|
|
83
|
+
onNone: () => false,
|
|
84
|
+
onSome: (route) => {
|
|
85
|
+
if (route.routeName !== props.to) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
// If params provided, check they match
|
|
89
|
+
if (props.params) {
|
|
90
|
+
for (const [key, value] of Object.entries(props.params)) {
|
|
91
|
+
if (route.params[key] !== value) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return true;
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
// Build class string
|
|
100
|
+
const activeClass = props.activeClass ?? "active";
|
|
101
|
+
const classes = [props.class, isActive ? activeClass : null]
|
|
102
|
+
.filter(Boolean)
|
|
103
|
+
.join(" ");
|
|
104
|
+
// Click handler - prevent default and use Navigator
|
|
105
|
+
const handleClick = (e) => {
|
|
106
|
+
// Allow ctrl/cmd click for new tab
|
|
107
|
+
if (e.ctrlKey || e.metaKey || e.shiftKey) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
e.preventDefault();
|
|
111
|
+
return navigator.go(props.to, {
|
|
112
|
+
path: props.params,
|
|
113
|
+
searchParams: props.search,
|
|
114
|
+
replace: props.replace,
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
// Return VElement directly - children are already normalized by JSX runtime
|
|
118
|
+
return {
|
|
119
|
+
type: "a",
|
|
120
|
+
props: {
|
|
121
|
+
href,
|
|
122
|
+
class: classes || undefined,
|
|
123
|
+
"data-cy": props["data-cy"],
|
|
124
|
+
onClick: handleClick,
|
|
125
|
+
children: [...(props.children ?? [])],
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=Link.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Link.js","sourceRoot":"","sources":["../../src/router/Link.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAiC3C,gFAAgF;AAChF,UAAU;AACV,gFAAgF;AAEhF;;GAEG;AACH,SAAS,eAAe,CAAC,MAAc,EAAE,IAAY;IACnD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClC,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACjC,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;gBACxB,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;AACvB,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,MAA+B;IACxD,MAAM,YAAY,GAAG,IAAI,eAAe,EAAE,CAAC;IAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YAC1C,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IACD,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,CAAC;IACpC,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9B,CAAC;AAED;;GAEG;AACH,SAAS,SAAS,CAChB,MAAc,EACd,SAAiB,EACjB,MAAgC,EAChC,MAAgC,EAChC,WAAmB,EAAE;IAErB,MAAM,KAAK,GAAG,eAAe,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACjD,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,GAAG,CAAC;IACb,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IACvD,MAAM,YAAY,GAAG,MAAM,CAAC,CAAC,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7D,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,YAAY,EAAE,CAAC;AACjD,CAAC;AAED,gFAAgF;AAChF,yBAAyB;AACzB,gFAAgF;AAEhF;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,OAAO,SAAS,IAAI,CAAC,KAAgB;QACnC,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACzB,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC;YACnC,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;YAE7D,uDAAuD;YACvD,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC;YAEzF,+BAA+B;YAC/B,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,YAAY,EAAE;gBAC1C,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK;gBACnB,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;oBAChB,IAAI,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,EAAE,EAAE,CAAC;wBACjC,OAAO,KAAK,CAAC;oBACf,CAAC;oBACD,uCAAuC;oBACvC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;wBACjB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;4BACxD,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,KAAK,EAAE,CAAC;gCAChC,OAAO,KAAK,CAAC;4BACf,CAAC;wBACH,CAAC;oBACH,CAAC;oBACD,OAAO,IAAI,CAAC;gBACd,CAAC;aACF,CAAC,CAAC;YAEH,qBAAqB;YACrB,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,IAAI,QAAQ,CAAC;YAClD,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;iBACzD,MAAM,CAAC,OAAO,CAAC;iBACf,IAAI,CAAC,GAAG,CAAC,CAAC;YAEb,oDAAoD;YACpD,MAAM,WAAW,GAAG,CAAC,CAAa,EAAE,EAAE;gBACpC,mCAAmC;gBACnC,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;oBACzC,OAAO;gBACT,CAAC;gBAED,CAAC,CAAC,cAAc,EAAE,CAAC;gBACnB,OAAO,SAAS,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE;oBAC5B,IAAI,EAAE,KAAK,CAAC,MAAM;oBAClB,YAAY,EAAE,KAAK,CAAC,MAAM;oBAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;iBACvB,CAAC,CAAC;YACL,CAAC,CAAC;YAEF,4EAA4E;YAC5E,OAAO;gBACL,IAAI,EAAE,GAAG;gBACT,KAAK,EAAE;oBACL,IAAI;oBACJ,KAAK,EAAE,OAAO,IAAI,SAAS;oBAC3B,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC;oBAC3B,OAAO,EAAE,WAAW;oBACpB,QAAQ,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;iBACtC;aACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navigator service - type-safe route-aware navigation.
|
|
3
|
+
*
|
|
4
|
+
* Provides route-aware navigation on top of History:
|
|
5
|
+
* - nav.go("routeName", { path: {...}, searchParams: {...} })
|
|
6
|
+
* - nav.back, nav.forward
|
|
7
|
+
* - nav.isActive("routeName", params) for active link detection
|
|
8
|
+
* - currentRoute Atom reflects matched route info
|
|
9
|
+
*
|
|
10
|
+
* Design: Navigator uses History internally but provides route-aware API.
|
|
11
|
+
* It knows about routes and can build URLs from route names.
|
|
12
|
+
*/
|
|
13
|
+
import * as Effect from "effect/Effect";
|
|
14
|
+
import * as Context from "effect/Context";
|
|
15
|
+
import * as Layer from "effect/Layer";
|
|
16
|
+
import * as Option from "effect/Option";
|
|
17
|
+
import { Atom, Registry as AtomRegistry } from "@effect-atom/atom";
|
|
18
|
+
import { History } from "./History.js";
|
|
19
|
+
import type { Router } from "./Router.js";
|
|
20
|
+
/**
|
|
21
|
+
* Current route info - what route is currently matched.
|
|
22
|
+
*/
|
|
23
|
+
export interface CurrentRoute {
|
|
24
|
+
readonly routeName: string;
|
|
25
|
+
readonly params: Record<string, unknown>;
|
|
26
|
+
readonly searchParams: Record<string, string>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Navigation options for go().
|
|
30
|
+
*/
|
|
31
|
+
export interface NavigateOptions<PathParams extends Record<string, unknown> = Record<string, unknown>, SearchParams extends Record<string, unknown> = Record<string, unknown>> {
|
|
32
|
+
readonly path?: PathParams;
|
|
33
|
+
readonly searchParams?: SearchParams;
|
|
34
|
+
readonly replace?: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Navigator service interface.
|
|
38
|
+
* Provides type-safe navigation by route name.
|
|
39
|
+
*/
|
|
40
|
+
export interface NavigatorService {
|
|
41
|
+
/**
|
|
42
|
+
* Base path prefix for all routes (e.g., "/ssr/router").
|
|
43
|
+
* Used for apps mounted at non-root paths.
|
|
44
|
+
*/
|
|
45
|
+
readonly basePath: string;
|
|
46
|
+
/**
|
|
47
|
+
* Current matched route info - updates on navigation.
|
|
48
|
+
*/
|
|
49
|
+
readonly currentRoute: Atom.Writable<Option.Option<CurrentRoute>, Option.Option<CurrentRoute>>;
|
|
50
|
+
/**
|
|
51
|
+
* Navigate to a route by name with optional params.
|
|
52
|
+
*/
|
|
53
|
+
readonly go: (routeName: string, options?: NavigateOptions) => Effect.Effect<void, never, AtomRegistry.AtomRegistry>;
|
|
54
|
+
/**
|
|
55
|
+
* Go back in history.
|
|
56
|
+
*/
|
|
57
|
+
readonly back: Effect.Effect<void, never, AtomRegistry.AtomRegistry>;
|
|
58
|
+
/**
|
|
59
|
+
* Go forward in history.
|
|
60
|
+
*/
|
|
61
|
+
readonly forward: Effect.Effect<void, never, AtomRegistry.AtomRegistry>;
|
|
62
|
+
/**
|
|
63
|
+
* Check if a route is currently active.
|
|
64
|
+
* Optionally match specific params.
|
|
65
|
+
*/
|
|
66
|
+
readonly isActive: (routeName: string, params?: Record<string, unknown>) => Effect.Effect<boolean, never, AtomRegistry.AtomRegistry>;
|
|
67
|
+
}
|
|
68
|
+
declare const Navigator_base: Context.TagClass<Navigator, "fibrae/Navigator", NavigatorService>;
|
|
69
|
+
/**
|
|
70
|
+
* Navigator service tag for Effect dependency injection.
|
|
71
|
+
*/
|
|
72
|
+
export declare class Navigator extends Navigator_base {
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Options for creating a Navigator layer.
|
|
76
|
+
*/
|
|
77
|
+
export interface NavigatorOptions {
|
|
78
|
+
/** Base path prefix for all routes (e.g., "/ssr/router") */
|
|
79
|
+
readonly basePath?: string;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Create a Navigator layer for the given router.
|
|
83
|
+
*
|
|
84
|
+
* Features:
|
|
85
|
+
* - Type-safe navigation by route name
|
|
86
|
+
* - Automatic URL building via route.interpolate
|
|
87
|
+
* - Tracks current matched route in an Atom
|
|
88
|
+
* - Delegates to History for actual navigation
|
|
89
|
+
* - Supports basePath for apps mounted at non-root paths
|
|
90
|
+
*/
|
|
91
|
+
export declare function NavigatorLive(router: Router, options?: NavigatorOptions): Layer.Layer<Navigator, never, History | AtomRegistry.AtomRegistry>;
|
|
92
|
+
/**
|
|
93
|
+
* Navigate to a route by name.
|
|
94
|
+
*/
|
|
95
|
+
export declare const go: (routeName: string, options?: NavigateOptions) => Effect.Effect<void, never, Navigator | AtomRegistry.AtomRegistry>;
|
|
96
|
+
/**
|
|
97
|
+
* Go back in history.
|
|
98
|
+
*/
|
|
99
|
+
export declare const back: Effect.Effect<void, never, Navigator | AtomRegistry.AtomRegistry>;
|
|
100
|
+
/**
|
|
101
|
+
* Go forward in history.
|
|
102
|
+
*/
|
|
103
|
+
export declare const forward: Effect.Effect<void, never, Navigator | AtomRegistry.AtomRegistry>;
|
|
104
|
+
/**
|
|
105
|
+
* Get current route info.
|
|
106
|
+
*/
|
|
107
|
+
export declare const getCurrentRoute: Effect.Effect<Option.Option<CurrentRoute>, never, Navigator | AtomRegistry.AtomRegistry>;
|
|
108
|
+
export {};
|