@markless/router 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,19 +1,11 @@
1
- import { i as normalizeRouteFileId, n as matchRouteManifest, r as normalizeRequestPathname, t as buildRouteManifestFromFileIds } from "./route-manifest-DPgGIyrS.js";
2
- import { n as parseRequestFile, r as transformRequestFileSource, t as normalizeRequestFileId } from "./request-files-C-0LLSsx.js";
1
+ import { c as PREFETCH_ATTRIBUTE, i as HTML_ATTRIBUTES_SLOT, l as REPLACE_ATTRIBUTE, o as routeMountTarget, s as LINK_ATTRIBUTE, t as rememberRequestLocals, u as SCROLL_ATTRIBUTE } from "./request-locals-z3rJTizs.js";
2
+ import { a as normalizeRequestPathname, c as parseRequestFile, i as matchRouteManifest, l as transformRequestFileSource, n as buildRouteManifestFromFileIds, o as normalizeRouteFileId, s as normalizeRequestFileId } from "./route-manifest-DMizh42B.js";
3
3
  import { createExternalDelegateStructureTokens, createExternalDelegateView } from "@markless/web/fns/external-delegate";
4
4
  import { render } from "@markless/web/render";
5
- import { MARKLESS_PENDING_MIN_VISIBLE_MS as MARKLESS_NAV_PENDING_MIN_MS, settleOrPendingDeadline } from "@markless/web";
6
- //#region src/navigation-hold.ts
7
- async function holdNavigationSwapUntilSettled(input) {
8
- const settled = input.runtime.whenAsyncBoundariesSettled?.();
9
- if (settled) {
10
- if (await settleOrPendingDeadline(settled, input.clock?.wait) === "deadline") await input.runtime.holdPendingSettleCommits?.(MARKLESS_NAV_PENDING_MIN_MS);
11
- }
12
- if (input.signal?.aborted) return false;
13
- }
14
- //#endregion
5
+ import { MARKLESS_PENDING_MIN_VISIBLE_MS as MARKLESS_NAV_PENDING_MIN_MS, settleOrPendingDeadline } from "@markless/web/fns/pending-timing";
15
6
  //#region src/route-state.ts
16
7
  const MARKLESS_ROUTER_ROUTE_EVENT = "marklessrouternavigate";
8
+ const MARKLESS_ROUTER_RENDERED_EVENT = "marklessrouterrendered";
17
9
  const MARKLESS_ROUTER_RENDERER_STARTED = "__marklessRouterRouteRendererStarted";
