@owlmeans/web-router 0.1.18-rc.5 → 0.1.18-rc.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,46 +1,149 @@
1
1
  # @owlmeans/web-router
2
2
 
3
- The default OwlMeans in-browser routing plugin for the OwlMeans context system. It depends on no
4
- third-party router — react-router is a separate opt-in plugin,
5
- [`@owlmeans/web-router-react-router`](../web-router-react-router).
6
-
7
- ## Overview
8
-
9
- - `makeWebRouterService()` — creates a router service backed by the OwlMeans browser router
10
- - `appendWebRouter(ctx)` — registers the web router service in the context
11
- - Provides `useParams`, `useLocation`, `useNavigate`, `outlet()`, `provider()` via the service
12
- - Used internally by `@owlmeans/web-client`'s `makeContext` — not typically used directly
3
+ The default OwlMeans in-browser routing plugin: standard URL routing over the browser History API,
4
+ a pure route matcher from `@owlmeans/router`, and a React provider, outlet and hooks — with no
5
+ third-party router. It registers itself as a `RouterPlugin` on the `@owlmeans/router` host, and
6
+ `@owlmeans/web-client` / `@owlmeans/web-panel` call `appendWebRouter` in their `makeContext`, so a
7
+ web app gets it without importing anything. An app imports it directly only for a custom context
8
+ setup or to reach the plugin's components and hooks outside the `context.router()` facade. To route
9
+ with react-router instead, add `@owlmeans/web-router-react-router` (higher priority, wins the
10
+ cascade); an SSR or React Native host is a plugin of its own.
13
11
 
14
12
  ## Installation
15
13
 
16
14
  ```bash
17
- bun add @owlmeans/web-router
15
+ bun add @owlmeans/web-router@^0.1.18-rc.51
18
16
  ```
19
17
 
18
+ `react` is a peer dependency.
19
+
20
+ ## Concepts
21
+
22
+ - **Router host** — the `RouterService` of `@owlmeans/router`, registered once per context
23
+ (`ensureRouterService`). Its facade (`provider`, `outlet`, `compile`, `useParams`, `useLocation`,
24
+ `useNavigate`, `useSearchParams`) delegates to the active plugin.
25
+ - **Browser plugin** — `makeBrowserRouterPlugin()`: alias `BROWSER_ROUTER`, priority 0, mode
26
+ `'browser'`, `match` always true — the universal browser fallback any higher-priority plugin
27
+ outranks.
28
+ - **Compile** — `compile(routes)` flattens and ranks the neutral `RouteObject[]`
29
+ (`{ index?, path?, children?, Component? }`) into matchable branches; synchronous.
30
+ - **Route chain** — `BrowserRouterProvider` re-matches on every history change and renders the chain
31
+ through `RouteChain`; each `<Outlet/>` renders the next-deeper match. A match without a
32
+ `Component` is a pass-through, as in react-router's implicit outlet.
33
+ - **Route syntax** — static segments, `:param`, nested routes and index routes; a static segment
34
+ outranks a `:param` sibling. No splat or optional segments.
35
+
20
36
  ## Usage
21
37
 
22
- This package is registered automatically when using `makeContext` from `@owlmeans/web-client`. Direct use is only needed for custom context setup:
38
+ ### Default wiring
39
+
40
+ Nothing to do with `@owlmeans/web-client` or `@owlmeans/web-panel`. A context assembled by hand
41
+ registers the plugin itself:
23
42
 
24
43
  ```typescript
25
44
  import { appendWebRouter } from '@owlmeans/web-router'
26
45
 
27
- appendWebRouter(context)
46
+ appendWebRouter(context) // ensures the router host, then registers the browser plugin
28
47
  ```
29
48
 
