@sigx/lynx-navigation 0.4.0 → 0.4.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.
Files changed (187) hide show
  1. package/dist/components/Drawer.js +74 -0
  2. package/dist/components/Drawer.js.map +1 -0
  3. package/dist/components/EdgeBackHandle.js +144 -0
  4. package/dist/components/EdgeBackHandle.js.map +1 -0
  5. package/dist/components/EntryScope.d.ts +1 -1
  6. package/dist/components/EntryScope.d.ts.map +1 -1
  7. package/dist/components/EntryScope.js +39 -0
  8. package/dist/components/EntryScope.js.map +1 -0
  9. package/dist/components/Header.js +103 -0
  10. package/dist/components/Header.js.map +1 -0
  11. package/dist/components/Layer.d.ts +2 -2
  12. package/dist/components/Layer.d.ts.map +1 -1
  13. package/dist/components/Layer.js +66 -0
  14. package/dist/components/Layer.js.map +1 -0
  15. package/dist/components/Link.d.ts +2 -2
  16. package/dist/components/Link.d.ts.map +1 -1
  17. package/dist/components/Link.js +51 -0
  18. package/dist/components/Link.js.map +1 -0
  19. package/dist/components/NavigationRoot.d.ts +2 -2
  20. package/dist/components/NavigationRoot.d.ts.map +1 -1
  21. package/dist/components/NavigationRoot.js +67 -0
  22. package/dist/components/NavigationRoot.js.map +1 -0
  23. package/dist/components/Screen.js +98 -0
  24. package/dist/components/Screen.js.map +1 -0
  25. package/dist/components/Stack.js +257 -0
  26. package/dist/components/Stack.js.map +1 -0
  27. package/dist/components/TabBar.d.ts +1 -1
  28. package/dist/components/TabBar.d.ts.map +1 -1
  29. package/dist/components/TabBar.js +63 -0
  30. package/dist/components/TabBar.js.map +1 -0
  31. package/dist/components/Tabs.d.ts +8 -3
  32. package/dist/components/Tabs.d.ts.map +1 -1
  33. package/dist/components/Tabs.js +168 -0
  34. package/dist/components/Tabs.js.map +1 -0
  35. package/dist/define-routes.d.ts +1 -1
  36. package/dist/define-routes.d.ts.map +1 -1
  37. package/{src/define-routes.d.ts → dist/define-routes.js} +4 -2
  38. package/dist/define-routes.js.map +1 -0
  39. package/dist/hooks/use-focus.js +87 -0
  40. package/dist/hooks/use-focus.js.map +1 -0
  41. package/dist/hooks/use-hardware-back.js +84 -0
  42. package/dist/hooks/use-hardware-back.js.map +1 -0
  43. package/dist/hooks/use-linking-nav.d.ts +3 -3
  44. package/dist/hooks/use-linking-nav.d.ts.map +1 -1
  45. package/dist/hooks/use-linking-nav.js +109 -0
  46. package/dist/hooks/use-linking-nav.js.map +1 -0
  47. package/dist/hooks/use-nav-internal.d.ts +2 -2
  48. package/dist/hooks/use-nav-internal.d.ts.map +1 -1
  49. package/dist/hooks/use-nav-internal.js +55 -0
  50. package/dist/hooks/use-nav-internal.js.map +1 -0
  51. package/dist/hooks/use-nav-serializer.d.ts +1 -1
  52. package/dist/hooks/use-nav-serializer.d.ts.map +1 -1
  53. package/dist/hooks/use-nav-serializer.js +181 -0
  54. package/dist/hooks/use-nav-serializer.js.map +1 -0
  55. package/dist/hooks/use-nav.d.ts +2 -2
  56. package/dist/hooks/use-nav.d.ts.map +1 -1
  57. package/dist/hooks/use-nav.js +11 -0
  58. package/dist/hooks/use-nav.js.map +1 -0
  59. package/dist/hooks/use-params.d.ts +1 -1
  60. package/dist/hooks/use-params.d.ts.map +1 -1
  61. package/{src/hooks/use-params.d.ts → dist/hooks/use-params.js} +6 -2
  62. package/dist/hooks/use-params.js.map +1 -0
  63. package/dist/hooks/use-screen-chrome.d.ts +1 -1
  64. package/dist/hooks/use-screen-chrome.d.ts.map +1 -1
  65. package/dist/hooks/use-screen-chrome.js +102 -0
  66. package/dist/hooks/use-screen-chrome.js.map +1 -0
  67. package/dist/hooks/use-screen-options.d.ts +1 -1
  68. package/dist/hooks/use-screen-options.d.ts.map +1 -1
  69. package/dist/hooks/use-screen-options.js +43 -0
  70. package/dist/hooks/use-screen-options.js.map +1 -0
  71. package/dist/hooks/use-search.d.ts +1 -1
  72. package/dist/hooks/use-search.d.ts.map +1 -1
  73. package/{src/hooks/use-search.d.ts → dist/hooks/use-search.js} +6 -2
  74. package/dist/hooks/use-search.js.map +1 -0
  75. package/dist/href.d.ts +2 -2
  76. package/dist/href.d.ts.map +1 -1
  77. package/dist/href.js +57 -0
  78. package/dist/href.js.map +1 -0
  79. package/dist/index.d.ts +33 -33
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +30 -1160
  82. package/dist/index.js.map +1 -1
  83. package/dist/internal/layer-plan.d.ts +1 -1
  84. package/dist/internal/layer-plan.d.ts.map +1 -1
  85. package/dist/internal/layer-plan.js +102 -0
  86. package/dist/internal/layer-plan.js.map +1 -0
  87. package/dist/internal/screen-registry.d.ts +1 -1
  88. package/dist/internal/screen-registry.d.ts.map +1 -1
  89. package/{src/internal/screen-registry.d.ts → dist/internal/screen-registry.js} +32 -21
  90. package/dist/internal/screen-registry.js.map +1 -0
  91. package/{src/internal/screen-width.d.ts → dist/internal/screen-width.js} +17 -2
  92. package/dist/internal/screen-width.js.map +1 -0
  93. package/dist/navigator/core.d.ts +3 -3
  94. package/dist/navigator/core.d.ts.map +1 -1
  95. package/dist/navigator/core.js +394 -0
  96. package/dist/navigator/core.js.map +1 -0
  97. package/dist/register.d.ts +1 -1
  98. package/dist/register.d.ts.map +1 -1
  99. package/dist/register.js +2 -0
  100. package/dist/register.js.map +1 -0
  101. package/dist/types.js +9 -0
  102. package/dist/types.js.map +1 -0
  103. package/dist/url/build.js +30 -0
  104. package/dist/url/build.js.map +1 -0
  105. package/dist/url/compile.js +83 -0
  106. package/dist/url/compile.js.map +1 -0
  107. package/dist/url/format.js +102 -0
  108. package/dist/url/format.js.map +1 -0
  109. package/dist/url/index.d.ts +6 -6
  110. package/dist/url/index.d.ts.map +1 -1
  111. package/dist/url/index.js +13 -0
  112. package/dist/url/index.js.map +1 -0
  113. package/dist/url/parse.d.ts +1 -1
  114. package/dist/url/parse.d.ts.map +1 -1
  115. package/dist/url/parse.js +94 -0
  116. package/dist/url/parse.js.map +1 -0
  117. package/dist/url/registry.d.ts +2 -2
  118. package/dist/url/registry.d.ts.map +1 -1
  119. package/{src/url/registry.d.ts → dist/url/registry.js} +28 -12
  120. package/dist/url/registry.js.map +1 -0
  121. package/dist/url/validate.d.ts +1 -1
  122. package/dist/url/validate.d.ts.map +1 -1
  123. package/dist/url/validate.js +37 -0
  124. package/dist/url/validate.js.map +1 -0
  125. package/package.json +16 -13
  126. package/src/components/EdgeBackHandle.tsx +2 -2
  127. package/src/components/EntryScope.tsx +3 -3
  128. package/src/components/Header.tsx +3 -3
  129. package/src/components/Layer.tsx +3 -3
  130. package/src/components/Link.tsx +4 -4
  131. package/src/components/NavigationRoot.tsx +6 -6
  132. package/src/components/Screen.tsx +3 -3
  133. package/src/components/Stack.tsx +8 -8
  134. package/src/components/TabBar.tsx +1 -1
  135. package/src/components/Tabs.tsx +8 -3
  136. package/src/define-routes.ts +1 -1
  137. package/src/hooks/use-focus.ts +2 -2
  138. package/src/hooks/use-hardware-back.ts +1 -1
  139. package/src/hooks/use-linking-nav.ts +4 -4
  140. package/src/hooks/use-nav-internal.ts +2 -2
  141. package/src/hooks/use-nav-serializer.ts +3 -3
  142. package/src/hooks/use-nav.ts +2 -2
  143. package/src/hooks/use-params.ts +2 -2
  144. package/src/hooks/use-screen-chrome.ts +3 -3
  145. package/src/hooks/use-screen-options.ts +3 -3
  146. package/src/hooks/use-search.ts +2 -2
  147. package/src/href.ts +6 -6
  148. package/src/index.ts +33 -33
  149. package/src/internal/layer-plan.ts +2 -2
  150. package/src/internal/screen-registry.ts +1 -1
  151. package/src/navigator/core.ts +3 -3
  152. package/src/register.ts +1 -1
  153. package/src/url/build.ts +2 -2
  154. package/src/url/index.ts +6 -6
  155. package/src/url/parse.ts +6 -6
  156. package/src/url/registry.ts +3 -3
  157. package/src/url/validate.ts +1 -1
  158. package/src/components/Drawer.d.ts +0 -55
  159. package/src/components/EdgeBackHandle.d.ts +0 -1
  160. package/src/components/EntryScope.d.ts +0 -25
  161. package/src/components/Header.d.ts +0 -6
  162. package/src/components/Layer.d.ts +0 -33
  163. package/src/components/Link.d.ts +0 -60
  164. package/src/components/NavigationRoot.d.ts +0 -36
  165. package/src/components/Screen.d.ts +0 -97
  166. package/src/components/Stack.d.ts +0 -90
  167. package/src/components/TabBar.d.ts +0 -38
  168. package/src/components/Tabs.d.ts +0 -109
  169. package/src/hooks/use-focus.d.ts +0 -45
  170. package/src/hooks/use-hardware-back.d.ts +0 -37
  171. package/src/hooks/use-linking-nav.d.ts +0 -91
  172. package/src/hooks/use-nav-internal.d.ts +0 -91
  173. package/src/hooks/use-nav-serializer.d.ts +0 -82
  174. package/src/hooks/use-nav.d.ts +0 -111
  175. package/src/hooks/use-screen-chrome.d.ts +0 -18
  176. package/src/hooks/use-screen-options.d.ts +0 -2
  177. package/src/href.d.ts +0 -54
  178. package/src/index.d.ts +0 -39
  179. package/src/internal/layer-plan.d.ts +0 -68
  180. package/src/navigator/core.d.ts +0 -96
  181. package/src/register.d.ts +0 -37
  182. package/src/types.d.ts +0 -217
  183. package/src/url/build.d.ts +0 -15
  184. package/src/url/compile.d.ts +0 -34
  185. package/src/url/format.d.ts +0 -28
  186. package/src/url/parse.d.ts +0 -20
  187. package/src/url/validate.d.ts +0 -23
