boltdocs 3.2.1 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/bin/boltdocs.js +20 -1
  2. package/bin/preloader.mjs +28 -0
  3. package/dist/{adapters-ByYY44Zy.mjs → adapters-BmB4TKCc.mjs} +1 -1
  4. package/dist/{adapters-DvmtpdSF.cjs → adapters-Cp1Maurn.cjs} +1 -1
  5. package/dist/audit-CLXjMzwQ.mjs +11 -0
  6. package/dist/audit-COgjnQ5n.cjs +11 -0
  7. package/dist/build-BS2hefiL.cjs +6 -0
  8. package/dist/build-C5mNh9D-.mjs +6 -0
  9. package/dist/cache-CJE9nWLE.cjs +6 -0
  10. package/dist/cache-OY4HzIXl.mjs +6 -0
  11. package/dist/callout-oI_LCbxS.d.ts +71 -0
  12. package/dist/callout-plqx9Dw-.d.cts +71 -0
  13. package/dist/client/index.cjs +1265 -569
  14. package/dist/client/index.d.cts +3 -542
  15. package/dist/client/index.d.ts +3 -542
  16. package/dist/client/index.js +1167 -521
  17. package/dist/client/mdx.cjs +1 -1
  18. package/dist/client/mdx.d.cts +24 -2
  19. package/dist/client/mdx.d.ts +24 -2
  20. package/dist/client/mdx.js +1 -1
  21. package/dist/client/primitives.cjs +14 -4
  22. package/dist/client/primitives.d.cts +281 -38
  23. package/dist/client/primitives.d.ts +281 -38
  24. package/dist/client/primitives.js +5 -5
  25. package/dist/client/router.cjs +49 -0
  26. package/dist/client/router.d.cts +3 -0
  27. package/dist/client/router.d.ts +3 -0
  28. package/dist/client/router.js +8 -0
  29. package/dist/client/theme/reset.css +40 -4
  30. package/dist/{config-BpzaPsJj.d.cts → config-BoRlz0Ca.d.mts} +157 -14
  31. package/dist/{config-xtS0ZkMm.d.mts → config-CTsX_tGM.d.cts} +157 -14
  32. package/dist/config-Ceib-AOH.cjs +7 -0
  33. package/dist/config-DGLQsy7y.mjs +7 -0
  34. package/dist/{package-BcISI5-P.cjs → config-files-BYHDOrKn.mjs} +1 -1
  35. package/dist/config-files-D9cdBMl2.cjs +6 -0
  36. package/dist/config-jJh8WYJI.cjs +6 -0
  37. package/dist/csp-C4LmN1ko.cjs +6 -0
  38. package/dist/csp-D6JEOqKh.mjs +6 -0
  39. package/dist/dev-Ch3hEe5Y.cjs +6 -0
  40. package/dist/dev-QPNxysrd.mjs +6 -0
  41. package/dist/{docs-layout-BdtZ9z6s.js → docs-layout-CbZ7LDRe.js} +504 -249
  42. package/dist/{docs-layout-CFWI15Id.cjs → docs-layout-vzfXed1Q.cjs} +516 -254
  43. package/dist/{doctor-BBtmNJE0.cjs → doctor-B1_YL0Wt.cjs} +1 -1
  44. package/dist/doctor-C7whl4ED.cjs +6 -0
  45. package/dist/doctor-DmQnml2k.mjs +6 -0
  46. package/dist/{generator-COepVWGi.cjs → generator-BlW45ST7.cjs} +5 -3
  47. package/dist/{generator-CcOBN5vI.mjs → generator-BwknNejd.mjs} +5 -3
  48. package/dist/{handler-XX7cLTqe.cjs → handler-CDEoKARD.cjs} +2 -2
  49. package/dist/{handler-DzQae4eA.mjs → handler-D7UyZlPm.mjs} +2 -2
  50. package/dist/{image-Bz87fCla.js → image-B-NDSDuY.js} +129 -11
  51. package/dist/{image-C4TlpMHn.cjs → image-Hnaa2bJd.cjs} +175 -9
  52. package/dist/index-C56bL-A-.d.cts +1344 -0
  53. package/dist/{sidebar-COTaMHR-.d.ts → index-CPUUKzxD.d.cts} +344 -327
  54. package/dist/{sidebar-CrEdJuHX.d.cts → index-Ci4kfOHF.d.ts} +344 -327
  55. package/dist/index-Cu9H6CRa.d.ts +1344 -0
  56. package/dist/link-CgdI6O2o.js +872 -0
  57. package/dist/{link-D1Gjkb-2.cjs → link-vZjmD-es.cjs} +406 -223
  58. package/dist/{mdx-BuO6a1f7.js → mdx-BRT71V5Q.js} +211 -168
  59. package/dist/{mdx-CD-tSjRG.cjs → mdx-D0Absdq5.cjs} +225 -169
  60. package/dist/native-BNCY7PRF.cjs +6 -0
  61. package/dist/native-Di9Fnfrv.mjs +6 -0
  62. package/dist/native-Kre2T5eS.cjs +6 -0
  63. package/dist/node/cache.cjs +1 -1
  64. package/dist/node/cache.d.cts +19 -3
  65. package/dist/node/cache.d.mts +19 -3
  66. package/dist/node/cache.mjs +1 -1
  67. package/dist/node/cli-entry.cjs +1 -9
  68. package/dist/node/cli-entry.mjs +1 -9
  69. package/dist/node/index.cjs +1 -1
  70. package/dist/node/index.d.cts +245 -17
  71. package/dist/node/index.d.mts +246 -18
  72. package/dist/node/index.mjs +1 -1
  73. package/dist/node/mdx/shiki-adapter.cjs +1 -1
  74. package/dist/node/mdx/shiki-adapter.d.cts +24 -3
  75. package/dist/node/mdx/shiki-adapter.d.mts +24 -2
  76. package/dist/node/mdx/shiki-adapter.mjs +1 -1
  77. package/dist/node-Du5UL5wo.mjs +23 -0
  78. package/dist/node-DunuGqyp.cjs +23 -0
  79. package/dist/{package-CCYEAYeA.mjs → package-DA848keM.mjs} +1 -1
  80. package/dist/package-wplzySjz.cjs +6 -0
  81. package/dist/pages-external-BBw8ObVK.mjs +6 -0
  82. package/dist/pages-external-DPiPFxxP.cjs +6 -0
  83. package/dist/parser-BqTOsEUa.cjs +6 -0
  84. package/dist/parser-CGMY2lJ7.mjs +6 -0
  85. package/dist/plugin-BcTzvHAJ.mjs +69 -0
  86. package/dist/plugin-TulVMt1z.cjs +69 -0
  87. package/dist/plugin-context-C-l90hFt.cjs +15 -0
  88. package/dist/plugin-context-wqHNSZum.mjs +15 -0
  89. package/dist/plugin-lifecycle-B1BzSHdn.cjs +6 -0
  90. package/dist/resolve-B7vWOd8R.cjs +6 -0
  91. package/dist/resolve-C2kKnk_5.mjs +6 -0
  92. package/dist/resolve-It1Iug83.cjs +6 -0
  93. package/dist/resolve-YdTrt_FG.mjs +6 -0
  94. package/dist/router-C3kygdRK.cjs +1285 -0
  95. package/dist/router-CZBxMRAH.js +987 -0
  96. package/dist/routes-BL4y79tb.mjs +6 -0
  97. package/dist/routes-C2nPecwu.cjs +6 -0
  98. package/dist/routes-U9L8psZ9.cjs +6 -0
  99. package/dist/{search-dialog-DHqwjJuG.cjs → search-dialog-BCOHQGSt.cjs} +1 -1
  100. package/dist/search-dialog-Bp_P1MPP.js +481 -0
  101. package/dist/{search-dialog-DFnlx8LO.js → search-dialog-BqkzDTq2.js} +107 -57
  102. package/dist/{search-dialog-BRFno7HD.cjs → search-dialog-CIeIUhAn.cjs} +218 -43
  103. package/dist/{search-dialog-D6TvjcuV.js → search-dialog-DtmxmfEu.js} +1 -1
  104. package/dist/{search-dialog-B1NERUNx.cjs → search-dialog-x_qHU8ty.cjs} +107 -58
  105. package/dist/server/index.cjs +1 -1
  106. package/dist/server/index.d.mts +1 -1
  107. package/dist/server/index.mjs +1 -1
  108. package/dist/update-check-BKqmYfwT.cjs +11 -0
  109. package/dist/update-check-QnrVR3q1.mjs +11 -0
  110. package/dist/utils-8lzafE49.mjs +7 -0
  111. package/dist/utils-ej6_SjIZ.cjs +7 -0
  112. package/dist/virtual-modules-Ca5tHk1X.cjs +30 -0
  113. package/dist/virtual-modules-Dlgt1fP0.mjs +30 -0
  114. package/package.json +18 -16
  115. package/src/shared/mdx-path.ts +12 -0
  116. package/src/shared/structured-data.ts +145 -0
  117. package/src/shared/types.ts +220 -16
  118. package/dist/banner-BMop1KLF.d.ts +0 -100
  119. package/dist/banner-CVD0GugR.d.cts +0 -100
  120. package/dist/cache-CO4Dy2uQ.cjs +0 -6
  121. package/dist/cache-Dk53iOo6.mjs +0 -6
  122. package/dist/config-CCXp3aXO.mjs +0 -7
  123. package/dist/config-CMd4XoFq.cjs +0 -6
  124. package/dist/config-DlGoEM9D.cjs +0 -7
  125. package/dist/csp-CRp_3U7a.cjs +0 -6
  126. package/dist/csp-oTC2ygvF.mjs +0 -6
  127. package/dist/doctor-Bv16QSrI.cjs +0 -7
  128. package/dist/doctor-DOAbSaiL.mjs +0 -7
  129. package/dist/headers-Ddwek8K7.mjs +0 -6
  130. package/dist/headers-dg4SDrjY.cjs +0 -6
  131. package/dist/link-2PzgTnaP.js +0 -673
  132. package/dist/meta-loader-C4Hu9RN9.mjs +0 -6
  133. package/dist/meta-loader-Dq1zcFnh.cjs +0 -6
  134. package/dist/node-BXtqM1uH.mjs +0 -6
  135. package/dist/node-BlVk4EVg.cjs +0 -104
  136. package/dist/node-DOf4zhWc.mjs +0 -104
  137. package/dist/node-aD1eisrR.cjs +0 -6
  138. package/dist/pages-external-BYa4JSda.mjs +0 -6
  139. package/dist/pages-external-XkYIs-d5.cjs +0 -6
  140. package/dist/parser-DCiEWkSk.cjs +0 -8
  141. package/dist/parser-qk9FIHJ7.mjs +0 -8
  142. package/dist/plugin-C95kvu6h.mjs +0 -73
  143. package/dist/plugin-DM3u2bVj.cjs +0 -73
  144. package/dist/routes-BW6zsk7Z.mjs +0 -6
  145. package/dist/routes-BgoIB5wb.cjs +0 -6
  146. package/dist/routes-CORVxc02.cjs +0 -6
  147. package/dist/search-dialog-BmxHiKwt.js +0 -312
  148. package/dist/shiki-adapter-CoQ6jyJ4.cjs +0 -6
  149. package/dist/shiki-adapter-D-aKw7PP.mjs +0 -6
  150. package/dist/utils-Crm9oPcV.mjs +0 -7
  151. package/dist/utils-D6Xtj0FZ.cjs +0 -7
  152. /package/dist/{errors-0zGHRLue.mjs → errors-N-b6Sn2G.mjs} +0 -0
  153. /package/dist/{index-1KsTDq4o.d.mts → index-lFoyzCXy.d.mts} +0 -0
  154. /package/dist/{types-generator-BXhv7Ill.cjs → types-generator-DDSA93P3.cjs} +0 -0
  155. /package/dist/{types-generator-bDPvUztF.mjs → types-generator-DG1GX1EZ.mjs} +0 -0