30
- ## API
49
+ ### Read the route in a component
50
+
51
+ Inside an OwlMeans app, reach the hooks through the facade so the active plugin answers:
52
+
53
+ ```tsx
54
+ import { useContext } from '@owlmeans/client'
55
+
56
+ export const UserScreen = () => {
57
+ const context = useContext()
58
+ const { id } = context.router().useParams<{ id: string }>()
59
+ const [query] = context.router().useSearchParams()
60
+ const navigate = context.router().useNavigate()
61
+
62
+ return (
63
+ <button onClick={() => navigate(`/users/${id}/edit`, { state: { from: query.get('tab') } })}>
64
+ edit
65
+ </button>
66
+ )
67
+ }
68
+ ```
31
69
 
32
- ### `makeWebRouterService(): RouterService`
70
+ `navigate(to, { replace?, state? })` takes a path string, a `{ pathname?, search?, hash? }`
71
+ object, or a number for history steps; an absolute `http…` string is a full-page navigation.
72
+ The setter returned by `useSearchParams` (`set(next, { replace?, state? })`) rewrites the search part
73
+ of the current path.
33
74
 
34
- Creates a router service with the OwlMeans browser router hooks and components.
75
+ ### Use the plugin standalone
35
76
 
36
- ### `appendWebRouter<C, T>(ctx): void`
77
+ The same components and hooks are exported directly — what the package's own browser harness does:
37
78
 
38
- Registers the web router service in the given context.
79
+ ```tsx
80
+ import { createRoot } from 'react-dom/client'
81
+ import type { RouteObject } from '@owlmeans/router'
82
+ import { makeBrowserRouterPlugin, Outlet, useParams } from '@owlmeans/web-router'
39
83
 
40
- ## Related Packages
84
+ const Layout = () => <main><Outlet /></main>
85
+ const Home = () => <p>home</p>
86
+ const User = () => <p>user {useParams().id}</p>
41
87
 
42
- - [`@owlmeans/router`](../router) — `RouterService` base interface
88
+ const routes: RouteObject[] = [
89
+ { path: '', Component: Layout, children: [
90
+ { index: true, Component: Home },
91
+ { path: 'users', children: [{ path: ':id', Component: User }] }, // component-less group passes through
92
+ ] },
93
+ ]
94
+
95
+ const plugin = makeBrowserRouterPlugin()
96
+ const Provider = plugin.provider()
97
+ createRoot(document.getElementById('root')!).render(<Provider router={plugin.compile(routes)} />)
98
+ ```
99
+
100
+ ### Switch to react-router
101
+
102
+ ```typescript
103
+ import { appendReactRouter } from '@owlmeans/web-router-react-router'
104
+
105
+ appendReactRouter(context) // priority 100 outranks the browser plugin
106
+ ```
107
+
108
+ ## API
109
+
110
+ | Symbol | Kind | Purpose |
111
+ |---|---|---|
112
+ | `appendWebRouter(ctx)` | function | `ensureRouterService(ctx)` + register the browser plugin; returns the context |
113
+ | `makeWebRouterService()` | function | back-compat: a new router host pre-loaded with the browser plugin |
114
+ | `makeBrowserRouterPlugin()` | function | the `RouterPlugin` itself |
115
+ | `BROWSER_ROUTER` | const | `'owlmeans-browser-router'` — the plugin alias |
116
+ | `BrowserRouterProvider` | component | `{ router: LibraryRouter }` — subscribes to history and renders the match chain |
117
+ | `Outlet` | component | renders the next-deeper match |
118
+ | `RouteChain` | component | `{ depth }` — renders the chain from a depth, skipping component-less matches |
119
+ | `useParams`, `useLocation`, `useNavigate`, `useSearchParams` | hooks | the plugin's hooks; throw outside the provider |
120
+ | `createBrowserHistory()` | function | `BrowserHistory` over `window.history` + `popstate`; an inert stub without `window` |
121
+ | `OwlLibraryRouter` | type | `{ routes, branches }` — what `compile` returns |
122
+ | `RouterState` | type | `{ location, matches, navigate }` — the provider's React context value |
123
+ | `BrowserHistory` | type | `location`, `push`, `replace`, `go`, `listen` |
124
+
125
+ ## Common pitfalls
126
+
127
+ - A hook used outside `BrowserRouterProvider` throws
128
+ `owlmeans-browser-router: router hook used outside of the router provider`.
129
+ - Calling the plugin's hooks directly bypasses the cascade; in an app, use `context.router().…` so a
130
+ switched plugin keeps working.
131
+ - Splat (`*`) and optional segments are not supported; declare explicit routes.
132
+ - `makeWebRouterService()` builds a separate host; prefer `appendWebRouter`, which reuses the
133
+ context's host.
134
+ - `noRouter` works on `<App>` only — `<PanelApp>` accepts the prop by type but still mounts the
135
+ router.
136
+
137
+ ## Related packages
138
+
139
+ - [`@owlmeans/router`](../router) — the router host, `RouterPlugin` contract and pure matcher
140
+ - [`@owlmeans/web-router-react-router`](../web-router-react-router) — opt-in react-router v7 plugin
141
+ - [`@owlmeans/client`](../client) — builds `RouteObject[]` from the entrypoint tree and exposes `context.router()`
43
142
  - [`@owlmeans/web-client`](../web-client) — calls `appendWebRouter` inside `makeContext`