@@ -1,4 +1,4 @@
1
- import type { ParamsOf, RouteMap, SearchOf } from './types';
1
+ import type { ParamsOf, RouteMap, SearchOf } from './types.js';
2
2
  /**
3
3
  * Module-augmentation surface for the user's typed route map.
4
4
  *
@@ -1 +1 @@
1
- {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE5D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,QAAQ;CAExB;AAED;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,SAAS;IAAE,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,CAAC,GAAG,QAAQ,CAAC;AAEnF,8EAA8E;AAC9E,MAAM,MAAM,OAAO,GAAG,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEtD,+CAA+C;AAC/C,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,OAAO,IAAI,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;AAE3E,+CAA+C;AAC/C,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,OAAO,IAAI,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,QAAQ;CAExB;AAED;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,SAAS;IAAE,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,CAAC,GAAG,QAAQ,CAAC;AAEnF,8EAA8E;AAC9E,MAAM,MAAM,OAAO,GAAG,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEtD,+CAA+C;AAC/C,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,OAAO,IAAI,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;AAE3E,+CAA+C;AAC/C,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,OAAO,IAAI,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=register.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.js","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":""}
package/dist/types.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Core types for @sigx/lynx-navigation.
3
+ *
4
+ * The type machinery here is the differentiating DX: route names, params, and
5
+ * search are all inferred end-to-end from the user's `defineRoutes` call so
6
+ * `nav.push('profile', { id: 42 })` is a TS error if `id` is typed as string.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Typed → URL: build the `url` field of an Href from a route's params/search.
3
+ *
4
+ * Mirror of parse.ts. Used by `hrefFor()` after schema validation succeeds.
5
+ */
6
+ import { formatSearch } from './format.js';
7
+ import { getCompiledPath, getRouteRegistry } from './registry.js';
8
+ /**
9
+ * Build the URL form of a route + params + search, or `null` if the route
10
+ * declares no `path` template (typed navigation still works — only deep-link
11
+ * serialization is unavailable).
12
+ *
13
+ * Params must already be valid for the route's schema (callers run this after
14
+ * `validateSync`). Search values are stringified as-is — schema-validated
15
+ * inputs survive round-tripping because `parseHref` re-runs the same schema.
16
+ */
17
+ export function buildUrl(routeName, params, search) {
18
+ const registry = getRouteRegistry();
19
+ const compiled = getCompiledPath(registry, routeName);
20
+ if (!compiled)
21
+ return null;
22
+ const formatted = compiled.format(
23
+ // The compiler accepts string|number; we widen to unknown and let it
24
+ // String()-coerce. Booleans/null get filtered by the missing-param
25
+ // check, which is what we want — boolean path params don't exist.
26
+ (params ?? {}));
27
+ const querystring = formatSearch(search);
28
+ return querystring.length > 0 ? `${formatted}?${querystring}` : formatted;
29
+ }
30
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/url/build.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAElE;;;;;;;;GAQG;AACH,MAAM,UAAU,QAAQ,CACpB,SAAiB,EACjB,MAA2C,EAC3C,MAA2C;IAE3C,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;IACpC,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACtD,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM;IAC7B,qEAAqE;IACrE,mEAAmE;IACnE,kEAAkE;IAClE,CAAC,MAAM,IAAI,EAAE,CAAoC,CACpD,CAAC;IACF,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACzC,OAAO,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,IAAI,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9E,CAAC"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Path template compiler.
3
+ *
4
+ * Turns a route's `path` (e.g. `/users/:id/posts/:postId`) into a compiled
5
+ * object that can both match a URL pathname against it and format a typed
6
+ * params object back into a URL.
7
+ *
8
+ * Supported syntax (intentionally minimal for v1):
9
+ * - Literal segments: `/users`, `/users/me`
10
+ * - Named params: `:id` (matches `[^/]+`)
11
+ * - Trailing slashes tolerated on match
12
+ *
13
+ * Out of scope for v1 (future-compatible — additions won't break v1 paths):
14
+ * - Wildcards `*`
15
+ * - Optional params `:id?`
16
+ * - Typed/constrained params `:id<number>` or `:id(\\d+)`
17
+ */
18
+ const PARAM_RE = /:([A-Za-z_][A-Za-z0-9_]*)/g;
19
+ /**
20
+ * Compile a path template. Throws on malformed input (duplicate param names,
21
+ * unexpected `:` syntax). Pure — safe to memoize.
22
+ */
23
+ export function compilePath(template) {
24
+ if (typeof template !== 'string') {
25
+ throw new TypeError(`compilePath: expected string, got ${typeof template}`);
26
+ }
27
+ if (template.length === 0) {
28
+ throw new Error('compilePath: path template must not be empty');
29
+ }
30
+ // Normalize: ensure leading `/`. Trailing slashes are tolerated on match,
31
+ // but the canonical formatted output preserves the template's trailing
32
+ // slash policy.
33
+ const normalized = template.startsWith('/') ? template : `/${template}`;
34
+ const paramNames = [];
35
+ // Build the regex by replacing :name with a capture group. We escape the
36
+ // surrounding literal text so paths with regex-special chars (`.`, `+`)
37
+ // match literally — only `:name` is treated as a placeholder.
38
+ let lastIndex = 0;
39
+ let pattern = '';
40
+ PARAM_RE.lastIndex = 0;
41
+ for (let m = PARAM_RE.exec(normalized); m !== null; m = PARAM_RE.exec(normalized)) {
42
+ const name = m[1];
43
+ if (paramNames.includes(name)) {
44
+ throw new Error(`compilePath: duplicate param name ':${name}' in '${template}'`);
45
+ }
46
+ paramNames.push(name);
47
+ pattern += escapeRegex(normalized.slice(lastIndex, m.index));
48
+ pattern += '([^/]+)';
49
+ lastIndex = m.index + m[0].length;
50
+ }
51
+ pattern += escapeRegex(normalized.slice(lastIndex));
52
+ // Trim a trailing slash from the pattern so `/users/` and `/users` both
53
+ // match `/users/:?`. We keep the formatter's output as-templated.
54
+ const matchPattern = pattern.endsWith('/') ? pattern.slice(0, -1) : pattern;
55
+ const regex = new RegExp(`^${matchPattern}/?$`);
56
+ return {
57
+ source: template,
58
+ paramNames,
59
+ regex,
60
+ format(params) {
61
+ let out = '';
62
+ let i = 0;
63
+ PARAM_RE.lastIndex = 0;
64
+ for (let m = PARAM_RE.exec(normalized); m !== null; m = PARAM_RE.exec(normalized)) {
65
+ const name = m[1];
66
+ const value = params[name];
67
+ if (value === undefined || value === null) {
68
+ throw new Error(`compilePath.format: missing required param ':${name}' for '${template}'`);
69
+ }
70
+ out += normalized.slice(i, m.index);
71
+ out += encodeURIComponent(String(value));
72
+ i = m.index + m[0].length;
73
+ }
74
+ out += normalized.slice(i);
75
+ return out;
76
+ },
77
+ };
78
+ }
79
+ const REGEX_SPECIALS = /[.*+?^${}()|[\]\\]/g;
80
+ function escapeRegex(s) {
81
+ return s.replace(REGEX_SPECIALS, '\\$&');
82
+ }
83
+ //# sourceMappingURL=compile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compile.js","sourceRoot":"","sources":["../../src/url/compile.ts"],"names":[],"mappings":"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"}
@@ -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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"}
@@ -4,10 +4,10 @@
4
4
  * Not re-exported from the package root. Public surface is `hrefFor` /
5
5
  * `parseHref` in ../href.ts plus `_setRouteRegistry` for tests/bootstrap.
6
6
  */
7
- export { compilePath, type CompiledPath } from './compile';
8
- export { buildUrl } from './build';
9
- export { parseHrefImpl } from './parse';
10
- export { formatSearch, parseSearch } from './format';
11
- export { _setRouteRegistry, _clearRouteRegistry, getRouteRegistry, getCompiledPath, } from './registry';
12
- export { validateSync, type ValidateOutcome } from './validate';
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
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +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,WAAW,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACrD,OAAO,EACH,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC"}
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"}
@@ -9,7 +9,7 @@
9
9
  * Validation failures return `null` rather than throwing — deep-link handlers
10
10
  * fall back to the initial route on a bad URL instead of crashing the app.
11
11
  */
12
- import type { Href } from '../href';
12
+ import type { Href } from '../href.js';
13
13
  /**
14
14
  * Parse a URL string against the active route registry.
15
15
  *
@@ -1 +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,SAAS,CAAC;AAQpC;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAkCtD"}
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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"}
@@ -11,8 +11,8 @@
11
11
  * directly. The leading underscore is a convention: not part of the supported
12
12
  * public API (test/integration use only).
13
13
  */
14
- import type { CompiledPath } from './compile';
15
- import type { RouteMap } from '../types';
14
+ import type { CompiledPath } from './compile.js';
15
+ import type { RouteMap } from '../types.js';
16
16
  interface RegistryState {
17
17
  readonly routes: RouteMap;
18
18
  /** Lazy-compiled paths keyed by route name. */
@@ -1 +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,WAAW,CAAC;AAE9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEzC,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"}
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"}
@@ -11,13 +11,8 @@
11
11
  * directly. The leading underscore is a convention: not part of the supported
12
12
  * public API (test/integration use only).
13
13
  */
14
- import type { CompiledPath } from './compile';
15
- import type { RouteMap } from '../types';
16
- interface RegistryState {
17
- readonly routes: RouteMap;
18
- /** Lazy-compiled paths keyed by route name. */
19
- readonly compiled: Map<string, CompiledPath>;
20
- }
14
+ import { compilePath } from './compile.js';
15
+ let current = null;
21
16
  /**
22
17
  * Set the active route registry. Called by `<NavigationRoot>` on setup and
23
18
  * available to tests/bootstrap code as `_setRouteRegistry`.
@@ -27,14 +22,35 @@ interface RegistryState {
27
22
  * specific registry for a one-off call, pass it explicitly to the helper
28
23
  * (parseHrefWithRoutes / hrefForWithRoutes — currently internal).
29
24
  */
30
- export declare function _setRouteRegistry(routes: RouteMap): void;
25
+ export function _setRouteRegistry(routes) {
26
+ current = { routes, compiled: new Map() };
27
+ }
31
28
  /** Clear the registry. Mainly for tests that want to assert the unset path. */
32
- export declare function _clearRouteRegistry(): void;
29
+ export function _clearRouteRegistry() {
30
+ current = null;
31
+ }
33
32
  /** Get the active registry or throw a friendly error if none is set. */
34
- export declare function getRouteRegistry(): RegistryState;
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
+ }
35
39
  /**
36
40
  * Look up (or lazily compile) the path template for a route name. Returns
37
41
  * `null` when the route exists but declares no `path`.
38
42
  */
39
- export declare function getCompiledPath(registry: RegistryState, name: string): CompiledPath | null;
40
- export {};
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"}
@@ -5,7 +5,7 @@
5
5
  * resolution) so we restrict to sync validators. Zod/Valibot/ArkType are all