@@ -0,0 +1,987 @@
1
+ /**
2
+ * Boltdocs - https://boltdocs.vercel.app
3
+ * Copyright (c) 2026 Jesus Alcala
4
+ * Licensed under the MIT License.
5
+ */
6
+ import React, { createContext, startTransition, use, useCallback, useEffect, useMemo, useRef, useState } from "react";
7
+ import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
8
+
9
+ //#region src/client/router/utils.ts
10
+ function normalizeBasename(basename) {
11
+ if (!basename || basename === "/") return "/";
12
+ return basename.replace(/\/$/, "") || "/";
13
+ }
14
+ function hasBasename(path, basename) {
15
+ const normalized = normalizeBasename(basename);
16
+ if (normalized === "/") return true;
17
+ if (path === normalized) return true;
18
+ return path.startsWith(`${normalized}/`);
19
+ }
20
+ function stripBasename(path, basename) {
21
+ const normalized = normalizeBasename(basename);
22
+ if (normalized === "/") return path;
23
+ if (!hasBasename(path, normalized)) return path;
24
+ const stripped = path.slice(normalized.length);
25
+ return stripped.startsWith("/") ? stripped : `/${stripped}`;
26
+ }
27
+ function addBasename(path, basename) {
28
+ const normalized = normalizeBasename(basename);
29
+ if (normalized === "/") return path;
30
+ if (hasBasename(path, normalized)) return path;
31
+ if (path.startsWith("/")) return `${normalized}${path}`;
32
+ return `${normalized}/${path}`;
33
+ }
34
+
35
+ //#endregion
36
+ //#region src/client/router/context.tsx
37
+ function getActiveLocale() {
38
+ if (typeof globalThis === "undefined") return void 0;
39
+ const instance = globalThis[Symbol.for("__BDOCS_BOLTDOCS_INSTANCE__")];
40
+ if (instance && typeof instance === "object" && "currentLocale" in instance && typeof instance.currentLocale === "string") return instance.currentLocale;
41
+ if (typeof window === "undefined") return void 0;
42
+ try {
43
+ const raw = window.localStorage.getItem("boltdocs-user-preferences");
44
+ if (!raw) return void 0;
45
+ const preferences = JSON.parse(raw);
46
+ return typeof preferences.locale === "string" ? preferences.locale : void 0;
47
+ } catch {
48
+ return;
49
+ }
50
+ }
51
+ const defaultNavigate = () => {};
52
+ const defaultLocation = {
53
+ pathname: "/",
54
+ search: "",
55
+ hash: ""
56
+ };
57
+ const g$1 = typeof globalThis !== "undefined" ? globalThis : {};
58
+ function getGlobalContext(key, create) {
59
+ const existing = g$1[key];
60
+ if (existing) return existing;
61
+ const context = create();
62
+ g$1[key] = context;
63
+ return context;
64
+ }
65
+ const LocationContext = getGlobalContext("__BOLTDOCS_LOCATION_CONTEXT__", () => createContext(defaultLocation));
66
+ const NavigateContext = getGlobalContext("__BOLTDOCS_NAVIGATE_CONTEXT__", () => createContext(defaultNavigate));
67
+ const PrefetchContext = getGlobalContext("__BOLTDOCS_PREFETCH_CONTEXT__", () => createContext(async () => {}));
68
+ const RouteDataContext = getGlobalContext("__BOLTDOCS_ROUTE_DATA_CONTEXT__", () => createContext(null));
69
+ const MatchesContext = getGlobalContext("__BOLTDOCS_MATCHES_CONTEXT__", () => createContext([]));
70
+ function normalizeComparablePath(pathname) {
71
+ if (pathname.length <= 1) return pathname || "/";
72
+ return pathname.replace(/\/$/, "") || "/";
73
+ }
74
+ function dispatchNavigationStart(location) {
75
+ if (typeof window === "undefined") return;
76
+ window.dispatchEvent(new CustomEvent("boltdocs:navigation-start", { detail: location }));
77
+ }
78
+ function scheduleLocationUpdate(setLocation, nextLocation, viewTransitions) {
79
+ setTimeout(() => {
80
+ const enabled = typeof viewTransitions === "boolean" ? viewTransitions : viewTransitions?.enabled === true;
81
+ const startViewTransition = document.startViewTransition;
82
+ if (enabled && startViewTransition) {
83
+ const types = typeof viewTransitions === "object" ? viewTransitions.types : void 0;
84
+ startViewTransition.call(document, {
85
+ update: () => setLocation(nextLocation),
86
+ ...types?.length ? { types } : {}
87
+ });
88
+ return;
89
+ }
90
+ setLocation(nextLocation);
91
+ }, 0);
92
+ }
93
+ function getLocationFromWindow() {
94
+ if (typeof window === "undefined") return defaultLocation;
95
+ return {
96
+ pathname: window.location.pathname || "/",
97
+ search: window.location.search || "",
98
+ hash: window.location.hash || ""
99
+ };
100
+ }
101
+ const LocationProvider = ({ pathname: propPathname, children, loaderData = null, matches = [], basename, defaultLocale, prefetch: prefetchProp, viewTransitions }) => {
102
+ const [location, setLocation] = useState(() => {
103
+ if (typeof window !== "undefined") return getLocationFromWindow();
104
+ return propPathname ? {
105
+ pathname: propPathname,
106
+ search: "",
107
+ hash: ""
108
+ } : defaultLocation;
109
+ });
110
+ useEffect(() => {
111
+ if (typeof window === "undefined") return;
112
+ const handlePopState = () => {
113
+ const nextLocation = getLocationFromWindow();
114
+ scheduleLocationUpdate(setLocation, nextLocation, viewTransitions);
115
+ dispatchNavigationStart(nextLocation);
116
+ };
117
+ window.addEventListener("popstate", handlePopState);
118
+ return () => window.removeEventListener("popstate", handlePopState);
119
+ }, [viewTransitions]);
120
+ const prefetched = useRef(/* @__PURE__ */ new Set());
121
+ const fallbackPrefetch = useCallback(async (to) => {
122
+ if (typeof window === "undefined" || !to || prefetched.current.has(to)) return;
123
+ prefetched.current.add(to);
124
+ }, []);
125
+ const prefetch = prefetchProp ?? fallbackPrefetch;
126
+ const navigate = useCallback((to, options) => {
127
+ if (typeof window === "undefined") return;
128
+ if (!to) return;
129
+ const normalizedBasename = normalizeBasename(basename);
130
+ if (/^https?:\/\//i.test(to) || to.startsWith("//")) {
131
+ window.location.href = to;
132
+ return;
133
+ }
134
+ if (to.startsWith("#")) {
135
+ const newUrl = new URL(to, window.location.href);
136
+ const nextLocation = {
137
+ pathname: window.location.pathname,
138
+ search: window.location.search,
139
+ hash: newUrl.hash
140
+ };
141
+ if (nextLocation.pathname === window.location.pathname && nextLocation.search === window.location.search && nextLocation.hash === window.location.hash) {
142
+ if (options?.state !== void 0) if (options.replace) window.history.replaceState(options.state, "", newUrl.href);
143
+ else window.history.pushState(options.state, "", newUrl.href);
144
+ return;
145
+ }
146
+ if (options?.replace) window.history.replaceState(options.state ?? null, "", newUrl.href);
147
+ else window.history.pushState(options?.state ?? null, "", newUrl.href);
148
+ scheduleLocationUpdate(setLocation, nextLocation, options?.viewTransition === false ? false : viewTransitions);
149
+ dispatchNavigationStart(nextLocation);
150
+ return;
151
+ }
152
+ const resolvedUrl = new URL(to, window.location.href);
153
+ if (resolvedUrl.origin !== window.location.origin) {
154
+ window.location.href = to;
155
+ return;
156
+ }
157
+ let finalPath = resolvedUrl.pathname + resolvedUrl.search + resolvedUrl.hash;
158
+ if (normalizedBasename !== "/" && !hasBasename(resolvedUrl.pathname, normalizedBasename)) {
159
+ const activeLocale = getActiveLocale();
160
+ if (activeLocale && defaultLocale && activeLocale !== defaultLocale && !finalPath.startsWith(`/${activeLocale}/`) && finalPath !== `/${activeLocale}`) finalPath = `/${activeLocale}${finalPath === "/" ? "" : finalPath}`;
161
+ } else if (normalizedBasename !== "/") finalPath = addBasename(finalPath, normalizedBasename);
162
+ const finalUrl = new URL(finalPath, window.location.href);
163
+ if (normalizeComparablePath(finalUrl.pathname) === normalizeComparablePath(window.location.pathname) && finalUrl.search === window.location.search && finalUrl.hash === window.location.hash) {
164
+ if (options?.state !== void 0) if (options.replace) window.history.replaceState(options.state, "", finalPath);
165
+ else window.history.pushState(options.state, "", finalPath);
166
+ return;
167
+ }
168
+ if (options?.replace) window.history.replaceState(options?.state ?? null, "", finalPath);
169
+ else window.history.pushState(options?.state ?? null, "", finalPath);
170
+ const nextLocation = {
171
+ pathname: finalUrl.pathname,
172
+ search: finalUrl.search,
173
+ hash: finalUrl.hash
174
+ };
175
+ scheduleLocationUpdate(setLocation, nextLocation, options?.viewTransition === false ? false : viewTransitions);
176
+ dispatchNavigationStart(nextLocation);
177
+ }, [
178
+ basename,
179
+ defaultLocale,
180
+ viewTransitions
181
+ ]);
182
+ useEffect(() => {
183
+ if (typeof window === "undefined") return;
184
+ const normalizedBasename = normalizeBasename(basename);
185
+ const handleClick = (e) => {
186
+ if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return;
187
+ let target = e.target;
188
+ while (target && target.nodeName !== "A") target = target.parentElement;
189
+ if (!target || target.nodeName !== "A") return;
190
+ const anchor = target;
191
+ const href = anchor.getAttribute("href");
192
+ if (!href || anchor.target === "_blank" || anchor.hasAttribute("download") || href.startsWith("#")) return;
193
+ if (/^https?:\/\//i.test(href) || href.startsWith("//")) return;
194
+ let resolvedPathname;
195
+ try {
196
+ const resolved = new URL(href, document.baseURI || window.location.href);
197
+ if (resolved.origin !== window.location.origin) return;
198
+ resolvedPathname = resolved.pathname;
199
+ } catch {
200
+ return;
201
+ }
202
+ const isUnderBasename = normalizedBasename === "/" || hasBasename(resolvedPathname, normalizedBasename);
203
+ const currentIsUnderBasename = normalizedBasename === "/" || hasBasename(window.location.pathname, normalizedBasename);
204
+ if (!isUnderBasename && currentIsUnderBasename) return;
205
+ e.preventDefault();
206
+ navigate(href);
207
+ };
208
+ document.addEventListener("click", handleClick);
209
+ return () => document.removeEventListener("click", handleClick);
210
+ }, [navigate, basename]);
211
+ return /* @__PURE__ */ jsx(LocationContext.Provider, {
212
+ value: location,
213
+ children: /* @__PURE__ */ jsx(NavigateContext.Provider, {
214
+ value: navigate,
215
+ children: /* @__PURE__ */ jsx(PrefetchContext.Provider, {
216
+ value: prefetch,
217
+ children: /* @__PURE__ */ jsx(RouteDataContext.Provider, {
218
+ value: loaderData,
219
+ children: /* @__PURE__ */ jsx(MatchesContext.Provider, {
220
+ value: matches,
221
+ children
222
+ })
223
+ })
224
+ })
225
+ })
226
+ });
227
+ };
228
+
229
+ //#endregion
230
+ //#region src/client/router/hooks.ts
231
+ function useLocation() {
232
+ return use(LocationContext);
233
+ }
234
+ function useNavigate() {
235
+ return use(NavigateContext);
236
+ }
237
+ function usePrefetch() {
238
+ return use(PrefetchContext);
239
+ }
240
+ function useRouteData() {
241
+ return use(RouteDataContext);
242
+ }
243
+ /** Alias for useRouteData — parity with react-router-dom's useLoaderData */
244
+ function useLoaderData() {
245
+ return use(RouteDataContext);
246
+ }
247
+ function useMatches() {
248
+ return use(MatchesContext);
249
+ }
250
+
251
+ //#endregion
252
+ //#region src/client/router/outlet.tsx
253
+ const g = typeof globalThis !== "undefined" ? globalThis : {};
254
+ const existingOutletContext = g.__BOLTDOCS_OUTLET_CONTEXT__;
255
+ const OutletContext = existingOutletContext ?? createContext(null);
256
+ if (!existingOutletContext) g.__BOLTDOCS_OUTLET_CONTEXT__ = OutletContext;
257
+ const Outlet = () => {
258
+ return /* @__PURE__ */ jsx(Fragment$1, { children: use(OutletContext) });
259
+ };
260
+
261
+ //#endregion
262
+ //#region src/client/router/renderer.tsx
263
+ function normalizePath(path) {
264
+ if (!path) return "/";
265
+ const normalized = path.replace(/\\/g, "/").replace(/\/+/g, "/");
266
+ if (normalized.length > 1 && normalized.endsWith("/")) return normalized.slice(0, -1);
267
+ return normalized;
268
+ }
269
+ function splitRoutePath(path) {
270
+ return normalizePath(path).replace(/^\//, "").split("/").filter((segment) => segment && segment !== ".");
271
+ }
272
+ const preparedRouteCache = /* @__PURE__ */ new WeakMap();
273
+ const routeMatchCache = /* @__PURE__ */ new WeakMap();
274
+ const routeSegmentsCache = /* @__PURE__ */ new WeakMap();
275
+ function getRouteSegments(route) {
276
+ let segments = routeSegmentsCache.get(route);
277
+ if (!segments) {
278
+ segments = route.path ? splitRoutePath(route.path) : [];
279
+ routeSegmentsCache.set(route, segments);
280
+ }
281
+ return segments;
282
+ }
283
+ function getPreparedRoutes(routes) {
284
+ const cached = preparedRouteCache.get(routes);
285
+ if (cached) return cached;
286
+ const prepared = routes.map((route, index) => ({
287
+ route,
288
+ index,
289
+ segments: getRouteSegments(route),
290
+ score: routeScore(route),
291
+ children: route.children ? getPreparedRoutes(route.children) : void 0
292
+ })).sort((a, b) => b.score - a.score || a.index - b.index).map(({ route, segments, score, children }) => ({
293
+ route,
294
+ segments,
295
+ score,
296
+ children
297
+ }));
298
+ preparedRouteCache.set(routes, prepared);
299
+ return prepared;
300
+ }
301
+ function matchRoutePath(preparedRoute, target) {
302
+ const route = preparedRoute.route;
303
+ if (route.index || route.path === "" || route.path === ".") return target.length === 0 ? {
304
+ consumed: 0,
305
+ exact: true,
306
+ wildcard: false,
307
+ params: {}
308
+ } : null;
309
+ if (!route.path) return {
310
+ consumed: 0,
311
+ exact: !route.children?.length && target.length === 0,
312
+ wildcard: false,
313
+ params: {}
314
+ };
315
+ const routeSegments = preparedRoute.segments;
316
+ if (routeSegments.length === 0) return target.length === 0 ? {
317
+ consumed: 0,
318
+ exact: true,
319
+ wildcard: false,
320
+ params: {}
321
+ } : null;
322
+ let consumed = 0;
323
+ const params = {};
324
+ for (const segment of routeSegments) {
325
+ if (segment === "*") {
326
+ params["*"] = target.slice(consumed).join("/");
327
+ return {
328
+ consumed: target.length,
329
+ exact: true,
330
+ wildcard: true,
331
+ params
332
+ };
333
+ }
334
+ const targetSegment = target[consumed];
335
+ if (targetSegment === void 0) return null;
336
+ if (segment.startsWith(":")) params[segment.slice(1)] = targetSegment;
337
+ else if (segment !== targetSegment) return null;
338
+ consumed++;
339
+ }
340
+ return {
341
+ consumed,
342
+ exact: consumed === target.length,
343
+ wildcard: false,
344
+ params
345
+ };
346
+ }
347
+ function routeScore(route) {
348
+ if (route.index || route.path === "" || route.path === ".") return 1e3;
349
+ if (route.path === "*") return -1e3;
350
+ if (!route.path) return 0;
351
+ return splitRoutePath(route.path).reduce((score, segment) => {
352
+ if (segment === "*") return score - 100;
353
+ if (segment.startsWith(":")) return score + 10;
354
+ return score + 100;
355
+ }, 0);
356
+ }
357
+ function matchRouteBranchInternal(routes, target) {
358
+ for (const preparedRoute of routes) {
359
+ const route = preparedRoute.route;
360
+ const match = matchRoutePath(preparedRoute, target);
361
+ if (!match) continue;
362
+ const remaining = target.slice(match.consumed);
363
+ if (preparedRoute.children?.length) {
364
+ const childBranch = matchRouteBranchInternal(preparedRoute.children, remaining);
365
+ if (childBranch.length > 0) return [{
366
+ route,
367
+ params: match.params
368
+ }, ...childBranch.map((child) => ({
369
+ route: child.route,
370
+ params: {
371
+ ...match.params,
372
+ ...child.params
373
+ }
374
+ }))];
375
+ if (!match.exact) continue;
376
+ }
377
+ if (match.exact || match.wildcard) return [{
378
+ route,
379
+ params: match.params
380
+ }];
381
+ }
382
+ return [];
383
+ }
384
+ function getTargetSegments(pathname, basename) {
385
+ const normalizedPathname = normalizePath(pathname);
386
+ const targets = [splitRoutePath(normalizedPathname)];
387
+ const normalizedBasename = normalizeBasename(basename);
388
+ if (normalizedBasename !== "/" && hasBasename(normalizedPathname, normalizedBasename)) targets.push(splitRoutePath(stripBasename(normalizedPathname, normalizedBasename)));
389
+ return targets;
390
+ }
391
+ function matchRouteBranchWithParams(routes, pathname, basename) {
392
+ const cacheKey = `${normalizePath(pathname)}\u0000${normalizeBasename(basename)}`;
393
+ let matches = routeMatchCache.get(routes);
394
+ if (!matches) {
395
+ matches = /* @__PURE__ */ new Map();
396
+ routeMatchCache.set(routes, matches);
397
+ }
398
+ const cached = matches.get(cacheKey);
399
+ if (cached) return cached;
400
+ const preparedRoutes = getPreparedRoutes(routes);
401
+ for (const target of getTargetSegments(pathname, basename)) {
402
+ const branch = matchRouteBranchInternal(preparedRoutes, target);
403
+ if (branch.length > 0) {
404
+ cacheRouteEntry(matches, cacheKey, branch);
405
+ return branch;
406
+ }
407
+ }
408
+ cacheRouteEntry(matches, cacheKey, []);
409
+ return [];
410
+ }
411
+ function matchRouteBranch(routes, pathname, basename) {
412
+ return matchRouteBranchWithParams(routes, pathname, basename).map((match) => match.route);
413
+ }
414
+ const resolvedLazyCache = /* @__PURE__ */ new WeakMap();
415
+ const pendingLazyCache = /* @__PURE__ */ new WeakMap();
416
+ const routeCacheScopes = /* @__PURE__ */ new WeakMap();
417
+ let nextRouteCacheScope = 0;
418
+ const prefetchCache = /* @__PURE__ */ new Map();
419
+ const loaderDataCache = /* @__PURE__ */ new Map();
420
+ const MAX_ROUTE_CACHE_ENTRIES = 128;
421
+ const MAX_ACTIVE_PREFETCHES = 2;
422
+ const MAX_PENDING_PREFETCHES = 16;
423
+ const ROUTE_RESOLUTION_TIMEOUT_MS = 4500;
424
+ const ROUTE_LOADER_TIMEOUT_MS = 4500;
425
+ const PREFETCH_SKIPPED = Symbol("prefetch-skipped");
426
+ function withTimeout(promise, timeoutMs, message) {
427
+ return new Promise((resolve, reject) => {
428
+ const timer = setTimeout(() => reject(new Error(message)), timeoutMs);
429
+ promise.then((value) => {
430
+ clearTimeout(timer);
431
+ resolve(value);
432
+ }, (error) => {
433
+ clearTimeout(timer);
434
+ reject(error);
435
+ });
436
+ });
437
+ }
438
+ function createPrefetchQueue() {
439
+ let activePrefetches = 0;
440
+ const pendingPrefetches = [];
441
+ const drain = () => {
442
+ while (activePrefetches < MAX_ACTIVE_PREFETCHES && pendingPrefetches.length > 0) {
443
+ const task = pendingPrefetches.shift();
444
+ if (!task) break;
445
+ activePrefetches++;
446
+ task.run().then(task.resolve, task.reject).finally(() => {
447
+ activePrefetches--;
448
+ drain();
449
+ });
450
+ }
451
+ };
452
+ return {
453
+ enqueue(run) {
454
+ if (pendingPrefetches.length >= MAX_PENDING_PREFETCHES) return PREFETCH_SKIPPED;
455
+ return new Promise((resolve, reject) => {
456
+ pendingPrefetches.push({
457
+ run,
458
+ resolve,
459
+ reject
460
+ });
461
+ drain();
462
+ });
463
+ },
464
+ clear() {
465
+ for (const task of pendingPrefetches.splice(0)) task.resolve();
466
+ }
467
+ };
468
+ }
469
+ function getRouteCacheScope(routes) {
470
+ let scope = routeCacheScopes.get(routes);
471
+ if (scope === void 0) {
472
+ scope = ++nextRouteCacheScope;
473
+ routeCacheScopes.set(routes, scope);
474
+ }
475
+ return scope;
476
+ }
477
+ function cacheRouteEntry(cache, key, value) {
478
+ if (cache.size >= MAX_ROUTE_CACHE_ENTRIES) {
479
+ const oldest = cache.keys().next().value;
480
+ if (oldest !== void 0) cache.delete(oldest);
481
+ }
482
+ cache.set(key, value);
483
+ }
484
+ async function resolveRouteBranch(branch) {
485
+ const resolved = [];
486
+ for (const route of branch) {
487
+ if (!route.element && !route.Component && route.lazy) try {
488
+ let result = resolvedLazyCache.get(route);
489
+ if (!result) {
490
+ let pending = pendingLazyCache.get(route);
491
+ if (!pending) {
492
+ pending = route.lazy();
493
+ pendingLazyCache.set(route, pending);
494
+ }
495
+ try {
496
+ result = await withTimeout(pending, ROUTE_RESOLUTION_TIMEOUT_MS, "Timed out while loading a documentation route");
497
+ } finally {
498
+ pendingLazyCache.delete(route);
499
+ }
500
+ resolvedLazyCache.set(route, result);
501
+ }
502
+ resolved.push({
503
+ ...route,
504
+ ...result
505
+ });
506
+ continue;
507
+ } catch (err) {
508
+ console.error("[RouteRenderer] Error resolving lazy route:", err);
509
+ }
510
+ resolved.push(route);
511
+ }
512
+ return resolved;
513
+ }
514
+ function asLoaderData(value) {
515
+ return value !== null && typeof value === "object" ? value : null;
516
+ }
517
+ function getLoaderDataKey(routeScope, pathname, search = "") {
518
+ return `${routeScope}:${pathname}${search}`;
519
+ }
520
+ function loadRouteData(key, matches, resolvedBranch, request) {
521
+ const cached = loaderDataCache.get(key);
522
+ if (cached) return cached;
523
+ const pending = withTimeout(Promise.all(resolvedBranch.map(async (route, index) => {
524
+ if (!route?.loader) return null;
525
+ try {
526
+ return asLoaderData(await route.loader({
527
+ request,
528
+ params: matches[index].params
529
+ }));
530
+ } catch (err) {
531
+ console.error("[RouteRenderer] loader error:", err);
532
+ throw err;
533
+ }
534
+ })).then((values) => {
535
+ const merged = {};
536
+ for (const value of values) if (value) Object.assign(merged, value);
537
+ return merged;
538
+ }), ROUTE_LOADER_TIMEOUT_MS, "Timed out while loading documentation data");
539
+ cacheRouteEntry(loaderDataCache, key, pending);
540
+ pending.catch(() => loaderDataCache.delete(key));
541
+ return pending;
542
+ }
543
+ function RouteContent({ routes, initialPathname, resolvedBranch: initialResolvedBranch, basename, hasLoaderData }) {
544
+ const location = useLocation();
545
+ const currentPathname = location.pathname || initialPathname || "/";
546
+ const currentSearch = location.search || "";
547
+ const currentHash = location.hash || "";
548
+ const routeMatches = useMemo(() => matchRouteBranchWithParams(routes, currentPathname, basename), [
549
+ routes,
550
+ currentPathname,
551
+ basename
552
+ ]);
553
+ const matchedBranch = useMemo(() => routeMatches.map((match) => match.route), [routeMatches]);
554
+ const isFirstRenderRef = useRef(true);
555
+ const isInitialNavigation = currentPathname === initialPathname && isFirstRenderRef.current;
556
+ const usesInitialResolvedBranch = !!initialResolvedBranch && isInitialNavigation;
557
+ const initialBranch = useMemo(() => usesInitialResolvedBranch ? initialResolvedBranch : matchedBranch, [
558
+ matchedBranch,
559
+ usesInitialResolvedBranch,
560
+ initialResolvedBranch
561
+ ]);
562
+ const initialLoaderData = asLoaderData(use(RouteDataContext));
563
+ const initialDataAvailable = isInitialNavigation && hasLoaderData === true;
564
+ const needsAsyncWork = initialBranch.some((route) => !usesInitialResolvedBranch && !route.element && !route.Component && !!route.lazy || !!route.loader && !initialDataAvailable);
565
+ const routeScope = getRouteCacheScope(routes);
566
+ const navigationKey = `${routeScope}\u0000${currentPathname}\u0000${currentSearch}`;
567
+ const outerData = initialDataAvailable ? initialLoaderData : null;
568
+ const outerDataRef = useRef(outerData);
569
+ outerDataRef.current = outerData;
570
+ const navigationIdRef = useRef(0);
571
+ const committedNavigationRef = useRef("");
572
+ const [resolved, setResolved] = useState(!needsAsyncWork ? {
573
+ branch: initialBranch,
574
+ key: navigationKey,
575
+ pathname: currentPathname,
576
+ search: currentSearch,
577
+ loaderData: outerData
578
+ } : null);
579
+ const routeNeedsAsyncWork = needsAsyncWork && resolved?.key !== navigationKey;
580
+ useEffect(() => {
581
+ isFirstRenderRef.current = false;
582
+ }, []);
583
+ useEffect(() => {
584
+ if (!routeNeedsAsyncWork) return;
585
+ const navigationId = ++navigationIdRef.current;
586
+ let cancelled = false;
587
+ const matches = routeMatches;
588
+ const branch = matchedBranch;
589
+ const requestUrl = typeof window !== "undefined" ? new URL(`${currentPathname}${currentSearch}`, window.location.origin).href : `http://localhost${currentPathname}${currentSearch}`;
590
+ resolveRouteBranch(branch).then(async (resolvedBranch) => {
591
+ if (cancelled || navigationId !== navigationIdRef.current) return;
592
+ let loaderData;
593
+ try {
594
+ loaderData = await loadRouteData(getLoaderDataKey(routeScope, currentPathname, currentSearch), matches, resolvedBranch, new Request(requestUrl));
595
+ } catch {
596
+ loaderData = {
597
+ ...outerDataRef.current || {},
598
+ __loaderError: true
599
+ };
600
+ }
601
+ if (cancelled || navigationId !== navigationIdRef.current) return;
602
+ startTransition(() => {
603
+ setResolved({
604
+ branch: resolvedBranch,
605
+ key: navigationKey,
606
+ pathname: currentPathname,
607
+ search: currentSearch,
608
+ loaderData
609
+ });
610
+ });
611
+ }).catch((error) => {
612
+ if (cancelled || navigationId !== navigationIdRef.current) return;
613
+ console.warn("[RouteRenderer] navigation error:", error);
614
+ startTransition(() => {
615
+ setResolved({
616
+ branch,
617
+ key: navigationKey,
618
+ pathname: currentPathname,
619
+ search: currentSearch,
620
+ loaderData: {
621
+ ...outerDataRef.current || {},
622
+ __routeError: true
623
+ }
624
+ });
625
+ });
626
+ });
627
+ return () => {
628
+ cancelled = true;
629
+ };
630
+ }, [
631
+ currentPathname,
632
+ currentSearch,
633
+ routeMatches,
634
+ matchedBranch,
635
+ routeNeedsAsyncWork,
636
+ navigationKey,
637
+ routeScope
638
+ ]);
639
+ const hasResolvedCurrentRoute = resolved?.key === navigationKey;
640
+ const hasRenderableBranch = useCallback((candidate) => candidate.some((route) => route.element || route.Component), []);
641
+ const previousBranch = resolved?.branch;
642
+ const lastRenderableBranchRef = useRef([]);
643
+ const branch = usesInitialResolvedBranch ? hasResolvedCurrentRoute ? resolved.branch : initialBranch : !routeNeedsAsyncWork || hasResolvedCurrentRoute ? hasResolvedCurrentRoute ? resolved.branch : initialBranch : hasRenderableBranch(previousBranch || []) ? previousBranch || [] : hasRenderableBranch(lastRenderableBranchRef.current) ? lastRenderableBranchRef.current : matchedBranch;
644
+ if (hasResolvedCurrentRoute && hasRenderableBranch(branch)) lastRenderableBranchRef.current = branch;
645
+ useEffect(() => {
646
+ if (typeof window === "undefined") return;
647
+ const commitKey = `${currentPathname}${currentSearch}${currentHash}`;
648
+ if (committedNavigationRef.current === commitKey) return;
649
+ if (routeNeedsAsyncWork && !hasResolvedCurrentRoute) return;
650
+ if (!hasRenderableBranch(branch)) return;
651
+ committedNavigationRef.current = commitKey;
652
+ window.dispatchEvent(new CustomEvent("boltdocs:route-commit", { detail: {
653
+ pathname: currentPathname,
654
+ search: currentSearch,
655
+ hash: currentHash
656
+ } }));
657
+ }, [
658
+ branch,
659
+ currentHash,
660
+ currentPathname,
661
+ currentSearch,
662
+ hasRenderableBranch,
663
+ hasResolvedCurrentRoute,
664
+ routeNeedsAsyncWork
665
+ ]);
666
+ if (branch.length === 0) return null;
667
+ let currentElement = null;
668
+ for (let i = branch.length - 1; i >= 0; i--) {
669
+ const route = branch[i];
670
+ let element = route.element;
671
+ if (!element && route.Component) {
672
+ const Comp = route.Component;
673
+ element = /* @__PURE__ */ jsx(Comp, {}, route.path ?? `${route.index ? "index" : "route"}-${route.locale ?? "none"}`);
674
+ }
675
+ if (!element) continue;
676
+ if (currentElement === null) currentElement = element;
677
+ else {
678
+ let wrappedElement = element;
679
+ if (React.isValidElement(element)) {
680
+ const el = element;
681
+ const existingChildren = el.props.children;
682
+ wrappedElement = React.cloneElement(el, { children: existingChildren ?? /* @__PURE__ */ jsx(Outlet, {}) });
683
+ }
684
+ currentElement = /* @__PURE__ */ jsx(OutletContext.Provider, {
685
+ value: currentElement,
686
+ children: wrappedElement
687
+ });
688
+ }
689
+ }
690
+ const finalData = hasResolvedCurrentRoute && resolved.loaderData ? resolved.loaderData : outerData;
691
+ return /* @__PURE__ */ jsx(RouteDataContext.Provider, {
692
+ value: finalData,
693
+ children: currentElement
694
+ });
695
+ }
696
+ const RouteRenderer = ({ routes, pathname, loaderData, resolvedBranch, basename, defaultLocale, hasLoaderData = false, prefetch: prefetchProp, viewTransitions }) => {
697
+ const prefetchQueueRef = useRef(null);
698
+ if (!prefetchQueueRef.current) prefetchQueueRef.current = createPrefetchQueue();
699
+ const prefetchQueue = prefetchQueueRef.current;
700
+ useEffect(() => {
701
+ return () => prefetchQueue.clear();
702
+ }, [prefetchQueue]);
703
+ const prefetch = useCallback(async (to) => {
704
+ if (typeof window === "undefined" || !to) return;
705
+ const target = new URL(to, window.location.href);
706
+ if (target.origin !== window.location.origin || target.hash) return;
707
+ const key = `${getRouteCacheScope(routes)}:${target.pathname}${target.search}`;
708
+ const cached = prefetchCache.get(key);
709
+ if (cached) return cached;
710
+ const pending = prefetchQueue.enqueue(async () => {
711
+ const matches = matchRouteBranchWithParams(routes, target.pathname, basename);
712
+ if (matches.length === 0) return;
713
+ const branch = await resolveRouteBranch(matches.map((match) => match.route));
714
+ await loadRouteData(getLoaderDataKey(getRouteCacheScope(routes), target.pathname, target.search), matches, branch, new Request(target.href));
715
+ });
716
+ if (pending === PREFETCH_SKIPPED) return;
717
+ cacheRouteEntry(prefetchCache, key, pending);
718
+ try {
719
+ await pending;
720
+ } catch (error) {
721
+ prefetchCache.delete(key);
722
+ throw error;
723
+ }
724
+ }, [
725
+ routes,
726
+ basename,
727
+ prefetchQueue
728
+ ]);
729
+ return /* @__PURE__ */ jsx(LocationProvider, {
730
+ pathname,
731
+ loaderData,
732
+ basename,
733
+ defaultLocale,
734
+ prefetch: prefetchProp || prefetch,
735
+ viewTransitions,
736
+ children: /* @__PURE__ */ jsx(RouteContent, {
737
+ routes,
738
+ initialPathname: pathname,
739
+ resolvedBranch,
740
+ basename,
741
+ hasLoaderData
742
+ })
743
+ });
744
+ };
745
+
746
+ //#endregion
747
+ //#region src/client/router/url-contract.ts
748
+ function normalizeUrlBase(base) {
749
+ if (!base || base === "/") return "/";
750
+ return (base.startsWith("/") ? base : `/${base}`).replace(/\/+/g, "/").replace(/\/$/, "") || "/";
751
+ }
752
+ function normalizeUrlPath(path) {
753
+ if (!path) return "/";
754
+ const normalized = `/${path}`.replace(/\/+/g, "/");
755
+ if (normalized.length > 1 && normalized.endsWith("/")) return normalized.slice(0, -1);
756
+ return normalized;
757
+ }
758
+ function hasUrlBase(pathname, base) {
759
+ const normalizedPath = normalizeUrlPath(pathname);
760
+ const normalizedBase = normalizeUrlBase(base);
761
+ if (normalizedBase === "/") return true;
762
+ return normalizedPath === normalizedBase || normalizedPath.startsWith(`${normalizedBase}/`);
763
+ }
764
+ function stripUrlBase(pathname, base) {
765
+ const normalizedPath = normalizeUrlPath(pathname);
766
+ const normalizedBase = normalizeUrlBase(base);
767
+ if (normalizedBase === "/") return normalizedPath;
768
+ if (!hasUrlBase(normalizedPath, normalizedBase)) return normalizedPath;
769
+ return normalizeUrlPath(normalizedPath.slice(normalizedBase.length) || "/");
770
+ }
771
+ function addUrlBase(pathname, base) {
772
+ const normalizedPath = normalizeUrlPath(pathname);
773
+ const normalizedBase = normalizeUrlBase(base);
774
+ if (normalizedBase === "/" || hasUrlBase(normalizedPath, normalizedBase)) return normalizedPath;
775
+ return normalizeUrlPath(`${normalizedBase}/${normalizedPath}`);
776
+ }
777
+ function getConfiguredLocales(config) {
778
+ if (!config.i18n) return [];
779
+ return Array.isArray(config.i18n.locales) ? [...config.i18n.locales] : Object.keys(config.i18n.locales);
780
+ }
781
+ function getConfiguredVersions(config) {
782
+ return config.versions?.versions.map((version) => version.path) || [];
783
+ }
784
+ function getVersionPrefixSegments(config) {
785
+ const prefix = config.versions?.prefix;
786
+ if (!prefix) return [];
787
+ return normalizeUrlPath(prefix).split("/").filter(Boolean);
788
+ }
789
+ function getVersionSegments(version, config) {
790
+ if (!version || !config.versions || !isConfiguredVersion(version, config)) return [];
791
+ return [...getVersionPrefixSegments(config), version];
792
+ }
793
+ function isConfiguredLocale(value, config) {
794
+ return !!value && getConfiguredLocales(config).includes(value);
795
+ }
796
+ function isConfiguredVersion(value, config) {
797
+ return !!value && getConfiguredVersions(config).includes(value);
798
+ }
799
+ function matchConfiguredVersion(parts, start, config) {
800
+ const prefix = getVersionPrefixSegments(config);
801
+ const versionIndex = start + prefix.length;
802
+ if (!prefix.every((segment, index) => parts[start + index] === segment) || !isConfiguredVersion(parts[versionIndex], config)) return { nextIndex: start };
803
+ return {
804
+ version: parts[versionIndex],
805
+ nextIndex: versionIndex + 1
806
+ };
807
+ }
808
+ function splitUrlReference(to) {
809
+ const firstDelimiter = to.search(/[?#]/);
810
+ if (firstDelimiter < 0) return {
811
+ pathname: to,
812
+ search: "",
813
+ hash: ""
814
+ };
815
+ const pathname = to.slice(0, firstDelimiter);
816
+ const suffix = to.slice(firstDelimiter);
817
+ const hashIndex = suffix.indexOf("#");
818
+ if (hashIndex < 0) return {
819
+ pathname,
820
+ search: suffix,
821
+ hash: ""
822
+ };
823
+ return {
824
+ pathname,
825
+ search: suffix.slice(0, hashIndex),
826
+ hash: suffix.slice(hashIndex)
827
+ };
828
+ }
829
+ function stripSiteProtocol(to) {
830
+ if (!to.startsWith("site:")) return {
831
+ value: to,
832
+ siteProtocol: false
833
+ };
834
+ return {
835
+ value: to.slice(5) || "/",
836
+ siteProtocol: true
837
+ };
838
+ }
839
+ /** Returns true for URI schemes such as `mailto:`, `tel:`, or `data:`. */
840
+ function hasUriScheme(value) {
841
+ return /^[a-z][a-z\d+.-]*:/i.test(value);
842
+ }
843
+ function normalizeSuffix(value, prefix) {
844
+ if (!value) return "";
845
+ return value.startsWith(prefix) ? value : `${prefix}${value}`;
846
+ }
847
+ function getRouteHint(pathname, routes) {
848
+ if (!routes) return void 0;
849
+ const normalized = normalizeUrlPath(pathname);
850
+ return routes.find((route) => normalizeUrlPath(route.path) === normalized);
851
+ }
852
+ function getCollectionFromPath(pathname, config) {
853
+ const parts = normalizeUrlPath(pathname).split("/").filter(Boolean);
854
+ const locales = getConfiguredLocales(config);
855
+ const collectionNames = Array.isArray(config.collections) ? config.collections : Object.keys(config.collections?.labels || {});
856
+ const collections = new Set(collectionNames);
857
+ let index = 0;
858
+ const versionMatch = matchConfiguredVersion(parts, index, config);
859
+ const version = versionMatch.version;
860
+ index = versionMatch.nextIndex;
861
+ let locale;
862
+ if (locales.includes(parts[index] || "")) {
863
+ locale = parts[index];
864
+ index++;
865
+ }
866
+ const collection = parts[index];
867
+ if (!collection || !collections.has(collection)) return { remainder: parts };
868
+ return {
869
+ collection,
870
+ locale,
871
+ version,
872
+ remainder: parts.slice(index + 1)
873
+ };
874
+ }
875
+ function classifyUrlPath(pathname, config, options = {}) {
876
+ if (options.kind) return options.kind;
877
+ const normalizedPath = normalizeUrlPath(pathname);
878
+ const hint = getRouteHint(normalizedPath, options.routes);
879
+ if (hint?.kind) return hint.kind;
880
+ if (hint?.collection) return "collection";
881
+ if (getCollectionFromPath(normalizedPath, config).collection) return "collection";
882
+ if (hasUrlBase(normalizedPath, config.base)) return "doc";
883
+ return "external";
884
+ }
885
+ function parseUrlReference(to, config, options = {}) {
886
+ const { value, siteProtocol } = stripSiteProtocol(to);
887
+ const split = splitUrlReference(value);
888
+ const pathname = normalizeUrlPath(split.pathname || "/");
889
+ const hadBase = hasUrlBase(pathname, config.base);
890
+ const kind = classifyUrlPath(pathname, config, options);
891
+ if (kind === "doc") {
892
+ const parts = stripUrlBase(pathname, config.base).split("/").filter(Boolean);
893
+ let index = 0;
894
+ const versionMatch = matchConfiguredVersion(parts, index, config);
895
+ const version = versionMatch.version;
896
+ index = versionMatch.nextIndex;
897
+ const locale = isConfiguredLocale(parts[index], config) ? parts[index] : void 0;
898
+ if (locale) index++;
899
+ return {
900
+ pathname,
901
+ routePath: normalizeUrlPath(parts.slice(index).join("/")),
902
+ search: split.search,
903
+ hash: split.hash,
904
+ kind,
905
+ locale,
906
+ version,
907
+ siteProtocol,
908
+ hadBase
909
+ };
910
+ }
911
+ if (kind === "collection") {
912
+ const collection = getCollectionFromPath(pathname, config);
913
+ return {
914
+ pathname,
915
+ routePath: normalizeUrlPath(collection.remainder.join("/")),
916
+ search: split.search,
917
+ hash: split.hash,
918
+ kind,
919
+ locale: collection.locale,
920
+ version: collection.version,
921
+ collection: collection.collection,
922
+ siteProtocol,
923
+ hadBase
924
+ };
925
+ }
926
+ const parts = pathname.split("/").filter(Boolean);
927
+ const locale = isConfiguredLocale(parts[0], config) ? parts[0] : void 0;
928
+ return {
929
+ pathname,
930
+ routePath: normalizeUrlPath((locale ? parts.slice(1) : parts).join("/")),
931
+ search: split.search,
932
+ hash: split.hash,
933
+ kind,
934
+ locale,
935
+ siteProtocol,
936
+ hadBase
937
+ };
938
+ }
939
+ function buildUrl(options, config) {
940
+ const routePath = normalizeUrlPath(options.path || "/");
941
+ const locale = options.locale && options.locale !== config.i18n?.defaultLocale ? options.locale : void 0;
942
+ const search = normalizeSuffix(options.search, "?");
943
+ const hash = normalizeSuffix(options.hash, "#");
944
+ let pathname;
945
+ if (options.kind === "doc") pathname = normalizeUrlPath([
946
+ normalizeUrlBase(config.base) === "/" ? void 0 : normalizeUrlBase(config.base).slice(1),
947
+ ...getVersionSegments(options.version, config),
948
+ locale,
949
+ routePath === "/" ? void 0 : routePath.slice(1)
950
+ ].filter(Boolean).join("/"));
951
+ else if (options.kind === "collection") {
952
+ if (!options.collection) throw new Error("A collection route requires a collection name");
953
+ pathname = normalizeUrlPath([
954
+ normalizeUrlBase(config.base) === "/" ? void 0 : normalizeUrlBase(config.base).slice(1),
955
+ ...getVersionSegments(options.version, config),
956
+ locale,
957
+ options.collection,
958
+ routePath === "/" ? void 0 : routePath.slice(1)
959
+ ].filter(Boolean).join("/"));
960
+ } else pathname = normalizeUrlPath([locale, routePath === "/" ? void 0 : routePath.slice(1)].filter(Boolean).join("/"));
961
+ return `${pathname}${search}${hash}`;
962
+ }
963
+ function resolveUrlReference(to, config, options = {}) {
964
+ const { value, siteProtocol } = stripSiteProtocol(to);
965
+ if (hasUriScheme(value) || /^(?:https?:)?\/\//i.test(value)) return to;
966
+ if (value.startsWith("#") || value.startsWith("?")) return siteProtocol ? value : to;
967
+ const parsed = parseUrlReference(value, config, options);
968
+ const kind = options.kind || parsed.kind;
969
+ const locale = options.locale ?? parsed.locale;
970
+ const version = options.version ?? parsed.version;
971
+ const derived = getCollectionFromPath(stripUrlBase(parsed.pathname, config.base), config);
972
+ const collection = parsed.collection ?? derived.collection;
973
+ const routePath = derived.collection && !parsed.collection ? normalizeUrlPath(derived.remainder.join("/")) : parsed.routePath;
974
+ return buildUrl({
975
+ kind: collection || parsed.kind === "collection" ? "collection" : kind,
976
+ path: routePath,
977
+ locale,
978
+ version,
979
+ collection,
980
+ search: parsed.search,
981
+ hash: parsed.hash,
982
+ hadBase: parsed.hadBase
983
+ }, config);
984
+ }
985
+
986
+ //#endregion
987
+ export { useRouteData as A, normalizeBasename as B, Outlet as C, useMatches as D, useLocation as E, PrefetchContext as F, RouteDataContext as I, defaultNavigate as L, LocationProvider as M, MatchesContext as N, useNavigate as O, NavigateContext as P, addBasename as R, resolveRouteBranch as S, useLoaderData as T, stripBasename as V, stripSiteProtocol as _, getConfiguredVersions as a, matchRouteBranch as b, hasUriScheme as c, isConfiguredVersion as d, normalizeUrlBase as f, splitUrlReference as g, resolveUrlReference as h, getConfiguredLocales as i, LocationContext as j, usePrefetch as k, hasUrlBase as l, parseUrlReference as m, buildUrl as n, getVersionPrefixSegments as o, normalizeUrlPath as p, classifyUrlPath as r, getVersionSegments as s, addUrlBase as t, isConfiguredLocale as u, stripUrlBase as v, OutletContext as w, matchRouteBranchWithParams as x, RouteRenderer as y, hasBasename as z };