143
+ - [`@owlmeans/web-panel`](../web-panel) — the panel layer, wired the same way
144
+
145
+ The `web-router` skill describes this plugin; the `router-plugins` skill covers the plugin contract,
146
+ cascade selection and authoring a new plugin.
44
147
 
45
148
  <!-- owlmeans:agent-guidance:start -->
46
149
  ## Agent guidance
@@ -50,7 +153,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
50
153
  your project's skill store (`.agents/skills/`):
51
154
 
52
155
  ```sh
53
- npx @owlmeans/agent-skills
156
+ npx @owlmeans/agent-skills@^0.1.18-rc.52
54
157
  ```
55
158
 
56
159
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/web-router",
4
- "version": "0.1.18-rc.0",
5
- "generatedAt": "2026-08-16T22:20:50.515Z",
4
+ "version": "0.1.18-rc.51",
5
+ "generatedAt": "2026-10-08T21:27:54.319Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -8,19 +8,20 @@ user-invocable: false
8
8
  # @owlmeans/web-router
9
9
 
10
10
  **Layer:** Web (React), build level L2
11
- **Install:** `"@owlmeans/web-router": "^0.1.18-rc.0"` in `dependencies`
11
+ **Install:** `"@owlmeans/web-router": "^0.1.18-rc.51"` in `dependencies`
12
12
 
13
- As of the pluggable-routing rework this package **is the default OwlMeans in-browser router** — a
14
- minimal, react-router-free implementation of standard URL routing over the browser History API. It
15
- registers itself as a `RouterPlugin` on the `@owlmeans/router` host. (react-router now lives in the
16
- separate opt-in `@owlmeans/web-router-react-router` package.)
13
+ This package **is the default OwlMeans in-browser router** — a minimal, react-router-free
14
+ implementation of standard URL routing over the browser History API. It registers itself as a
15
+ `RouterPlugin` on the `@owlmeans/router` host. (react-router is the separate opt-in
16
+ `@owlmeans/web-router-react-router` package.)
17
17
 
18
18
  ## Key Exports
19
19
 
20
20
  | Export | Description |
21
21
  |--------|-------------|
22
22
  | `appendWebRouter(ctx)` | Register the OwlMeans browser plugin on a context's router host (idempotent via `ensureRouterService`). Called transparently by `@owlmeans/web-client`. |
23
- | `makeBrowserRouterPlugin()` | The `RouterPlugin` itself (alias `owlmeans-browser-router`, priority 0). |
23
+ | `makeBrowserRouterPlugin()` | The `RouterPlugin` itself (alias `BROWSER_ROUTER`, priority 0). |
24
+ | `BROWSER_ROUTER` | The plugin's alias string, `'owlmeans-browser-router'` — name the constant rather than the literal when selecting or overriding the plugin. |
24
25
  | `makeWebRouterService()` | Back-compat: a host pre-loaded with the browser plugin. |