6
6
  * sync, which covers the common case. Async validators throw a clear error.
7
7
  */
8
- import type { StandardSchemaV1 } from '../types';
8
+ import type { StandardSchemaV1 } from '../types.js';
9
9
  /** Outcome of a sync validation call — discriminated for explicit handling. */
10
10
  export type ValidateOutcome = {
11
11
  readonly ok: true;
@@ -1 +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,UAAU,CAAC;AAmBjD,+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"}
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@sigx/lynx-navigation",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Type-first native navigator for sigx-lynx — Stack, Tabs, Drawer, modals, lazy routes, deep links",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -16,9 +16,10 @@
16
16
  "dist"
17
17
  ],
18
18
  "peerDependencies": {
19
- "@sigx/lynx-linking": "^0.4.0",
20
- "@sigx/lynx": "^0.4.0",
21
- "@sigx/lynx-motion": "^0.4.0"
19
+ "@sigx/lynx": "^0.4.2",
20
+ "@sigx/lynx-linking": "^0.4.2",
21
+ "@sigx/lynx-icons": "^0.4.2",
22
+ "@sigx/lynx-motion": "^0.4.2"
22
23
  },
23
24
  "peerDependenciesMeta": {
24
25
  "@sigx/lynx-linking": {
@@ -26,13 +27,14 @@
26
27
  }
27
28
  },
