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,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RouterOutlet - Reactive route rendering component.
|
|
3
|
+
*
|
|
4
|
+
* Subscribes to Navigator.currentRoute and renders the matched route's component.
|
|
5
|
+
* When route changes, runs the new route's loader and renders the component.
|
|
6
|
+
*
|
|
7
|
+
* For SSR hydration, the RouterStateAtom is pre-populated and the loader is skipped
|
|
8
|
+
* on first render.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```typescript
|
|
12
|
+
* function App() {
|
|
13
|
+
* return (
|
|
14
|
+
* <div>
|
|
15
|
+
* <Nav />
|
|
16
|
+
* <RouterOutlet />
|
|
17
|
+
* </div>
|
|
18
|
+
* );
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
import * as Effect from "effect/Effect";
|
|
23
|
+
import * as Stream from "effect/Stream";
|
|
24
|
+
import * as Option from "effect/Option";
|
|
25
|
+
import { Registry as AtomRegistry } from "@effect-atom/atom";
|
|
26
|
+
import { Navigator } from "./Navigator.js";
|
|
27
|
+
import { RouterHandlers } from "./RouterBuilder.js";
|
|
28
|
+
import { RouterStateAtom } from "./RouterState.js";
|
|
29
|
+
// =============================================================================
|
|
30
|
+
// RouterOutlet Component
|
|
31
|
+
// =============================================================================
|
|
32
|
+
/**
|
|
33
|
+
* RouterOutlet component for reactive route rendering.
|
|
34
|
+
*
|
|
35
|
+
* The RouterOutlet:
|
|
36
|
+
* 1. Checks if RouterStateAtom has data (SSR hydration case)
|
|
37
|
+
* 2. Subscribes to Navigator.currentRoute for navigation changes
|
|
38
|
+
* 3. When route changes, runs the matched route's loader
|
|
39
|
+
* 4. Updates RouterStateAtom with the full state (for DI access)
|
|
40
|
+
* 5. Renders the route's component with loader data
|
|
41
|
+
*
|
|
42
|
+
* For SSR hydration, the RouterStateAtom is pre-populated by the server,
|
|
43
|
+
* so the first render uses that data and skips the loader.
|
|
44
|
+
*/
|
|
45
|
+
export function RouterOutlet(_props = {}) {
|
|
46
|
+
// Track if this is the first render (for SSR hydration)
|
|
47
|
+
let isFirstRender = true;
|
|
48
|
+
return Stream.unwrap(Effect.gen(function* () {
|
|
49
|
+
const navigator = yield* Navigator;
|
|
50
|
+
const routerHandlers = yield* RouterHandlers;
|
|
51
|
+
const registry = yield* AtomRegistry.AtomRegistry;
|
|
52
|
+
// Check if we have hydrated state from SSR
|
|
53
|
+
const hydratedState = registry.get(RouterStateAtom);
|
|
54
|
+
// Create a stream from the currentRoute atom (navigation trigger)
|
|
55
|
+
const routeStream = AtomRegistry.toStream(registry, navigator.currentRoute);
|
|
56
|
+
// Map route changes to rendered VElements
|
|
57
|
+
return Stream.mapEffect(routeStream, (currentRoute) => Effect.gen(function* () {
|
|
58
|
+
if (Option.isNone(currentRoute)) {
|
|
59
|
+
// No route matched - clear router state and render 404
|
|
60
|
+
registry.set(RouterStateAtom, Option.none());
|
|
61
|
+
return {
|
|
62
|
+
type: "div",
|
|
63
|
+
props: {
|
|
64
|
+
children: [
|
|
65
|
+
{
|
|
66
|
+
type: "TEXT_ELEMENT",
|
|
67
|
+
props: { nodeValue: "404 - Not Found", children: [] },
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
const { routeName, params, searchParams } = currentRoute.value;
|
|
74
|
+
// Get handler for this route
|
|
75
|
+
const handler = routerHandlers.getHandler(routeName);
|
|
76
|
+
if (Option.isNone(handler)) {
|
|
77
|
+
registry.set(RouterStateAtom, Option.none());
|
|
78
|
+
return {
|
|
79
|
+
type: "div",
|
|
80
|
+
props: {
|
|
81
|
+
children: [
|
|
82
|
+
{
|
|
83
|
+
type: "TEXT_ELEMENT",
|
|
84
|
+
props: {
|
|
85
|
+
nodeValue: `No handler for route: ${routeName}`,
|
|
86
|
+
children: [],
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
let loaderData;
|
|
94
|
+
let routerState;
|
|
95
|
+
// Check if we should use hydrated SSR state
|
|
96
|
+
const shouldUseHydratedState = isFirstRender &&
|
|
97
|
+
Option.isSome(hydratedState) &&
|
|
98
|
+
hydratedState.value.routeName === routeName;
|
|
99
|
+
if (shouldUseHydratedState) {
|
|
100
|
+
// Use SSR-hydrated state
|
|
101
|
+
routerState = hydratedState.value;
|
|
102
|
+
loaderData = routerState.loaderData;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
// Run the loader
|
|
106
|
+
const loaderCtx = { path: params, searchParams };
|
|
107
|
+
loaderData = yield* handler.value.loader(loaderCtx);
|
|
108
|
+
// Build the new router state
|
|
109
|
+
routerState = {
|
|
110
|
+
routeName,
|
|
111
|
+
params,
|
|
112
|
+
searchParams,
|
|
113
|
+
loaderData,
|
|
114
|
+
};
|
|
115
|
+
// Update RouterStateAtom (for DI access by other components)
|
|
116
|
+
registry.set(RouterStateAtom, Option.some(routerState));
|
|
117
|
+
}
|
|
118
|
+
// Mark first render complete
|
|
119
|
+
isFirstRender = false;
|
|
120
|
+
// Render the component with both props patterns:
|
|
121
|
+
// 1. Traditional props (loaderData, path, searchParams)
|
|
122
|
+
// 2. Components can also access via RouterStateAtom/RouterStateService
|
|
123
|
+
const element = handler.value.component({
|
|
124
|
+
loaderData,
|
|
125
|
+
path: params,
|
|
126
|
+
searchParams,
|
|
127
|
+
});
|
|
128
|
+
return element;
|
|
129
|
+
}));
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=RouterOutlet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RouterOutlet.js","sourceRoot":"","sources":["../../src/router/RouterOutlet.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RouterState - Unified serializable state for the router.
|
|
3
|
+
*
|
|
4
|
+
* Contains everything needed to render the current route:
|
|
5
|
+
* - routeName: The matched route's name
|
|
6
|
+
* - params: Decoded path parameters
|
|
7
|
+
* - searchParams: Query string parameters
|
|
8
|
+
* - loaderData: Data returned by the route's loader
|
|
9
|
+
*
|
|
10
|
+
* This atom is:
|
|
11
|
+
* - Serializable for SSR hydration
|
|
12
|
+
* - Updated on navigation
|
|
13
|
+
* - Accessible via Effect DI
|
|
14
|
+
*/
|
|
15
|
+
import * as Schema from "effect/Schema";
|
|
16
|
+
import * as Option from "effect/Option";
|
|
17
|
+
import * as Context from "effect/Context";
|
|
18
|
+
import * as Effect from "effect/Effect";
|
|
19
|
+
import { Atom, Registry as AtomRegistry } from "@effect-atom/atom";
|
|
20
|
+
/**
|
|
21
|
+
* Complete router state including loader data.
|
|
22
|
+
* This is what gets serialized for SSR and hydrated on client.
|
|
23
|
+
*/
|
|
24
|
+
export interface RouterState {
|
|
25
|
+
readonly routeName: string;
|
|
26
|
+
readonly params: Record<string, unknown>;
|
|
27
|
+
readonly searchParams: Record<string, string>;
|
|
28
|
+
readonly loaderData: unknown;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Schema for RouterState - used for serialization.
|
|
32
|
+
*
|
|
33
|
+
* Note: loaderData uses Schema.Unknown since the actual type
|
|
34
|
+
* is inferred from the loader Effect return type.
|
|
35
|
+
*/
|
|
36
|
+
export declare const RouterStateSchema: Schema.Struct<{
|
|
37
|
+
routeName: typeof Schema.String;
|
|
38
|
+
params: Schema.Record$<typeof Schema.String, typeof Schema.Unknown>;
|
|
39
|
+
searchParams: Schema.Record$<typeof Schema.String, typeof Schema.String>;
|
|
40
|
+
loaderData: typeof Schema.Unknown;
|
|
41
|
+
}>;
|
|
42
|
+
/**
|
|
43
|
+
* The router state atom - a serializable atom containing the full route state.
|
|
44
|
+
*
|
|
45
|
+
* When None, no route is matched.
|
|
46
|
+
* When Some, contains the matched route info and loader data.
|
|
47
|
+
*
|
|
48
|
+
* This atom is automatically:
|
|
49
|
+
* - Dehydrated during SSR (included in __FIBRAE_STATE__)
|
|
50
|
+
* - Hydrated on client (restored from __FIBRAE_STATE__)
|
|
51
|
+
*/
|
|
52
|
+
export declare const RouterStateAtom: Atom.Writable<Option.Option<RouterState>, Option.Option<RouterState>> & Atom.Serializable<Schema.Option<Schema.Struct<{
|
|
53
|
+
routeName: typeof Schema.String;
|
|
54
|
+
params: Schema.Record$<typeof Schema.String, typeof Schema.Unknown>;
|
|
55
|
+
searchParams: Schema.Record$<typeof Schema.String, typeof Schema.String>;
|
|
56
|
+
loaderData: typeof Schema.Unknown;
|
|
57
|
+
}>>>;
|
|
58
|
+
declare const RouterStateService_base: Context.TagClass<RouterStateService, "@fibrae/router/RouterStateService", {
|
|
59
|
+
/**
|
|
60
|
+
* Get the current router state.
|
|
61
|
+
*/
|
|
62
|
+
readonly get: Effect.Effect<Option.Option<RouterState>, never, AtomRegistry.AtomRegistry>;
|
|
63
|
+
/**
|
|
64
|
+
* Set the router state (used internally by Navigator).
|
|
65
|
+
*/
|
|
66
|
+
readonly set: (state: Option.Option<RouterState>) => Effect.Effect<void, never, AtomRegistry.AtomRegistry>;
|
|
67
|
+
/**
|
|
68
|
+
* The underlying atom (for reactive subscriptions).
|
|
69
|
+
*/
|
|
70
|
+
readonly atom: Atom.Writable<Option.Option<RouterState>, Option.Option<RouterState>>;
|
|
71
|
+
}>;
|
|
72
|
+
/**
|
|
73
|
+
* Service tag for accessing router state via Effect DI.
|
|
74
|
+
*
|
|
75
|
+
* Usage in components:
|
|
76
|
+
* ```typescript
|
|
77
|
+
* const Component = () => Effect.gen(function* () {
|
|
78
|
+
* const state = yield* RouterStateService;
|
|
79
|
+
* if (Option.isSome(state)) {
|
|
80
|
+
* const { loaderData, params } = state.value;
|
|
81
|
+
* // ...
|
|
82
|
+
* }
|
|
83
|
+
* });
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export declare class RouterStateService extends RouterStateService_base {
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get the current router state.
|
|
90
|
+
*/
|
|
91
|
+
export declare const getRouterState: Effect.Effect<Option.Option<RouterState>, never, RouterStateService | AtomRegistry.AtomRegistry>;
|
|
92
|
+
/**
|
|
93
|
+
* Get the current loader data (typed by the caller).
|
|
94
|
+
* Returns None if no route is matched.
|
|
95
|
+
*/
|
|
96
|
+
export declare const getLoaderData: <T>() => Effect.Effect<Option.Option<T>, never, RouterStateService | AtomRegistry.AtomRegistry>;
|
|
97
|
+
/**
|
|
98
|
+
* Get the current route params.
|
|
99
|
+
* Returns None if no route is matched.
|
|
100
|
+
*/
|
|
101
|
+
export declare const getRouteParams: Effect.Effect<Option.Option<Record<string, unknown>>, never, RouterStateService | AtomRegistry.AtomRegistry>;
|
|
102
|
+
export {};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RouterState - Unified serializable state for the router.
|
|
3
|
+
*
|
|
4
|
+
* Contains everything needed to render the current route:
|
|
5
|
+
* - routeName: The matched route's name
|
|
6
|
+
* - params: Decoded path parameters
|
|
7
|
+
* - searchParams: Query string parameters
|
|
8
|
+
* - loaderData: Data returned by the route's loader
|
|
9
|
+
*
|
|
10
|
+
* This atom is:
|
|
11
|
+
* - Serializable for SSR hydration
|
|
12
|
+
* - Updated on navigation
|
|
13
|
+
* - Accessible via Effect DI
|
|
14
|
+
*/
|
|
15
|
+
import * as Schema from "effect/Schema";
|
|
16
|
+
import * as Option from "effect/Option";
|
|
17
|
+
import * as Context from "effect/Context";
|
|
18
|
+
import * as Effect from "effect/Effect";
|
|
19
|
+
import { Atom, Registry as AtomRegistry } from "@effect-atom/atom";
|
|
20
|
+
/**
|
|
21
|
+
* Schema for RouterState - used for serialization.
|
|
22
|
+
*
|
|
23
|
+
* Note: loaderData uses Schema.Unknown since the actual type
|
|
24
|
+
* is inferred from the loader Effect return type.
|
|
25
|
+
*/
|
|
26
|
+
export const RouterStateSchema = Schema.Struct({
|
|
27
|
+
routeName: Schema.String,
|
|
28
|
+
params: Schema.Record({ key: Schema.String, value: Schema.Unknown }),
|
|
29
|
+
searchParams: Schema.Record({ key: Schema.String, value: Schema.String }),
|
|
30
|
+
loaderData: Schema.Unknown,
|
|
31
|
+
});
|
|
32
|
+
// =============================================================================
|
|
33
|
+
// Serializable Atom
|
|
34
|
+
// =============================================================================
|
|
35
|
+
/**
|
|
36
|
+
* The router state atom - a serializable atom containing the full route state.
|
|
37
|
+
*
|
|
38
|
+
* When None, no route is matched.
|
|
39
|
+
* When Some, contains the matched route info and loader data.
|
|
40
|
+
*
|
|
41
|
+
* This atom is automatically:
|
|
42
|
+
* - Dehydrated during SSR (included in __FIBRAE_STATE__)
|
|
43
|
+
* - Hydrated on client (restored from __FIBRAE_STATE__)
|
|
44
|
+
*/
|
|
45
|
+
export const RouterStateAtom = Atom.make(Option.none()).pipe(Atom.serializable({
|
|
46
|
+
key: "@fibrae/router/state",
|
|
47
|
+
schema: Schema.Option(RouterStateSchema),
|
|
48
|
+
}));
|
|
49
|
+
// =============================================================================
|
|
50
|
+
// Service Tag
|
|
51
|
+
// =============================================================================
|
|
52
|
+
/**
|
|
53
|
+
* Service tag for accessing router state via Effect DI.
|
|
54
|
+
*
|
|
55
|
+
* Usage in components:
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const Component = () => Effect.gen(function* () {
|
|
58
|
+
* const state = yield* RouterStateService;
|
|
59
|
+
* if (Option.isSome(state)) {
|
|
60
|
+
* const { loaderData, params } = state.value;
|
|
61
|
+
* // ...
|
|
62
|
+
* }
|
|
63
|
+
* });
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export class RouterStateService extends Context.Tag("@fibrae/router/RouterStateService")() {
|
|
67
|
+
}
|
|
68
|
+
// =============================================================================
|
|
69
|
+
// Convenience Accessors
|
|
70
|
+
// =============================================================================
|
|
71
|
+
/**
|
|
72
|
+
* Get the current router state.
|
|
73
|
+
*/
|
|
74
|
+
export const getRouterState = Effect.gen(function* () {
|
|
75
|
+
const service = yield* RouterStateService;
|
|
76
|
+
return yield* service.get;
|
|
77
|
+
});
|
|
78
|
+
/**
|
|
79
|
+
* Get the current loader data (typed by the caller).
|
|
80
|
+
* Returns None if no route is matched.
|
|
81
|
+
*/
|
|
82
|
+
export const getLoaderData = () => Effect.gen(function* () {
|
|
83
|
+
const state = yield* getRouterState;
|
|
84
|
+
return Option.map(state, (s) => s.loaderData);
|
|
85
|
+
});
|
|
86
|
+
/**
|
|
87
|
+
* Get the current route params.
|
|
88
|
+
* Returns None if no route is matched.
|
|
89
|
+
*/
|
|
90
|
+
export const getRouteParams = Effect.gen(function* () {
|
|
91
|
+
const state = yield* getRouterState;
|
|
92
|
+
return Option.map(state, (s) => s.params);
|
|
93
|
+
});
|
|
94
|
+
//# sourceMappingURL=RouterState.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RouterState.js","sourceRoot":"","sources":["../../src/router/RouterState.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAiBnE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAC;IAC7C,SAAS,EAAE,MAAM,CAAC,MAAM;IACxB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;IACpE,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;IACzE,UAAU,EAAE,MAAM,CAAC,OAAO;CAC3B,CAAC,CAAC;AAEH,gFAAgF;AAChF,oBAAoB;AACpB,gFAAgF;AAEhF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CACtC,MAAM,CAAC,IAAI,EAAE,CACd,CAAC,IAAI,CACJ,IAAI,CAAC,YAAY,CAAC;IAChB,GAAG,EAAE,sBAAsB;IAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC;CACzC,CAAC,CACH,CAAC;AAEF,gFAAgF;AAChF,cAAc;AACd,gFAAgF;AAEhF;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,kBAAmB,SAAQ,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,EAkBrF;CAAG;AAEN,gFAAgF;AAChF,wBAAwB;AACxB,gFAAgF;AAEhF;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAIvB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IACtB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,kBAAkB,CAAC;IAC1C,OAAO,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC;AAC5B,CAAC,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,GAI3B,EAAE,CACF,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC;IACpC,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAe,CAAC,CAAC;AACrD,CAAC,CAAC,CAAC;AAEL;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAIvB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IACtB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC;IACpC,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAC5C,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Router module exports.
|
|
3
|
+
*
|
|
4
|
+
* Provides Effect-first routing with the HttpApi pattern:
|
|
5
|
+
* - Route declarations with schema-validated params
|
|
6
|
+
* - RouterBuilder for handler implementation
|
|
7
|
+
* - History service for navigation
|
|
8
|
+
* - Type-safe navigation
|
|
9
|
+
*/
|
|
10
|
+
export * as Route from "./Route.js";
|
|
11
|
+
export * as Router from "./Router.js";
|
|
12
|
+
export * as RouterBuilder from "./RouterBuilder.js";
|
|
13
|
+
export * as History from "./History.js";
|
|
14
|
+
export * as Navigator from "./Navigator.js";
|
|
15
|
+
export * as RouterState from "./RouterState.js";
|
|
16
|
+
export type { LoaderContext, ComponentProps, HandlerConfig, RouteHandler, GroupHandlers, } from "./RouterBuilder.js";
|
|
17
|
+
export { RouterHandlers } from "./RouterBuilder.js";
|
|
18
|
+
export type { HistoryLocation, HistoryService } from "./History.js";
|
|
19
|
+
export { History as HistoryTag, BrowserHistoryLive, MemoryHistoryLive } from "./History.js";
|
|
20
|
+
export type { CurrentRoute, NavigateOptions, NavigatorService } from "./Navigator.js";
|
|
21
|
+
export { Navigator as NavigatorTag, NavigatorLive } from "./Navigator.js";
|
|
22
|
+
export type { LinkProps } from "./Link.js";
|
|
23
|
+
export { createLink } from "./Link.js";
|
|
24
|
+
export type { RouterOutletProps } from "./RouterOutlet.js";
|
|
25
|
+
export { RouterOutlet } from "./RouterOutlet.js";
|
|
26
|
+
export type { ServerLayerOptions, BrowserLayerOptions, DehydratedRouterState, SSRRouteResult, } from "./Router.js";
|
|
27
|
+
export { CurrentRouteElement } from "./Router.js";
|
|
28
|
+
export { RouterStateAtom, RouterStateService, RouterStateSchema, getRouterState, getLoaderData, getRouteParams, } from "./RouterState.js";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Router module exports.
|
|
3
|
+
*
|
|
4
|
+
* Provides Effect-first routing with the HttpApi pattern:
|
|
5
|
+
* - Route declarations with schema-validated params
|
|
6
|
+
* - RouterBuilder for handler implementation
|
|
7
|
+
* - History service for navigation
|
|
8
|
+
* - Type-safe navigation
|
|
9
|
+
*/
|
|
10
|
+
// Route declaration
|
|
11
|
+
export * as Route from "./Route.js";
|
|
12
|
+
// Router and groups
|
|
13
|
+
export * as Router from "./Router.js";
|
|
14
|
+
// Handler implementation
|
|
15
|
+
export * as RouterBuilder from "./RouterBuilder.js";
|
|
16
|
+
// History service
|
|
17
|
+
export * as History from "./History.js";
|
|
18
|
+
// Navigator service
|
|
19
|
+
export * as Navigator from "./Navigator.js";
|
|
20
|
+
// Router state (unified serializable state)
|
|
21
|
+
export * as RouterState from "./RouterState.js";
|
|
22
|
+
export { RouterHandlers } from "./RouterBuilder.js";
|
|
23
|
+
export { History as HistoryTag, BrowserHistoryLive, MemoryHistoryLive } from "./History.js";
|
|
24
|
+
export { Navigator as NavigatorTag, NavigatorLive } from "./Navigator.js";
|
|
25
|
+
export { createLink } from "./Link.js";
|
|
26
|
+
export { RouterOutlet } from "./RouterOutlet.js";
|
|
27
|
+
export { CurrentRouteElement } from "./Router.js";
|
|
28
|
+
// Re-export RouterState utilities for convenience
|
|
29
|
+
// (RouterState type is accessible via RouterState.RouterState namespace)
|
|
30
|
+
export { RouterStateAtom, RouterStateService, RouterStateSchema, getRouterState, getLoaderData, getRouteParams, } from "./RouterState.js";
|
|
31
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/router/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,oBAAoB;AACpB,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AAEpC,oBAAoB;AACpB,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAEtC,yBAAyB;AACzB,OAAO,KAAK,aAAa,MAAM,oBAAoB,CAAC;AAEpD,kBAAkB;AAClB,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAExC,oBAAoB;AACpB,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAC;AAE5C,4CAA4C;AAC5C,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAWhD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIpD,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAI5F,OAAO,EAAE,SAAS,IAAI,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAI1E,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAIvC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AASjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,kDAAkD;AAClD,yEAAyE;AACzE,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,cAAc,GACf,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Scope from "effect/Scope";
|
|
3
|
+
import * as Layer from "effect/Layer";
|
|
4
|
+
import * as Ref from "effect/Ref";
|
|
5
|
+
import * as Option from "effect/Option";
|
|
6
|
+
import * as Context from "effect/Context";
|
|
7
|
+
import { Atom, Registry as AtomRegistry } from "@effect-atom/atom";
|
|
8
|
+
import type { Fiber } from "./shared.js";
|
|
9
|
+
export interface FiberState {
|
|
10
|
+
currentRoot: Option.Option<Fiber>;
|
|
11
|
+
wipRoot: Option.Option<Fiber>;
|
|
12
|
+
nextUnitOfWork: Option.Option<Fiber>;
|
|
13
|
+
deletions: Fiber[];
|
|
14
|
+
renderQueue: Set<Fiber>;
|
|
15
|
+
batchScheduled: boolean;
|
|
16
|
+
listenerStore: WeakMap<HTMLElement, Record<string, EventListener>>;
|
|
17
|
+
}
|
|
18
|
+
export declare const makeFiberState: () => FiberState;
|
|
19
|
+
export declare const CustomAtomRegistryLayer: Layer.Layer<AtomRegistry.AtomRegistry, never, never>;
|
|
20
|
+
declare const FibraeRuntime_base: Effect.Service.Class<FibraeRuntime, "FibraeRuntime", {
|
|
21
|
+
readonly accessors: true;
|
|
22
|
+
readonly dependencies: readonly [Layer.Layer<AtomRegistry.AtomRegistry, never, never>];
|
|
23
|
+
readonly scoped: Effect.Effect<{
|
|
24
|
+
registry: AtomRegistry.Registry;
|
|
25
|
+
rootScope: Scope.CloseableScope;
|
|
26
|
+
runFork: <XE extends unknown, XA extends unknown>(effect: Effect.Effect<XA, XE, AtomRegistry.AtomRegistry>, options?: import("effect/Runtime").RunForkOptions | undefined) => import("effect/Fiber").RuntimeFiber<XA, XE>;
|
|
27
|
+
AtomOps: {
|
|
28
|
+
get: <A>(atom: Atom.Atom<A>) => A;
|
|
29
|
+
set: <R, W>(atom: Atom.Writable<R, W>, value: W) => void;
|
|
30
|
+
update: <R, W>(atom: Atom.Writable<R, W>, f: (_: R) => W) => void;
|
|
31
|
+
modify: <R, W, A>(atom: Atom.Writable<R, W>, f: (_: R) => [returnValue: A, nextValue: W]) => A;
|
|
32
|
+
};
|
|
33
|
+
fiberState: Ref.Ref<FiberState>;
|
|
34
|
+
fullContextRef: Ref.Ref<Context.Context<unknown>>;
|
|
35
|
+
}, never, Scope.Scope | AtomRegistry.AtomRegistry>;
|
|
36
|
+
}>;
|
|
37
|
+
export declare class FibraeRuntime extends FibraeRuntime_base {
|
|
38
|
+
static Live: Layer.Layer<FibraeRuntime, never, never>;
|
|
39
|
+
/**
|
|
40
|
+
* Layer that provides both FibraeRuntime AND AtomRegistry.
|
|
41
|
+
* Use this when composing with user layers that need AtomRegistry access.
|
|
42
|
+
* (FibraeRuntime.Default consumes AtomRegistry internally but doesn't re-export it)
|
|
43
|
+
*/
|
|
44
|
+
static LiveWithRegistry: Layer.Layer<AtomRegistry.AtomRegistry | FibraeRuntime, never, never>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Fork an effect with the full application context.
|
|
48
|
+
*
|
|
49
|
+
* The fullContextRef contains ALL services (FibraeRuntime, AtomRegistry, Navigator, etc.)
|
|
50
|
+
* captured at render() time after all layers are built.
|
|
51
|
+
*
|
|
52
|
+
* IMPORTANT: fullContextRef must be set by render() before this is called.
|
|
53
|
+
*/
|
|
54
|
+
export declare const runForkWithRuntime: (runtime: FibraeRuntime) => <A, E>(effect: Effect.Effect<A, E, unknown>) => import("effect/Fiber").RuntimeFiber<unknown, unknown>;
|
|
55
|
+
export {};
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Scope from "effect/Scope";
|
|
3
|
+
import * as FiberSet from "effect/FiberSet";
|
|
4
|
+
import * as Layer from "effect/Layer";
|
|
5
|
+
import * as Ref from "effect/Ref";
|
|
6
|
+
import * as Option from "effect/Option";
|
|
7
|
+
import * as Context from "effect/Context";
|
|
8
|
+
import { Atom, Registry as AtomRegistry } from "@effect-atom/atom";
|
|
9
|
+
export const makeFiberState = () => ({
|
|
10
|
+
currentRoot: Option.none(),
|
|
11
|
+
wipRoot: Option.none(),
|
|
12
|
+
nextUnitOfWork: Option.none(),
|
|
13
|
+
deletions: [],
|
|
14
|
+
renderQueue: new Set(),
|
|
15
|
+
batchScheduled: false,
|
|
16
|
+
listenerStore: new WeakMap(),
|
|
17
|
+
});
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// Runtime Service
|
|
20
|
+
// =============================================================================
|
|
21
|
+
export const CustomAtomRegistryLayer = AtomRegistry.layerOptions({
|
|
22
|
+
scheduleTask: (f) => f()
|
|
23
|
+
});
|
|
24
|
+
export class FibraeRuntime extends Effect.Service()("FibraeRuntime", {
|
|
25
|
+
accessors: true,
|
|
26
|
+
dependencies: [CustomAtomRegistryLayer],
|
|
27
|
+
scoped: Effect.gen(function* () {
|
|
28
|
+
const registry = yield* AtomRegistry.AtomRegistry;
|
|
29
|
+
const rootScope = yield* Scope.make();
|
|
30
|
+
const runFork = yield* FiberSet.makeRuntime();
|
|
31
|
+
// Store the full context in a Ref so it can be updated after all layers are built
|
|
32
|
+
// Initially empty - will be set by render() after user layers are applied
|
|
33
|
+
const fullContextRef = yield* Ref.make(Context.empty());
|
|
34
|
+
// Each render tree gets its own fiber state
|
|
35
|
+
const fiberState = yield* Ref.make(makeFiberState());
|
|
36
|
+
const AtomOps = {
|
|
37
|
+
get: (atom) => registry.get(atom),
|
|
38
|
+
set: (atom, value) => registry.set(atom, value),
|
|
39
|
+
update: (atom, f) => registry.update(atom, f),
|
|
40
|
+
modify: (atom, f) => registry.modify(atom, f),
|
|
41
|
+
};
|
|
42
|
+
return { registry, rootScope, runFork, AtomOps, fiberState, fullContextRef };
|
|
43
|
+
}),
|
|
44
|
+
}) {
|
|
45
|
+
static Live = FibraeRuntime.Default;
|
|
46
|
+
/**
|
|
47
|
+
* Layer that provides both FibraeRuntime AND AtomRegistry.
|
|
48
|
+
* Use this when composing with user layers that need AtomRegistry access.
|
|
49
|
+
* (FibraeRuntime.Default consumes AtomRegistry internally but doesn't re-export it)
|
|
50
|
+
*/
|
|
51
|
+
static LiveWithRegistry = Layer.merge(FibraeRuntime.Default, CustomAtomRegistryLayer);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Fork an effect with the full application context.
|
|
55
|
+
*
|
|
56
|
+
* The fullContextRef contains ALL services (FibraeRuntime, AtomRegistry, Navigator, etc.)
|
|
57
|
+
* captured at render() time after all layers are built.
|
|
58
|
+
*
|
|
59
|
+
* IMPORTANT: fullContextRef must be set by render() before this is called.
|
|
60
|
+
*/
|
|
61
|
+
export const runForkWithRuntime = (runtime) => (effect) => {
|
|
62
|
+
const withContext = Effect.gen(function* () {
|
|
63
|
+
const fullContext = yield* Ref.get(runtime.fullContextRef);
|
|
64
|
+
return yield* Effect.provide(effect, fullContext);
|
|
65
|
+
});
|
|
66
|
+
return runtime.runFork(withContext);
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Scope from "effect/Scope";
|
|
3
|
+
import * as Ref from "effect/Ref";
|
|
4
|
+
/**
|
|
5
|
+
* Clear content by closing the current scope in the Ref and creating a fresh one.
|
|
6
|
+
* This triggers finalizers that remove DOM nodes and cancel subscriptions.
|
|
7
|
+
* Returns the new scope for convenience.
|
|
8
|
+
*/
|
|
9
|
+
export declare const clearContentScope: (contentScopeRef: Ref.Ref<Scope.Scope.Closeable>) => Effect.Effect<Scope.Scope.Closeable, never, never>;
|
|
10
|
+
/**
|
|
11
|
+
* Register a DOM node for cleanup when scope closes.
|
|
12
|
+
* Removes the node from its parent when the scope is closed.
|
|
13
|
+
*/
|
|
14
|
+
export declare const registerNodeCleanup: (node: Node, scope: Scope.Scope.Closeable) => Effect.Effect<void, never, never>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Scope from "effect/Scope";
|
|
3
|
+
import * as Exit from "effect/Exit";
|
|
4
|
+
import * as Ref from "effect/Ref";
|
|
5
|
+
// =============================================================================
|
|
6
|
+
// Scope Utilities
|
|
7
|
+
// =============================================================================
|
|
8
|
+
/**
|
|
9
|
+
* Clear content by closing the current scope in the Ref and creating a fresh one.
|
|
10
|
+
* This triggers finalizers that remove DOM nodes and cancel subscriptions.
|
|
11
|
+
* Returns the new scope for convenience.
|
|
12
|
+
*/
|
|
13
|
+
export const clearContentScope = (contentScopeRef) => Effect.gen(function* () {
|
|
14
|
+
const oldScope = yield* Ref.get(contentScopeRef);
|
|
15
|
+
yield* Scope.close(oldScope, Exit.void);
|
|
16
|
+
const newScope = yield* Scope.make();
|
|
17
|
+
yield* Ref.set(contentScopeRef, newScope);
|
|
18
|
+
return newScope;
|
|
19
|
+
});
|
|
20
|
+
/**
|
|
21
|
+
* Register a DOM node for cleanup when scope closes.
|
|
22
|
+
* Removes the node from its parent when the scope is closed.
|
|
23
|
+
*/
|
|
24
|
+
export const registerNodeCleanup = (node, scope) => Scope.addFinalizer(scope, Effect.sync(() => {
|
|
25
|
+
if (node.parentNode) {
|
|
26
|
+
node.parentNode.removeChild(node);
|
|
27
|
+
}
|
|
28
|
+
}));
|
|
29
|
+
//# sourceMappingURL=scope-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-utils.js","sourceRoot":"","sources":["../src/scope-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AACpC,OAAO,KAAK,GAAG,MAAM,YAAY,CAAC;AAElC,gFAAgF;AAChF,kBAAkB;AAClB,gFAAgF;AAEhF;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,eAA+C,EACK,EAAE,CACtD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACjD,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IACrC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;IAC1C,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC,CAAC;AAEL;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,IAAU,EACV,KAA4B,EACO,EAAE,CACrC,KAAK,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;IACzC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;AACH,CAAC,CAAC,CAAC,CAAC"}
|