18
10
  function routePageProps(route) {
19
11
  const url = new URL(route.url);
@@ -31,6 +23,66 @@ function dispatchRouteUpdate(document, update) {
31
23
  document.dispatchEvent(new CustomEvent(MARKLESS_ROUTER_ROUTE_EVENT, { detail: update }));
32
24
  }
33
25
  //#endregion
26
+ //#region src/navigate.ts
27
+ /**
28
+ * Navigates to `href` exactly as a click on `<Link href={href}>` would: a page of this app swaps in
29
+ * place, anything else loads as a document. The promise resolves once the new page is in place; a
30
+ * navigation that loads a new document leaves this page, so its promise never settles.
31
+ * Runs in the browser only.
32
+ */
33
+ function navigate(href, options = {}) {
34
+ if (typeof document === "undefined") throw new Error("MARKLESS_ROUTER_NAVIGATE_SERVER: navigate() runs in the browser. Call it from an event handler or other browser code, not while the page renders on the server.");
35
+ const d = document;
36
+ const w = d.defaultView ?? window;
37
+ const url = new URL(href, d.baseURI);
38
+ const rendered = new Promise((resolve) => d.addEventListener(MARKLESS_ROUTER_RENDERED_EVENT, () => resolve(), { once: true }));
39
+ const anchor = d.createElement("a");
40
+ anchor.href = url.href;
41
+ anchor.hidden = true;
42
+ anchor.setAttribute(LINK_ATTRIBUTE, "");
43
+ if (options.replace) anchor.setAttribute(REPLACE_ATTRIBUTE, "");
44
+ if (options.scroll === false) anchor.setAttribute(SCROLL_ATTRIBUTE, "manual");
45
+ let reached = false;
46
+ let routed = false;
47
+ const last = (event) => {
48
+ if (event.target !== anchor) return;
49
+ reached = true;
50
+ routed = event.defaultPrevented;
51
+ event.preventDefault();
52
+ };
53
+ w.addEventListener("click", last);
54
+ d.body.append(anchor);
55
+ try {
56
+ anchor.dispatchEvent(new MouseEvent("click", {
57
+ bubbles: true,
58
+ cancelable: true,
59
+ composed: true,
60
+ button: 0
61
+ }));
62
+ } finally {
63
+ anchor.remove();
64
+ w.removeEventListener("click", last);
65
+ }
66
+ if (routed || !reached) return rendered;
67
+ const inPage = url.hash !== "" && withoutHash(url.href) === withoutHash(w.location.href);
68
+ if (options.replace) w.location.replace(url.href);
69
+ else w.location.assign(url.href);
70
+ return inPage ? Promise.resolve() : new Promise(() => {});
71
+ }
72
+ function withoutHash(href) {
73
+ const at = href.indexOf("#");
74
+ return at === -1 ? href : href.slice(0, at);
75
+ }
76
+ //#endregion
77
+ //#region src/navigation-hold.ts
78
+ async function holdNavigationSwapUntilSettled(input) {
79
+ const settled = input.runtime.whenAsyncBoundariesSettled?.();
80
+ if (settled) {
81
+ if (await settleOrPendingDeadline(settled, input.clock?.wait) === "deadline") await input.runtime.holdPendingSettleCommits?.(MARKLESS_NAV_PENDING_MIN_MS);
82
+ }
83
+ if (input.signal?.aborted) return false;
84
+ }
85
+ //#endregion
34
86
  //#region src/route-renderer.ts
35
87
  const CURRENT_ROUTE_CONTAINER = "__marklessRouterCurrentRouteContainer";
36
88
  function startRouteUpdateRenderer(document = window.document) {
@@ -48,22 +100,26 @@ async function renderRouteUpdate(document, update) {
48
100
  if (!artifact.renderData) throw new Error(`MARKLESS_ROUTER_RENDER_DATA_MISSING: Navigated route ${JSON.stringify(update.route.file)} has no linked render-data module.`);
49
101
  const props = routePageProps(update.route);
50
102
  const state = document;
103
+ const outgoing = state[CURRENT_ROUTE_CONTAINER];
51
104
  const container = await render({
52
105
  renderData: artifact.renderData,
53
106
  loadSymbol: artifact.loadSymbol,
54
107
  props
55
108
  }, {
56
- target: document.body,
109
+ target: routeMountTarget(document, outgoing?.root),
57
110
  beforeMount: async (incoming) => {
58
111
  if (await holdNavigationSwapUntilSettled({
59
112
  runtime: incoming.runtime,
60
113
  signal: update.signal
61
114
  }) === false) return false;
62
- state[CURRENT_ROUTE_CONTAINER]?.runtime?.dispose?.();
115
+ outgoing?.runtime?.dispose?.();
63
116
  return true;
64
117
  }
65
118
  });
66
- if (!update.signal?.aborted) state[CURRENT_ROUTE_CONTAINER] = container;
119
+ if (!update.signal?.aborted) {
120
+ state[CURRENT_ROUTE_CONTAINER] = container;
121
+ document.dispatchEvent(new CustomEvent(MARKLESS_ROUTER_RENDERED_EVENT));
122
+ }
67
123
  } finally {
68
124
  update.onRendered?.();
69
125
  }
@@ -71,9 +127,6 @@ async function renderRouteUpdate(document, update) {
71
127
  //#endregion
72
128
  //#region src/spa-navigation.ts
73
129
  const STARTED = "__marklessRouterSpaNavigationStarted";
74
- const LINK_ATTRIBUTE = "data-markless-router-link";
75
- const REPLACE_ATTRIBUTE = "data-markless-router-replace";
76
- const SCROLL_ATTRIBUTE = "data-markless-router-scroll";
77
130
  const LINK_INFO = "__marklessRouterLink";
78
131
  function hashRoutePath(url) {
79
132
  return url.hash.startsWith("#/") ? url.hash.slice(1) : void 0;
@@ -88,6 +141,7 @@ async function __marklessRouterStartSpaNavigation(options) {
88
141
  state[STARTED] = true;
89
142
  const context = {
90
143
  documentModuleLoader: options.documentModuleLoader,
144
+ documentNavigation: options.documentNavigation,
91
145
  manifest: buildRouteManifestFromFileIds(options.routeFileIds),
92
146
  pageModuleLoaders: options.pageModuleLoaders,
93
147
  preloadRouteModule: options.preloadRouteModule,
@@ -98,23 +152,30 @@ async function __marklessRouterStartSpaNavigation(options) {
98
152
  navigation.addEventListener("navigate", (event) => {
99
153
  handleNavigateEvent(event, context);
100
154
  });
101
- if (typeof __MARKLESS_DEBUG_ENABLED__ !== "undefined" && __MARKLESS_DEBUG_ENABLED__) await import("./debug-channel-CSzo-Zj8.js").then((n) => n.n).then((debug) => debug.__marklessDebugRegisterRouter(void 0, "spa-click-listener")).catch(() => {});
102
- if (typeof __MARKLESS_DEBUG_ENABLED__ !== "undefined" && __MARKLESS_DEBUG_ENABLED__) await import("./debug-channel-CSzo-Zj8.js").then((n) => n.n).then((debug) => debug.__marklessDebugRegisterRouter(void 0, "navigation-event")).catch(() => {});
155
+ if (typeof __MARKLESS_DEBUG_ENABLED__ !== "undefined" && __MARKLESS_DEBUG_ENABLED__) await import("./debug-channel-CewnC9fH.js").then((debug) => debug.__marklessDebugRegisterRouter(void 0, "spa-click-listener")).catch(() => {});
156
+ if (typeof __MARKLESS_DEBUG_ENABLED__ !== "undefined" && __MARKLESS_DEBUG_ENABLED__) await import("./debug-channel-CewnC9fH.js").then((debug) => debug.__marklessDebugRegisterRouter(void 0, "navigation-event")).catch(() => {});
103
157
  {
104
158
  const bootUrl = parseSameOriginUrl(runtimeWindow.location.href, runtimeWindow.location.href);
105
159
  const bootHashPath = bootUrl ? hashRoutePath(bootUrl) : void 0;
106
160
  if (bootUrl && bootHashPath && matchRouteManifest(bootHashPath, context.manifest)) renderRoute(bootUrl, context);
107
161
  }
108
162
  }
163
+ const pendingPolyfills = /* @__PURE__ */ new WeakMap();
109
164
  async function ensureNavigationRuntime(runtimeWindow = browserWindow(), loadPolyfill) {
110
- if (!runtimeWindow.navigation) {
111
- const { applyPolyfill } = await (loadPolyfill?.() ?? import("@virtualstate/navigation"));
112
- runtimeWindow.navigation = applyPolyfill({
113
- interceptEvents: false,
114
- window: runtimeWindow
115
- });
165
+ if (runtimeWindow.navigation) return runtimeWindow.navigation;
166
+ let pending = pendingPolyfills.get(runtimeWindow);
167
+ if (!pending) {
168
+ pending = (async () => {
169
+ const { applyPolyfill } = await (loadPolyfill?.() ?? import("@virtualstate/navigation"));
170
+ return runtimeWindow.navigation ??= applyPolyfill({
171
+ interceptEvents: false,
172
+ window: runtimeWindow
173
+ });
174
+ })();
175
+ pendingPolyfills.set(runtimeWindow, pending);
176
+ pending.catch(() => pendingPolyfills.delete(runtimeWindow));
116
177
  }
117
- return runtimeWindow.navigation;
178
+ return pending;
118
179
  }
119
180
  function handleNavigateEvent(event, context) {
120
181
  const url = routeUrl(event, context);
@@ -136,7 +197,10 @@ async function renderRoute(url, context, signal) {
136
197
  return;
137
198
  }
138
199
  preloadRouteModule(context, match.route.file);
139
- const [page, document] = await Promise.all([loadPageModule(), context.documentModuleLoader?.()]);
200
+ const [page, document] = await Promise.all([loadPageModule(), context.documentModuleLoader?.()]).catch((error) => {
201
+ context.window.location.assign(url.href);
202
+ throw error;
203
+ });
140
204
  if (signal?.aborted) return;
141
205
  const update = {
142
206
  document,
@@ -167,17 +231,17 @@ function warnUnknownHashRoute(url) {
167
231
  function handleLinkClick(event, context, navigation) {
168
232
  if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) return;
169
233
  const anchor = sourceAnchor(event);
170
- if (!anchor || !anchor.hasAttribute(LINK_ATTRIBUTE) || !isEligibleLink(anchor)) return;
234
+ if (!anchor || !anchor.hasAttribute("data-markless-router-link") || !isEligibleLink(anchor)) return;
171
235
  const url = parseSameOriginUrl(anchor.href, context.window.location.href);
172
236
  const match = url && matchRouteManifest(routePathname(url, context), context.manifest);
173
- if (!match) return;
237
+ if (!url || !match || context.documentNavigation === "document" && !hashRoutePath(url)) return;
174
238
  preloadRouteModule(context, match.route.file);
175
239
  event.preventDefault();
176
240
  navigation.navigate(url.href, {
177
- history: anchor.hasAttribute(REPLACE_ATTRIBUTE) ? "replace" : "push",
241
+ history: anchor.hasAttribute("data-markless-router-replace") ? "replace" : "push",
178
242
  info: {
179
243
  [LINK_INFO]: true,
180
- scroll: anchor.getAttribute(SCROLL_ATTRIBUTE) === "manual" ? "manual" : void 0
244
+ scroll: anchor.getAttribute("data-markless-router-scroll") === "manual" ? "manual" : void 0
181
245
  }
182
246
  });
183
247
  }
@@ -189,7 +253,7 @@ function preloadRouteModule(context, file) {
189
253
  function routeUrl(event, context) {
190
254
  const destination = parseSameOriginUrl(event.destination.url, context.window.location.href);
191
255
  const hashRouteNavigation = event.hashChange === true && !!destination && hashRoutePath(destination) !== void 0;
192
- if (!isMarklessRouterNavigation(event) && !hashRouteNavigation || event.canIntercept === false || event.navigationType === "reload" || event.hashChange && !hashRouteNavigation || event.downloadRequest != null || event.formData != null) return;
256
+ if (!isMarklessRouterNavigation(event) && !hashRouteNavigation || context.documentNavigation === "document" && !hashRouteNavigation || event.canIntercept === false || event.navigationType === "reload" || event.hashChange && !hashRouteNavigation || event.downloadRequest != null || event.formData != null) return;
193
257
  const matched = destination && matchRouteManifest(routePathname(destination), context.manifest) ? destination : void 0;
194
258
  if (!matched && hashRouteNavigation && destination) warnUnknownHashRoute(destination);
195
259
  return matched;
@@ -226,6 +290,7 @@ function browserWindow() {
226
290
  function __marklessCreateHttpContext(event) {
227
291
  const context = event.context ?? {};
228
292
  const url = event.url ?? new URL("http://localhost/");
293
+ if (event.req) rememberRequestLocals(event.req, context, event.res?.headers);
229
294
  return {
230
295
  locals: context,
231
296
  params: context.params ?? {},
@@ -236,9 +301,11 @@ function __marklessCreateHttpContext(event) {
236
301
  }
237
302
  const Html = Object.assign(function Html(props) {
238
303
  return props.children;
239
- }, { renderSsr(props = {}) {
304
+ }, { renderSsr(props = {}, renderContext) {
305
+ const { children, ...attributes } = props;
306
+ if (renderContext?.["__marklessRouterHtmlAttributes"]) Object.assign(renderContext[HTML_ATTRIBUTES_SLOT], attributes);
240
307
  return {
241
- html: props.children == null ? "" : String(props.children),
308
+ html: children == null ? "" : String(children),
242
309
  elementCount: 0
243
310
  };
244
311
  } });
@@ -252,7 +319,7 @@ const Link = Object.assign(function Link(props = {}) {
252
319
  return {
253
320
  root: anchor,
254
321
  view: linkExternalDelegateView(),
255
- liveHostNodes: new Map([["router:link", anchor]])
322
+ liveHostNodes: /* @__PURE__ */ new Map([["router:link", anchor]])
256
323
  };
257
324
  },
258
325
  renderSsr(props = {}, renderContext) {
@@ -285,9 +352,10 @@ function linkAnchorAttributes(props) {
285
352
  if (attributeValue !== void 0) attributes.set(name, attributeValue);
286
353
  }
287
354
  attributes.set("href", typeof props.href === "string" ? props.href : "#");
288
- attributes.set("data-markless-router-link", "");
289
- if (props.replace) attributes.set("data-markless-router-replace", "");
290
- if (props.scroll === false) attributes.set("data-markless-router-scroll", "manual");
355
+ attributes.set(LINK_ATTRIBUTE, "");
356
+ if (props.replace) attributes.set(REPLACE_ATTRIBUTE, "");
357
+ if (props.prefetch === false) attributes.set(PREFETCH_ATTRIBUTE, "none");
358
+ if (props.scroll === false) attributes.set(SCROLL_ATTRIBUTE, "manual");
291
359
  return [...attributes];
292
360
  }
293
361
  function isLinkInternalProp(name) {
@@ -306,4 +374,4 @@ function escapeHtml(value) {
306
374
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;");
307
375
  }
308
376
  //#endregion
309
- export { Html, Link, MARKLESS_ROUTER_ROUTE_EVENT, __marklessCreateHttpContext, __marklessRouterStartSpaNavigation, buildRouteManifestFromFileIds, dispatchRouteUpdate, ensureNavigationRuntime, matchRouteManifest, normalizeRequestFileId, normalizeRequestPathname, normalizeRouteFileId, parseRequestFile, routePageProps, startRouteUpdateRenderer, transformRequestFileSource };
377
+ export { Html, Link, MARKLESS_ROUTER_RENDERED_EVENT, MARKLESS_ROUTER_ROUTE_EVENT, __marklessCreateHttpContext, __marklessRouterStartSpaNavigation, buildRouteManifestFromFileIds, dispatchRouteUpdate, ensureNavigationRuntime, matchRouteManifest, navigate, normalizeRequestFileId, normalizeRequestPathname, normalizeRouteFileId, parseRequestFile, routePageProps, startRouteUpdateRenderer, transformRequestFileSource };
@@ -0,0 +1,26 @@
1
+ function protocolStateVersion(storage) {
2
+ return (storage?.length ?? 0) > 0 ? 2 : 1;
3
+ }
4
+ const PROTOCOL_INSTANCE_PATH = /^(?:[cpm]\d+:|r:[^:]*:)+/;
5
+ /** One island's identity segment, outermost of any path it prefixes. */
6
+ function protocolIslandSegment(islandIndex) {
7
+ return `m${islandIndex}:`;
8
+ }
9
+ /** The leading instance path of a composed id, or '' when the id is page-local. */
10
+ function protocolInstancePath(id) {
11
+ return PROTOCOL_INSTANCE_PATH.exec(id)?.[0] ?? "";
12
+ }
13
+ const PROTOCOL_INSTANCE_QUALIFIABLE = [
14
+ "prop:",
15
+ "state:",
16
+ "computed:",
17
+ "element:"
18
+ ];
19
+ const PROTOCOL_PAGE_SPACE_ID_PREFIXES = ["shared:", "storage:"];
20
+ function protocolInstanceQualifies(graphNodeId) {
21
+ const local = graphNodeId.slice(protocolInstancePath(graphNodeId).length);
22
+ if (PROTOCOL_PAGE_SPACE_ID_PREFIXES.some((prefix) => local.startsWith(prefix))) return false;
23
+ return PROTOCOL_INSTANCE_QUALIFIABLE.some((prefix) => local.startsWith(prefix)) ? true : void 0;
24
+ }
25
+ //#endregion
26
+ export { protocolStateVersion as i, protocolInstanceQualifies as n, protocolIslandSegment as r, protocolInstancePath as t };
@@ -0,0 +1,90 @@
1
+ import { CONTAINER_ATTRIBUTE, PROTOCOL_CONTAINER_END_COMMENT, PROTOCOL_CONTAINER_START_COMMENT, PROTOCOL_LOCATOR_PATHS_ATTRIBUTE } from "@markless/web/fns/container-region";
2
+ //#region src/link-attributes.ts
3
+ const LINK_ATTRIBUTE = "data-markless-router-link";
4
+ const REPLACE_ATTRIBUTE = "data-markless-router-replace";
5
+ const SCROLL_ATTRIBUTE = "data-markless-router-scroll";
6
+ const PREFETCH_ATTRIBUTE = "data-markless-router-prefetch";
7
+ //#endregion
8
+ //#region src/route-dom.ts
9
+ const ROUTE_SCRIPT_TYPE = "@markless/core/route";
10
+ const HTML_ATTRIBUTES_SLOT = "__marklessRouterHtmlAttributes";
11
+ function routeMountTarget(document, currentRoot) {
12
+ const root = currentRoot ?? document.querySelector(`script[type="@markless/core/route"]`)?.parentElement;
13
+ if (!root) return document.body;
14
+ const host = [root, root.parentElement].find((element) => element && containerPageEnd(element));
15
+ if (host) return authorElementTarget(document, host);
16
+ return {
17
+ ownerDocument: document,
18
+ replaceChildren(...children) {
19
+ root.replaceWith(...children);
20
+ }
21
+ };
22
+ }
23
+ function containerPageEnd(host) {
24
+ for (let node = host.lastChild; node; node = node.previousSibling) if (node.nodeType === 8 && node.data === PROTOCOL_CONTAINER_END_COMMENT) return node;
25
+ }
26
+ function containerPageStart(host) {
27
+ for (let node = host.firstChild; node; node = node.nextSibling) if (node.nodeType === 8 && node.data === PROTOCOL_CONTAINER_START_COMMENT) return node;
28
+ }
29
+ function authorElementTarget(document, host) {
30
+ const copy = () => {
31
+ const next = host.cloneNode(false);
32
+ next.removeAttribute(CONTAINER_ATTRIBUTE);
33
+ next.removeAttribute(PROTOCOL_LOCATOR_PATHS_ATTRIBUTE);
34
+ return next;
35
+ };
36
+ let holder;
37
+ return {
38
+ ownerDocument: document,
39
+ createHolder: () => holder = copy(),
40
+ replaceChildren(...children) {
41
+ const next = children.length === 1 && children[0] === holder ? holder : copy();
42
+ if (next !== holder) next.append(...children);
43
+ const end = containerPageEnd(host);
44
+ const focused = document.activeElement;
45
+ const start = containerPageStart(host);
46
+ if (start) {
47
+ const before = [];
48
+ for (let node = host.firstChild; node !== start; node = node.nextSibling) before.push(node);
49
+ next.prepend(...before, document.createComment(PROTOCOL_CONTAINER_START_COMMENT));
50
+ }
51
+ next.append(document.createComment(PROTOCOL_CONTAINER_END_COMMENT));
52
+ while (end.nextSibling) next.append(end.nextSibling);
53
+ host.replaceWith(next);
54
+ if (focused && focused !== document.activeElement && next.contains(focused)) focused.focus({ preventScroll: true });
55
+ }
56
+ };
57
+ }
58
+ //#endregion
59
+ //#region src/request-locals.ts
60
+ const REQUEST_LOCALS = Symbol.for("@markless/router/request-locals");
61
+ function table() {
62
+ const scope = globalThis;
63
+ return scope[REQUEST_LOCALS] ??= /* @__PURE__ */ new WeakMap();
64
+ }
65
+ function rememberRequestLocals(request, locals, headers) {
66
+ const known = table().get(request);
67
+ table().set(request, {
68
+ locals,
69
+ headers: headers ?? known?.headers,
70
+ taken: known?.taken
71
+ });
72
+ }
73
+ function requestLocals(request) {
74
+ return table().get(request)?.locals;
75
+ }
76
+ /**
77
+ * The response headers middleware wrote for this request, moved off the server's
78
+ * response: it drops them from a non-2xx page and would repeat them on a 2xx one.
79
+ */
80
+ function takeMiddlewareHeaders(request) {
81
+ const record = table().get(request);
82
+ if (!record?.headers) return void 0;
83
+ if (!record.taken) {
84
+ record.taken = new Headers(record.headers);
85
+ for (const name of Array.from(record.headers.keys())) record.headers.delete(name);
86
+ }
87
+ return record.taken;
88
+ }
89
+ //#endregion
90
+ export { ROUTE_SCRIPT_TYPE as a, PREFETCH_ATTRIBUTE as c, HTML_ATTRIBUTES_SLOT as i, REPLACE_ATTRIBUTE as l, requestLocals as n, routeMountTarget as o, takeMiddlewareHeaders as r, LINK_ATTRIBUTE as s, rememberRequestLocals as t, SCROLL_ATTRIBUTE as u };
@@ -0,0 +1,18 @@
1
+ //#region src/vite/entries/route-href.ts
2
+ const routeParamPattern = /\[\.\.\.([A-Za-z_$][\w$]*)\]|\[([A-Za-z_$][\w$]*)\]/g;
3
+ function __marklessRouteHref(pattern, params) {
4
+ return pattern.replace(routeParamPattern, (_match, catchAll, dynamic) => {
5
+ const name = catchAll || dynamic;
6
+ const value = params?.[name];
7
+ if (catchAll) return encodeCatchAllParam(pattern, value);
8
+ if (value === null || value === void 0) throw new Error(`Typed route error: ${pattern} requires params:\n- ${name}`);
9
+ return encodeURIComponent(String(value));
10
+ });
11
+ }
12
+ function encodeCatchAllParam(pattern, value) {
13
+ const segments = Array.isArray(value) ? value : value === null || value === void 0 ? [] : String(value).split("/");
14
+ if (segments.length === 0 || segments.some((segment) => String(segment) === "")) throw new Error(`Typed route error: ${pattern} requires a non-empty catch-all param.`);
15
+ return segments.map((segment) => encodeURIComponent(String(segment))).join("/");
16
+ }
17
+ //#endregion
18
+ export { __marklessRouteHref as t };