28
29
  "devDependencies": {
30
+ "@typescript/native-preview": "7.0.0-dev.20260521.1",
29
31
  "typescript": "^6.0.3",
30
- "vitest": "^4.1.6",
31
- "@sigx/vite": "^0.4.3",
32
- "@sigx/lynx": "^0.4.0",
33
- "@sigx/lynx-linking": "^0.4.0",
34
- "@sigx/lynx-testing": "^0.4.0",
35
- "@sigx/lynx-motion": "^0.4.0"
32
+ "vitest": "^4.1.7",
33
+ "@sigx/lynx": "^0.4.2",
34
+ "@sigx/lynx-linking": "^0.4.2",
35
+ "@sigx/lynx-icons": "^0.4.2",
36
+ "@sigx/lynx-motion": "^0.4.2",
37
+ "@sigx/lynx-testing": "^0.4.2"
36
38
  },
37
39
  "keywords": [
38
40
  "sigx",
@@ -60,10 +62,11 @@
60
62
  "access": "public"
61
63
  },
62
64
  "scripts": {
63
- "build": "vite build && tsgo --emitDeclarationOnly",
64
- "dev": "vite build --watch",
65
+ "build": "node ../../scripts/clean.mjs dist && tsgo",
66
+ "dev": "tsgo --watch",
65
67
  "test": "vitest run",
66
68
  "test:types": "tsc --noEmit",
67
- "bench": "vitest bench --run"
69
+ "bench": "vitest bench --run",
70
+ "clean": "node ../../scripts/clean.mjs dist .turbo"
68
71
  }
69
72
  }
@@ -7,8 +7,8 @@ import {
7
7
  type MainThread,
8
8
  } from '@sigx/lynx';
9
9
  import { withTiming } from '@sigx/lynx-motion';
10
- import { useNavInternals } from '../hooks/use-nav-internal';
11
- import { SCREEN_WIDTH } from '../internal/screen-width';
10
+ import { useNavInternals } from '../hooks/use-nav-internal.js';
11
+ import { SCREEN_WIDTH } from '../internal/screen-width.js';
12
12
 
13
13
  /**
14
14
  * Edge-pan recognizer for iOS-style swipe-back. Mounts as an absolutely-