@sigx/lynx-navigation 0.1.0 → 0.1.2
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/LICENSE +1 -1
- package/README.md +355 -0
- package/dist/components/Drawer.d.ts +56 -0
- package/dist/components/Drawer.d.ts.map +1 -0
- package/dist/components/Drawer.js +74 -0
- package/dist/components/Drawer.js.map +1 -0
- package/dist/components/EdgeBackHandle.js +144 -0
- package/dist/components/EdgeBackHandle.js.map +1 -0
- package/dist/components/EntryScope.d.ts +26 -0
- package/dist/components/EntryScope.d.ts.map +1 -0
- package/dist/components/EntryScope.js +33 -0
- package/dist/components/EntryScope.js.map +1 -0
- package/dist/components/Header.d.ts +7 -0
- package/dist/components/Header.d.ts.map +1 -0
- package/dist/components/Header.js +103 -0
- package/dist/components/Header.js.map +1 -0
- package/dist/components/Link.js +1 -4
- package/dist/components/Link.js.map +1 -1
- package/dist/components/NavigationRoot.d.ts +1 -1
- package/dist/components/NavigationRoot.d.ts.map +1 -1
- package/dist/components/NavigationRoot.js +29 -3
- package/dist/components/NavigationRoot.js.map +1 -1
- package/dist/components/Screen.d.ts +98 -0
- package/dist/components/Screen.d.ts.map +1 -0
- package/dist/components/Screen.js +94 -0
- package/dist/components/Screen.js.map +1 -0
- package/dist/components/ScreenContainer.d.ts.map +1 -1
- package/dist/components/ScreenContainer.js +77 -0
- package/dist/components/ScreenContainer.js.map +1 -0
- package/dist/components/Stack.d.ts.map +1 -1
- package/dist/components/Stack.js +60 -24
- package/dist/components/Stack.js.map +1 -1
- package/dist/components/TabBar.d.ts +40 -0
- package/dist/components/TabBar.d.ts.map +1 -0
- package/dist/components/TabBar.js +63 -0
- package/dist/components/TabBar.js.map +1 -0
- package/dist/components/Tabs.d.ts +101 -0
- package/dist/components/Tabs.d.ts.map +1 -0
- package/dist/components/Tabs.js +140 -0
- package/dist/components/Tabs.js.map +1 -0
- package/dist/hooks/use-focus.d.ts +46 -0
- package/dist/hooks/use-focus.d.ts.map +1 -0
- package/dist/hooks/use-focus.js +81 -0
- package/dist/hooks/use-focus.js.map +1 -0
- package/dist/hooks/use-hardware-back.js +50 -0
- package/dist/hooks/use-hardware-back.js.map +1 -0
- package/dist/hooks/use-linking-nav.d.ts +92 -0
- package/dist/hooks/use-linking-nav.d.ts.map +1 -0
- package/dist/hooks/use-linking-nav.js +109 -0
- package/dist/hooks/use-linking-nav.js.map +1 -0
- package/dist/hooks/use-nav-internal.d.ts +38 -1
- package/dist/hooks/use-nav-internal.d.ts.map +1 -1
- package/dist/hooks/use-nav-internal.js +32 -0
- package/dist/hooks/use-nav-internal.js.map +1 -1
- package/dist/hooks/use-nav-serializer.d.ts +83 -0
- package/dist/hooks/use-nav-serializer.d.ts.map +1 -0
- package/dist/hooks/use-nav-serializer.js +181 -0
- package/dist/hooks/use-nav-serializer.js.map +1 -0
- package/dist/hooks/use-nav.js.map +1 -1
- package/dist/hooks/use-screen-options.d.ts +3 -0
- package/dist/hooks/use-screen-options.d.ts.map +1 -0
- package/dist/hooks/use-screen-options.js +43 -0
- package/dist/hooks/use-screen-options.js.map +1 -0
- package/dist/href.d.ts +16 -1
- package/dist/href.d.ts.map +1 -1
- package/dist/href.js +50 -7
- package/dist/href.js.map +1 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/screen-registry.d.ts +49 -0
- package/dist/internal/screen-registry.d.ts.map +1 -0
- package/dist/internal/screen-registry.js +59 -0
- package/dist/internal/screen-registry.js.map +1 -0
- package/dist/internal/screen-width.js +30 -0
- package/dist/internal/screen-width.js.map +1 -0
- package/dist/navigator/core.d.ts +20 -1
- package/dist/navigator/core.d.ts.map +1 -1
- package/dist/navigator/core.js +231 -36
- package/dist/navigator/core.js.map +1 -1
- package/dist/types.d.ts +56 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/url/build.d.ts +16 -0
- package/dist/url/build.d.ts.map +1 -0
- package/dist/url/build.js +30 -0
- package/dist/url/build.js.map +1 -0
- package/dist/url/compile.d.ts +35 -0
- package/dist/url/compile.d.ts.map +1 -0
- package/dist/url/compile.js +83 -0
- package/dist/url/compile.js.map +1 -0
- package/dist/url/format.d.ts +29 -0
- package/dist/url/format.d.ts.map +1 -0
- package/dist/url/format.js +102 -0
- package/dist/url/format.js.map +1 -0
- package/dist/url/index.d.ts +13 -0
- package/dist/url/index.d.ts.map +1 -0
- package/dist/url/index.js +13 -0
- package/dist/url/index.js.map +1 -0
- package/dist/url/parse.d.ts +21 -0
- package/dist/url/parse.d.ts.map +1 -0
- package/dist/url/parse.js +94 -0
- package/dist/url/parse.js.map +1 -0
- package/dist/url/registry.d.ts +41 -0
- package/dist/url/registry.d.ts.map +1 -0
- package/dist/url/registry.js +56 -0
- package/dist/url/registry.js.map +1 -0
- package/dist/url/validate.d.ts +24 -0
- package/dist/url/validate.d.ts.map +1 -0
- package/dist/url/validate.js +37 -0
- package/dist/url/validate.js.map +1 -0
- package/package.json +44 -15
- package/src/components/Drawer.tsx +119 -0
- package/src/components/EdgeBackHandle.tsx +1 -1
- package/src/components/EntryScope.tsx +38 -0
- package/src/components/Header.tsx +129 -0
- package/src/components/NavigationRoot.tsx +9 -1
- package/src/components/Screen.tsx +116 -0
- package/src/components/ScreenContainer.tsx +14 -1
- package/src/components/Stack.tsx +21 -2
- package/src/components/TabBar.tsx +104 -0
- package/src/components/Tabs.tsx +216 -0
- package/src/hooks/use-focus.ts +88 -0
- package/src/hooks/use-linking-nav.ts +159 -0
- package/src/hooks/use-nav-internal.ts +48 -1
- package/src/hooks/use-nav-serializer.ts +239 -0
- package/src/hooks/use-screen-options.ts +48 -0
- package/src/href.ts +68 -11
- package/src/index.ts +29 -0
- package/src/internal/screen-registry.ts +89 -0
- package/src/navigator/core.ts +86 -4
- package/src/types.ts +56 -0
- package/src/url/build.ts +35 -0
- package/src/url/compile.ts +109 -0
- package/src/url/format.ts +95 -0
- package/src/url/index.ts +18 -0
- package/src/url/parse.ts +102 -0
- package/src/url/registry.ts +69 -0
- package/src/url/validate.ts +67 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format helpers: typed params/search → URL string.
|
|
3
|
+
*
|
|
4
|
+
* Used by `hrefFor()` to render the `url` field of an Href.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Serialize an object as a `key=value&key=value` querystring.
|
|
8
|
+
*
|
|
9
|
+
* Keys are sorted to make the output deterministic (useful for tests and
|
|
10
|
+
* persistence diffs). `undefined`/`null` values are skipped. Non-primitive
|
|
11
|
+
* values are JSON-stringified on the way out — `parseSearch` returns the
|
|
12
|
+
* raw string and leaves any JSON decoding to the route's `search` schema
|
|
13
|
+
* (e.g. a Zod `transform`), so the round-trip is intentionally one-way at
|
|
14
|
+
* this layer.
|
|
15
|
+
*
|
|
16
|
+
* Returns `''` (empty string) when there are no entries — callers join with
|
|
17
|
+
* `?` only when the result is non-empty.
|
|
18
|
+
*/
|
|
19
|
+
export declare function formatSearch(search: Record<string, unknown> | undefined): string;
|
|
20
|
+
/**
|
|
21
|
+
* Parse a `key=value&key=value` querystring into a string-keyed bag. Values
|
|
22
|
+
* are decoded but kept as strings — typed coercion happens in the route's
|
|
23
|
+
* `search` schema (e.g. Zod's `z.coerce.number()`).
|
|
24
|
+
*
|
|
25
|
+
* Multiple occurrences of the same key produce an array. Schemas that don't
|
|
26
|
+
* expect arrays will reject this — that's the right failure mode.
|
|
27
|
+
*/
|
|
28
|
+
export declare function parseSearch(query: string): Record<string, string | string[]>;
|
|
29
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/url/format.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,MAAM,CAkBhF;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAwB5E"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format helpers: typed params/search → URL string.
|
|
3
|
+
*
|
|
4
|
+
* Used by `hrefFor()` to render the `url` field of an Href.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Serialize an object as a `key=value&key=value` querystring.
|
|
8
|
+
*
|
|
9
|
+
* Keys are sorted to make the output deterministic (useful for tests and
|
|
10
|
+
* persistence diffs). `undefined`/`null` values are skipped. Non-primitive
|
|
11
|
+
* values are JSON-stringified on the way out — `parseSearch` returns the
|
|
12
|
+
* raw string and leaves any JSON decoding to the route's `search` schema
|
|
13
|
+
* (e.g. a Zod `transform`), so the round-trip is intentionally one-way at
|
|
14
|
+
* this layer.
|
|
15
|
+
*
|
|
16
|
+
* Returns `''` (empty string) when there are no entries — callers join with
|
|
17
|
+
* `?` only when the result is non-empty.
|
|
18
|
+
*/
|
|
19
|
+
export function formatSearch(search) {
|
|
20
|
+
if (!search)
|
|
21
|
+
return '';
|
|
22
|
+
const keys = Object.keys(search).sort();
|
|
23
|
+
const parts = [];
|
|
24
|
+
for (const key of keys) {
|
|
25
|
+
const value = search[key];
|
|
26
|
+
if (value === undefined || value === null)
|
|
27
|
+
continue;
|
|
28
|
+
const encoded = encodeURIComponent(key);
|
|
29
|
+
if (Array.isArray(value)) {
|
|
30
|
+
for (const item of value) {
|
|
31
|
+
if (item === undefined || item === null)
|
|
32
|
+
continue;
|
|
33
|
+
parts.push(`${encoded}=${encodeURIComponent(serializeScalar(item))}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
parts.push(`${encoded}=${encodeURIComponent(serializeScalar(value))}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return parts.join('&');
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Parse a `key=value&key=value` querystring into a string-keyed bag. Values
|
|
44
|
+
* are decoded but kept as strings — typed coercion happens in the route's
|
|
45
|
+
* `search` schema (e.g. Zod's `z.coerce.number()`).
|
|
46
|
+
*
|
|
47
|
+
* Multiple occurrences of the same key produce an array. Schemas that don't
|
|
48
|
+
* expect arrays will reject this — that's the right failure mode.
|
|
49
|
+
*/
|
|
50
|
+
export function parseSearch(query) {
|
|
51
|
+
const result = {};
|
|
52
|
+
if (!query)
|
|
53
|
+
return result;
|
|
54
|
+
// Strip leading `?` if present (formatHref doesn't include it but callers
|
|
55
|
+
// sometimes pass `?a=1`).
|
|
56
|
+
const cleaned = query.startsWith('?') ? query.slice(1) : query;
|
|
57
|
+
if (!cleaned)
|
|
58
|
+
return result;
|
|
59
|
+
for (const pair of cleaned.split('&')) {
|
|
60
|
+
if (!pair)
|
|
61
|
+
continue;
|
|
62
|
+
const eqIdx = pair.indexOf('=');
|
|
63
|
+
const rawKey = eqIdx === -1 ? pair : pair.slice(0, eqIdx);
|
|
64
|
+
const rawValue = eqIdx === -1 ? '' : pair.slice(eqIdx + 1);
|
|
65
|
+
const key = safeDecode(rawKey);
|
|
66
|
+
const value = safeDecode(rawValue);
|
|
67
|
+
const existing = result[key];
|
|
68
|
+
if (existing === undefined) {
|
|
69
|
+
result[key] = value;
|
|
70
|
+
}
|
|
71
|
+
else if (Array.isArray(existing)) {
|
|
72
|
+
existing.push(value);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
result[key] = [existing, value];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
function serializeScalar(v) {
|
|
81
|
+
switch (typeof v) {
|
|
82
|
+
case 'string': return v;
|
|
83
|
+
case 'number':
|
|
84
|
+
case 'boolean':
|
|
85
|
+
case 'bigint':
|
|
86
|
+
return String(v);
|
|
87
|
+
default:
|
|
88
|
+
// Objects/arrays/etc — JSON. Schemas can decide how to interpret.
|
|
89
|
+
return JSON.stringify(v);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function safeDecode(s) {
|
|
93
|
+
try {
|
|
94
|
+
return decodeURIComponent(s.replace(/\+/g, ' '));
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Malformed % escape — fall back to the raw text rather than throwing
|
|
98
|
+
// from a navigation hot path. The schema will reject if it matters.
|
|
99
|
+
return s;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/url/format.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL bridge — internal barrel.
|
|
3
|
+
*
|
|
4
|
+
* Not re-exported from the package root. Public surface is `hrefFor` /
|
|
5
|
+
* `parseHref` in ../href.ts plus `_setRouteRegistry` for tests/bootstrap.
|
|
6
|
+
*/
|
|
7
|
+
export { compilePath, type CompiledPath } from './compile.js';
|
|
8
|
+
export { buildUrl } from './build.js';
|
|
9
|
+
export { parseHrefImpl } from './parse.js';
|
|
10
|
+
export { formatSearch, parseSearch } from './format.js';
|
|
11
|
+
export { _setRouteRegistry, _clearRouteRegistry, getRouteRegistry, getCompiledPath, } from './registry.js';
|
|
12
|
+
export { validateSync, type ValidateOutcome } from './validate.js';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/url/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EACH,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,GAClB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL bridge — internal barrel.
|
|
3
|
+
*
|
|
4
|
+
* Not re-exported from the package root. Public surface is `hrefFor` /
|
|
5
|
+
* `parseHref` in ../href.ts plus `_setRouteRegistry` for tests/bootstrap.
|
|
6
|
+
*/
|
|
7
|
+
export { compilePath } from './compile.js';
|
|
8
|
+
export { buildUrl } from './build.js';
|
|
9
|
+
export { parseHrefImpl } from './parse.js';
|
|
10
|
+
export { formatSearch, parseSearch } from './format.js';
|
|
11
|
+
export { _setRouteRegistry, _clearRouteRegistry, getRouteRegistry, getCompiledPath, } from './registry.js';
|
|
12
|
+
export { validateSync } from './validate.js';
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/url/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAqB,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EACH,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,GAClB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAwB,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL → typed Href parser.
|
|
3
|
+
*
|
|
4
|
+
* Walks every registered route with a `path`, tries to match its compiled
|
|
5
|
+
* regex against the URL's pathname, and on a hit validates the extracted
|
|
6
|
+
* params + search through the route's Standard Schema. First match wins;
|
|
7
|
+
* iteration order follows `Object.keys` of the registered routes map.
|
|
8
|
+
*
|
|
9
|
+
* Validation failures return `null` rather than throwing — deep-link handlers
|
|
10
|
+
* fall back to the initial route on a bad URL instead of crashing the app.
|
|
11
|
+
*/
|
|
12
|
+
import type { Href } from '../href.js';
|
|
13
|
+
/**
|
|
14
|
+
* Parse a URL string against the active route registry.
|
|
15
|
+
*
|
|
16
|
+
* Accepts both absolute URLs (`myapp://host/users/42?tab=about`) and
|
|
17
|
+
* pathname-only forms (`/users/42?tab=about`). Returns `null` if no route's
|
|
18
|
+
* `path` matches the URL or if schema validation rejects the extracted bits.
|
|
19
|
+
*/
|
|
20
|
+
export declare function parseHrefImpl(url: string): Href | null;
|
|
21
|
+
//# sourceMappingURL=parse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/url/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAQvC;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAkCtD"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL → typed Href parser.
|
|
3
|
+
*
|
|
4
|
+
* Walks every registered route with a `path`, tries to match its compiled
|
|
5
|
+
* regex against the URL's pathname, and on a hit validates the extracted
|
|
6
|
+
* params + search through the route's Standard Schema. First match wins;
|
|
7
|
+
* iteration order follows `Object.keys` of the registered routes map.
|
|
8
|
+
*
|
|
9
|
+
* Validation failures return `null` rather than throwing — deep-link handlers
|
|
10
|
+
* fall back to the initial route on a bad URL instead of crashing the app.
|
|
11
|
+
*/
|
|
12
|
+
import { parse as parseUrl } from '@sigx/lynx-linking';
|
|
13
|
+
import { parseSearch } from './format.js';
|
|
14
|
+
import { getCompiledPath, getRouteRegistry } from './registry.js';
|
|
15
|
+
import { validateSync } from './validate.js';
|
|
16
|
+
/**
|
|
17
|
+
* Parse a URL string against the active route registry.
|
|
18
|
+
*
|
|
19
|
+
* Accepts both absolute URLs (`myapp://host/users/42?tab=about`) and
|
|
20
|
+
* pathname-only forms (`/users/42?tab=about`). Returns `null` if no route's
|
|
21
|
+
* `path` matches the URL or if schema validation rejects the extracted bits.
|
|
22
|
+
*/
|
|
23
|
+
export function parseHrefImpl(url) {
|
|
24
|
+
if (typeof url !== 'string' || url.length === 0)
|
|
25
|
+
return null;
|
|
26
|
+
// Use lynx-linking's parser for the scheme/host split — but accept paths
|
|
27
|
+
// that don't have a scheme too (raw `/users/42` is the common case from
|
|
28
|
+
// in-app routing).
|
|
29
|
+
const { pathname, query } = splitPathAndQuery(url);
|
|
30
|
+
if (!pathname)
|
|
31
|
+
return null;
|
|
32
|
+
const registry = getRouteRegistry();
|
|
33
|
+
const rawSearch = parseSearch(query);
|
|
34
|
+
for (const name of Object.keys(registry.routes)) {
|
|
35
|
+
const compiled = getCompiledPath(registry, name);
|
|
36
|
+
if (!compiled)
|
|
37
|
+
continue;
|
|
38
|
+
const match = compiled.regex.exec(pathname);
|
|
39
|
+
if (!match)
|
|
40
|
+
continue;
|
|
41
|
+
const rawParams = extractParams(compiled, match);
|
|
42
|
+
const def = registry.routes[name];
|
|
43
|
+
const paramsOutcome = validateSync(def.params, rawParams);
|
|
44
|
+
if (!paramsOutcome.ok)
|
|
45
|
+
continue;
|
|
46
|
+
const searchOutcome = validateSync(def.search, rawSearch);
|
|
47
|
+
if (!searchOutcome.ok)
|
|
48
|
+
continue;
|
|
49
|
+
return {
|
|
50
|
+
route: name,
|
|
51
|
+
params: paramsOutcome.value,
|
|
52
|
+
search: searchOutcome.value,
|
|
53
|
+
url,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
function extractParams(compiled, match) {
|
|
59
|
+
const out = {};
|
|
60
|
+
for (let i = 0; i < compiled.paramNames.length; i++) {
|
|
61
|
+
const raw = match[i + 1];
|
|
62
|
+
try {
|
|
63
|
+
out[compiled.paramNames[i]] = decodeURIComponent(raw);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
out[compiled.paramNames[i]] = raw;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
function splitPathAndQuery(url) {
|
|
72
|
+
// Drop the fragment before any path/query work — `#…` is a client-side
|
|
73
|
+
// anchor that must not leak into the route pathname or query values.
|
|
74
|
+
const hashIdx = url.indexOf('#');
|
|
75
|
+
const noHash = hashIdx >= 0 ? url.slice(0, hashIdx) : url;
|
|
76
|
+
// If the URL has a scheme, defer to lynx-linking's parser — handles
|
|
77
|
+
// `myapp://host/path?q` correctly. Otherwise treat the whole thing as
|
|
78
|
+
// pathname+query.
|
|
79
|
+
const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.\-]*:/.test(noHash);
|
|
80
|
+
if (hasScheme) {
|
|
81
|
+
const parsed = parseUrl(noHash);
|
|
82
|
+
// Reconstruct the query string from the already-parsed bag. We have
|
|
83
|
+
// to do this because parseUrl decoded keys/values, but parseSearch
|
|
84
|
+
// expects encoded form. Simpler: split the original ourselves.
|
|
85
|
+
const qIdx = noHash.indexOf('?');
|
|
86
|
+
const query = qIdx >= 0 ? noHash.slice(qIdx + 1) : '';
|
|
87
|
+
return { pathname: parsed.path, query };
|
|
88
|
+
}
|
|
89
|
+
const qIdx = noHash.indexOf('?');
|
|
90
|
+
if (qIdx === -1)
|
|
91
|
+
return { pathname: noHash, query: '' };
|
|
92
|
+
return { pathname: noHash.slice(0, qIdx), query: noHash.slice(qIdx + 1) };
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../../src/url/parse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module-level route registry for the URL bridge.
|
|
3
|
+
*
|
|
4
|
+
* `hrefFor()` and `parseHref()` are designed to be callable from anywhere —
|
|
5
|
+
* including outside the component tree (e.g. deep-link bootstrapping in
|
|
6
|
+
* native bridge code). They can't reach the in-tree `useNavRoutes` injectable
|
|
7
|
+
* directly, so we mirror the active routes here.
|
|
8
|
+
*
|
|
9
|
+
* `<NavigationRoot>` sets this synchronously during setup. Tests that exercise
|
|
10
|
+
* the URL helpers without a NavigationRoot can call `_setRouteRegistry`
|
|
11
|
+
* directly. The leading underscore is a convention: not part of the supported
|
|
12
|
+
* public API (test/integration use only).
|
|
13
|
+
*/
|
|
14
|
+
import type { CompiledPath } from './compile.js';
|
|
15
|
+
import type { RouteMap } from '../types.js';
|
|
16
|
+
interface RegistryState {
|
|
17
|
+
readonly routes: RouteMap;
|
|
18
|
+
/** Lazy-compiled paths keyed by route name. */
|
|
19
|
+
readonly compiled: Map<string, CompiledPath>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Set the active route registry. Called by `<NavigationRoot>` on setup and
|
|
23
|
+
* available to tests/bootstrap code as `_setRouteRegistry`.
|
|
24
|
+
*
|
|
25
|
+
* Last write wins — multi-root apps and rapid mount/unmount cycles in tests
|
|
26
|
+
* always see the most recent `<NavigationRoot>`'s routes. If you need a
|
|
27
|
+
* specific registry for a one-off call, pass it explicitly to the helper
|
|
28
|
+
* (parseHrefWithRoutes / hrefForWithRoutes — currently internal).
|
|
29
|
+
*/
|
|
30
|
+
export declare function _setRouteRegistry(routes: RouteMap): void;
|
|
31
|
+
/** Clear the registry. Mainly for tests that want to assert the unset path. */
|
|
32
|
+
export declare function _clearRouteRegistry(): void;
|
|
33
|
+
/** Get the active registry or throw a friendly error if none is set. */
|
|
34
|
+
export declare function getRouteRegistry(): RegistryState;
|
|
35
|
+
/**
|
|
36
|
+
* Look up (or lazily compile) the path template for a route name. Returns
|
|
37
|
+
* `null` when the route exists but declares no `path`.
|
|
38
|
+
*/
|
|
39
|
+
export declare function getCompiledPath(registry: RegistryState, name: string): CompiledPath | null;
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/url/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,UAAU,aAAa;IACnB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,+CAA+C;IAC/C,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CAChD;AAID;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAExD;AAED,+EAA+E;AAC/E,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,IAAI,aAAa,CAOhD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAU1F"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module-level route registry for the URL bridge.
|
|
3
|
+
*
|
|
4
|
+
* `hrefFor()` and `parseHref()` are designed to be callable from anywhere —
|
|
5
|
+
* including outside the component tree (e.g. deep-link bootstrapping in
|
|
6
|
+
* native bridge code). They can't reach the in-tree `useNavRoutes` injectable
|
|
7
|
+
* directly, so we mirror the active routes here.
|
|
8
|
+
*
|
|
9
|
+
* `<NavigationRoot>` sets this synchronously during setup. Tests that exercise
|
|
10
|
+
* the URL helpers without a NavigationRoot can call `_setRouteRegistry`
|
|
11
|
+
* directly. The leading underscore is a convention: not part of the supported
|
|
12
|
+
* public API (test/integration use only).
|
|
13
|
+
*/
|
|
14
|
+
import { compilePath } from './compile.js';
|
|
15
|
+
let current = null;
|
|
16
|
+
/**
|
|
17
|
+
* Set the active route registry. Called by `<NavigationRoot>` on setup and
|
|
18
|
+
* available to tests/bootstrap code as `_setRouteRegistry`.
|
|
19
|
+
*
|
|
20
|
+
* Last write wins — multi-root apps and rapid mount/unmount cycles in tests
|
|
21
|
+
* always see the most recent `<NavigationRoot>`'s routes. If you need a
|
|
22
|
+
* specific registry for a one-off call, pass it explicitly to the helper
|
|
23
|
+
* (parseHrefWithRoutes / hrefForWithRoutes — currently internal).
|
|
24
|
+
*/
|
|
25
|
+
export function _setRouteRegistry(routes) {
|
|
26
|
+
current = { routes, compiled: new Map() };
|
|
27
|
+
}
|
|
28
|
+
/** Clear the registry. Mainly for tests that want to assert the unset path. */
|
|
29
|
+
export function _clearRouteRegistry() {
|
|
30
|
+
current = null;
|
|
31
|
+
}
|
|
32
|
+
/** Get the active registry or throw a friendly error if none is set. */
|
|
33
|
+
export function getRouteRegistry() {
|
|
34
|
+
if (!current) {
|
|
35
|
+
throw new Error('[lynx-navigation] No route registry set — render a <NavigationRoot> first, or call _setRouteRegistry() for tests.');
|
|
36
|
+
}
|
|
37
|
+
return current;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Look up (or lazily compile) the path template for a route name. Returns
|
|
41
|
+
* `null` when the route exists but declares no `path`.
|
|
42
|
+
*/
|
|
43
|
+
export function getCompiledPath(registry, name) {
|
|
44
|
+
const def = registry.routes[name];
|
|
45
|
+
if (!def)
|
|
46
|
+
return null;
|
|
47
|
+
if (!def.path)
|
|
48
|
+
return null;
|
|
49
|
+
let compiled = registry.compiled.get(name);
|
|
50
|
+
if (!compiled) {
|
|
51
|
+
compiled = compilePath(def.path);
|
|
52
|
+
registry.compiled.set(name, compiled);
|
|
53
|
+
}
|
|
54
|
+
return compiled;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/url/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAS3C,IAAI,OAAO,GAAyB,IAAI,CAAC;AAEzC;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAgB;IAC9C,OAAO,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;AAC9C,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,mBAAmB;IAC/B,OAAO,GAAG,IAAI,CAAC;AACnB,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,gBAAgB;IAC5B,IAAI,CAAC,OAAO,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACX,mHAAmH,CACtH,CAAC;IACN,CAAC;IACD,OAAO,OAAO,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,QAAuB,EAAE,IAAY;IACjE,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,IAAI,CAAC,GAAG,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAC3B,IAAI,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC3C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACZ,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACjC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,QAAQ,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard Schema validation helper (sync only).
|
|
3
|
+
*
|
|
4
|
+
* `hrefFor` and `parseHref` run on hot paths (link rendering, deep-link
|
|
5
|
+
* resolution) so we restrict to sync validators. Zod/Valibot/ArkType are all
|
|
6
|
+
* sync, which covers the common case. Async validators throw a clear error.
|
|
7
|
+
*/
|
|
8
|
+
import type { StandardSchemaV1 } from '../types.js';
|
|
9
|
+
/** Outcome of a sync validation call — discriminated for explicit handling. */
|
|
10
|
+
export type ValidateOutcome = {
|
|
11
|
+
readonly ok: true;
|
|
12
|
+
readonly value: unknown;
|
|
13
|
+
} | {
|
|
14
|
+
readonly ok: false;
|
|
15
|
+
readonly issues: ReadonlyArray<string>;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Run a Standard Schema's `validate` synchronously. When the schema lacks a
|
|
19
|
+
* `validate` function (e.g. our test `fakeSchema`), passthrough — assume the
|
|
20
|
+
* input is already in the correct shape. This is a deliberate ergonomic
|
|
21
|
+
* choice so the type-spike fixtures stay terse.
|
|
22
|
+
*/
|
|
23
|
+
export declare function validateSync(schema: StandardSchemaV1 | undefined, input: unknown): ValidateOutcome;
|
|
24
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/url/validate.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAmBpD,+EAA+E;AAC/E,MAAM,MAAM,eAAe,GACrB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAC9C;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC;AAErE;;;;;GAKG;AACH,wBAAgB,YAAY,CACxB,MAAM,EAAE,gBAAgB,GAAG,SAAS,EACpC,KAAK,EAAE,OAAO,GACf,eAAe,CAiBjB"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard Schema validation helper (sync only).
|
|
3
|
+
*
|
|
4
|
+
* `hrefFor` and `parseHref` run on hot paths (link rendering, deep-link
|
|
5
|
+
* resolution) so we restrict to sync validators. Zod/Valibot/ArkType are all
|
|
6
|
+
* sync, which covers the common case. Async validators throw a clear error.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Run a Standard Schema's `validate` synchronously. When the schema lacks a
|
|
10
|
+
* `validate` function (e.g. our test `fakeSchema`), passthrough — assume the
|
|
11
|
+
* input is already in the correct shape. This is a deliberate ergonomic
|
|
12
|
+
* choice so the type-spike fixtures stay terse.
|
|
13
|
+
*/
|
|
14
|
+
export function validateSync(schema, input) {
|
|
15
|
+
if (!schema)
|
|
16
|
+
return { ok: true, value: input };
|
|
17
|
+
const validate = schema['~standard']?.validate;
|
|
18
|
+
if (!validate)
|
|
19
|
+
return { ok: true, value: input };
|
|
20
|
+
const result = validate(input);
|
|
21
|
+
if (isPromiseLike(result)) {
|
|
22
|
+
throw new Error('[lynx-navigation] Async schema validation is not supported on the URL bridge — use a sync validator (Zod/Valibot/ArkType are all sync).');
|
|
23
|
+
}
|
|
24
|
+
if (result.issues !== undefined && result.issues.length > 0) {
|
|
25
|
+
return {
|
|
26
|
+
ok: false,
|
|
27
|
+
issues: result.issues.map((i) => i.message),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
return { ok: true, value: result.value };
|
|
31
|
+
}
|
|
32
|
+
function isPromiseLike(v) {
|
|
33
|
+
return (v !== null
|
|
34
|
+
&& typeof v === 'object'
|
|
35
|
+
&& typeof v.then === 'function');
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/url/validate.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA0BH;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CACxB,MAAoC,EACpC,KAAc;IAEd,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAC/C,MAAM,QAAQ,GAAI,MAAgC,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC;IAC1E,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IACjD,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/B,IAAI,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACX,yIAAyI,CAC5I,CAAC;IACN,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1D,OAAO;YACH,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;SAC9C,CAAC;IACN,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAG,MAA6B,CAAC,KAAK,EAAE,CAAC;AACrE,CAAC;AAED,SAAS,aAAa,CAAI,CAAU;IAChC,OAAO,CACH,CAAC,KAAK,IAAI;WACP,OAAO,CAAC,KAAK,QAAQ;WACrB,OAAQ,CAAwB,CAAC,IAAI,KAAK,UAAU,CAC1D,CAAC;AACN,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sigx/lynx-navigation",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Type-first native
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Type-first native navigator for sigx-lynx — Stack, Tabs, Drawer, modals, lazy routes, deep links",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./
|
|
7
|
-
"types": "./
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
8
|
"exports": {
|
|
9
|
-
".":
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
10
13
|
},
|
|
11
14
|
"files": [
|
|
12
15
|
"src",
|
|
13
16
|
"dist"
|
|
14
17
|
],
|
|
15
18
|
"peerDependencies": {
|
|
16
|
-
"@sigx/lynx": "^0.1.
|
|
17
|
-
"@sigx/lynx
|
|
19
|
+
"@sigx/lynx-motion": "^0.1.1",
|
|
20
|
+
"@sigx/lynx": "^0.1.4",
|
|
21
|
+
"@sigx/lynx-linking": "^0.1.2"
|
|
18
22
|
},
|
|
19
23
|
"peerDependenciesMeta": {
|
|
20
24
|
"@sigx/lynx-linking": {
|
|
@@ -22,18 +26,43 @@
|
|
|
22
26
|
}
|
|
23
27
|
},
|
|
24
28
|
"devDependencies": {
|
|
25
|
-
"typescript": "^
|
|
26
|
-
"vitest": "^
|
|
27
|
-
"@sigx/lynx": "^0.1.
|
|
28
|
-
"@sigx/lynx-linking": "^0.1.
|
|
29
|
-
"@sigx/testing
|
|
29
|
+
"typescript": "^6.0.3",
|
|
30
|
+
"vitest": "^4.1.6",
|
|
31
|
+
"@sigx/lynx": "^0.1.4",
|
|
32
|
+
"@sigx/lynx-linking": "^0.1.2",
|
|
33
|
+
"@sigx/lynx-testing": "^0.2.6",
|
|
34
|
+
"@sigx/lynx-motion": "^0.1.1"
|
|
30
35
|
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"sigx",
|
|
38
|
+
"lynx",
|
|
39
|
+
"navigation",
|
|
40
|
+
"router",
|
|
41
|
+
"stack",
|
|
42
|
+
"tabs",
|
|
43
|
+
"drawer",
|
|
44
|
+
"deep-linking",
|
|
45
|
+
"type-safe"
|
|
46
|
+
],
|
|
31
47
|
"author": "Andreas Ekdahl",
|
|
32
48
|
"license": "MIT",
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "git+https://github.com/signalxjs/lynx.git",
|
|
52
|
+
"directory": "packages/lynx-navigation"
|
|
53
|
+
},
|
|
54
|
+
"homepage": "https://github.com/signalxjs/lynx/tree/main/packages/lynx-navigation",
|
|
55
|
+
"bugs": {
|
|
56
|
+
"url": "https://github.com/signalxjs/lynx/issues"
|
|
57
|
+
},
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"access": "public"
|
|
60
|
+
},
|
|
33
61
|
"scripts": {
|
|
34
|
-
"build": "tsc
|
|
35
|
-
"dev": "tsc --
|
|
62
|
+
"build": "tsc",
|
|
63
|
+
"dev": "tsc --watch",
|
|
36
64
|
"test": "vitest run",
|
|
37
|
-
"test:types": "tsc --noEmit"
|
|
65
|
+
"test:types": "tsc --noEmit",
|
|
66
|
+
"bench": "vitest bench --run"
|
|
38
67
|
}
|
|
39
68
|
}
|