25
26
  | `BrowserRouterProvider`, `Outlet` | React provider/outlet components. |
26
27
  | `RouteChain` | Renders the matched chain from a given depth, skipping `Component`-less matches (used by the provider and `Outlet`). |
@@ -35,8 +36,8 @@ separate opt-in `@owlmeans/web-router-react-router` package.)
35
36
  chain via `RouteChain`; `<Outlet/>` renders the next-deeper match (depth tracked via context).
36
37
  Composes unchanged with the `@owlmeans/client` route renderer (parent components emit `<Outlet/>`).
37
38
  - **Component-less matches are pass-through** — exactly like react-router's implicit `<Outlet/>` for
38
- element-less routes. `@owlmeans/client` emits `RouteObject`s with no `Component` for modules that
39
- have no handler (grouping modules such as `client-authentication` →
39
+ element-less routes. `@owlmeans/client` emits `RouteObject`s with no `Component` for entrypoints
40
+ that have no handler (grouping entrypoints such as `client-authentication` →
40
41
  `client-authentication:authentication`), so rendering only the exact node at a depth would blank
41
42
  the whole subtree. `RouteChain` walks down to the first match that has a `Component`, and the
42
43
  `OutletContext` depth it publishes is that match's depth, so nested outlets stay aligned.
@@ -4,11 +4,11 @@ import type { FC } from 'react';
4
4
  *
5
5
  * A matched route that carries no `Component` is a **pass-through**: it renders
6
6
  * the next-deeper match, exactly like react-router's implicit `<Outlet/>` for
7
- * element-less routes. OwlMeans module trees depend on this — grouping modules
8
- * (e.g. `client-authentication` → `client-authentication:authentication`) have
9
- * no handler, so `@owlmeans/client` emits `RouteObject`s without a `Component`
7
+ * element-less routes. OwlMeans entrypoint trees depend on this — grouping
8
+ * entrypoints (e.g. `client-authentication` → `client-authentication:authentication`)
9
+ * have no handler, so `@owlmeans/client` emits `RouteObject`s without a `Component`
10
10
  * for them. Rendering only the exact node at `depth` would leave the whole
11
- * subtree blank whenever a group sits above the screen module.
11
+ * subtree blank whenever a group sits above the screen entrypoint.
12
12
  */
13
13
  export declare const RouteChain: FC<{
14
14
  depth: number;
@@ -5,11 +5,11 @@ import { RouterStateContext, OutletContext } from './context.js';
5
5
  *
6
6
  * A matched route that carries no `Component` is a **pass-through**: it renders
7
7
  * the next-deeper match, exactly like react-router's implicit `<Outlet/>` for
8
- * element-less routes. OwlMeans module trees depend on this — grouping modules
9
- * (e.g. `client-authentication` → `client-authentication:authentication`) have
10
- * no handler, so `@owlmeans/client` emits `RouteObject`s without a `Component`
8
+ * element-less routes. OwlMeans entrypoint trees depend on this — grouping
9
+ * entrypoints (e.g. `client-authentication` → `client-authentication:authentication`)
10
+ * have no handler, so `@owlmeans/client` emits `RouteObject`s without a `Component`
11
11
  * for them. Rendering only the exact node at `depth` would leave the whole
12
- * subtree blank whenever a group sits above the screen module.
12
+ * subtree blank whenever a group sits above the screen entrypoint.
13
13
  */
14
14
  export const RouteChain = ({ depth }) => {
15
15
  const state = useContext(RouterStateContext);
@@ -1,4 +1,4 @@
1
- import { flattenRoutes, rankRouteBranches } from '@owlmeans/router';
1
+ import { routeMatcherHelper } from '@owlmeans/router';
2
2
  import { BROWSER_ROUTER } from '../consts.js';
3
3
  import { BrowserRouterProvider } from './provider.js';
4
4
  import { Outlet } from './outlet.js';
@@ -15,7 +15,7 @@ export const makeBrowserRouterPlugin = () => ({
15
15
  match: () => true,
16
16
  compile: routes => ({
17
17
  routes,
18
- branches: rankRouteBranches(flattenRoutes(routes))
18
+ branches: routeMatcherHelper.rankRouteBranches(routeMatcherHelper.flattenRoutes(routes))
19
19
  }),
20
20
  provider: () => BrowserRouterProvider,
21
21
  outlet: () => Outlet,
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/browser/plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAGjF;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAiB,EAAE,CAAC,CAAC;IAC1D,KAAK,EAAE,cAAc;IACrB,QAAQ,EAAE,CAAC;IACX,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI;IACjB,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QAClB,MAAM;QACN,QAAQ,EAAE,iBAAiB,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;KACN,CAAA;IAC9C,QAAQ,EAAE,GAAG,EAAE,CAAC,qBAAqB;IACrC,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM;IACpB,SAAS;IACT,WAAW;IACX,WAAW;IACX,eAAe;CAChB,CAAC,CAAA"}
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/browser/plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAGjF;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAiB,EAAE,CAAC,CAAC;IAC1D,KAAK,EAAE,cAAc;IACrB,QAAQ,EAAE,CAAC;IACX,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI;IACjB,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QAClB,MAAM;QACN,QAAQ,EAAE,kBAAkB,CAAC,iBAAiB,CAAC,kBAAkB,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;KAC5C,CAAA;IAC9C,QAAQ,EAAE,GAAG,EAAE,CAAC,qBAAqB;IACrC,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM;IACpB,SAAS;IACT,WAAW;IACX,WAAW;IACX,eAAe;CAChB,CAAC,CAAA"}
@@ -1,5 +1,5 @@
1
1
  import { createElement, useEffect, useMemo, useRef, useState } from 'react';
2
- import { matchRoutes } from '@owlmeans/router';
2
+ import { routeMatcherHelper } from '@owlmeans/router';
3
3
  import { createBrowserHistory } from './history.js';
4
4
  import { RouterStateContext } from './context.js';
5
5
  import { RouteChain } from './chain.js';
@@ -14,7 +14,7 @@ export const BrowserRouterProvider = ({ router }) => {
14
14
  const history = historyRef.current;
15
15
  const [location, setLocation] = useState(() => history.location);
16
16
  useEffect(() => history.listen(setLocation), []);
17
- const matches = useMemo(() => matchRoutes(lib.branches, location.pathname) ?? [], [lib, location.pathname]);
17
+ const matches = useMemo(() => routeMatcherHelper.matchRoutes(lib.branches, location.pathname) ?? [], [lib, location.pathname]);
18
18
  const navigate = useMemo(() => ((to, options) => {
19
19
  if (typeof to === 'number') {
20
20
  history.go(to);
@@ -1 +1 @@
1
- {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/browser/provider.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAE3E,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAGvC;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAkC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE;IACjF,MAAM,GAAG,GAAG,MAA0B,CAAA;IACtC,MAAM,UAAU,GAAG,MAAM,CAAC,oBAAoB,EAAE,CAAC,CAAA;IACjD,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAA;IAElC,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAW,GAAG,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IAE1E,SAAS,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,EAAE,CAAC,CAAA;IAEhD,MAAM,OAAO,GAAG,OAAO,CACrB,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,EACxD,CAAC,GAAG,EAAE,QAAQ,CAAC,QAAQ,CAAC,CACzB,CAAA;IAED,MAAM,QAAQ,GAAG,OAAO,CAAmB,GAAG,EAAE,CAAC,CAAC,CAAC,EAA2E,EAAE,OAA4C,EAAE,EAAE;QAC9K,IAAI,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC;YAC3B,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;YACd,OAAM;QACR,CAAC;QACD,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACpD,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,EAAE,CAAA;YACzB,OAAM;QACR,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,EAAE,KAAK,QAAQ;YACjC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAA;QAC5D,IAAI,OAAO,EAAE,OAAO,EAAE,CAAC;YACrB,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;QACtC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAA;QACpC,CAAC;IACH,CAAC,CAAqB,EAAE,CAAC,OAAO,CAAC,CAAC,CAAA;IAElC,OAAO,aAAa,CAClB,kBAAkB,CAAC,QAAQ,EAC3B,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAC1C,aAAa,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CACxC,CAAA;AACH,CAAC,CAAA"}
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/browser/provider.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAE3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAGvC;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAkC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE;IACjF,MAAM,GAAG,GAAG,MAA0B,CAAA;IACtC,MAAM,UAAU,GAAG,MAAM,CAAC,oBAAoB,EAAE,CAAC,CAAA;IACjD,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAA;IAElC,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAW,GAAG,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IAE1E,SAAS,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,EAAE,CAAC,CAAA;IAEhD,MAAM,OAAO,GAAG,OAAO,CACrB,GAAG,EAAE,CAAC,kBAAkB,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,EAC3E,CAAC,GAAG,EAAE,QAAQ,CAAC,QAAQ,CAAC,CACzB,CAAA;IAED,MAAM,QAAQ,GAAG,OAAO,CAAmB,GAAG,EAAE,CAAC,CAAC,CAAC,EAA2E,EAAE,OAA4C,EAAE,EAAE;QAC9K,IAAI,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC;YAC3B,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;YACd,OAAM;QACR,CAAC;QACD,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACpD,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,EAAE,CAAA;YACzB,OAAM;QACR,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,EAAE,KAAK,QAAQ;YACjC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAA;QAC5D,IAAI,OAAO,EAAE,OAAO,EAAE,CAAC;YACrB,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;QACtC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAA;QACpC,CAAC;IACH,CAAC,CAAqB,EAAE,CAAC,OAAO,CAAC,CAAC,CAAA;IAElC,OAAO,aAAa,CAClB,kBAAkB,CAAC,QAAQ,EAC3B,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAC1C,aAAa,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CACxC,CAAA;AACH,CAAC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/web-router",
3
- "version": "0.1.18-rc.5",
3
+ "version": "0.1.18-rc.51",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -26,20 +26,20 @@
26
26
  },
27
27
  "devDependencies": {
28
28
  "@owlmeans/dep-config": "workspace:*",
29
- "@owlmeans/test-ui": "^0.1.18-rc.5",
30
- "@types/node": "^26.1.0",
31
- "@types/react": "^19.2.17",
32
- "@vitejs/plugin-react": "^6.0.3",
29
+ "@owlmeans/test-ui": "^0.1.18-rc.51",
30
+ "@types/node": "^26.6.4",
31
+ "@types/react": "^19.3.0",
32
+ "@vitejs/plugin-react": "^6.1.1",
33
33
  "nodemon": "^3.1.14",
34
- "playwright": "^1.61.1",
35
- "react": "^19.2.7",
36
- "react-dom": "^19.2.7",
34
+ "playwright": "^1.63.0",
35
+ "react": "^19.3.0",
36
+ "react-dom": "^19.3.0",
37
37
  "typescript": "^7.0.2",
38
- "vite": "^8.1.2"
38
+ "vite": "^8.3.2"
39
39
  },
40
40
  "dependencies": {
41
- "@owlmeans/context": "^0.1.18-rc.5",
42
- "@owlmeans/router": "^0.1.18-rc.5"
41
+ "@owlmeans/context": "^0.1.18-rc.34",
42
+ "@owlmeans/router": "^0.1.18-rc.34"
43
43
  },
44
44
  "publishConfig": {
45
45
  "access": "public"
@@ -7,11 +7,11 @@ import { RouterStateContext, OutletContext } from './context.js'
7
7
  *
8
8
  * A matched route that carries no `Component` is a **pass-through**: it renders
9
9
  * the next-deeper match, exactly like react-router's implicit `<Outlet/>` for
10
- * element-less routes. OwlMeans module trees depend on this — grouping modules
11
- * (e.g. `client-authentication` → `client-authentication:authentication`) have
12
- * no handler, so `@owlmeans/client` emits `RouteObject`s without a `Component`
10
+ * element-less routes. OwlMeans entrypoint trees depend on this — grouping
11
+ * entrypoints (e.g. `client-authentication` → `client-authentication:authentication`)
12
+ * have no handler, so `@owlmeans/client` emits `RouteObject`s without a `Component`
13
13
  * for them. Rendering only the exact node at `depth` would leave the whole
14
- * subtree blank whenever a group sits above the screen module.
14
+ * subtree blank whenever a group sits above the screen entrypoint.
15
15
  */
16
16
  export const RouteChain: FC<{ depth: number }> = ({ depth }) => {
17
17
  const state = useContext(RouterStateContext)
@@ -1,5 +1,5 @@
1
1
  import type { LibraryRouter, RouterPlugin } from '@owlmeans/router'
2
- import { flattenRoutes, rankRouteBranches } from '@owlmeans/router'
2
+ import { routeMatcherHelper } from '@owlmeans/router'
3
3
  import { BROWSER_ROUTER } from '../consts.js'
4
4
  import { BrowserRouterProvider } from './provider.js'
5
5
  import { Outlet } from './outlet.js'
@@ -18,7 +18,7 @@ export const makeBrowserRouterPlugin = (): RouterPlugin => ({
18
18
  match: () => true,
19
19
  compile: routes => ({
20
20
  routes,
21
- branches: rankRouteBranches(flattenRoutes(routes))
21
+ branches: routeMatcherHelper.rankRouteBranches(routeMatcherHelper.flattenRoutes(routes))
22
22
  } satisfies OwlLibraryRouter as LibraryRouter),
23
23
  provider: () => BrowserRouterProvider,
24
24
  outlet: () => Outlet,
@@ -1,7 +1,7 @@
1
1
  import type { FC } from 'react'
2
2
  import { createElement, useEffect, useMemo, useRef, useState } from 'react'
3
3
  import type { LibraryRouter, Location, NavigateFunction } from '@owlmeans/router'
4
- import { matchRoutes } from '@owlmeans/router'
4
+ import { routeMatcherHelper } from '@owlmeans/router'
5
5
  import { createBrowserHistory } from './history.js'
6
6
  import { RouterStateContext } from './context.js'
7
7
  import { RouteChain } from './chain.js'
@@ -22,7 +22,7 @@ export const BrowserRouterProvider: FC<{ router: LibraryRouter }> = ({ router })
22
22
  useEffect(() => history.listen(setLocation), [])
23
23
 
24
24
  const matches = useMemo(
25
- () => matchRoutes(lib.branches, location.pathname) ?? [],
25
+ () => routeMatcherHelper.matchRoutes(lib.branches, location.pathname) ?? [],
26
26
  [lib, location.pathname]
27
27
  )
28
28
 
@@ -49,7 +49,7 @@ const routes: RouteObject[] = [
49
49
  ]
50
50
  },
51
51
  // component-less grouping node at the very top of the chain — this is the shape
52
- // `@owlmeans/client` emits for modules without a handler (e.g. `client-authentication`)
52
+ // `@owlmeans/client` emits for entrypoints without a handler (e.g. `client-authentication`)
53
53
  { path: 'group', children: [{ path: 'leaf', Component: Leaf }] }
54
54
  ]
55
55
 
@@ -1,14 +1,14 @@
1
1
  import { afterAll, describe, expect, test } from 'bun:test'
2
- import { closeBrowser, withPage } from '@owlmeans/test-ui'
3
2
  import { HARNESS_URL } from './context.js'
3
+ import { browserHelper } from '@owlmeans/test-ui'
4
4
 
5
5
  const at = (path: string) => new URL(path, HARNESS_URL).toString()
6
6
 
7
- afterAll(async () => { await closeBrowser() })
7
+ afterAll(async () => { await browserHelper.closeBrowser() })
8
8
 
9
9
  describe('@owlmeans/web-router — OwlMeans browser routing (chromium e2e)', () => {
10
10
  test('renders the index route inside the layout at /', async () => {
11
- await withPage(async page => {
11
+ await browserHelper.withPage(async page => {
12
12
  await page.goto(HARNESS_URL)
13
13
  await page.waitForSelector('#home')
14
14
  expect(await page.locator('#layout').isVisible()).toBe(true)
@@ -17,7 +17,7 @@ describe('@owlmeans/web-router — OwlMeans browser routing (chromium e2e)', ()
17
17
  })
18
18
 
19
19
  test('programmatic navigate resolves nested route, params and search', async () => {
20
- await withPage(async page => {
20
+ await browserHelper.withPage(async page => {
21
21
  await page.goto(HARNESS_URL)
22
22
  await page.waitForSelector('#nav-user')
23
23
  await page.click('#nav-user')
@@ -33,7 +33,7 @@ describe('@owlmeans/web-router — OwlMeans browser routing (chromium e2e)', ()
33
33
  })
34
34
 
35
35
  test('nested index route renders at /users', async () => {
36
- await withPage(async page => {
36
+ await browserHelper.withPage(async page => {
37
37
  await page.goto(HARNESS_URL)
38
38
  await page.waitForSelector('#nav-users')
39
39
  await page.click('#nav-users')
@@ -44,7 +44,7 @@ describe('@owlmeans/web-router — OwlMeans browser routing (chromium e2e)', ()
44
44
  })
45
45
 
46
46
  test('static segment outranks the :id dynamic sibling', async () => {
47
- await withPage(async page => {
47
+ await browserHelper.withPage(async page => {
48
48
  await page.goto(at('users/settings'))
49
49
  await page.waitForSelector('#settings')
50
50
  expect(await page.locator('#settings').textContent()).toContain('settings-screen')
@@ -53,7 +53,7 @@ describe('@owlmeans/web-router — OwlMeans browser routing (chromium e2e)', ()
53
53
  })
54
54
 
55
55
  test('deep-links directly to a :param URL (synchronous initial match)', async () => {
56
- await withPage(async page => {
56
+ await browserHelper.withPage(async page => {
57
57
  await page.goto(at('users/7?token=xyz'))
58
58
  await page.waitForSelector('#user')
59
59
  expect(await page.locator('#user').textContent()).toBe('user:7:token:xyz')
@@ -61,7 +61,7 @@ describe('@owlmeans/web-router — OwlMeans browser routing (chromium e2e)', ()
61
61
  })
62
62
 
63
63
  test('renders through a component-less group at the top of the chain', async () => {
64
- await withPage(async page => {
64
+ await browserHelper.withPage(async page => {
65
65
  await page.goto(at('group/leaf'))
66
66
  await page.waitForSelector('#leaf')
67
67
  expect(await page.locator('#leaf').textContent()).toContain('leaf-screen')
@@ -71,7 +71,7 @@ describe('@owlmeans/web-router — OwlMeans browser routing (chromium e2e)', ()
71
71
  })
72
72
 
73
73
  test('outlet falls through a component-less group in the middle of the chain', async () => {
74
- await withPage(async page => {
74
+ await browserHelper.withPage(async page => {
75
75
  await page.goto(at('users/nested/deep'))
76
76
  await page.waitForSelector('#deep')
77
77
  expect(await page.locator('#layout').isVisible()).toBe(true)
@@ -81,7 +81,7 @@ describe('@owlmeans/web-router — OwlMeans browser routing (chromium e2e)', ()
81
81
  })
82
82
 
83
83
  test('browser back/forward restores the matched route', async () => {
84
- await withPage(async page => {
84
+ await browserHelper.withPage(async page => {
85
85
  await page.goto(HARNESS_URL)
86
86
  await page.waitForSelector('#home')
87
87
  await page.click('#nav-user')
package/build/.gitkeep DELETED
File without changes